Skip to content

FormerLab/ck37-core

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

21 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

ck37-core

License: CERN OHL-S v2 License: CC BY 4.0 Simulation Synthesis Timing Target Former Lab

A soft-core FPGA replica of the Saab CK37 — the world's first airborne computer built with integrated circuits.

The CK37 was the central navigation and fire control computer of the Saab 37 Viggen, Sweden's Mach 2 multirole fighter. First flown in 1963, it was the first computer in the world to use first-generation integrated circuits in an airborne application. Nearly 200 units were delivered between 1970 and 1978. Some remained in active service until the early 2000s.

This project reconstructs the CK37 as a working FPGA soft-core, assembled from primary sources — including the official FMV maintenance manual Beskrivning CK37, Del 1 (1985-11-01) and Bengt Jiewertz's original design documentation. The goal is a functionally accurate replica: correct instruction set, correct number representation, correct I/O architecture — running original-class navigation software.

The end target is a fixed-wing drone with a CK37 soft-core as its navigation computer. The same algorithms that guided a Viggen at Mach 1.1 and 30 metres AGL are more than sufficient for autonomous flight.


Status

Component Status
CPU core (Verilog) All bugs fixed — 25/25 tests passing
Instruction encoding Confirmed from FMV manual Bild 12/13
Memory model VM/PM/EEPROM/PROM regions
Interrupt system 6-level, vectors from Tabell 3
Clock interrupts 2.864ms + 34.37ms generators
Analogue I/O bridge Memory-mapped, channel table confirmed
Digital I/O Synthesisable (packed arrays, no dynamic range selects)
Assembler Octal, Q1.25, full 26-bit encoding
Navigation kernel 198 words — IRQ4/IRQ5 handlers, PID nav loop
FPGA synthesis Clean — 1,978 LUT4, 12/12 EBR, ECP5-25F
Place and route 85 MHz max (10 MHz target) — 8.5× timing margin
Hardware bring-up Pending OrangeCrab flash

Architecture

The CK37 is a 26-bit fixed-point fractional computer (Q1.25 format), single-accumulator, with a 15-bit address space spanning up to 18432 words across four memory regions.

Machine parameters

Parameter Value Source
Word length 26 bits + 2 parity FMV Del 1, Sida 13
Number format Q1.25 signed fraction, range [−1, +1) Sida 12
Number base Octal (10 octal digits per word) Sida 12
Clock 2.860 MHz Appendix 2
Instructions 48 basic, 4 formats Bild 11–14
Interrupts 6 priority levels Tabell 3
Analogue I/O 32 in + 32 out, sampled every 17ms Sida 16
Digital I/O 450 binary signals Appendix 3
Multiply time 23.8 µs (two-shift method) Appendix 2
MTBF >200 hours flight operation Appendix 3

Memory map (octal addresses)

000000 – 003777   VM   Variable Memory (RAM, read/write)
100000 – 177777   PM   Permanent Memory (ROM, write-protected)
204000 – 314000   EEPROM
400000 – 404000   PROM  (interrupt vectors)

In the 15-bit CPU address space: PM base = 15'h1000 (4096 decimal). PROM vectors at 0x4000 + level*2.

Instruction formats

Four formats confirmed from FMV manual Bild 11–14:

Halvords enadress  [13 bits] pos 00-12
  [0] [opcode 4b] [address 8b]

Helords enadress   [26 bits] pos 00-25
  [opcode 13b, pos 00-12] [address 13b, pos 13-25]

Helordsorder med operand  [26 bits]
  [opcode 10b] [irrel 3b] [operand sign] [operand 12b]

Helords tvåadress  [26 bits] — GOTOPR only
  [opcode 3b] [L address 10b] [M address 13b]
  Stores return address at L, jumps to M

Bit layout: position 00 = MSB = IR[25], position 25 = LSB = IR[0]. IR[25] = 1 distinguishes full-word (26-bit) from half-word (13-bit) instructions.

Interrupt system (Tabell 3)

All addresses octal:

Level Type Store address Jump address
0 Power off 000000 400000
1 Power on 000002 400002
2 Parity / write error 000004 400004
3 Program load 000006 400006
4 Clock 2.864 ms (~350 Hz) 000010 400010
5 Clock 34.37 ms (~29 Hz) 000012 400012

The 2.864ms clock interrupt (8191 cycles at 2.860MHz — exactly 2¹³−1) drives the fast navigation update loop. The 34.37ms interrupt drives position integration and display updates.


Repository Structure

ck37-core/
├── rtl/
│   ├── ck37_cpu.v         CPU core — 26-bit, Q1.25, 48-instruction ISA
│   ├── ck37_mem.v         Memory — VM/PM/EEPROM/PROM, parity-guarded
│   ├── ck37_aio.v         Analogue I/O bridge (32+32 channels, SPI)
│   ├── ck37_dio.v         Digital I/O (450 bits, 35×13-bit words)
│   ├── ck37_clk_irq.v     Clock interrupt generator (IRQ4 + IRQ5)
│   └── ck37_top.v         Integration top-level (memory bus arbitration)
├── syn/
│   ├── ck37_syn_top.v     Synthesis wrapper — OrangeCrab r0.2 (ECP5-25F)
│   ├── ck37_orangecrab.lpf             Pin constraints (LED/UART)
│   └── ck37_orangecrab_unconstrained.lpf   Timing-only variant
├── sim/
│   └── nav_tb.v           Navigation kernel testbench — 25/25 passing
├── asm/
│   ├── ck37asm.py         Assembler — octal, Q1.25, full 26-bit encoding
│   └── gen_labels.py      Regenerates nav/nav_labels.vh from kernel
├── nav/
│   ├── nav_kernel.asm     Navigation kernel (198 words, CK37 assembly)
│   ├── nav_kernel.hex     Assembled ROM image ($readmemh format)
│   ├── nav_kernel.lst     Listing with addresses and encoding
│   └── nav_labels.vh      Auto-generated handler addresses (Verilog include)
├── docs/
│   ├── ISA_v2.md          Full ISA specification with manual corrections
│   ├── confirmed_architecture.md
│   └── instruction_encoding.md
├── Makefile               sim / labels / synth / pnr / bit / flash
├── README.md
└── RUNBOOK_NO_HARDWARE.md

Quick Start

Simulate (25/25 tests)

# Requires iverilog
sudo apt install iverilog

make sim
# Assembles nav_kernel.asm → nav_kernel.hex, then runs nav_tb.v
# Expected: ╔══╗  RESULTS:  25 passed,  0 failed  ╚══╝

Synthesise (Yosys → ECP5)

# Requires: yosys, nextpnr-ecp5, fpga-trellis, dfu-util
sudo apt install yosys nextpnr-ecp5 fpga-trellis dfu-util

make synth   # → build/ck37.json
make pnr     # → build/ck37_routed.config
make bit     # → build/ck37.bit
make flash   # DFU flash to OrangeCrab r0.2

See RUNBOOK_NO_HARDWARE.md for step-by-step instructions with expected output at each stage.

Assemble manually

python3 asm/ck37asm.py nav/nav_kernel.asm --memh -o nav/nav_kernel.hex
python3 asm/gen_labels.py   # regenerates nav/nav_labels.vh

Synthesis Results (ECP5-25F, OrangeCrab r0.2)

Target:   LFE5U-25F-8MG285C (OrangeCrab r0.2)
Tool:     Yosys 0.52 (ABC9) + nextpnr-ecp5 0.6

Resource utilisation:
  LUT4:        1,978  /  24,288   ( 8%)
  TRELLIS_FF:    386  /  24,288   ( 1%)
  DP16KD:         12  /      12   (100% — exactly fits ECP5-25F)
  CCU2C:         264  (carry chains)

Timing (post-route, speed grade 8):
  ck37_clk max:   85.46 MHz   PASS at 10.00 MHz  (8.5× margin)
  clk_48m max:   435.54 MHz   PASS at 48.00 MHz
  7 warnings, 0 errors

Clock divider:   48 MHz ÷ 17 ≈ 2.82 MHz  (target: 2.860 MHz)
Note: integer divider gives 98.7% of target frequency.
      A fractional PLL can hit 2.860 MHz exactly for production.

The BRAM fit is exact — 8192 × 26-bit data memory maps to exactly 12 × 18Kbit EBR blocks. Parity checking is active in simulation (ifndef SYNTHESIS) and stubbed to zero on silicon to free the 13th block.


What's Left Before You Flash

1. Flash via DFU

# Hold BTN0 while plugging USB to enter DFU bootloader
sudo apt install dfu-util
make flash

2. Verify boot on hardware

On power-up the CPU boots at PM_BASE (0x1000), loads gain constants, runs self-test, then enters the IRQ-driven nav loop:

  • led_g (green) heartbeats at ~1 Hz — CPU is running
  • led_r (red) stays off — not halted
  • led_b (blue) stays off — no parity errors

3. UART debug transmitter (optional)

uart_tx is currently held high (idle). Implement a 57600 8N1 transmitter in syn/ck37_syn_top.v to stream the AR register value each IRQ4 cycle.

4. Wire up ADC/DAC SPI

Connect ADS8688A (ADC) and DAC8568 (DAC) to the GPIO pins and restore the LPF constraints. The AIO module (rtl/ck37_aio.v) is already written; it just needs the SPI physical wiring.


CPU Bug History

All bugs found and fixed across the development cycle:

# Module Bug
1 asm .ORG 0100000 overflow → corrected to 010000
2 asm First-pass PC not incremented for .FLOAT/.WORD → all ROM constants resolved to same address
3 asm Short-form (halvord) encoding placed bits in wrong positions → disabled, always full 26-bit
4 cpu GOTOPR not detected — opcodes embedded in IR[25:23], masked by casez fall-through
5 cpu SC+ halvord: value placed in AR[12:0] instead of AR[25:13]
6 cpu S= halvord: symmetric placement error
7 cpu AND/ANDOP/OR/OROP: operated on AR[12:0] instead of AR[25:13]
8 cpu ABSOP: subtracted from lower half instead of upper half
9 cpu C+OP/+OP/-OP/S+/S-/SIGNOP: same upper/lower swap
10 cpu MUL: treated AR as unsigned — negative AR gave wrong product
11 cpu INT_EN reset to 0 — interrupts never fired
12 cpu INT_EN cleared in S_INTR — interrupts fired once then stopped
13 kernel SELF_TEST and SAVE_BOOT at same address (000055) — INDGOTO jumped to PC=0
14 kernel SPD_TARGET never loaded from ROM at boot
15 kernel ONE_HALF never loaded from ROM at boot
16 kernel AU_THR init used SC+ before S=SC+ overwrote AR
17 kernel ERR_HDG/ERR_ALT only computed in IRQ5 — IRQ4 used stale values
18 rtl/dio Unpacked array ports not synthesisable by Yosys → flattened to packed vectors
19 rtl/dio For-loop reset with variable index on packed vector → replaced with direct zero assign
20 rtl/dio bi01_shadow double sequential assignment → merged into single concatenation
21 rtl/dio Double-slice bu_data[N:M][P:Q] → replaced with flat computed slice
22 rtl/mem Parity logic not guarded → 13 EBR needed vs 12 available; guarded with `ifndef SYNTHESIS
23 syn Async set+reset FFs (initial values + async reset) → removed initialisers, reset-only blocks

Navigation Kernel

The nav kernel (nav/nav_kernel.asm, 198 words at PM_BASE=0x1000) implements a full PID navigation loop:

IRQ4 handler (2.864ms fast loop):

  • Reads sensors: PITCH, ROLL, YAW, AIRSPEED, BARO, BEARING
  • Computes heading error (ERR_HDG) and altitude error (ERR_ALT) fresh every cycle
  • Calls ALT_CONTROL, HDG_CONTROL, SPEED_CONTROL via GOTOPR
  • Writes actuator commands: CMD_AIL, CMD_ELEV, CMD_RUD, CMD_THR

IRQ5 handler (34.37ms slow loop):

  • Reads GPS velocity (VEL_N, VEL_E, VEL_D)
  • Integrates position: POS_N, POS_E, POS_ALT += VEL × DT_SLOW

Handler addresses (current assembly):

Label Address (hex) Octal
BOOT 0x1000 010000
IRQ4_HANDLER 0x102F 010057
IRQ5_HANDLER 0x104D 010115
ALT_CONTROL 0x1069 010151
HDG_CONTROL 0x1079 010171
SPEED_CONTROL 0x1089 010211

Primary Sources

  1. FMV "Beskrivning CK37, Del 1" (1985-11-01, M7773-460011) — Official Swedish Air Force maintenance manual, Saab-Scania — Confirms: word length, number format, instruction encoding, memory map, interrupt vectors, I/O architecture, clock timings

  2. Bengt Jiewertz, "Central Computer for aircraft Saab 37, Viggen" — Datasaab Vänner paper, written by the CK37's chief designer — Appendices 2 & 3: prototype specifications, instruction timing, I/O counts — Available: datasaab.se/Papers/Articles/Viggenck37.pdf

  3. "BITS & BYTES — TEMA FLYG, ur Datasaabs historia" (1995, ISBN 91-972464-17) — Datasaabs Vänner, edited by Bengt Jiewertz — Chapters by original design team members

  4. DCS World AJS37 Viggen — CK37 Input Codes cheatsheet (community document) — Reveals the pilot data-entry interface: 6-digit BCD keypad codes, mode structure, ~700 system variables across 30 program blocks


The Drone

The CK37 was designed to replace a human navigator in a single-seat aircraft. Its entire function was sensor fusion, dead-reckoning navigation, and weapon delivery computation — which maps directly onto autonomous drone flight:

Viggen system Drone equivalent
INS platform (attitude) BNO085 IMU
Air data (IAS, altitude) Pitot + baro
Radio altimeter Lidar/radar altimeter
Attack radar (range) GPS range to waypoint
HUD output Telemetry downlink
Weapon release Payload trigger
Pilot data panel GCS command uplink

Contributing

This project is part of Former Lab — a sovereignty-focused hardware and software lab building across embedded systems, RF, and autonomous platforms.

The immediate priorities are:

  • Hardware bring-up — OrangeCrab flash, UART debug tap
  • ADC/DAC wiring — connect ADS8688A + DAC8568 to the AIO module
  • Del 3 schematics — the CEM circuit schematic volumes (Flik 4–5) for board-accurate replica
  • Kanaldata pages — sida 35–40 for electrical I/O specifications

If you have access to CK37 programming documentation or Del 3 schematics, please open an issue.


Licence


Former Lab — Sweden

About

A soft-core FPGA replica of the Saab CK37 — the world's first IC-based airborne computer, rebuilt from the original 1963 FMV maintenance manual and running a PID navigation loop in the original Q1.25 assembly. Target: OrangeCrab r0.2 (ECP5-25F)

Resources

Stars

Watchers

Forks

Contributors