← the Mirage case study
Cora Z7-07S · XC7Z007S

Mirage: a 6-layer, 512-wide INT8
transformer encoder
on a $99 FPGA

An autonomous loop was handed a 19-task plan for this design and closed all 19, finding four real defects in the RTL on the way — but every claim it made was a projection. Then it went on a board: 2,352 jobs replayed on the fabric, 61,632 values matched byte-for-byte, zero mismatched, with a negative control proving the check could fail.

Mirage top-level body diagram: four blocks — a DSP array for the matrix multiplies, multi-head attention with its weight caches, softmax, and the feed-forward network — each with its own state machine and BRAM caches.
↔ drag the diagram sideways
The top-level body diagram. A DSP array carrying every matrix multiply, multi-head attention with its weight caches, a softmax with no divider in it, the feed-forward network. This is the design as drawn; sections 04 and 05 give the shape the bitstream has.
34 DSP · 51.5%
36.5 BRAM · 73%
2,724 LUT · 18.9%
+0.214 ns WNS
61,632 values · 0 mismatched
19 / 19 tasks gated on evidence
The board's own printout · vector replay
=== Mirage vector replay ===
ID       : 0x4d495247  OK          <- "MIRG" out of the AXI4-Lite ID register
cmd tape : 0x00400000
res tape : 0x00600000

   board verdict: VECTOR REPLAY PASS -- silicon matches the reference bit-exactly.

5. BIT-EXACT RECHECK (host-side, independent of the board's own)
   61,632 values compared across 138 tensors
   PASS: every byte the board produced matches the model

--- and with one activation byte flipped, 05 -> 06 ---
   112 MISMATCHES -- the silicon and the model differ
   NEGATIVE CONTROL PASSED -- one flipped byte was caught.
Both halves matter. The comparison runs twice — once by the ARM core against a copy of the expected results in DDR, once on the host against the reference tape on disk — so a bug in one comparator cannot pass both. And a checker that always passes is worth nothing, which is what the second run is for.

01 What was attempted

A deliberately unforgiving target, chosen so that a mistake could not hide.

The subject is Mirage, an open-source, ultra-small FPGA transformer accelerator for EN→AR translation. What is documented here is not the accelerator but the experiment wrapped around it: a planner–designer–verifier loop given the 19 tasks, with every one gated on evidence rather than on the agent's own confidence.

The part is a Digilent Cora Z7-07S carrying an XC7Z007S: 14,400 LUTs, 66 DSP48E1 slices, 50 RAMB36 blocks. The spec demanded end-to-end INT8, no floating-point, 30% slack left over. A part this small leaves no room to be sloppy — which is the point.

Most agent demos are single-shot: one prompt, one file, one plausible answer. A chip is the opposite — a long chain of decisions where an error in week three quietly poisons week nine. The question was whether a loop could hold a multi-week engineering program together.

Board map of the Cora Z7-07S — press to open
XC7Z007S Zynq-7000 · 1× A9 + PL 66 DSP · 50 BRAM 14,400 LUT DDR3 512 MB · MT41K256M16 QSPI flash · empty micro-USB · PROG/UART the only cable in this build microSD boot · empty Pmod JA Pmod JB BTN0/1 LD0–LD3 CORA Z7-07S · PROGRAMMED · BITSTREAM CLOSED 2026-08-01
↔ drag the diagram sideways
The whole constraint is the die at centre. 66 DSP48E1 slices is the number the architecture is bent around. Note what is not populated: no boot flash, no SD card. Nothing runs until code arrives over the one micro-USB cable on the left edge — which is also the console section 07 records.

02 The harness that built it

The agent occupies exactly one box. The only path from "I think I'm done" to "task closed" reads the disk, not the transcript.

An agent will declare victory on a vague instruction, so the first step had nothing to do with hardware. 6 phases, 19 tasks, each with a numeric acceptance criterion, the artifacts it must produce, and a risk note. "Verify the softmax" is not a task. smx_core passes end-to-end SMX test for 8 heads × 1 layer × 4 tokens is, because it can fail.

The model is a swappable part. The 19-task run was Kimi K2; the v2 rebuild, the board bring-up and this write-up are Claude; a little of the early scaffolding was Codex. Across the whole project that is roughly 60 / 30 / 10 Claude / Kimi / Codex — an operator's estimate, not a measured split. The harness never asked which: it hands out one brief at a time and reads the disk to decide whether a task closed. That is the point of gating on artifacts rather than on a transcript — the gate does not care who wrote the file.

The agent harness, end to end — press to open
PLAN HARNESS · workflow.cli AGENT TOOLS · EVIDENCE mirage-mvp-spec mission + exit criteria 19-task plan 6 phases · W1–W19 mirage.state.json 59 nodes · truth next ≤500 token reply start locks one task verify ≤2 retries → block complete artifact gate · exit 2 the agent plans · writes · repairs scope guards no submodule writes ghdl -a · -e · -r goldens bit-accurate · py artifacts reports/*.md dashboard live trajectory task closed 19 / 19 blocked human --force only gate check missing veto record result
↔ drag the diagram sideways
harness Select any block — click it, or tab to it and press enter. complete is the one that matters: it is the reason nothing on this page is the agent's own opinion of its work.
Every closure passes the artifact gate. Solid lines are task flow; amber dashed lines are the gate check, the scope-guard veto and the write-back. The agent is one box in the middle, and it cannot reach the box that decides whether it succeeded.

The gate, in two commands

complete exits 2 and refuses to close a task unless the artifacts the plan declared actually exist on disk. There is a --force flag, and it is human-only.

workflow.cli
# 1. Ask the harness what's next — never the agent's memory
$ python -m workflow.cli next
p1/w3 taylor_exp_approx

# 2. Try to close a task with no artifacts on disk
$ python -m workflow.cli complete p1/w3
refusing: declared artifact missing — reports/w3-exp-error-histogram.md
$ echo $?
2
The workflow dashboard showing the harness graph with pipeline, memory-tier and tool zones on the left, and a live trajectory feed on the right listing agent spawns, memory reads and tool calls with millisecond durations.
The harness was debugged live, not from logs. Left, the architecture rendered from its own config; right, every agent spawn, memory read/write and tool call streaming with its duration. Watching it light up caught two bugs a log tail would have hidden: a reducer re-lighting the same nodes every cycle, so the active count read 0 while the data was fine, and a cached SVG served without the glow rules.

03 What the agent found

Write the bit-accurate model from the spec first, then audit the RTL against it. That ordering is the whole method — the model has no knowledge of how the RTL cheats.

This is where the project stopped being a workflow demo and became engineering. The audits kept finding real defects, and four of them were the kind a human reviewer skims past:

WeekModuleWhat the golden model exposedResolution
W3Taylor exp The 3rd-order core was supposed to hold under 1% relative error. It met that bar at exactly one point — x = 0 — and 64 of 64 sampled points came back 100% or more off, because an implicit clamp of 0 swallowed the input. Root cause: Q-format. Move to Q9 (scale 512), clamp |x| ≤ 0.5352. Max relative error 0.9683%.
W5Softmax e2e End-to-end attention came back 87–107 int8 LSB from golden. The exp input scaling was wrong by a factor of about 218. Shift z << 6, range-reduced exp, no divider. Worst case 2.99 LSB, RMS 0.52, exactly 0.00 one-hot.
W7FFN Failed all 10 random trials at 100% relative error — and ghdl confirmed it could not even compile: no declaration for "mac_array". Its weight BRAMs alone came to ~278 KB against a device total of ~225 KB. Not a fix — a redesign spec. 16-bit activations, per-layer power-of-two scales.
W9Layer cascade With int8 inter-stage activations, drift blew past 1 LN unit by layer 1. Softmax is chaotic under coarse quantisation: a small perturbation does not stay small. int16 between layers, int8 weights kept. 3-layer chain: worst drift 0.0062 LN units, RMS 0.0019.

The release gate is one command

Nine bit-accurate golden checks with numeric criteria, plus a self-checking ghdl bench. This is the independent re-run recorded for the v1.0 freeze:

reports/final-regression.log
$ bash outputs/run_regression.sh
== model-level golden checks ==
PASS taylor_exp_model.py
PASS smx_e2e_model.py         (2.99 < 4.0)
PASS ffn_model.py
PASS encoder_e2e_model.py     (0.0296 < 0.05)
PASS multilayer_e2e_model.py  (0.0047 < 0.01)
PASS greedy_decode_model.py
PASS bidirectional_demo.py
PASS beam_search_model.py     (13 >= 13)
PASS robustness_model.py
== RTL (ghdl) checks ==
PASS ghdl:gemm_latency_tb
==
REGRESSION: ALL GREEN                      exit=0

Fault injection went further: saturated inputs, single-bit upsets, patterns built to maximise accumulator growth. Every scenario passed — but under an all-max input the FFN hidden accumulator can hit 4.0× int32 overflow. That became outputs/rc2_hardening.vhd, drafted and ghdl-analysed, not a footnote.

Why this section is load-bearing. Everything after it is a measurement; this is why the measurement was worth taking. What went onto silicon is not the upstream RTL — it is that RTL with four defects corrected and the memory architecture rebuilt around them.

04 The three domains

Weights and activations go down; computed tensors come back up. The fabric never sees a sentence.

"An FPGA that translates" is the wrong model. The fabric runs a transformer encoder's matmul chain — about 99.8% of the arithmetic — and nothing else. The ARM is a courier; an x86 host stitches the layers and runs the decoder. Being exact about that split is what makes this page checkable.

The three domains: host, ARM, fabric — press to open
Host · x86 translate_hw.py MarianMT en-ar · 298 MB SentencePiece → [3213, 0] elementwise glue · float LayerNorm · residual · embed decoder · 6 layers + LM head tape generator 858,037 records Transport · openocd/JTAG tape_split.py commands 0.91 MB · 7 s weight blob 25.17 MB · once ~130 KiB/s measured DDR up via main() breakpoint Processing System · ARM A9 vectors.c — courier crt0.S → ps7_init() level shifters up · caches OFF ACT · CFG · WBLK · JOB · DUMP gemm · stage · smax · scale DDR 512 MB 0x01000000 blob 25.17 MB 0x00400000 commands 0.91 MB 0x00600000 expected results AXI4-Lite @ 0x43C00000 ID = 0x4d495247 "MIRG" Programmable Logic · 99.8% of the arithmetic Q · K · Vprojections147k MAC QKᵀ scoresstage_engine9.2k MAC softmaxexp + recipno divider A · Vattention out9.2k MAC W_oout projection49k MAC FFN512→2048→512 2,163k · 91% 2.38 M MACs / decode step · 6 layers · 2,352 jobs / sentence tensors read back over MMIO — DUMP / DUMPS int8 tiles out, int8/int32 tiles in — never text ↑ back to the host for LayerNorm, then the next layer
↔ drag the diagram sideways
The fabric never sees a sentence. It takes quantised tiles and returns tensors — tokenisation, embedding, LayerNorm and the entire decoder stay on the host.

The split, to scale

Drawn proportionally, the host's share of the arithmetic is the amber sliver.

PL · matmul chain + softmax · ~99.8%
2.38 M MACs per decode step on the fabric ~0.2% host — LayerNorm, residual, embed

On the fabric

99.8%Every multiply-accumulate in all six encoder layers — including the 512→2048→512 FFN, 91% of a layer by itself.

On the host

~0.2%FP.layer_norm(x1 + yf, …) — cheap in FLOPs, indispensable to the result, and it runs between every pass.

05 Inside the fabric

Every multiply forced into a DSP, every array into a BRAM. The memory budget is the architecture.

Inside the fabric, lane by lane — press to open
layer_ctrl job dispatcher, not a fixed op walker JOB gemm_sequencer reduction chunking programmable requant weight_window 16 RAMB36 · double-buffered buf A buf B writes go to !rd_buf, so load overlaps compute 16 banks × 32 b × 1024 mac_array 32 × mac_lane, output-stationary 32 DSP48E1 — the product only accumulators kept in fabric 1,024 LUT = 32 × 32-bit act_mem 8 b × 65536 · 15 BRAM 0 LUT score_mem 21 b × 4096 · 3 BRAM 0 LUT softmax_unit exp_rom · 1 BRAM recip_rom · 1 BRAM no divider reciprocal multiply; a divider would not have fit Measured in context · Vivado 2022.2 · run 16 51.5% DSP48E1 34 / 66 73% BRAM tile 36.5 / 50 18.9% Slice LUT 2,724 / 14,400 Timing +0.214 ns WNS 0 of 8,803 failing v1, for scale 18,100 LUT · 126% did not fit
↔ drag the diagram sideways
v1 declared 212% of device BRAM and could not be synthesised. v2 moved every array into a BRAM primitive and every multiply into a DSP; the same design now fits in 73% with timing met at +0.214 ns of slack.
The number that changed the project. Timing met in context — not on the module in isolation, but with the processing system, the AXI interconnect and the clock generator around it. That is the bitstream at build_cora/mirage_cora_wrapper.bit, and it is the one the recording below loads.

06 How the Arabic is produced

The part most easily overclaimed. Follow which box the tokens come out of.

Where the Arabic tokens come from — press to open
"Hello"→ [3213, 0] embed · host2 × 512 fp32 × 6 encoder layers matmuls · PL392 jobs / layer LayerNorm · host+ residual, float next layer encoder state2 × 512 encoder state → decoder Decoder — entirely on the host 6 decoder layers + LM head · numpy the fabric has no embedding table, no decoder, no beam search, and not enough BRAM for one weight matrix → [1466, 1466, 0] Output مرحبا مرحبا identical to the float32 reference cosine 0.9835 / 0.9875 · token ids match exactly
↔ drag the diagram sideways
The tokens come out of the host, not the board. The fabric produced every matmul feeding this result — but the decode ran on the model's encoder state.
The inference still outstanding. enc_hw is the host's own computation, so the decode check runs on the model's state. The board's 61,632 values match it bit-exactly, so decoding the board's numbers must give the same Arabic — but "must" is an inference, not a demonstration.

07 Bring-up, on one cable

One unbroken session against the board. Nothing was re-run between clips.

Eight clips, one take, in order. Long waits are compressed, but the elapsed clock counts true seconds, so a cut shows as the clock jumping, never as time that did not happen. The terminal is 145 columns: go full-screen, landscape on a phone.

Reproduce the whole thingMirage/sw/board/demo
  1. ./demo.sh --check
  2. ./demo.sh
  3. ./demo.sh --negative
Three commands, about ten minutes, no Vivado and no licence — the bitstream already exists. Each stage below lists the lower-level command actually typed on camera, which is what demo.sh drives for you.
Stage 01

JTAG and console, one cable

00:10

Both channels into the board arrive on one micro-USB.

The FT2232H puts JTAG on interface 0 and the console on interface 1. Only /dev/ttyUSB1 has a tty node, and that is correct — openocd claims interface 0 over libusb, so the kernel drops the other. Nothing else may hold the port: a tty gives each byte to one reader.
  • FT2232H
  • /dev/ttyUSB1
Typed on cameraMirage/sw/board/bare
  1. uname -sr; echo; lsusb -d 0403:6010
  2. ls -l /dev/ttyUSB*
  3. fuser -v /dev/ttyUSB1 2>&1 || echo 'no reader holds /dev/ttyUSB1'
Stage 02

Scanning the JTAG chain

00:07

ID codes burned into the die, not printed by the host.

0x13723093 is the XC7Z007S fabric, 0x4ba00477 the ARM core beside it. Neither can be faked from this machine.
  • openocd
  • scan_chain
Typed on cameraMirage/sw/board/bare
  1. openocd -f cora.cfg -c 'init' -c 'scan_chain' -c 'targets' -c 'shutdown' 2>&1 | grep -vE 'Warn|deprecated|srst|Info : (clock|Listening|starting)'
The grep only quiets openocd’s startup noise; drop it and the ID codes read the same.
Stage 03

Opening the console at 115200

00:10

115200 8N1 — chosen by the block design, not by this terminal.

The baud rate comes out of PCW_UART0_BAUD_RATE in the XSA and the host has to match it. picocom then attaches to a board that says nothing: no boot flash, no SD card, nothing running on the processor yet. Code arrives over JTAG.
  • 115200 8N1
  • picocom
Typed on cameraMirage/sw/board/bare
  1. stty -F /dev/ttyUSB1 115200 raw -echo -crtscts; stty -F /dev/ttyUSB1 -a | head -3
  2. picocom -b 115200 /dev/ttyUSB1
Leave it attached. ctrl-a ctrl-x detaches when stage 05 needs the port.
Stage 04

Bitstream load and smoke test

00:11

Bitstream and smoke test, both over the same JTAG cable.

0x4d495247 is “MIRG” out of the AXI4-Lite ID register. The loopback returns 0 of 64 bytes wrong and the gemm the expected 64 in all 32 lanes. That output came out of the chip, not out of this machine — it is the mac_array from section 05 answering for the first time.
  • 0x4d495247
  • 32 lanes
Typed on cameraMirage/sw/board/bare · second terminal
  1. make load
load is openocd: halt, pld load the bitstream, load_image the smoke test, resume. Replies come back on the console from stage 03.

08 Why the tape was taken apart

64.3 MB against an on-chip ceiling of ~64 KB per tape. Counting it changed the problem.

The tape, and the 98.6% that never changes — press to open
Before — one tape, re-sent every sentence WROW · 786,432 weight rows · 63.4 MB · 98.6% ACT 0.7 MB CFG 0.2 · rest 0.03 64.3 MB · 858,037 records · OCM allows ~64 KB per tape tape_split.py — 1,152 runs become 1,152 WBLK records After — weights once, commands per sentence commands 0.91 MB 71× smaller ~7 s over JTAG weight blob 25.17 MB binary, resident in DDR loaded once · ~3 min round-trip proven byte-identical expand(split(tape)) == tape · 786,432 rows and again through the board's own C interpreter
↔ drag the diagram sideways
The tape was not too big; it was re-shipping the model. The weight rows never change, so they became a blob loaded once — and the split was proven reversible byte-for-byte before anything was trusted to it.
Stage 05

Splitting the recorded run

00:10

64.3 MB of recorded simulator run, cut in two.

A full 6-layer MarianMT encoder pass, recorded off the RTL simulator. It splits into 0.91 MB of commands and 25.17 MB of weights — 1,152 window loads over 786,432 rows — and a DDR map is built for both. The diagram above, executing.
  • 1,152 window loads
  • 786,432 rows
Typed on cameraMirage/sw/board/bare
  1. ./ddr_replay.sh ../../../sim/xlate_cmd.txt ../../../sim/xlate_res.txt
One command covers stages 05, 06 and 07 — they are one run, cut into three clips.
Stage 06

Loading 25 MB over JTAG

00:11

25,165,824 bytes of weights, at about 132 KiB/s.

openocd loads the bitstream, then breaks at main() so ps7_init finishes and DDR is initialised before anything is written to it. 25,165,824 bytes of weights go to 0x01000000, the command stream to 0x00400000, at about 132 KiB/s. The elapsed clock runs 00:32 to 04:18 in eleven seconds of video — that is the cable, not the array.
  • 132 KiB/s
  • 0x01000000
No new command — stage 05’s run, still going.

09 Chain of custody

Four representations of one computation, each required to agree with the last at zero tolerance.

Chain of custody: float32 to silicon — press to open
float32 refMarianMT in numpyno torch int8 modelmirage_int8.Fabricbit-exact datapath GHDL simencoder_layer_tb729 s wall silicon2,352 jobs 61,632 · 0 wrong quantise bit-exact bit-exact Negative control — a test that cannot fail proves nothing one byte of 64.3 MB changed · ACT 0000 05 → 06 112 of 61,632 mismatched · REPLAY FAIL divergence propagates through six layers, damped by requantisation — mostly ±1, the expected signature
↔ drag the diagram sideways
The board is computing, not echoing. Values are read over MMIO from the AXI slave; the expected tape sits in DDR only for comparison. One changed input byte breaks the match.
Stage 07

Comparing 61,632 values on chip

00:17

The board grades itself; the host only reads the tally.

2,352 jobs run on the programmable logic, and every value the fabric computes is compared against the simulator’s answer on the chip itself. 61,632 values checked, 0 mismatched: VECTOR REPLAY PASS. The rightmost arrow in the diagram above, happening.
  • 2,352 jobs
  • 61,632 values
No new command — stage 05’s run, reaching its verdict.
Stage 08

Negative control: one byte flipped

00:17

One activation byte flipped, 05 to 06 — 112 mismatches.

The same comparison against a tape with one activation byte changed, 05 to 06: 112 of 61,632 values wrong, and it prints VECTOR REPLAY FAIL. The pass is a result, not a default. Note what does not happen — the Arabic still comes out right. An int8 activation off by one almost never moves an argmax, which is exactly why the byte comparison is the verdict and the translation only the presentation.
  • 05 → 06
  • 112 wrong
Typed on cameraMirage/sw/board/bare
  1. grep -E 'records|values|VECTOR REPLAY' build/ddr_console.negctl-2026-08-13.log | sed 's/\r$//'
That log is written by ./demo.sh --negative; the date in the name is the run’s.
What is demonstrated. The programmable logic is programmed with Mirage (0x4d495247). Its GEMM passes a known-answer test. It ran 2,352 jobs and returned 61,632 values over MMIO matching simulation exactly — and one changed input byte breaks that match in 112 places.

Nothing was re-recorded and nothing dropped between the clips. The take is here in one piece — mirage-board-demo.mp4 · 1:34 · 4.9 MB — the same eight clips this page plays a stage at a time.

10 Projection vs measurement

The honest way to read this project is to ask which claims moved tier, and which did not.

The evidence ladder, five rungs — press to open
Evidence tier — weakest at the left 1 · estimate 16.74 ms / sentence ~360 tok/s ~1,700× vs Python 5–10 mJ / sentence still here 2 · golden model 2.99 int8 LSB softmax 0.0062 LN drift ×3 token-identical decode 3 · GHDL sim encoder_layer_tb gemm tile 36/36 all RTL elaborates 4 · implemented 34 DSP · 36.5 BRAM 2,724 LUT +0.214 ns in context 5 · silicon ID 0x4d495247 gemm known-answer 61,632 values · 0 wrong 112 wrong when corrupted every arithmetic claim moved four rungs — that is what the recording below is for every performance claim stayed on rung 1 weights reach the board over JTAG at ~132 KiB/s, so this run is bandwidth-bound by a wide margin. Any timing number taken from it describes the cable, not the array. Ignore the wall clock.
↔ drag the diagram sideways
One diagram, one claim: correctness moved, speed did not. Datapath bit-exactness is now a measurement on silicon. Latency, throughput and energy are as speculative as they were before the board arrived — this was never a performance run.

What the v1.0 freeze claimed, and where each claim stands now

Claim at v1.0ThenNow
Bitstream existsNot fabricated — human + Vivado stepbuild_cora/mirage_cora_wrapper.bit, timing closed 2026-08-01
Board bring-upPlanned future workRun, recorded, and reproducible from demo.sh
DSP budget65 / 66 · 98% pre-synthesis — fails the <90% bar34 / 66 · 51.5% measured in context
BRAM budget≈84% projected36.5 / 50 · 73% measured
Timing at clockAssumed, no Vivado runWNS +0.214 ns · 0 of 8,803 paths failing
Datapath correctnessBit-accurate Python models only61,632 values from silicon, 0 mismatched, negative control caught
Latency ≈16.74 msCycle model @ 200 MHzUnchanged — still a cycle model
≈360 tok/s, ≈1,700× CPUEstimate vs unoptimised PythonUnchanged — and the project's own report narrows it to 10–20× against a competent int8 baseline
Translation qualityToy-scale random weights, no BLEUReal MarianMT weights now drive the tape; still no BLEU evaluation

11 What is not proven

Stated plainly, because a page that only lists wins is not evidence of anything.

Speed

This run says nothing about it. Weights reach the board over JTAG at ~132 KiB/s, which makes the whole session bandwidth-bound by a wide margin. The wall clock describes the cable.

Translation quality

The tape comes from real MarianMT weights and the decode is token-identical to the float32 reference, but no BLEU evaluation exists. "Correct Arabic on one word" is not a quality claim.

The last decode hop

enc_hw is the host's own computation, so the check decodes the model's encoder state. The board's values match it bit-exactly, so the same Arabic must follow — but that is an inference, not a demonstration.

Power

No rail was ever measured. The 5–10 mJ/sentence figure is a projection and has not moved.

Sustained operation

One sentence, replayed twice. Nothing here speaks to thermals, long runs, or a second sentence without a reload.

The FFN overflow

The adversarial all-max input that reaches 4.0× int32 overflow is a modelled result. rc2_hardening.vhd is drafted and analysed, not integrated upstream.

And the honest framing of the agent result. The loop closed 19 of 19 and found four real defects — the clamped exp, the 218 softmax scaling, the FFN that would not compile, the int8 inter-layer drift. What it could not do was anything needing a board, a licence decision, or a trained checkpoint.

12 Where it goes next

In rough order of impact, now that the biggest gap on the old page is closed.

NextWhy it is the next one
Load weights over something other than JTAGSD or Ethernet turns a 3-minute load into a fraction of a second, and it is the only thing standing between this run and a timing measurement worth reporting.
DDR bandwidth tuningAfter the optimisation pass the pipeline is memory-bound, not compute-bound — crossover at roughly 192 MB/s against 100 MB/s of streaming bandwidth. That is where the next real speedup lives.
Decode from the board's own stateCloses the one inference left in section 06 and turns "must give the same Arabic" into a demonstration.
int4 FFN weight compressionRelieves BRAM pressure and stream time at once — the FFN is 91% of a layer.
Integrate RC2 saturating readbackPlus ECC or scrubbing for multi-bit upsets. Single-bit upsets already absorb at the noise floor, at most 0.051 LN drift.
Real BLEUQuantise the trained checkpoints through the model scripts and run a proper evaluation, so translation quality stops being an open box.
The question this started with. Could an autonomous loop hold a multi-week hardware program together? It closed 19 tasks through a gate that reads the disk, not the transcript, and what it produced went onto a $99 FPGA and returned 61,632 values without one wrong. That is the answer — and the one claim here a corrupted byte could have falsified.