ND-120 Boot Golden Spec — Microcode Execution Reference¶
Full path: Verilog/docs/boot-golden-spec.md
Last updated: 2026-07-03 (stale FPGA-failure notes removed 28-SEP-2026:
the Tang Nano 20K and Nexys 4 DDR boot SINTRAN, so the phase 3 stall below is history)
Ground-truth description of the ND-120 microcode boot flow: what address the CPU executes, in what order, and the per-phase assertions that let tooling detect when the boot does NOT do what it should. Built by cross-checking three sources that agree:
- Observed trace —
MA/CSApath from microcode address 0, extracted from the Verilator run (sim/trace_latch.csv,sim/waveform.fst). - ND-120 microcode listing (version L, what the ROMs hold) —
Code/Microcode/ND-120-DELILAH-L.LISTING.txt(column 2 = octal WCS address). - ND-110 microcode source (clean, ROM-validated) —
$ND_REPOS/ND110Compile/ND110Compile/uCode/ND-110-RASK.uc. Same label names as ND-120; use it to decode OCR ambiguities. NOTE: ND-110 is similar in principle, not identical to ND-120 — trust the trace + ND-120 listing for exact addresses, use ND-110 for semantics/labels.
0. The execution model (READ FIRST)¶
- The microcode address the CPU is executing is
MA_12_0[12:0], produced inCGA_MIC(DELILAH-CPU/CGA_MIC/circuit/CGA_MIC.v, port line 63). - Trace convention: sample
MA_12_0once per microinstruction atposedge MACLK— NOT every sysclk (that oversamples ~68x). This is only a sampling point.MACLKis the micro-address latch strobe: the address latches are transparent while it is high and capture on its falling edge (SIGNALS.md,RETRACTED.md). Do not read the rising edge as the moment a microinstruction "commits". CSA_12_0at board level is the same address:SignalReport.md:178—XMA_12_0 from CGA <= MA_12_0 from CGA.MIC. SoMA(inside CGA) andCSA(board) are the same value; sample whichever is convenient atposedge MACLK.- Address generation path (
mic-calculation.md):MASEL (jmp/ret/next/repeat) -> W[12:0] -> IPOS mux -> MA[12:0]. TheIPOSmux can overlay an opcode dispatch (CD[15:6]), a control-store write (WCA), or a trap vector (TVEC[3:0]), selected byTRAPN/MAPN/EWCAN. - Only genuinely async steer during a clean boot is the RTC interrupt -> trap vector -> microcode octal 16 (see Phase 5). Everything else is deterministic.
Golden trace = the sequence of (MA_12_0 @ posedge MACLK) values, plus the
branch-condition context at each step.
1. Boot phase flow¶
flowchart TD
P0["Phase 0: POR\nMA=000000\nMASTER CLEAR / POWER CLEAR\nAB,MACL -> MACL"]
P1["Phase 1: uCode load\nLCS_n=0\nMA ramps 000000..017777\n~68 sysclk/addr (hardware, not MACLK exec)"]
P2["Phase 2: Init\nMA=002001 MACL:\nseq + calls, jmp 005660/005670"]
P3["Phase 3: Delay loop\nMA=002045/002046 (SEQFS/STRSW)\nALU countdown F=A-Q"]
P4["Phase 4: Self-test\nMA=002116..002123 loop x16\nCOND,LC=0 exit"]
P5["Phase 5: OPCOM ready\nRTC -> MA=000016 PANEL INT\nCOMM,LDLC -> PANEL -> PANVC"]
P0 --> P1 --> P2 --> P3
P3 -->|"ZF=1 at 002046 -> 002047"| P4
P4 --> P5
P5 -->|"RTC every ~8192 sysclk"| P5
classDef start fill:#2196F3,stroke:#1565C0,color:#fff;
classDef proc fill:#009688,stroke:#00695C,color:#fff;
classDef done fill:#4CAF50,stroke:#2E7D32,color:#fff;
classDef special fill:#9C27B0,stroke:#7B1FA2,color:#fff;
classDef hot fill:#FFA726,stroke:#F57C00,color:#000;
class P0 start;
class P1,P2 proc;
class P3 hot;
class P4 done;
class P5 special;
2. Phase-by-phase reference (observed addresses, octal)¶
All addresses verified against the Verilator trace from address 0. Ticks are
sim/trace_latch.csv cycle numbers (sysclk samples) for orientation only.
Phase 0 — Power-on / Master Clear¶
MA=000000=MACL:(MASTER CLEAR / POWER CLEAR). Trap vector 0.- ND-110 ref:
ND-110-RASK.uc:15(0/ % MASTER CLEAR / POWER CLEAR AB,MACL ... MACL;). - Entered on reset deassert (
test_nd120.cpp:188,btn1=trueat cnt=100).
Phase 1 — Microcode (WCS) load¶
LCS_n=0.MA/CSAcounts sequentially 000000 -> 017777 (all 8192 words), ~68 sysclk per address. This is the hardware load state machine (MR_n -> LCS_n,CYC_36/PAL_44403C), not MACLK-driven execution.- After 017777 it wraps to 000000 and
LCS_n -> 1. - Observed: ramp begins ~cycle 16,415; wrap at ~cycle 573,403. (
boot-sequence.md.)
Phase 2 — Initialization¶
- First executed address after load:
MA=002001(MACL:). - Observed path (trace, collapsed):
002001 -> ...seq... -> 002017, jmp005660 -> 005670 -> 002020, then CALLs001006,001020,003707,001021,001163-001165,001022-001027,001112-001116,002173-002201, falling through002042 -> 002044. - This is CPU + MOPC (operator comms) variable init.
Phase 3 — Delay loop (ALU countdown)¶
MA=002045 / 002046(labelsSEQFS/STRSW). ALU countdown:Qpreloaded (e.g. 0x3FFF), each iterationF = A - Q; loop whileZF=0.- Reusable subroutine, called 3x during boot (136 / 6 / ~180,213 iters). The big 3rd call is the ~0.5-1 s power-up delay.
- SHOULD: at
002046, whenZF=1(F reached 0), branch to002047. - Historical: in July 2026 the FPGA build stuck oscillating
002045/002046here and never reached002047. Solved; the Tang and Nexys boot SINTRAN.
Phase 4 — CPU self-test¶
- Exit
002047 -> 003710(util)-> 001035-001037 -> 002050 ... 002115(setup)-> 002116-002123self-test loop. - Self-test loop
002116-002123: iterates ~16 times;COND,LC=0at002123exits when the loop counter reaches 0. (002116area in ND-120 listing lines 6560-6610; error pathSTERR1at002121.) - This loop is self-test TEST 6 (loop counter / shift-right-double via GPR),
a pure CPU-core test. It does NOT touch memory parity - the
IDBS,PEAselect in the002123loop-back word is a don't-care (ALUD,NONE); no self-test subtest exercises memory parity at all. Full evidence:docs/nd120-parity-analysis.md. - Then UART output routines run (self-test result banner).
Phase 5 — OPCOM ready + RTC async¶
- CPU reaches OPCOM (operator communication) ready and waits for a UART command.
- RTC interrupt now fires periodically -> trap vector ->
MA=000016= PANEL INTERRUPT:IDBS,PANEL COMM,LDLC T,JMP -> PANEL -> PANVC(ND-110-RASK.uc:9016/, and lines 234-250; ND-120:Code/Microcode/ND-120-DELILAH-L.LISTING.txtlines 90-96). The trap vector reaches o000016 through theCGA_MIC_IPOSoverride (MA = TVEC), not through MASEL.COMM,LDLC(CSCOMM o17;LDLCN= CSCOMM o17 ANDLCS_n=1, decoded inCGA_DCD.v) loads the loop counter LC fromCD[5:0]at the next MCLK rise; LC then indexes the PANVC jump table at o003760 (L listing lines 10254-10278):0:STOP 1:MS20 2:PRQ 3:SING2 4:LOAD 5:CONT 6:RSTRT 7:MACL. The 20 ms RTC interrupt loads LC = o01, so it runs o003761 -> MS20 (o002333 in DELILAH-L, o002261 in RASK). Path:PANEL(o000050) -> o000051 -> o000052 -> o003761 -> MS20 -> MOPC/MRET1. Verilator shows LC = o01 loaded at the first PANVC dispatch (14-APR-2026); the ND110Compile emulator asserts the same in its unit testPanelInterruptDispatch_TakesPanvcEntry1_MS20(commit 4376d46, both RASK and DELILAH-L). - Observed RTC/PANVC dispatches begin ~cycle 755,233 in the trace.
3. Detection rule — "did it do what it should?"¶
Reduce a run (sim or FPGA) to MA @ posedge MACLK and check against the phases
above. Classify every divergence:
- STRUCTURAL divergence = BUG — same
(MA, branch condition)yields a different nextMAthan the golden spec. Detection examples:- Phase 2: first post-load
MA != 002001, or the002017 -> 005660 -> 005670 -> 002020jump chain is wrong -> IPOS/MASEL address selection broken. - Phase 3:
MAstays in {002045, 002046} beyond the max expected iteration count and never reaches002047-> the July 2026 FPGA stall. Concretely: at002046withZF=1, observed nextMA=002045instead of002047. - Phase 4: self-test loop
002116-002123does not exit after LC reaches 0.
- Phase 2: first post-load
- BENIGN divergence = IGNORE — different delay-loop iteration counts, different RTC dispatch timing/count, different absolute ticks. These follow from clock/RTC phase, not from logic errors.
Minimum signals to log per step for this rule: MA_12_0, MACLK (edge),
LCS_n, ZF (or COND), TVEC_3_0, TRAPN, and an RTC-active flag.
4. How to capture the golden trace (sim)¶
The harness (sim/test_nd120.cpp) already reads top->CSA_12_0 every tick and
already event-logs the delay-loop exit (:204-209). To produce the per-step
golden trace, add a hook that, on posedge MACLK, appends
{tick, MA, ZF, TVEC, TRAPN, rtc} to boot_trace.json. Expose MACLK, ZF,
TVEC, TRAPN, RTC as top-level s_debug_* signals (some already are), or read
them via rootp as latch_ff_compare.cpp does.
Then the FPGA ILA capture (same signals, s_debug_csa = MA) reduces to the
same form, and compare_boot_trace.py applies the Section 3 rule.
5. Source references¶
Verilog/mic-calculation.md—MA_12_0generation (MASEL/W/IPOS).Verilog/cycle_clock.md—MACLK_n/MCLK_ncycle-state timing.Verilog/sim/boot_analysis.md— boot timeline Phases 1-5.Verilog/boot-sequence.md— PROM -> WCS microcode load.Verilog/SignalReport.md— signal cross-ref (CSA_12_0 = MA_12_0).Code/Microcode/ND-120-DELILAH-L.LISTING.txt— ND-120 listing, version L (addresses).$ND_REPOS/ND110Compile/ND110Compile/uCode/ND-110-RASK.uc— clean ND-110 source (label/semantic decode).Verilog/sim/FPGA_DEBUG_RUNBOOK.md— last section: the golden-model comparison method and scripted ILA capture (moved from the retired FPGA-BRINGUP-PLAN.md).