6621ce9d0d
TX: the TWD fix lands a full 60-byte ARP on the wire, byte-for-byte correct (previously truncated at 16). TI sets. RX: a real 342-byte DHCP broadcast from the PC was received in MACRAW mode, assembled into the SPRAM ring and DMA-read back over EXI. The descriptor length 0x015A cross-checks exactly against the frame's own IP total-length field plus the ethernet header, so the assembler's length is confirmed, not merely plausible. Also replaces the full-path probe's 'is 08 and 06 somewhere in the buffer' guess with a real descriptor decode -- it called a DHCP frame an ARP reply. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
138 lines
5.2 KiB
Python
138 lines
5.2 KiB
Python
#!/usr/bin/env python3
|
|
"""Full BBA path test: Swiss init, GC->wire TX, wire->GC RX.
|
|
|
|
Sends a broadcast ARP request ("who has 10.0.0.50, tell 10.0.0.99"). The PC
|
|
should answer, which exercises the RX half in the same shot.
|
|
"""
|
|
import time, sys
|
|
import RPi.GPIO as GPIO
|
|
|
|
CLK, MOSI, MISO, CS = 11, 10, 9, 8
|
|
GPIO.setmode(GPIO.BCM); GPIO.setwarnings(False)
|
|
GPIO.setup(CLK, GPIO.OUT, initial=1); GPIO.setup(MOSI, GPIO.OUT, initial=0)
|
|
GPIO.setup(CS, GPIO.OUT, initial=1); GPIO.setup(MISO, GPIO.IN)
|
|
|
|
def xfer(b):
|
|
r = 0
|
|
for i in range(7, -1, -1):
|
|
GPIO.output(MOSI, (b >> i) & 1)
|
|
GPIO.output(CLK, 0)
|
|
r = (r << 1) | GPIO.input(MISO)
|
|
GPIO.output(CLK, 1)
|
|
return r
|
|
|
|
def read(addr, n):
|
|
GPIO.output(CS, 0); GPIO.output(CLK, 1); time.sleep(0.0003)
|
|
xfer((addr >> 6) & 0x7F); xfer(((addr & 0x3F) << 2) | ((n - 1) & 3))
|
|
time.sleep(0.002)
|
|
out = [xfer(0) for _ in range(n)] # EXACTLY n
|
|
GPIO.output(CS, 1); time.sleep(0.0005)
|
|
return out
|
|
|
|
def write(addr, data):
|
|
GPIO.output(CS, 0); GPIO.output(CLK, 1); time.sleep(0.0003)
|
|
xfer(0x80 | ((addr >> 6) & 0x7F))
|
|
xfer(((addr & 0x3F) << 2) | ((len(data) - 1) & 3))
|
|
for b in data: xfer(b)
|
|
GPIO.output(CS, 1); time.sleep(0.0005)
|
|
|
|
def stream(addr, n):
|
|
"""SPRAM/DMA read: addr >= 0x100 streams until CS, so clock freely."""
|
|
GPIO.output(CS, 0); GPIO.output(CLK, 1); time.sleep(0.0003)
|
|
xfer((addr >> 6) & 0x7F); xfer(((addr & 0x3F) << 2) | 3)
|
|
time.sleep(0.002)
|
|
out = [xfer(0) for _ in range(n)]
|
|
GPIO.output(CS, 1); time.sleep(0.0005)
|
|
return out
|
|
|
|
def hx(v): return " ".join("%02x" % x for x in v)
|
|
|
|
MAC = [0x00, 0x09, 0xBF, 0x00, 0x00, 0x01]
|
|
SRC_IP, DST_IP = [10, 0, 0, 99], [10, 0, 0, 50]
|
|
|
|
print("=" * 62)
|
|
print("PHASE 1 Swiss init sequence")
|
|
print("=" * 62)
|
|
dev = read(0x0000, 4)
|
|
print(f" 1. device ID {hx(dev)} {'OK' if dev==[4,2,2,0] else 'FAIL'}")
|
|
if dev != [4, 2, 2, 0]:
|
|
print(" (ID is a one-shot before the first NCRA reset - continuing)")
|
|
write(0x00, [0x01]); print(" 2. NCRA = 0x01 (reset + W5100 init)")
|
|
for i in range(40):
|
|
v = read(0x00, 1)[0]
|
|
if not (v & 1): print(f" 3. NCRA reset self-cleared after {i} polls"); break
|
|
time.sleep(0.005)
|
|
else:
|
|
print(" 3. NCRA reset did NOT clear")
|
|
write(0x20, MAC[:4]); write(0x24, MAC[4:])
|
|
print(f" 4. PAR0-5 = {hx(read(0x20,4))} {hx(read(0x24,2))}")
|
|
write(0x26, [0xFF]*4); write(0x2A, [0xFF]*4); print(" 5. MAR0-7 = FF (promiscuous multicast)")
|
|
write(0x2E, [0xD6]); print(" 6. ANALOG = 0xD6")
|
|
write(0x30, [0x01]); print(" 7. NWAYC")
|
|
write(0x08, [0x86]); print(f" 8. IMR = 0x86, readback {hx(read(0x08,1))}")
|
|
write(0x32, [0x02]); print(" 9. GCA")
|
|
write(0x00, [0x08]); print(f" 10. NCRA SR=1, readback {hx(read(0x00,1))}")
|
|
nw = read(0x31, 1); print(f" 11. NWAYS = {hx(nw)} {'link up' if nw==[0x17] else 'UNEXPECTED'}")
|
|
|
|
# ---------------- build the ARP frame ----------------
|
|
frame = [0xFF]*6 + MAC + [0x08, 0x06]
|
|
frame += [0x00,0x01, 0x08,0x00, 0x06, 0x04, 0x00,0x01]
|
|
frame += MAC + SRC_IP + [0x00]*6 + DST_IP
|
|
frame += [0x00] * (60 - len(frame))
|
|
|
|
print()
|
|
print("=" * 62)
|
|
print(f"PHASE 2 GC -> wire: {len(frame)}-byte broadcast ARP")
|
|
print("=" * 62)
|
|
print(f" who has {'.'.join(map(str,DST_IP))}, tell {'.'.join(map(str,SRC_IP))}")
|
|
print(f" src MAC {':'.join('%02x'%b for b in MAC)}")
|
|
rwp_before = read(0x16, 2)
|
|
write(0x34, [len(frame) & 0xFF]); write(0x35, [(len(frame) >> 8) & 0xFF])
|
|
print(f" TWD = {hx(read(0x34,2))} (frame length)")
|
|
t0 = time.time()
|
|
for b in frame:
|
|
write(0x48, [b]) # one byte per transaction: eff_addr increments
|
|
print(f" streamed {len(frame)} bytes to TXDATA in {time.time()-t0:.2f}s")
|
|
write(0x00, [0x08 | 0x02]) # SR + ST0 -> TX trigger
|
|
print(" NCRA ST set -> TX triggered")
|
|
for i in range(30):
|
|
ir = read(0x09, 1)[0]
|
|
if ir & 0x04:
|
|
print(f" IR = {ir:02x} TI (transmit complete) SET after {i} polls"); break
|
|
time.sleep(0.01)
|
|
else:
|
|
print(f" IR = {read(0x09,1)[0]:02x} TI never set")
|
|
|
|
print()
|
|
print("=" * 62)
|
|
print("PHASE 3 wire -> GC: waiting for the ARP reply")
|
|
print("=" * 62)
|
|
print(f" RWP before = {hx(rwp_before)}")
|
|
got = None
|
|
for i in range(60):
|
|
rwp = read(0x16, 2)
|
|
ir = read(0x09, 1)[0]
|
|
if rwp != rwp_before or (ir & 0x02):
|
|
got = (rwp, ir); break
|
|
time.sleep(0.05)
|
|
if got:
|
|
rwp, ir = got
|
|
print(f" RWP moved to {hx(rwp)}, IR = {ir:02x} (RI {'set' if ir&2 else 'clear'})")
|
|
data = stream(0x0100, 80)
|
|
print(f" SPRAM @0x100: {hx(data[:16])}")
|
|
print(f" {hx(data[16:32])}")
|
|
print(f" {hx(data[32:48])}")
|
|
# Decode the ring descriptor instead of guessing from stray bytes: the
|
|
# first 4 bytes are the descriptor, whose length INCLUDES itself.
|
|
dlen = (data[2] << 8) | data[3]
|
|
etype = (data[16] << 8) | data[17]
|
|
print(f" descriptor length 0x{dlen:04X} = {dlen} -> {dlen-4} frame bytes")
|
|
print(f" dst {':'.join('%02x'%b for b in data[4:10])}"
|
|
f" src {':'.join('%02x'%b for b in data[10:16])} ethertype 0x{etype:04X}")
|
|
if 4 < dlen <= 1518:
|
|
print(" -> RX PATH WORKS: a real frame was assembled into the SPRAM ring.")
|
|
else:
|
|
print(f" no RX after 3s (RWP still {hx(read(0x16,2))}, IR {read(0x09,1)[0]:02x})")
|
|
print(" TX may still have worked - check the capture.")
|
|
GPIO.cleanup()
|