Files
Roflin 59948812f3 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>
2026-09-20 14:33:49 +00:00

66 lines
3.3 KiB
Python

#!/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.")