diff --git a/CLAUDE.md b/CLAUDE.md index 12828d0..6862be8 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -232,30 +232,62 @@ still datasheet-from-memory — **confirm at hardware bring-up**. `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.** +**3.4 MHz** — eight times slower than the real GC's ~27 MHz, so the suite +passes green on designs that fail outright on a console. `HALF` is +`EXI_HALF`-configurable; **run `EXI_HALF=1 python -m exi_bba.bba_top` before +trusting any timing-related change.** -`HALF` is now `EXI_HALF`-configurable. **Run `EXI_HALF=1 python -m -exi_bba.bba_top` before trusting any timing-related change.** +Worse, **every testbench drives MOSI many capture ticks before toggling the +clock**, so MOSI is stable at BOTH edges and the sampling-edge convention is +structurally undetectable. That is how a Mode 2/Mode 3 mix-up survived to +silicon with a fully green suite. `bba_top`'s master now models a true Mode 3 +master (drive on falling, sample on rising). -Measured 2026-10-03: +### ✅ EXI edge convention — FIXED 2026-10-03 (was Mode 2, must be Mode 3) -| 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` ❌ | +The notes below once said "Mode 3 (CPOL=1, CPHA=1)" and then described +sampling MOSI on the **falling** edge. Idle-high + sample-on-falling is +**Mode 2**. Real Mode 3 samples on the second edge of each cycle — the +**rising** one — and that is what the GameCube does: it DRIVES MOSI on the +falling edge, so sampling there latched the previous bit. -⚠️ **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. +Symptom on console: every byte one bit late in both directions, at every clock +speed. The `0x80` register-space header byte read as `0x00`, which decodes as a +command-space device-ID query, so NWAYS reads returned the device ID and the +speed sweep failed at every index. Raw 8-slot dump, first transaction after an +FPGA reset: `00 00 02 01 01 00 00 00` = `04 02 02 00 >> 1`. + +`SPIMode3Slave(mode2=False)` (the default) now samples MOSI on RISING. +`mode2=True` restores the old behaviour, which is what the Pi/J4 probe scripts +were written against. + +### ⚠️ OPEN: MISO output is ~2 bits late at 27 MHz + +With MOSI fixed, register reads are **correct up to 4 MHz** and fail from +8 MHz (hardware speed sweep 2026-10-03: `ok=[1,1,1,0,0,0]`, highest good +index 2). At index 5 a NWAYS read returns `05 C0` = `0x17 >> 2`. + +Cause: the MISO output path is delayed by roughly **3 capture ticks ≈ 55 ns** — +2 ticks of input `FFSynchronizer` on EXI CLK plus 1 tick to register the shift. +At 27 MHz one bit is 37 ns, so the master samples ~2 bits behind. The failure +threshold (pass at 250 ns/bit, fail at 125 ns/bit) brackets the delay at +60–125 ns, consistent with that structure. + +**Lowering the EXI clock is NOT an option:** Swiss hardcodes +`EXI_SPEED_32MHZ` in `exi_select()` (`cube/patches/bba/bba.c`) — there is no +setting to turn down. + +Options, none yet done: +1. **Clock the bit engine from the EXI clock itself** (route the pin to a + global buffer and make it a real clock domain) — the textbook SPI-slave + architecture, removes oversampling and the synchroniser entirely. Biggest + change, but the only one that is structurally correct rather than tuned. +2. **Output-bit lookahead** — drive the bit the master will sample ~2 bit-times + from now. Deterministic compensation for a deterministic delay, but it needs + lookahead into the next byte at byte boundaries, and the constant may not + hold across parts/temperature. +3. **Raise the capture clock** — 81 MHz would give 3× oversampling, but the + design's best measured fmax is 67 MHz, so it will not close. ### EXI on the J4 debug header (`EXI_J4=1`) — bench host without SP1