diff --git a/CLAUDE.md b/CLAUDE.md index 37c851f..12828d0 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -228,6 +228,35 @@ RX + 2 KB TX buffer at reset (`RMSR/TMSR=0x55`), so socket-3 frames are stored in the chip independently — no extra FPGA buffer needed. Register addresses are still datasheet-from-memory — **confirm at hardware bring-up**. +### ⚠️ The simulation clocks EXI 8× slower than hardware + +`bba_top`'s testbench derives the EXI clock from `HALF` (capture ticks per SPI +half-period): **EXI = 54 MHz / (2 × HALF)**. The default `HALF=8` gives +**3.4 MHz** — eight times slower than the real GC's ~27 MHz. At that rate the +entire CDC response latency fits inside one bit time, so the suite passes green +on a design that fails outright on a console. **Both the wrong-header-format +and the TX-pop-threshold bugs reached real hardware with a fully green test +suite.** + +`HALF` is now `EXI_HALF`-configurable. **Run `EXI_HALF=1 python -m +exi_bba.bba_top` before trusting any timing-related change.** + +Measured 2026-10-03: + +| EXI rate | device ID | NWAYS | +|---|---|---| +| `EXI_HALF=8` → 3.4 MHz | `04 02 02 00` ✅ | `0x17` ✅ | +| `EXI_HALF=2` → 13.5 MHz | ✅ | ✅ (TX drops 4 of 34 bytes) | +| `EXI_HALF=1` → **27 MHz, the hardware rate** | `02 01 01 00` ❌ | `0x0B` ❌ | + +⚠️ **OPEN BUG:** at the hardware rate every read comes back **shifted right by +one bit** (`04 02 02 00` → `02 01 01 00`; `0x17` → `0x0B` = `0x17 >> 1`). This +is a bit-level sampling misalignment, NOT the byte-level response latency that +was chased earlier — at 54 MHz capture there are only 2 ticks per EXI bit, and +the 2-stage `FFSynchronizer` on the EXI clock costs 2 ticks, i.e. a whole bit. +On console the same build returns all zeros. Fix this before any further +hardware runs. + ### EXI on the J4 debug header (`EXI_J4=1`) — bench host without SP1 `EXI_J4=1` lets a bench host (the Pi) drive EXI through the unpopulated J4 diff --git a/exi_bba/bba_top.py b/exi_bba/bba_top.py index 0a9de7c..6ad4808 100644 --- a/exi_bba/bba_top.py +++ b/exi_bba/bba_top.py @@ -398,6 +398,7 @@ class BBATop(Elaboratable): # ↔ BBARegisterFile (exi domain) ↔ sync modules, across all three clock domains. if __name__ == "__main__": + import os import sys from amaranth.sim import Simulator, Period @@ -405,7 +406,19 @@ if __name__ == "__main__": status_panel=True) # also exercise the panel wiring errors = [] - HALF = 8 # capture ticks per SPI half-period (well-oversampled) + # Capture ticks per SPI half-period -> EXI rate = 54 MHz / (2 * HALF). + # HALF=8 3.4 MHz fast regression default + # HALF=2 13.5 MHz + # HALF=1 27 MHz THE HARDWARE RATE + # ⚠️ The default is 8, which clocks EXI EIGHT TIMES SLOWER than the real + # GC. At that rate the whole CDC response latency hides inside a single + # bit time, so this suite passed green while hardware failed outright -- + # that is how both the header-format and pop-threshold bugs reached a + # console. Run `EXI_HALF=1 python -m exi_bba.bba_top` before trusting any + # timing-related change; at 27 MHz reads currently come back shifted right + # by one bit (04 02 02 00 -> 02 01 01 00, NWAYS 0x17 -> 0x0B), which is an + # open bug. + HALF = int(os.environ.get("EXI_HALF", "8")) async def spi_byte(ctx, mosi_val): """Drive one EXI Mode-3 byte; return the assembled MISO byte."""