From 007c4d40f9016b29a33b5a61fad8b0886eb32d78 Mon Sep 17 00:00:00 2001 From: Dennis Brentjes Date: Sat, 10 Oct 2026 09:18:50 +0200 Subject: [PATCH] Record the 16-bit SPRAM response as falsified, and why Returning both bytes of the SPRAM word per request was the one approach that looked like it addressed the real constraint rather than working around it. Implemented fully (16-bit exi_rsp_data + exi_rsp_odd in SPRAMArbiter, sp_buf/ sp_cnt pair-pushing in BBARegisterFile, address advance by 2, tests updated) and it made things WORSE: bba_top T9 went 6/6 / 3/6 / 1/6 at EXI_HALF=8/2/1 against the baseline's 6/6 / 6/6 / 1/6, breaking HALF=2, leaving the DE DE duplication at the hardware rate, and adding a new fault where the first read after an idle period returns all zeros. Reverted. The reason is worth keeping: the budget only doubles if the next request issues while the second byte is still being pushed, and it cannot, because that byte sits in a holding register the prefetch guard has to wait on. The sequence stays request -> response -> push -> push -> request, so round-trip latency per pair is unchanged and the first byte of each pair faces the same deadline as before. The constraint is unhidden round-trip latency, not bytes per round trip. Also corrects the recorded T9 ratio at EXI_HALF=1 from 2/6 to 1/6, which is what the committed test deterministically prints. Co-Authored-By: Claude Opus 5 --- CLAUDE.md | 30 ++++++++++++++++++------------ 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 125f1f5..e92b937 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -313,11 +313,11 @@ never overlapped). ``` EXI_HALF=8 T9 concurrent DMA reads: 6/6 byte-exact EXI_HALF=2 T9 concurrent DMA reads: 6/6 byte-exact -EXI_HALF=1 T9 concurrent DMA reads: 2/6 byte-exact <-- the hardware rate +EXI_HALF=1 T9 concurrent DMA reads: 1/6 byte-exact <-- the hardware rate ``` -2-of-6 good matches the console ratio closely (`desc@+0=100 +1=343`, 23% good), -and the corruption is exactly the hardware symptom — **one extra byte inserted +1-of-6 good is the same order as the console ratio (`desc@+0=100 +1=343`, +23% good), and the corruption is exactly the hardware symptom — **one extra byte inserted at the front**, shifting everything: ``` @@ -356,21 +356,27 @@ absolute priority and preempt it) → SPRAM (1 cycle) → response → push → over budget under a frame write. Only ONE request may be in flight, so the latency is never hidden. -⚠️ **THREE FIXES TRIED AND FALSIFIED — do not repeat them:** +⚠️ **FOUR FIXES TRIED AND FALSIFIED — do not repeat them:** | attempt | result | |---|---| -| **Fair (round-robin) arbitration** instead of strict ETH priority | still 2/6. Bounds the wait to ~2 cycles but does not close the gap, and it breaks `spram_arbiter` T3. | +| **Fair (round-robin) arbitration** instead of strict ETH priority | still 1/6. Bounds the wait to ~2 cycles but does not close the gap, and it breaks `spram_arbiter` T3. | | **Deepen `tx_fifo` 2 → 8** for cushion | **0/6**, with leading zeros. The engine drains leftovers with ONE POP PER HEADER BYTE, so a deeper FIFO leaks stale bytes into the data phase. `tx_depth=2` is deliberate — keep it shallow. | | **Allow 2 outstanding requests** to pipeline the latency | **0/6 at every rate**, including `HALF=8` which previously passed. With no response FIFO, responses must be consumed the cycle they are valid; two in flight loses bytes. The existing comment was right. | +| **Return both SPRAM bytes per request** (16-bit response + `exi_rsp_odd`, two pushes per round trip) | **WORSE: 6/6 / 3/6 / 1/6** at `HALF=8/2/1` against the baseline's 6/6 / 6/6 / 1/6 — it broke `HALF=2`, left the `DE DE` duplication at `HALF=1`, and introduced a new bug where the FIRST read after an idle period returns all zeros. Reverted. | -**The promising direction, not yet attempted:** SPRAM is **16 bits wide**, and -the arbiter currently reads a word and returns a single byte selected by -address bit 0. Returning **both** bytes per request would let the register file -push two bytes per round trip, doubling the budget to 592 ns per request and -making it comfortable. That needs changes to `SPRAMArbiter`, the register -file's response handling, and their tests — the only approach found so far -that addresses the actual constraint rather than working around it. +**Why doubling the payload did not work, which is the useful part.** The budget +per round trip doubles to 592 ns only if the next request issues while the +second byte is still being pushed. It cannot: the second byte lives in a +holding register (`sp_buf`/`sp_cnt`) and the prefetch guard must wait for +`sp_cnt == 0`, or the next response overwrites a byte that has not been pushed +yet. So the sequence is still request → response → push → push → request, and +the *latency* per round trip is unchanged while the deadline for the first byte +of each pair is exactly as tight as before. **The constraint is round-trip +latency that nothing hides, not bytes per round trip** — so any fix must either +overlap requests with pushes (which needs a real response FIFO, the thing whose +absence killed the 2-outstanding attempt) or remove the round trip from the +critical path altogether. ### ⚠️ Simulation rate — always re-run at the hardware rate