Skip the raw ID dump once id_phase is consumed; record the full console pass
The whole gc_bba_test suite now passes on a real GameCube: Identity (device ID 04 02 02 00, HIPR, NWAYS), Registers, Reset/control, EXI integrity (speed sweep ok=[1,1,1,1,1,1], per-speed dump clean at all six indices, raw reg dump 0x17 in slot 0), Ethernet, and SPRAM/DMA -- at the real 27 MHz EXI rate. The one reported failure was t_raw_id, and it was not a fault: an earlier suite had already issued the NCRA reset that clears id_phase, after which address 0 correctly reads back NCRA. t_devid already guarded for this; t_raw_id did not, so a full run cried wolf. It now returns SKIP like t_devid, and both messages say reflash/reset the FPGA rather than power-cycle the console, which is what actually restores id_phase. CLAUDE.md records the device-ID fix and, importantly, that simulation CANNOT reproduce the bug it fixed -- bba_top passed the ID at EXI_HALF=1 beforehand, because sim's sync-to-EXI latency ratio does not match silicon closely enough to miss a 296 ns window. Anyone "simplifying" the ID back into the register file will keep a green suite and break hardware. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -82,6 +82,15 @@ The `exi_bba/` module tree is **fully implemented** with simulation testbenches.
|
||||
All modules elaborate without errors and pass their unit tests, including
|
||||
`bba_top` at the real 27 MHz EXI rate (`EXI_HALF=1`).
|
||||
|
||||
✅ **FULL TEST SUITE PASSES ON A REAL CONSOLE (2026-10-09)** — every suite in
|
||||
`gc_bba_test`: Identity (device ID `04 02 02 00`, HIPR, NWAYS), Registers,
|
||||
Reset/control, EXI integrity (speed sweep `ok=[1,1,1,1,1,1]`, per-speed dump
|
||||
clean at all six indices, raw reg dump `0x17` in slot 0), Ethernet, and
|
||||
SPRAM/DMA. The EXI front end runs at the real 27 MHz hardware rate.
|
||||
|
||||
(`Raw ID dump` reports SKIP once an earlier suite has issued the NCRA reset
|
||||
that clears `id_phase` — reflash or reset the FPGA to restore it.)
|
||||
|
||||
**Timing closes on EVERY seed (2026-10-03).** `python -m exi_bba.synth
|
||||
--seeds 8`:
|
||||
|
||||
@@ -310,31 +319,53 @@ Side effect: EXI-domain fmax rose 41.6–49.7 → **62.1–65.4 MHz**, because t
|
||||
setup path into the IOB register became posedge→posedge against a full period
|
||||
instead of posedge→negedge against a half. Same doubling, seen internally.
|
||||
|
||||
### ⚠️ OPEN: command-space (device-ID) response is marginal at 27 MHz
|
||||
### ✅ Device ID answered by the BIT ENGINE (fixed 2026-10-09)
|
||||
|
||||
`t_raw_id` at index 5 returns `00 04 02 02 00 02 02 02` — the ID is **correct
|
||||
but one byte late**. Cause is latency, not phase: a command-space header is
|
||||
only 2 bytes, so the response must be in the tx FIFO by the **last rising edge
|
||||
of the header transfer** (one byte time, 296 ns at 27 MHz). The clock pauses
|
||||
after that edge, so the inter-transfer gap CANNOT help — there are no edges
|
||||
during it for anything to update. The round trip is rx `AsyncFIFO` (~125 ns) +
|
||||
one sync cycle + tx `AsyncFIFO` (~110 ns) ≈ 280–320 ns against 296 ns, i.e.
|
||||
**marginal by construction**. `HEADER0` already pushes ID byte 0 in the same
|
||||
cycle it consumes byte 0, so the FSM side cannot be made earlier.
|
||||
`bba_devid` returned `00 04 02 02` on a console — the ID correct but **one
|
||||
byte late** — and it failed in its REAL shape (16 µs `gap()`, exactly four
|
||||
bytes clocked), so it was not a probe artefact. Register-space reads were
|
||||
fine throughout; only command space was affected.
|
||||
|
||||
Register-space reads have the same one-byte-time budget and DO work, so the
|
||||
command-space path is only slightly slower — consistent with a marginal miss.
|
||||
**Cause was latency, not phase.** A command-space header is two bytes, so the
|
||||
reply must be in the tx FIFO by the **last rising edge of the header
|
||||
transfer** — one byte time, 296 ns at 27 MHz. The round trip is rx `AsyncFIFO`
|
||||
(~125 ns) + a sync cycle + tx `AsyncFIFO` (~110 ns) ≈ 280–320 ns: marginal by
|
||||
construction. `HEADER0` already pushed in the same cycle it consumed byte 0,
|
||||
so the FSM side was already as early as possible.
|
||||
|
||||
Note `t_raw_id` is harsher than anything the GC does: it omits the `gap()`
|
||||
that `bba_devid` uses AND clocks 8 bytes for a 4-byte query (the documented
|
||||
over-clocking artefact — hence the trailing `02 02 02`). Establish whether
|
||||
`bba_devid` (Identity suite) passes before treating this as fatal.
|
||||
⚠️ **The inter-transfer gap cannot help, and this is the non-obvious part.**
|
||||
The load happens on that last rising edge and the clock is PAUSED afterwards,
|
||||
so no edge exists during the gap for anything to update. That is why
|
||||
`bba_devid`'s 16 µs gap made no difference, and why "give it more idle time"
|
||||
is not a fix. **Pre-pushing at CS-fall does not work either:** the engine's
|
||||
unconditional per-byte pops would eat it, and the header length is not known
|
||||
until byte 0 has been decoded — which is the latency in question.
|
||||
|
||||
Fix direction if it is real: shorten the CDC round trip, e.g. replace the tx
|
||||
`AsyncFIFO` with a 2-deep ping-pong CDC (see the lightweight-TX-CDC note).
|
||||
Pre-pushing the ID at CS-fall does NOT work — the engine's unconditional
|
||||
per-byte pops would eat it, and the header length is not known until byte 0
|
||||
has been decoded.
|
||||
**Fix: take the round trip out of the path.** The device ID is a hardwired
|
||||
constant and belongs to the physical interface, not the register file, and
|
||||
`byte 0 == 0x00` identifies the query exactly (bit 7 clear = command space,
|
||||
bit 6 clear = read, reg 0). `SPIMode3Slave` recognises it and answers
|
||||
`04 02 02 00` from the EXI clock domain with **no CDC at all**. The only thing
|
||||
it needs from `sync` is `id_phase`, a single bit that changes once, crossed
|
||||
with an `FFSynchronizer`.
|
||||
|
||||
The register file's ID path was **removed**, not kept as a fallback: left in,
|
||||
it pushes bytes a 2-byte header has too few pops to drain — exactly how an
|
||||
earlier version made the *next* enumeration come back a slot late.
|
||||
|
||||
Tests: `spi_mode3_slave` **T7** (reply is `04 02 02 00` with the tx FIFO
|
||||
deliberately feeding `0x00`, so only the fast path can answer) and **T8** (it
|
||||
is GATED — with `id_arm` low, `0x5A` comes through from the FIFO; without this
|
||||
the fast path would hijack address 0 forever and the GC could never read NCRA
|
||||
back after its own reset). `bba_register_file` **T1** asserts the FSM pushes
|
||||
NOTHING for an ID query.
|
||||
|
||||
⚠️ **Simulation cannot reproduce this bug.** `bba_top` passed the device ID at
|
||||
`EXI_HALF=1` *before* the fix, because the sync domain's latency relative to
|
||||
the EXI clock in simulation does not match silicon closely enough to miss a
|
||||
296 ns window. T7/T8 prove the fast path works; they do not prove it is
|
||||
necessary. Do not "simplify" the ID back into the register file because the
|
||||
suite stays green — it will, and hardware will not.
|
||||
|
||||
### ⚠️ TX bit phase MUST come from the rising-edge counter (fixed 2026-10-03)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user