Bring-up: W5100 bus self-test, eFuse OVLO bug, Pi SPI EXI host

From the physical V2 bring-up session (2026-09-20):

- exi_bba/w5100_selftest.py (NEW): standalone FPGA<->W5100 bus diagnostic.
  Clock-independent data-bus walk (direct-mode GAR0 read-back, all 8 lines) +
  MR software-reset self-clear (needs the 25 MHz crystal). Reports 4 verdicts on
  D6/D7 LEDs AND over UART (115200): GOOD / data-line-N / clock-dead / no-response.
  Boot wait is 85 ms (> the W5100S 60.3 ms init). On this board: GOOD -- bus +
  all 8 data lines + 25 MHz clock all verified (unlike V1's broken D3).

- REVIEW.md / TODO.md: eFuse U13 OVLO is tied to GND, which on the TPS25961
  selects the internal FIXED ~6 V overvoltage cutoff -- so it blocks the 12 V
  rail entirely (bench-confirmed: 12V_EXI=0, GC_ON=0, W5100 unpowered). FAB-
  BLOCKER for V2.1: fix with an OVLO divider (~1.1M/100k -> trip ~15 V). Bench
  workaround: lift U13 pin 2 (OVLO) off GND. Also closed the eFuse-placement,
  GND-via/3V3-zone, and CC-ESD TODO items.

- hardware/re-bba-rb/exi_devid_spi.py (NEW): Pi hardware-SPI (~25 MHz) EXI
  device-ID host, companion to the bit-bang exi_devid_rpi.py.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-09-20 14:33:49 +00:00
parent 162dbf7d5f
commit 59948812f3
5 changed files with 549 additions and 12 deletions
+13
View File
@@ -308,6 +308,19 @@ datasheet extraction, WebSearch/WebFetch for parts and reference designs.
EFUSE_EN : R46, R47, U13.5 EN/UVLO
EFUSE_ILIM: R45, U13.3 ILIM
GND : U13.2 OVLO, U13.4 GND, R45.2, R47.2, C66.2
⚠️ **BUG FOUND AT BRING-UP 2026-09-16 — this OVLO=GND is WRONG for a 12 V
rail and the eFuse passes NOTHING.** Per the TPS25961 datasheet, tying OVLO to
GND selects the *internal fixed* overvoltage threshold **VOVP(R) = 5.98 V typ
(5.55–6.5 V)** — NOT "OVLO disabled". A 12 V input is far above that, so the
device sits in overvoltage lockout with the FET off (bench-confirmed: 12V_EXI =
0 V, GC_ON = 0 V, W5100 unpowered). The earlier review note ("OVLO tied to GND
for the internal fixed threshold, abs max 6.5 V fine") caught the pin abs-max
but MISSED that the fixed VIN cutoff is ~6 V. **Fix (V2.1):** drive OVLO from a
divider off 12V_RAW so the trip sits ~14–16 V (above 12 V, below the 19 V max /
the D3 clamp): e.g. **R_top ≈ 1.1 MΩ (12V_RAW→OVLO) + R_bottom ≈ 100 kΩ
(OVLO→GND)** → OVLO pin = 1.24 V at ~15 V VIN, and ~1.0 V at 12 V (stays on).
BENCH WORKAROUND to keep bring-up moving: inject 12 V straight onto 12V_EXI
(C65+ pad or U3.3 VIN), bypassing the mis-set eFuse.
**D3 (SMAJ12A TVS) stays on the INPUT side on purpose** — it clamps
GC-side surges before they reach the eFuse's 21 V absolute maximum. **C65
is on the OUTPUT side on purpose** — it is the bulk cap whose inrush we are
+19 -12
View File
@@ -241,6 +241,14 @@ schematic changes.
at (148.49, 61.7225) near U11 pin 48. Verified 2026-07-22: nets
`/ethernet/ETH_RST` + `/ethernet/ETH_3V3` both present on the footprint,
0 unconnected items.
- [ ] 🔴 **eFuse U13 OVLO MISCONFIGURED — BLOCKS 12 V (found at bring-up
2026-09-16, FAB-BLOCKER for V2.1).** OVLO (pin 2) is tied to GND, which on
the TPS25961 selects the internal *fixed* overvoltage cutoff ~5.98 V — so a
12 V rail is permanently in overvoltage lockout and the eFuse passes
nothing (bench-confirmed: 12V_EXI=0, GC_ON=0, W5100 unpowered). **Fix:**
OVLO divider off 12V_RAW, R_top ≈ 1.1 M / R_bottom ≈ 100 k → trip ~15 V.
See REVIEW.md eFuse entry. Bench workaround: feed 12 V onto 12V_EXI (C65+ /
U3 VIN) to bypass it. This must be fixed before any V2.1 fab order.
- [ ] **12 V inrush limiter (U13 TPS25961 eFuse) — SCHEMATIC DONE 2026-07-31**,
full design rationale in REVIEW.md §6. J3.5 → 12V_RAW → U13 → 12V_EXI;
D3 (TVS) stays upstream, C65 downstream. R45 100 k sets I_LIM ≈ 500 mA
@@ -273,16 +281,14 @@ schematic changes.
board PCF.
- [ ] **Gateware: EXI INT drive style** — prefer open-drain emulation (drive
low / release) on J3.3 rather than push-pull high.
- [ ] **New 2026-07-22: 2 GND vias short the `/Power/3V3` zone on In2.Cu** —
found during the last DRC pass, 4 error-severity violations (2×
`clearance` + 2× `hole_clearance`, both 0 mm actual). Two GND vias at
(190.78, 58.58) and (190.34, 58.58) — just south of J3's courtyard,
likely stitching vias added during the J3 reroute — sit directly on
top of the 3V3 pour on In2.Cu. This is a real short risk, not a
manufacturability nit: move or delete these two vias (or void the 3V3
zone locally) before ordering. Also 1 harmless `track_dangling`: a
0.06 mm stub on `/exi/EXI_MOSI_RAW` near (188.39, 57.52) — routing
debris, delete when convenient.
- [x] **2 GND vias vs `/Power/3V3` zone on In2.Cu — RESOLVED 2026-08-28.**
Was 4 error-severity DRC violations (2× `clearance` + 2× `hole_clearance`,
0 mm) from GND stitching vias sitting on the 3V3 pour. Cleared by refilling
the zones (the pour now voids around the vias); `kicad-cli pcb drc
--severity-error` = 0 on the filled board. The `flash`/production skill
(kicad-manufacturing-export) now refills zones before export, so this
can't silently regress. (Any leftover `track_dangling` stub is
warning-level debris, delete when convenient.)
## Broader items (beyond the wiring review — block fab on the first two)
@@ -374,8 +380,9 @@ schematic changes.
exactly what stock iceprog opens by default, which now matches the
rewired pins. Optionally program serial/product string later with
FT_PROG (do NOT change VID/PID, or iceprog needs -d vid:pid).
- [ ] CC1/CC2 have no ESD protection (USBLC6 covers D+/D− only) — optional,
most hobby designs skip; note spare-channel budget if respinning D2 area.
- [x] CC1/CC2 ESD protection — **WON'T FIX** (Dennis, 2026-08-28). USBLC6
covers D+/D− only; CC lines left unprotected by choice, as most hobby
designs do. Closed, not a pending item.
- [x] `.history/` (embedded `.git`) — VERIFIED ignored via root
`.gitignore:28`, nothing tracked.
+65
View File
@@ -0,0 +1,65 @@
#!/usr/bin/env python3
"""EXI device-ID query over the Pi's HARDWARE SPI (spidev) — a near-full-speed
(~25 MHz, close to the GameCube's real ~27 MHz EXI clock) companion to the
bit-bang exi_devid_rpi.py.
Use bit-bang (exi_devid_rpi.py) for a slow logic smoke test; use THIS to prove
the FPGA's SPI Mode-3 capture front-end also samples cleanly *at rate* without a
GameCube. The Pi 4 SPI SCK is core_clk/even-divisor, so a 27 MHz request lands
on ~25 MHz — comfortably inside the design's 54 MHz 2x oversampling (and a hair
more margin than the real 27 MHz). Do NOT push past ~27 MHz: the capture domain
only closes ~49-58 MHz across seeds, so >~27 MHz input under-samples.
The FPGA prefetches the reply during a clock-IDLE gap between the header and the
data bytes (the GC pauses the clock there), so CS must stay LOW across that gap.
spidev toggles CS per call, so we park SPI0's hardware CS on BCM7 (unused) and
drive the real CS manually on BCM8. Pi config (then reboot):
/boot/firmware/config.txt: dtoverlay=spi0-1cs,cs0_pin=7
(That frees BCM8 and puts the unused hw CE0 on BCM7. To go back to the bit-bang
script, remove that line + reboot so BCM9/10/11 return to plain GPIO.)
Wiring (Pi -> FPGA EXI, all 3.3 V, shared GND) — identical to the bit-bang one:
SCLK BCM11 (pin 23) -> EXI CLK (FPGA 44)
MOSI BCM10 (pin 19) -> EXI MOSI (FPGA 4)
MISO BCM9 (pin 21) <- EXI MISO (FPGA 3)
CS BCM8 (pin 24) -> EXI CS (FPGA 45) [held low manually across the txn]
GND (pin 25) <-> GND
Run: sudo python3 exi_devid_spi.py [speed_hz] [gap_us]
"""
import sys, time
import spidev
try:
import RPi.GPIO as GPIO
except ImportError:
raise SystemExit("needs RPi.GPIO -> sudo apt install python3-rpi.gpio")
SPEED = int(sys.argv[1]) if len(sys.argv) > 1 else 27_000_000 # ~25 MHz actual (core/10)
GAP_US = int(sys.argv[2]) if len(sys.argv) > 2 else 50 # header->data prefetch gap
CS = 8 # BCM8 manual chip-select (hardware CE0 parked on BCM7 by the overlay)
spi = spidev.SpiDev()
spi.open(0, 0)
spi.mode = 0b11 # SPI Mode 3: CPOL=1, CPHA=1 (EXI CLK idles high)
spi.max_speed_hz = SPEED
GPIO.setmode(GPIO.BCM)
GPIO.setup(CS, GPIO.OUT, initial=1) # CS idle high
GPIO.output(CS, 0) # assert CS (active low) for the whole transaction
spi.xfer2([0x00, 0x03]) # header: read, addr[12:0]=0, len-1=3 (4 bytes)
time.sleep(GAP_US / 1e6) # clock-idle gap -> FPGA prefetches the reply
resp = spi.xfer2([0x00] * 6) # clock read bytes; MISO carries the response
GPIO.output(CS, 1) # deassert CS
spi.close(); GPIO.cleanup()
print(f"SPI mode 3, {SPEED/1e6:.1f} MHz requested (actual ~= core/even-divisor, ~25 MHz), "
f"header->data gap {GAP_US} us")
print("read bytes:", " ".join("%02x" % b for b in resp))
want = [0x04, 0x02, 0x02, 0x00]
if any(resp[i:i + 4] == want for i in range(len(resp) - 3)):
print("EXI device-ID 04 02 02 00 -> FOUND. Capture path works at ~full EXI rate!")
else:
print("device-ID not found. If the slow bit-bang test (exi_devid_rpi.py) PASSES "
"but this fails, the front-end is marginal at rate on the flashed seed -> "
"reflash the BEST --seeds bitstream, and/or raise the gap (arg2) or lower "
"the speed (arg1). Otherwise check wiring/GND and that the FPGA is running.")