59948812f3
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>
66 lines
3.3 KiB
Python
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.")
|