Files
rebbarb/hardware/re-bba-rb/exi_devid_rpi.py
T
Roflin a082e15c4f EXI bring-up: device enumerates on hardware over the J4 header
First silicon proof of the EXI capture path.  A Pi bit-banging EXI into J4
reads the device ID 04 02 02 00 21/21, NWAYS 0x17, HIPR 0x01, writes and reads
back PAR0-3, writes IMR, and sees NCRA's reset bit self-clear.

Fixes two host-side bugs found getting there:
- exi_devid_rpi.py sampled MISO just after the rising edge, racing the slave's
  own update and landing one bit early (04 came back as 08).  Sample in the
  CLK-low phase, as the simulation does.
- pi-exi-wirecheck.py's bridge scan drove HIGH against pull-downs, but the
  FPGA's SB_IO pull-up out-pulls the Pi's internal pull-down, so every pin
  reported as bridged.  The aggressor now drives LOW against pull-ups.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-30 18:32:18 +00:00

72 lines
3.2 KiB
Python

#!/usr/bin/env python3
"""EXI device-ID query for re-bba-rb bring-up — run on a Raspberry Pi wired to
the FPGA's EXI pins, to validate the GameCube-facing capture path with NO W5100
involved (so it works even on a unit whose W5100/ethernet side is dead).
It bit-bangs the EXI SPI the way the GameCube does: CLK idles HIGH; the FPGA
samples MOSI on the falling edge and drives MISO on the rising edge. It sends
the device-ID read header (read 4 bytes @ addr 0 = 0x00 0x03), then pauses (the
GC pauses the clock between the header and the data so the FPGA can prefetch the
response), then clocks read bytes and looks for the reply 04 02 02 00.
Wiring (Pi BCM GPIO -> FPGA EXI, all 3.3 V, shared GND):
CLK -> EXI CLK (FPGA pin 44)
MOSI -> EXI MOSI (FPGA pin 4, an FPGA *input*)
MISO <- EXI MISO (FPGA pin 3, an FPGA *output*)
CS -> EXI CS (FPGA pin 45)
GND <-> GND
Reach these at the SP1 edge connector (J3) or on the series damper resistors.
Edit the BCM pin numbers below to match your wiring. Run: sudo python3 exi_devid_rpi.py
Requires the FPGA to be running the full BBA design (e.g. build/seed3/top.bin)
and its 12 MHz clock (X1) alive — both independent of the W5100.
"""
import time
try:
import RPi.GPIO as GPIO
except ImportError:
raise SystemExit("needs RPi.GPIO -> sudo apt install python3-rpi.gpio")
# ---- set these to your actual Pi BCM pin numbers ----
CLK, MOSI, MISO, CS = 11, 10, 9, 8
GPIO.setmode(GPIO.BCM)
GPIO.setup(CLK, GPIO.OUT, initial=1) # CPOL=1: clock idles HIGH
GPIO.setup(MOSI, GPIO.OUT, initial=0)
GPIO.setup(CS, GPIO.OUT, initial=1) # inactive HIGH
GPIO.setup(MISO, GPIO.IN)
def xfer(b):
"""Clock one byte MSB-first; FPGA samples MOSI on falling, drives MISO on rising."""
r = 0
for i in range(7, -1, -1):
GPIO.output(MOSI, (b >> i) & 1) # MOSI stable while CLK is high
GPIO.output(CLK, 0) # falling edge -> FPGA samples MOSI
# Sample MISO HERE, while CLK is low. The FPGA drives MISO on the
# RISING edge, so reading just after that edge races the slave's own
# update and lands one bit position early (04 02 02 00 comes back as
# 08 ...). The low phase is when the bit is settled.
r = (r << 1) | GPIO.input(MISO)
GPIO.output(CLK, 1) # rising edge -> FPGA drives next bit
return r
GPIO.output(CS, 0) # assert CS (active low)
xfer(0x00) # header byte 0: read, addr[12:6]=0
xfer(0x03) # header byte 1: addr[5:0]=0, len-1=3 (4 B)
time.sleep(0.001) # gap so the FPGA can prefetch the reply
resp = [xfer(0x00) for _ in range(6)] # clock a few read bytes
GPIO.output(CS, 1) # deassert CS
GPIO.cleanup()
print("read bytes:", " ".join("%02x" % x for x in resp))
want = [0x04, 0x02, 0x02, 0x00]
found = any(resp[i:i + 4] == want for i in range(len(resp) - 3))
if found:
print("EXI device-ID 04 02 02 00 -> FOUND. EXI capture path works!")
else:
print("device-ID not found. Try: swap to mode-2 edges (move the MISO sample "
"to just before the falling edge), check wiring/GND, confirm the FPGA "
"is running the BBA design and its 12 MHz clock is alive.")