diff --git a/CLAUDE.md b/CLAUDE.md index cd11231..e159990 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -228,6 +228,43 @@ 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 still datasheet-from-memory — **confirm at hardware bring-up**. +### 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 +header instead of the SP1 edge connector. It does NOT repin the EXI resource — +it **merges** a second set of pads onto the existing ones, so SP1 stays live and +the default build is unchanged: + +| J4 | DBG | FPGA pin | EXI | +|---|---|---|---| +| 1 | — | — | GND | +| 2 | DBG0 | 9 | CLK (in) | +| 3 | DBG1 | 10 | MOSI (in) | +| 4 | DBG2 | 11 | MISO (out) | +| 5 | DBG3 | 12 | CS_N (in) | +| 6 | DBG4 | 6 | INT_N (out) | + +The three inputs are **AND-merged** with `PULLUP=1` on both pad sets. AND is +exact, not a hack: every EXI input is inactive-HIGH (CPOL=1 → CLK idles high, +CS_N is active-low, MOSI only matters while CS_N is low), so the unplugged side +idles at 1 through its pull-up and passes the other side through — no select +signal needed. Outputs drive both pads. **Drive one side at a time**; a GC on +SP1 plus a host on J4 will fight and the AND yields the mangled product. +Build refuses to run alongside `UART_J4` (also pins 9/10) or an `ETH_D3_PIN` +aimed at a J4 pad. + +**Why merge rather than repin:** all five J4 pads are on the SAME bottom die +edge as the EXI pads (x=13–18 vs x=5–9, both y=0 per icestorm's `pinloc_db` +`5k-sg48`), so the capture domain keeps its bottom-edge anchor. Repinning would +have perturbed the one placement variable the pin-co-location experiment showed +is worth ±5 MHz — see "Remaining capture bottleneck" above. + +**Measured 2026-09-30**, `EXI_J4=1` + socket-3 UDP, `--seeds 8`: +`clk` 29.2–31.4 MHz (PASS on all 8); `capture_clk` **4 of 8 PASS** (seeds 2, 5, +6, 8), best **seed 8 = 63.09 MHz** (+9.1 MHz / 17% margin). Same pass rate as +the default build and a better best seed, so the merge costs no measurable +capture timing — but it is still seed-dependent, so **always sweep**. + ### Pending work - **Synthesis/timing**: ⚠️ partially — synthesizes and P&Rs, `clk` closes with wide margin, but `capture_clk` closes on only **2 of 8 seeds** and the default