sim: make the EXI clock rate configurable; default was 8x too slow
bba_top derived its EXI clock from HALF, defaulting to 3.4 MHz against the real GC's ~27 MHz. At that rate the whole CDC response latency fits inside one bit time, so the suite passed green on a design that failed on console -- this is how both the wrong-header-format and the TX-pop-threshold bugs reached hardware with everything green. HALF is now EXI_HALF-configurable (default 8 for fast regression). At the real rate, EXI_HALF=1, every read comes back shifted right by ONE BIT: 04 02 02 00 -> 02 01 01 00, NWAYS 0x17 -> 0x0B. That is a bit-level sampling misalignment, not the byte-level latency chased earlier: at 54 MHz capture there are only 2 ticks per EXI bit and the 2-stage FFSynchronizer on EXI CLK costs 2 ticks, a whole bit. Recorded as an open bug in CLAUDE.md with the measurements. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -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
|
in the chip independently — no extra FPGA buffer needed. Register addresses are
|
||||||
still datasheet-from-memory — **confirm at hardware bring-up**.
|
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 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
|
`EXI_J4=1` lets a bench host (the Pi) drive EXI through the unpopulated J4
|
||||||
|
|||||||
+14
-1
@@ -398,6 +398,7 @@ class BBATop(Elaboratable):
|
|||||||
# ↔ BBARegisterFile (exi domain) ↔ sync modules, across all three clock domains.
|
# ↔ BBARegisterFile (exi domain) ↔ sync modules, across all three clock domains.
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
import os
|
||||||
import sys
|
import sys
|
||||||
from amaranth.sim import Simulator, Period
|
from amaranth.sim import Simulator, Period
|
||||||
|
|
||||||
@@ -405,7 +406,19 @@ if __name__ == "__main__":
|
|||||||
status_panel=True) # also exercise the panel wiring
|
status_panel=True) # also exercise the panel wiring
|
||||||
errors = []
|
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):
|
async def spi_byte(ctx, mosi_val):
|
||||||
"""Drive one EXI Mode-3 byte; return the assembled MISO byte."""
|
"""Drive one EXI Mode-3 byte; return the assembled MISO byte."""
|
||||||
|
|||||||
Reference in New Issue
Block a user