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>
This commit is contained in:
2026-09-30 18:32:18 +00:00
parent 28b7665b33
commit a082e15c4f
4 changed files with 93 additions and 11 deletions
+13 -10
View File
@@ -85,25 +85,28 @@ for pin, name, j4 in DRIVEN_BY_PI:
print()
print("=" * 66)
print("T3 Bridge scan on the Pi-driven lines (catches solder bridges on J4).")
print(" Drive one HIGH, hold the others as inputs with pull-down: any")
print(" victim reading HIGH shares copper with the aggressor.")
print("T3 Bridge scan on the Pi-driven lines (catches solder bridges).")
print(" The aggressor drives LOW and victims are pulled UP: a strong")
print(" driver beats any pull, so a victim dragged low IS bridged.")
print(" (Driving HIGH against pull-downs does NOT work here -- the FPGA's")
print(" SB_IO pull-up out-pulls the Pi's internal pull-down, which would")
print(" report every pin as bridged. T2 shows this directly: down=1.)")
print("=" * 66)
for pin, name, j4 in DRIVEN_BY_PI:
victims = [(p, n) for p, n, _ in DRIVEN_BY_PI if p != pin]
victims = [(p2, n) for p2, n, _ in DRIVEN_BY_PI if p2 != pin]
for vp, _ in victims:
GPIO.setup(vp, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)
GPIO.setup(vp, GPIO.IN, pull_up_down=GPIO.PUD_UP)
GPIO.setup(pin, GPIO.OUT)
GPIO.output(pin, 1)
GPIO.output(pin, 0) # aggressor drives LOW
time.sleep(0.003)
bad = [n for vp, n in victims if GPIO.input(vp) == 1]
GPIO.output(pin, 0)
bad = [n for vp, n in victims if GPIO.input(vp) == 0]
GPIO.output(pin, 1)
GPIO.setup(pin, GPIO.IN, pull_up_down=GPIO.PUD_OFF)
if bad:
print(f" drive {name:<5} HIGH -> ** BRIDGED to {', '.join(bad)} **")
print(f" drive {name:<5} LOW -> ** BRIDGED to {', '.join(bad)} **")
fails.append(f"{name} is bridged to {', '.join(bad)}")
else:
print(f" drive {name:<5} HIGH -> no bridge to {', '.join(n for _, n in victims)}")
print(f" drive {name:<5} LOW -> no bridge to {', '.join(n for _, n in victims)}")
print()
print("=" * 66)