From 5db3b7751a3df487fb5bf58d2ecaa42c1aba83bc Mon Sep 17 00:00:00 2001 From: Dennis Brentjes Date: Fri, 9 Oct 2026 10:42:12 +0200 Subject: [PATCH] docs: MISO timing solved on hardware; command-space ID latency still open Console confirms the full-cycle-early launch: speed sweep ok=[1,1,1,1,1,1], per-speed dump 0:1700 through 5:1700, raw reg dump 0x17 in slot 0. Register reads are correct at every EXI speed index including 27 MHz. Records what the PHASE_METER measurement showed, why three rounds of delay-cutting could not work (9 ns removed, byte-identical result each time, so the remaining cost is pad + global clock insertion + IOB + pad, which no RTL change reaches), and the testbench rule that falls out of it: a master must sample MISO in the CLK-LOW phase or a zero-delay simulation reads the NEXT bit and reports everything one bit early while hardware is correct. Also records the one remaining failure as OPEN rather than fixed: the command-space device-ID response is one byte late at 27 MHz because a 2-byte header leaves only one byte time (296 ns) for a round trip that costs ~280-320 ns, and the inter-transfer gap cannot help because the clock is paused across it. HEADER0 already pushes in the same cycle it consumes byte 0, so the FSM side is already as early as possible. Co-Authored-By: Claude Opus 5 --- CLAUDE.md | 71 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) diff --git a/CLAUDE.md b/CLAUDE.md index 4a52fca..2e97c5e 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -265,6 +265,77 @@ hold across parts and temperature, and 3× oversampling needs 81 MHz, which this part cannot close. `SPIMode3Slave` has no `mode2` parameter any more — there is only one correct convention and it is enforced by mutation tests. +### ✅ MISO timing SOLVED 2026-10-09 — launch a full cycle early + +**Confirmed on a console:** speed sweep `ok=[1,1,1,1,1,1]`, per-speed dump +`0:1700 … 5:1700`, raw reg dump `0x17` in slot 0. Register reads are correct +at **every** EXI speed index including 5 (27 MHz, the real hardware rate). + +The engine drives MISO on the **RISING** edge carrying the bit the host will +sample at the NEXT rising edge, so the output path gets a **FULL period +(37 ns)** rather than the half period (18.5 ns) a falling-edge launch gives. +`tx_out` is clocked on the rising edge and loads at `rx_bit == 7`; there is no +`exiclk_n` domain any more — one clock edge, one phase reference. + +**Why a half period was not enough, and why no RTL change could fix it.** +~9 ns was removed from the fabric output path in three steps — the EXI_J4 pad +merge, then the `Mux(cs_n, 1, …)` LUT, then the entire fabric route by moving +the output into an `SB_IO` register — and the hardware result stayed **byte +identical** every time (`0B 80`). Nine nanoseconds out of 18.5 cannot change +nothing if propagation were the whole story. What remains is irreducible: +input pad + **global clock-network insertion** + IOB clock-to-Q + output pad, +plus the host's own setup. So the fix was to double the budget, not shrink the +delay. + +`PHASE_METER` is what settled it: `0:8080 1:8080 2:8080 3:8080 4:8080 5:C040` +— the bit engine's own byte phase, perfect to 16 MHz and one bit late at 27. +The 8-slot dump read `C0 40 40 40 C0 40 40 40`: a uniform one-bit lag with +extra highs at bits 0 and 32, where libogc splits the read into two 4-byte +transfers and MISO tri-states to its pull-up between them. + +⚠️ **Testbench consequence.** A master must sample MISO in the **CLK-LOW +phase** (the value latched ON the rising edge). Sampling after raising the +clock sees the NEXT bit in a zero-delay simulation, so the suite would report +everything one bit early while hardware was correct. Both `bba_top` and +`spi_mode3_slave` now sample in the low phase. + +⚠️ **This relies on the delay landing in a window:** MISO must change late +enough to clear the host's hold time on the previous bit, and within a full +period for its setup. The measured insertion delay (~12–20 ns against 37 ns) +sits comfortably inside. If a LOW speed index ever regresses while 27 MHz +works, that is the hold edge of the window — the launch is too early — not a +setup problem. + +Side effect: EXI-domain fmax rose 41.6–49.7 → **62.1–65.4 MHz**, because the +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 + +`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. + +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. + +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. + +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. + ### ⚠️ TX bit phase MUST come from the rising-edge counter (fixed 2026-10-03) **Never give the TX shift register its own free-running falling-edge counter.**