Black-box fuzzing pipeline for Altera Cyclone IV EP4CE6F17C8 bitstream (.rbf) reverse engineering. Goal: complete CRAM bit dictionary + open-source toolchain (Yosys → nextpnr → FASM → RBF).
Historical narrative (dated mining events, HW-flash results, closed experiments) lives in ~/.claude/projects/-home-test-EP4CE6/memory/. This file is the operational reference only — if you want "when / why / how did this land", consult MEMORY.md.
export PATH=$PATH:$HOME/intelFPGA_lite/21.1/quartus/bin
cd fuzz
python3 runner.py baseline # generate zero baseline
python3 runner.py --node lut_inst lut_single 10 10 0 # fuzz LUT at (10,10,0)
python3 runner.py n_sweep 10 10 # calibrate 16 minterms
python3 analyze.py read_tt design.rbf zero.rbf 10 10 0
python3 analyze.py write_tt zero.rbf 0x8888 output.rbf 10 10 0
python3 fuzz/test_green_zone_harden.py # 24 islands, 731/731 bit-perfect
# ζ production pipeline (one-shot; preferred entry point):
python3 scripts/bit_workaround/zeta_pipeline.py gold.rbf # round-trip + byte-identity gate
python3 scripts/bit_workaround/zeta_pipeline.py design.qpf --flash \
--uart-seconds 10 --baud 19200 --expect "NEORV32" # end-to-end w/ HW
python3 scripts/bit_workaround/zeta_selftest.py # sub-second CI smoke test
python3 scripts/bit_workaround/zeta_regression.py # full corpus regression (~1.5s)
python3 scripts/bit_workaround/zeta_rbf_diff.py A.rbf B.rbf --top-frames 10 # region-aware diff
python3 scripts/bit_workaround/zeta_manifest_diff.py A.manifest.json B.manifest.json # build-vs-build drift
# Pre-commit hook (opt-in per clone): git config core.hooksPath .githooks
# Raw two-step form (use when you want the BIT FASM as an inspectable intermediate):
python3 scripts/bit_workaround/quartus_gold_to_bit_fasm.py gold.rbf design.bit.fasm
python3 fuzz/fasm2rbf.py design.bit.fasm results/rbf/nv_zero_global.rbf rebuilt.rbf
python3 scripts/uart_observe.py --baud 19200 --seconds 30 # --baud required, no default
# Sig-cache + audit / decomposition tooling (2026-05-04 session):
python3 scripts/sigcache_remine/mine_x4_cross_lab_route.py --src 4,17,14 --dst 4,4,0 --port dataa
# Single-edge sig-cache miner (Plan D' factory pipeline); writes
# nv_route_cells.json + route_cells_full.json. --dry-run to preview.
bash scripts/sigcache_remine/batch_x4_crosslab_multiport.sh
# Batch driver: 72 X=4 cross-LAB R4 entries (Y4 multi-port + Y17
# multi-N). ~13 min Quartus wall. Re-runnable; cached pair RBFs
# reused. Clone for other X columns by editing the spec loops.
python3 scripts/iob_slice_mining/audit_clk_pin_pad_nv_overlap.py --save
# Per-pin IOB_CLK_INPUT × IOB_PAD_NV overlap audit (12 pins).
python3 scripts/cross_lab/probe2_shim_decompose.py --save --verify
# Decompose 37-cell X33Y4_PROBE2_INFRA shim into ADD/OVER buckets.
python3 scripts/cross_lab/analyze_pl_corpus.py --save
# 7-variant 1-LE TT corpus intersection vs probe2 shim ADDITIVE.
python3 fuzz/nv_sig_cache_merge.py
# Merge nv_route_cells.json + legacy → route_cells_full.json.
python3 scripts/iob_slice_mining/mine_padnv_x4y4n0.py
# IOB→slice sig-cache miner for X4Y4N0 (P1/P2 pragma-driven,
# Track B1 2026-05-21). Builds Quartus single_pin (mask=0xAAAA
# bypass) + two_pin (mask=0x8888 canon_2input 'a&b') golds, derives
# IOB_E16->4,4,0,dataa + IOB_M16->4,4,0,datab via padnv algebra;
# Step 5 verification asserts 0 byte diffs vs both Quartus golds.
# Re-runnable (idempotent); rollback automatic on FAIL.
python3 scripts/sigcache_validation/build_quartus_xlab.py --le-a-mask 0x4444
# Build Quartus cross-LAB gold (LE_A @ X4Y4N0, LE_B @ X4Y21N0) as
# the byte-identity reference for build_test --canon-2input absolute
# output. ~60-90 s wall. Pre-flash audit gate.fuzz/— fuzzing pipeline (~96 Python modules):config.py(constants),bitstream.py(LutCodec/RouteCodec/FFCodec/CRC),fasm2rbf.py/rbf2fasm.py(FASM toolchain),route_synth.py(green-island synthesis),route_signatures.py(sig-cache backend),chipdb_gen.py(nextpnr chipdb),runner.py/compile.py/analyze.py(orchestration)synth/— open-source toolchain:ep4ce6_map.v+prims.v(Yosys techmap),synth_ep4ce6.ys+synth_ep4ce6.sh(run the.shwrapper — it envsubst's$HOME/$NEORV32_ROOT),np2fasm.py(nextpnr JSON → FASM)scripts/— one-off investigation scripts kept for reproducibilitytmp/— local scratch only, gitignored. Do NOT use/tmp/; use this dir instead. Promote scripts out oftmp/toscripts/the moment they're cited from docs or memory.jailbreak/— CE10 fitter probes (CE6≡CE10 same die, +65% fabric unlocked)results/—rbf/(~2500 files),route_cells_full.json(sig-cache; gitignored, regenerate viaroute_signatures.build(); legacy fallbackroute_cells.json= 1,725 entries),nv_route_cells.json(canonical Plan D' source for the merger; survivesnv_sig_cache_merge.pyre-runs),r4_iindex_table.json,ep4ce6_bitdb.sqlite,fingerprint_*.json,sigma_inv_fb8_groups.json(2,112-entry σ⁻¹ 3-key table),nv_baseline_pack.json,m9k_mode_bits.json,iob_clk_input_pad_nv_audit.json(per-pin overlap risk for IOB_CLK_INPUT × IOB_PAD_NV combination),x33y4_probe2_shim_decomposition.json(sidecar for the 37-cell 1-LE shim),x33y4_pl_corpus_analysis.json(7-variant 1-LE TT corpus analysis)
RBF: 368,011 bytes fixed. Preamble 32B 0xFF | Config 367,920B (1752 frames × 210B) | Postamble 59B 0xFF.
CRAM geometry: LAB column step = 7,350 bytes. Pair spacing = 210 bytes. Ctrl→Data offset = 48 bytes. CRC bytes at offset 208/209 per frame.
Y-address (universal across all switch types):
slot = (y - 2) % 3; group = (y - 2) // 3
bp = (6 - group) if slot == 2 else (7 - group)CE6 whitelist: LAB_X=[3,4,6,7,8,10,11,12,13,16,17,18,19,21,22,23,24,25,26,28,29,31], LAB_Y=[2..14,16..19,21], N=[0,2,4,...,30]. 392 LABs / 6,272 LEs.
True silicon (CE10 jailbreak, HW-verified): LAB_X adds {5,9,14,30,32,33} → 28 cols. LAB_Y adds {15} → 20 rows. NON_LAB_X = {15,27} (M9K), {20} (mult). 520+ LABs / 10,320 LEs (+65%).
| Codec | Key methods | Notes |
|---|---|---|
LutCodec |
write_tt(base_rbf, mask, *, canon_from='a', canon_to=None, neg_from=False, neg_to=None, bypass=False, canon_2input=None) → RBF, read_tt(rbf, zero, *, bypass_aware=False), from_cram_model(x,y,n), predict_sram(mask) |
XOR-delta, not absolute (mask = target ^ base_tt). σ⁻¹ permutation lookup via (foff, fb8, group) 3-key, 5-level fallback (results/sigma_inv_fb8_groups.json, 2112 entries). σ⁻¹ valid for symmetric masks at LE_0; 2-input asymmetric closed at X4Y4N0 via P2 absolute table — see Pitfall #16. P1 (1-input): Phase 3 canon-cell layer landed 2026-05-13 (35-cell global table, shared config, 0 lab_cram) + Phase 4 bypass=True skips SRAM emit for 8 BYPASS_1INPUT_MASKS ∪ {0x0000, 0xFFFF}. P2 (2-input): canon_2input='a&b'.. kwarg (mutex w/ canon_to) XOR-applies absolute cells from CANON_2INPUT_ABSOLUTE[(x,y,n)][label] (~240 cells/label; 24 labels: 18 perm AND/OR/XOR + 6 neg). Position scope: X4Y4N0 only; raise KeyError elsewhere. Helpers: lut_input_dependence, is_bypass_mask, canon_axis_diff, canon_apply_transition, canon_classify_transition, canon_2input_label_for_mask, canon_2input_apply, canon_2input_cells, canon_2input_unique_cells, canon_2input_unique_apply (P5c wire4-baseline-subtracted parallel sidecar CANON_2INPUT_UNIQUE from canon_2input_codec_table_unique.json; opt-in via canon_2input_unique_aware=1 pragma + P5c apply-time codec-overlap filter + P5d per-LAB LI envelope filter via li_lab_for_offset — P5d SILICON-FAILED for cross-LAB 2026-05-21, do not enable canon_2input_unique_aware for cross-LAB builds; see Pitfall #16 P5d row). Silicon-validated: canon_na via direct apply (mask=0x5555); canon_b via bitgen(bypass_aware=True) (mask=0xCCCC); canon_naandb via bitgen(canon_2input_aware=True) (mask=0x4444 = !KEY1&KEY2, 2026-05-13). Legacy defaults byte-identical. Memos: phase3_canon_cell_codec_landed_2026_05_13, phase4_bypass_tt_model_2026_05_13, p1_bypass_aware_silicon_validated_2026_05_13, p2_canon_2input_silicon_validated_2026_05_13, p5b_canon_unique_landed_2026_05_15, p5c_canon_unique_safety_blocked_2026_05_15. |
RouteCodec |
read_c4/r4/r24/local_interconnect(), apply_routing(zero_data, switches) |
Round-trip verified. LI writes take explicit pairs, NOT i_idx |
FFCodec |
write_arst/write_ena() — 61 absolute offsets each |
FASM DFF.ARST/DFF.ENA disabled (header-band noise) |
patch_rbf_crc(rbf) |
Recomputes CRC-16 for frames 25..1751 | Mandatory before flashing any modified RBF |
CRC spec: CRC-16/IBM, poly 0x8005 (reflected 0xA001), init 0xFE54, frames 25..1751 (208 data + 2 CRC per frame). Frames 0..24 = header, do NOT touch.
SAFETY: validate_safe_for_hardware(rbf, zero) checks LI MUX envelopes (P0 anchor / P8 tail / mode = paired/alternating/driver*/edge_*). Recognized modes include driver_single ({8: [single_base]}) silicon-validated against Quartus gold (memory step_3_substantially_closed_2026_05_02). Always call before flashing — but validator scope is LI MUX class only; it does NOT check header band consistency, non-LAB-column writes, GCLK distribution completeness, or output-buffer drive contention (memory path_alpha_led_blink_silicon_failed_2026_05_02). For silicon-functional validation, also cross-check vs Quartus gold cell set or use ζ pipeline. LI-MUX composition gate (fuzz/li_mux_conflict_gate.py, gold-anchored): when a build combines a LUT/canon directive with routing directives (ROUTE/IOB_ROUTE/SRC) at the same LE, those LI-MUX cells are co-determined by XOR composition and validate_safe_for_hardware is BLIND to the result. li_mux_gate(fasm, base, composed_rbf=, gold_rbf=) FAILs on gold byte-identity mismatch at the co-determined cells and refuses to certify when no gold is supplied — this is the under-driven-LE_A class that silicon-killed P5d (Track D 2026-05-28). No gold-free local authority exists (the correct LI value is global-routing-dependent — Quartus's own golds disagree at the same (off,bp) across designs). Memory: track_d_xlab_codec_quartus_forensic_2026_05_28.
| Directive | Example | Status |
|---|---|---|
LUT |
X10Y10N0.LUT = 0x8888 |
OK — auto-compensates XOR base via minterm_0 |
ROUTE |
ROUTE X10Y10 -> X10Y12N4.datab |
OK — sig-cache lookup (6 or 7-tuple) |
ROUTE (sn>0) |
ROUTE X5Y3N4 -> X4Y3N6.datad |
OK — 7-tuple key |
GCLK |
GCLK |
OK — 17 position-independent cells (legacy local-clock, not real GCLK_BUS) |
GCLK_PIN |
GCLK_PIN PIN_E1 |
OK — per-pin XOR activate set. 12 pins mined on F17 (E1/R8/N1 + 9 dedicated clock pins). Mining: fuzz/clk_pin_autoforce_probe.py --pin {PIN}. Data: results/clk_cross_pin_spine_check.json. See memory clk_pins_full_f17_coverage.md. |
LAB_CLK_SEL |
LAB_CLK_SEL X10Y4 |
OK — per-LAB CLK_SEL N-invariant layer (XOR). 13 LABs mined. Re-mine: fuzz/clk_lab_sel_probe.py --lab X,Y. |
LAB_CLK_SEL_LE |
LAB_CLK_SEL_LE X10Y4N0 |
OK — per-LE CLK_SEL layer (XOR, disjoint from LAB_CLK_SEL). Functionally required (N-invariant alone = LED-always-off). N∈{0,2,4,6,8} mined for all 14 LABs. Data: results/clk_lab_sel_per_le.json. Extend via clk_lab_sel_n2_batch.py after editing N_SLOTS in clk_lab_sel_probe.py. |
DFF |
X10Y10N0.DFF |
NO-OP — DFF is silicon default (no CRAM cells) |
BIT |
BIT offset bp |
OK — raw cell flip |
SRC |
SRC X10Y10 |
OK — per-source overhead |
LUT_ARITH |
X4Y18N0.LUT_ARITH = 0x0000 |
v4 position-independent blob (100 SETs + 4 CLEARs); per-width table results/arith_blockband_by_width.json (widths 2..16). |
LUT_ARITH_MULTI_LAB |
LUT_ARITH_MULTI_LAB WIDTH=17 |
Widths 17..32 multi-LAB chain. Silicon-validated W=17/W=23 at LAB(4,18)+(4,17). Each entry has set (XOR) + clear (AND-clear, dedup-override). np2fasm not wired (Yosys $alu caps at single-LAB). Memory: multi_lab_carry_silicon_validated_2026_05_03. |
IOB_IN/IOB_OUT |
IOB_IN PIN_M16 |
Single-axis 44/44 bit-perfect. For G15-output use IOB_PAD_NV + OUTROUTE_G15. |
IOB_ROUTE |
IOB_ROUTE PIN_E16 -> X16Y4N0.dataa |
Two apply-paths: default live (padnv_cells > absolute_cells; absolute_cells get dedup + off<5282 hdr-skip, padnv_cells skip both); legacy (bitgen(legacy_iob_route=True) for simple_led single-LE). np2fasm pragma # fasm2rbf: legacy_iob_route=1. Apply order: post-all_luts before patch_crc (Track B1 reorder 2026-05-21) — XOR composition with OUTROUTE_G15 + LUT Phase 1 now correctly produces gold byte at LI-aliased cells. Mine X4Y4N0: mine_padnv_x4y4n0.py (P1/P2 pragma-driven, σ⁻¹ contamination bypassed; E16→dataa + M16→datab landed 2026-05-21). Memory: iob_route_hdr_skip_pivot_2026_05_11, gamma_iob_x4y4_padnv_mining_2026_05_21. |
IOB_BASELINE_NV |
IOB_BASELINE_NV |
129-cell hdr-band bridge. SUPERSEDED by IOB_PAD_NV. |
IOB_PAD_NV |
IOB_PAD_NV |
139-cell E16+M16-in + G15-out infra (delta vs nv_zero_global). + 74-cell iob_pad_arith_ext_cells when LUT_ARITH* present (carry G15 designs, W=17/W=23 silicon-validated). np2fasm auto-emits for SLICE→G15. Data: results/output_route_nv_mining.json. |
OUTROUTE_G15 |
OUTROUTE_G15 X10Y10N0 |
Position-specific SLICE→G15 (38-67 cells, 33 positions mined). Data: results/output_route_sigcache.json. |
IOB_CLK_INPUT |
IOB_CLK_INPUT PIN_E1 |
12 pins F17. Extensible: scripts/iob_slice_mining/compute_clk_pin_hdr.py --build --pin X. Calibrated vs IOB_BASELINE_NV — overlap with IOB_PAD_NV double-flips; PIN_E1 corrected by IOB_RESERVE_PIN_M16 Group B; other 11 pins latent mismatch (runtime warning emitted). Audit: audit_clk_pin_pad_nv_overlap.py. |
IOB_RESERVE_PIN_M16 |
IOB_RESERVE_PIN_M16 |
66-cell XOR for Quartus default RESERVE_ALL_UNUSED=tri-state+pull-up when M16 unused. Groups A/B/C hdr (24+9+9) + D fabric (24). np2fasm auto-emit: use_pad_nv AND PIN_M16∉IOBs AND not has_carry. Memory: probe2_open_software_bisect_2026_05_03. |
X33Y4_PROBE2_INFRA (auto) |
implicit shim | Auto-applied at 1-LE @ SLICE_X33_Y4_N4. 37-cell override (33 ADD + 4 OVER-CANCEL). Disjoint from cl_and 2-LE shim. Data: results/x33y4_probe2_infra.json + decomposition sidecar. Memory: probe2_open_software_bisect_2026_05_03. |
NV_BASELINE_PACK |
NV_BASELINE_PACK |
Meta directive (21,640 XOR cells) reproducing nv_zero_global.rbf on top of make_pure_zero_rbf(). np2fasm --base pure. HW equivalence not confirmed — keep nv_zero_global.rbf as default. |
DFF.ARST/ENA |
— | DISABLED — header-band noise unresolved. |
M9K.INIT_{w}x{d} |
X15Y10N0.INIT_9x512 = ... |
9×512 (33 sites) + 18×512 (5 sites) via linear formula in m9k_init_basis.write_init. SDP 4×2048 / SP 9×1024 / SP 36×256 use dedicated codecs at X15_Y10_N0 only (linear formula WRONG, flagged). SP 9×512 cross-site silicon-validated at 8 sites. Memory: sp_9x512_cross_site_silicon_hw_validated_2026_04_28, sp_9x1024_36x256_codec_silicon_hw_validated_2026_04_28. |
M9K_MODE_{w}x{d}[_template] |
X15Y10N0.M9K_MODE_9x512_m9k_blink_diff_nv |
Per-site M9K mode bits. Template _m9k_blink_diff_nv silicon-validated (solo/blink-class). For multi-M9K NEORV32-class, use DESIGN_BLOCK_BAND_PACK instead. Memory: option_3_residual_silicon_validated_2026_05_01. |
DESIGN_BLOCK_BAND_PACK |
DESIGN_BLOCK_BAND_PACK neorv32 |
Silicon-validated 2026-05-01. XOR-applies full block-band cell set per-design tag. Mine: mine_design_block_band.py --tag X --rbf X.rbf. Tags mined: neorv32 (417), m9k_23_clone (16), arbiter_2m9k (11), visible_2m9k (12). |
FASM footgun: SRC overhead + ROUTE sig cells MUST union before XOR-flip (double-flip cancels shared cells).
All switch types use the same Y-address (slot/group/bp). Key differences:
| Type | Column | Byte model | Coverage |
|---|---|---|---|
| C4 I=0 | self (LAB_CRAM_END) | slot-dependent SLOT_BASE + 3×group | 63 wires, 0 false pred |
| C4 I≠0 | self | fixed-byte per (X,I), 44 mappings | lookup table |
| R4 | prev LAB col | R4_BASE_PREV[I] + slot offset | 24/37 I-indices, 60-97% |
| R24 I=0 | prev LAB col | fixed byte (no slot/group adj) | 66% accuracy |
| LOCAL_INTERCONNECT | self col | base 70 + pair×210 + SLOT_OFFSET | 70% cross-val, 22 cols |
Sig-cache (route_cells_full.json, 13,562 entries) short-circuits all formula paths for production routing. Formulas are fallback only.
⚠️ WRITE-path status (verified 2026-05-28 vsbitstream.pyDEPRECATED 2026-04-08comments): the accuracy figures above are READ-path only. C4 I≠0 (_C4_FIXED_OFFSETS, 44/44), R24 (_R24_FIXED_OFFSETS, 56/56), and FF ENA/ARST (_FF_ENA_CELLS, 168/168) write tables land entirely on frame CRC bytes → overwritten bypatch_rbf_crc→ emit NOTHING (dead code, need re-mining). R4 = 26/37 I-indices emittable. C4 I=0 + LI are the only broadly-emittable formula classes. This is the gating data gap for any real-routing-graph work — seerouting_model_scoping_2026_05_28.
synth_route(zero, src, dst) → bit-perfect RBF. Sig-cache path serves all CE6-standard green-zone + Plan D' factory routes. CRAM is interleaved (NOT topologically isomorphic to layout).
Current harness score: 731/731 bit-perfect across all 24 islands (CE6 standard 686/686 + jailbreak/edge 45/45). synth_route does a snapshot lookup (_snapshot_ops_if_present) BEFORE parse_need, so jailbreak/edge LABs hit the exact Quartus cell set from the fingerprint snapshot. See memory harness_731_closure.md.
15 CE6 green islands: (4,4), (10,4), (10,10), (10,14), (13,10), (16,4), (16,8), (16,14), (19,14), (22,12), (22,16), (25,6), (28,10), (28,18), (31,12). 9 jailbreak/edge islands: Y=15 × {10,11,12,13,14,17,18}, Y=5 × {18,19}.
LOC syntax (hierarchical MegaFunction path, NOT coordinate alias):
- DSPMULT:
-to "lpm_mult:u|mult_qpl:auto_generated|mac_mult1", 42 sites (Y1..21 × N{0,1}) - M9K:
-to "u"(short form), X∈{15,27}, 126 sites
Two CRAM bands: block enable/mode (frames 1692-1738) + clock-net (~1007-1013).
M9K init codec: byte(word,bit) = anchor + (word//2)*210 - (word%2) - 2*bit, bp=6. 33 anchors calibrated.
Rules: NEVER mine non-LAB with VIRTUAL_PIN. Always filter CRAM-only (off ≥ 5282) — header band has 4-5 bit/seed noise floor.
Target: Verilog → Yosys → nextpnr-generic → np2fasm → fasm2rbf → openFPGALoader
Completed: chipdb_gen.py (12,131 bels, 90,261 wires, 3.66M pips), Yosys techmap (LUT4+DFF+$alu→CE6_CARRY via alumacc), np2fasm.py (emits IOB_PAD_NV / IOB_ROUTE / OUTROUTE_G15 / GCLK_PIN / LAB_CLK_SEL{_LE} / IOB_CLK_INPUT / M9K.INIT / M9K_MODE / DESIGN_BLOCK_BAND_PACK / LUT_ARITH), fasm2rbf all directives.
HW-validated: registered AND @X16Y4N0, 5-bit carry @LAB(16,4), 2-LAB cross-LAB AND→DFF, 2-M9K visible-blink (DESIGN_BLOCK_BAND_PACK visible_2m9k). W=23 byte-identical zero-Quartus (build_open23.py, md5 905dfc85, 5.96 Hz blink). Memory: pipeline_test_e2e_status, two_lab_gold_validated, option_3_residual_silicon_validated_2026_05_01, zero_quartus_arith_byte_identity_2026_05_04.
nextpnr: source $HOME/opt/oss-cad-suite/environment, --router router2 (router1 can't multi-hop), --pre-pack via chipdb guard.
Step 3 SAFETY closure (2026-05-02): 22→0 LI MUX UNSAFE via LI-snapshot lockdown (defeats σ⁻¹ over-claim) + driver_single validator mode + nojb sidecar. Memory: step_3_substantially_closed_2026_05_02.
Step 4 gap (pending): pipeline_test/led_blink open builds flash clean but LED stuck — 494 OPEN-only OTHER (NV_BASELINE_PACK over-emit), 64 GOLD-only HEADER, 270 GOLD-only BLOCK_BAND (GCLK column distribution unmined). Memory: path_alpha_led_blink_silicon_failed_2026_05_02.
Arithmetic mode activation lives in the block band (frames 1692-1738, bp=2), NOT in LAB CRAM columns. The LUT_ARITH FASM directive applies a per-LAB blob of ~100 cells.
Pieces landed:
chipdb_gen.py— 8,126cout→cindirect pips between adjacent LE belssynth/ep4ce6_map.v+synth/prims.v—$alu→ per-bit CE6_CARRY chain (LE-internal feedback, no Route-A buffers)synth/np2fasm.py— CE6_CARRY chain walker, emitsLUT_ARITH = 0x0000+DFF, skips intra-LE ROUTEfuzz/fasm2rbf.py—LUT_ARITHdirective loads arith block-band blobfuzz/prepack_carry.py— BEL pinning up to 16-bit chain (ALL_NS=16), skips nextpnr
Key facts:
- Arith blob is per-LAB, not per-LE (100 cells regardless of which LEs use arith)
- Arith blob is position-independent (v4 triangle test) and per-WIDTH, not per-N-slot
- LUT SRAM = 0x0000 for standard +1 counter (function encoded in block-band)
- Quartus carry counter has ZERO external route cells — DFF→carry feedback is LE-internal
- 4 CLEAR cells are constant across all single-LAB widths (universal LAB arith-enable reset)
History: memory phase54_first_flash_lab418.md, carry_chain_synth_landed.md.
Two reachable paths from Verilog/VHDL to AX301 silicon:
- Native:
.v → Yosys → nextpnr-generic → np2fasm → fasm2rbf → flash. HW-verified for small/medium designs. Blocked on chipdb routing-model density at NEORV32 scale. - ζ escape hatch:
.v → Quartus → scripts/bit_workaround/quartus_gold_to_bit_fasm.py → fasm2rbf → flash. Diffs Quartus gold RBF againstresults/rbf/nv_zero_global.rbf, emits oneBITdirective per differing bit. Rebuilt RBF is byte-identical to Quartus gold; total wall time ≈ 0.5 s regardless of design density.
ζ production pipeline (canonical entry point): scripts/bit_workaround/zeta_pipeline.py wraps raw ζ + fasm2rbf + byte-identity gate + optional flash + optional UART verify. Emits sidecar <stem>.manifest.json (SHA256 + region cell counts + git HEAD) consumed by zeta_manifest_diff.py. Supports --rebuild-check on .qpf input to catch non-deterministic Quartus builds. Companions: zeta_selftest.py (sub-second pre-commit gate, git config core.hooksPath .githooks), zeta_regression.py (corpus walker over tests/zeta_corpus/manifest.json, --reanchor[-all] to update), zeta_rbf_diff.py (region-aware diff — use instead of cmp -l), zeta_manifest_diff.py (manifest-only build-vs-build drift).
HW-validated designs via ζ: NEORV32 bootloader (full 4712 LE / 19 M9K) on AX301 at 19200-8N1 UART; Linux 6.6.83 RISC-V boot via xmodem. See memory neorv32_zeta_hw_validated.md, path1_bit_workaround_validated.md.
When to use which: native for small/medium single/cross-LAB; ζ for NEORV32-class (>1000 LE) or anything Quartus can build but nextpnr can't route.
- Quartus 21.1 Lite:
$HOME/intelFPGA_lite/21.1/quartus/bin/ - RBF generation:
quartus_cpf -c -o bitstream_compression=off(NEVERsof2rbf.py) - Programming:
$HOME/see_neorv32_run_linux/tools/openFPGALoader/build/openFPGALoader -c usb-blaster - Hardware: AX301 board, EP4CE6F17C8, USB-Blaster JTAG
- Pin map: KEY1=E15, KEY2=E16, KEY3=M16, KEY4=M15, LED0=G15 (active-high; keys active-low)
- UART capture:
scripts/uart_observe.py(pyserial; default/dev/ttyUSB0, specify--baud— no default) - NEORV32 Linux host flow:
~/see_neorv32_run_linux/host/boot_linux.py --rbf <file.rbf>drives stage2 upload + xmodem of kernel/DTB/initramfs
Code: GPL-3.0-or-later (all .py/.v/.tcl must have SPDX header). Docs: CC BY-SA 4.0.
- Left-edge columns (X=3,4,6,7) have CRAM addresses below 0x10000
- Quartus fit reports: non-UTF8 bytes — use
errors="replace" - Don't share
work/across parallel campaigns sof2rbf.pyproduces invalid bitstreams — alwaysquartus_cpfLutCodec.write_ttis XOR-delta, not absolute mask- LI
write_local_interconnect()takes explicit pairs — auto-expansion is physically dangerous - Disk: Phase 3 needs work-dir cleanup (
compile.clean_work_dir()) or in-memory diff - Always cross-check codec output against Quartus's own build of the same Verilog before chasing low-level bugs. If your open-toolchain build of design D doesn't behave as expected, build D in Quartus, flash it, and diff the two RBFs before patching the codec.
- Self-loop sig-cache entries are topologically irrelevant (src==dst).
route_cells_full.jsonnow has 2,760 self-loop entries (not 61). The old "bloated noise, 90-754 cells" claim is FALSE/STALE (verified 2026-05-28: max self-loop 156 cells, median 131 — same as cross-entries' median 131, max 159). Self-loops aren't bloated; they're just useless as inter-LAB routes (source==dest). Drop them from any routing graph; avoid at synthesis level. - DFF has no per-LE CRAM enable cell. Cyclone IV's flip-flop is intrinsic.
dff_cells_mined.jsonis bogus. The FASMDFFdirective is a parsed no-op. - Preamble-offset bug in CRC detection: RBF has a 32-byte preamble before CRAM data. CRC position detection must use
(off - 32) % 210 >= 208, NOToff % 210 >= 208. Header CRC cells (frames 0-24) are mandatory in directive sets — excluding them causes board reset on flash. - Cross-LAB ROUTE without sig-cache is silicon-hostile (silicon-validated 2026-05-04). When np2fasm warns
no sig-cache (cross-LAB): ..., the formula path emits cells at structurally wrong CRAM offsets → corrupts config-controller-validated cells → FPGA reset on flash (NOT just functional incorrectness).validate_safe_for_hardwaredoes NOT detect this class.scripts/led_blink/build_open.pyhas a REFUSE-TO-BUILD guard scanning np2fasm warnings; future build_open scripts should follow the same pattern. Mining tool:scripts/sigcache_remine/mine_x4_cross_lab_route.py(single-edge sig-cache miner via Plan D' factory pipeline). Memory:d_i_silicon_failed_2026_05_04.md. - Phase 3 LI MUX std_lut TT collision — FIXED 2026-05-05 (silicon-validated for LE_0 symmetric masks). Phase 3 restored 18 LI MUX cells; only 2/18 are LI MUX, the other 16 are real TT. Fix: Phase 3 builds
std_lut_tt_cellsand skips those(off, bp)keys inli_locked_state. Closure scope: LE_0 symmetric masks (0x6996/0x9669); asymmetric masks at X=4 remain broken (Pitfall #16). Regression:fuzz/test_phase3_li_mux_lut_tt.py. Memory:phase3_fix_silicon_validated_2026_05_05. - Mined cross-LAB R4 sig-cache entries are context-dependent (silicon-validated 2026-05-04). Diff-vs-nv_zero mining captures Quartus router decision; in dense runtime context (24-LE chain) chain consumes resources the mined route assumed free. Codec-level evidence does NOT prove silicon function. Memory:
d_i_silicon_two_failures_2026_05_04. - Mining script
n // 2convention bug (audit 2026-05-08, ext 2026-05-10). Runtime convention:n = chipdb SLICE_N = Quartus LCCOMB N = 2 × LE_index. Buggy scripts (mine_intralab_route_x4y17n14_to_n16.py,mine_real_tt.py, sweep_outroute_nv.py) usedloc=LCCOMB_X{x}_Y{y}_N{n//2}. Fixed; σ⁻¹ analysis scripts usek = n // 2as LE_idx array index — not buggy. Audit before any new mining. Memory:conv_bug_discovery_2026_05_08,gamma_x4_crosslab_batch1_2026_05_10. - γ X=4 cross-LAB — 1-input bypass + 2-input canon BOTH CLOSED 2026-05-13 at X4Y4N0.
- Bug #1 (
df335d8) — IOB+CLK infra baked into ROUTE diff; double-XOR with runtime IOB_PAD_NV/LAB_CLK_SEL_LE. Fix:scripts/sigcache_remine/strip_iob_overlap.py --apply --column X(idempotent; re-run afternv_sig_cache_merge.py). 96/109 X=4 entries; same class at X=10/16/22/25/28/31. - Bug #2/#3 (FIXED,
03b91c6+93f9cb9) — Phase 3 IOB_PAD_NV/OUTROUTE_G15 skip (X33Y4 exempt); padnv-bucket IOB_ROUTE hdr-skip gating. Silicon md5s preserved (W=23905dfc85, probe2d4073d2e, cross_lab_x3347ad9ed9). - σ⁻¹ canon-cell layer 1-input — CLOSED Phase 3+4 (
583baa6/c819a61/5905643/b28554b): codeccanon_axis_diff/canon_apply_transition/canon_classify_transition+write_tt(canon_from, canon_to, neg_from, neg_to, bypass)+lut_input_dependence/is_bypass_mask/BYPASS_1INPUT_MASKS. np2fasm--bypass-aware+ fasm2rbfbypass_aware=Truepragma → byte-identical to Quartuscanon_*_X4Y4N0.rbffor all 8 1-input + 2 constants. Silicon-validated canon_b (mask=0xCCCC). Phase 2 verdict: GLOBAL POSITION-INVARIANT (12 positions, shared config). - σ⁻¹ 2-input asymmetric (P2) — CLOSED at X4Y4N0 (commit
82ecd872026-05-13): codeccanon_2input_label_for_mask/canon_2input_cells/canon_2input_apply+LutCodec.write_tt(canon_2input=label)+CANON_2INPUT_ABSOLUTE[(x,y,n)][label]per-position table (~240 cells/label; 18 perm AND/OR/XOR + 6 neg = 24 labels). Sidesteps σ⁻¹ predict_sram gap (~108 lab_cram cells) by mining the full delta. fasm2rbfcanon_2input_aware=Truepragma byte-identical for all 24 labels. Silicon-validated mask=0x4444 = !KEY1 & KEY2 (md5bd373139..) on AX301 2026-05-13. Position scope: X4Y4N0 ONLY — KeyError at unmined positions (5-position pair-diff probe shows 2-input is NOT globally position-invariant; per-position mining required). Mining:scripts/sigma_inv_real_tt_mining/build_canon_2input_codec_table.py. Regressionfuzz/test_canon_2input_codec.py14/14 + pre-commit gate. - σ⁻¹ 2-input per-position canon table (Path 5 — d1pp) — LANDED 2026-05-21; CRTM Part-3 WRAPPED 2026-05-28.
results/canon_2input_codec_table_d1pp.json(md54dc37fbab8828034acda1270b19fa5e5) encodes per-position canon-cell XOR delta for 14 pin-friendly D1++ positions = 44 (x,y,n,mask) entries (per-mask0x6996×14, 0xDEAD×14, 0x4444×14, 0xFFFF×2; 58 golds inresults/golden_rbf_modeH/). Consumer formula (mode H lutcodec.c):Quartus_mask_M_at_(x,y,n) = baseline_mask0 XOR predict_sram(M) XOR canon_cells[(x,y,n)][M], thenpatch_rbf_crc. Verifierscripts/mode_h/verify_canon_d1pp_table.pyreconstructs 44/44 byte-identical to Quartus Lite 21.1 gold (excluding D4 per-build variable bytes[0x29..0x35)+[0x49..0x4B); CRC recomputed viapatch_rbf_crc). Scope caveat: lossless mine→verify ROUND-TRIP gate (same builds mined+checked), NOT hold-out/silicon validation (negative control confirms the gate is live; tautology by construction — do not cite as model-generalization or silicon proof). Mining tool:scripts/mode_h/build_canon_d1pp_table.py(no Quartus compiles; mines from existing D1++.cellsdiffs). Scope: N=0 only, 14 positions X∈{10,16,22,28} × Y∈{2,8,14,21} with one substitution (X28Y17N0 ← X28Y17N14). Part-3 commits8bd29e1(0xDEAD all-14) →d2d0762(hook de-hardcode) →a54d289(0x4444 all-14); more masks/positions = same recipe + Quartus compiles. Replaces P5d's per-LAB filter at non-X4Y4 positions; X4Y4N0 still usesCANON_2INPUT_ABSOLUTE(P2 silicon-validated path). Memo:path5_canon_d1pp_table_landed_2026_05_21. - σ⁻¹ 2-input cross-LAB (P5b/P5c/P5d) — SILICON-FAILED 2026-05-21 PM. P5d pre-flash gates green (safety 0 violations, analytical 10/61 CANDIDATE) but flash of
ae9aaef6cba4d48ab4a10f78ac994aefproduced LED0 stuck ON at idle, keys unresponsive, no UART. Root cause: the per-LAB LI filter conflated codec's outbound cross-LAB ROUTE LI cells at LAB(4,4) (5 cells from sigcache4,4,0->4,21,0,dataa) with canon_unique's inbound IOB→LE_A input-routing LI cells at the same LAB (4 cells at0x9354/0x9427/0x94F8/0x95CBbp=6 — empirically confirmed absent in flashed RBF). Same LAB tag, disjoint at (off, bp), but semantically different routes — filter dropped the inbound, undriving LE_A on silicon. Do NOT enablecanon_2input_unique_awarefor cross-LAB builds. Single-LE wire4 byte-identity path is unaffected (codec emits 0 LI cells at LAB(4,4) in single-LE context → filter doesn't fire; 24/24 + 5/5 regression preserved). Track B1 (2026-05-21 PM) unblocks the parallelIOB_E16/M16 → X4Y4N0.dataa/databsig-cache (mine_padnv_x4y4n0.pynow P1/P2 pragma-driven, 236+280 cells injected into padnv_cells; bitgen iob_routes reordered to post-all_luts) — cross-LAB builds at X4Y4 can now use the P2 silicon-validated canon_2input absolute path WITHOUT needing the P5d filter at all. Path 5 (canon_2input_codec_table_d1pp.json) covers non-X4Y4 pin-friendly positions. Flash budget 0/3 remaining. History memos:p5d_per_lab_li_filter_2026_05_21(pre-flash; SUPERSEDED),p5d_silicon_failed_2026_05_21(failure analysis + forensic),path5_canon_d1pp_table_landed_2026_05_21(canon-cell replacement),gamma_iob_x4y4_padnv_mining_2026_05_21(IOB sig-cache replacement). - OUTROUTE_G15 X4Y7N0 quarantined 2026-05-13 to
results/quarantine/after silicon-reset; codec hard-fails on missing. - Pivot guidance (updated 2026-05-15): at X4Y4N0, asymmetric masks (e.g. 0x4444, 0xBBBB, 0xA0A0) are codec-supported via
canon_2input_aware=Truefor single-LE wire4 designs only (silicon-validated). Cross-LAB designs at X=4 still BLOCKED — analytical gate green but safety validator catches LI MUX class. Other X=4 src-LE positions (Y∈{2,7,17,21}) still require symmetric 0x6996 workaround OR per-position canon-2input mining. N>0 (P4 axis) SILICON-VALIDATED 2026-05-31 at X10Y2N2 mask 0x4444 (gold md5f4f971d4, idle LED OFF / KEY1-alone → ON); codec formulabaseline⊕predict_sram(M)⊕canonreconstructs the N>0 gold byte-identical — but canon is per-N (N-invariance FALSIFIED), so other N need per-N mining, exactly like the per-position nature here. Memo:p4_n_invariance_2026_05_29. Pre-flash protocol: always runRouteCodec().validate_safe_for_hardware(rbf, nv_zero, raise_on_fail=False)and check empty before declaring an analytical gate "candidate" — analytical mismatch ≤ threshold is necessary but not sufficient. - Latent CLI bug fixed (
cf0bef72026-05-15):fasm2rbf.main()was silently dropping all FASM pragmas (noparse_pragmascall). Pre-fix, CLI builds with bypass_aware/canon_*_aware pragmas were no-ops. Fixed; only directbitgen()callers (regression tests, ζ pipeline, build_open scripts) had ever honored pragmas. ζ regression unaffected (pragma-less FASM). Test fixture:scripts/sigcache_validation/build_test.py(now with--le-a-mask+--canon-2inputCLI flags, no uncommitted Verilog edit needed). Memos:gamma_*,iob_route_hdr_skip_pivot_2026_05_11,sigma_inv_x4y4n0_not_a_4perm_2026_05_12,phase3_canon_cell_codec_landed_2026_05_13,phase4_bypass_tt_model_2026_05_13,p1_bypass_aware_silicon_validated_2026_05_13,p2_canon_2input_silicon_validated_2026_05_13,phase4_p1_p5_landed_2026_05_13,p5b_canon_unique_landed_2026_05_15,p5c_canon_unique_safety_blocked_2026_05_15.
- Bug #1 (