diff --git a/CLAUDE.md b/CLAUDE.md index a773d89..d93a047 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -272,6 +272,30 @@ device and exercised the register file end-to-end: | IMR @0x08 write `0x86` + readback | `0x86` | | NCRA @0x00 write `0x01` (reset) | self-cleared to `0x00` on the first poll | +**The full Swiss init sequence runs on hardware (2026-09-30)**, driven from the +Pi over J4 with the board on 12 V — device ID `04 02 02 00`, NCRA reset +self-clearing, PAR0–5, MAR0–7, ANALOG, NWAYC, IMR `0x86` readback, NCRA SR, +and NWAYS `0x17`. See `hardware/re-bba-rb/exi_fullpath_rpi.py`. + +⚠️ **OPEN: GC→wire TX does not complete, and the W5100 is unresponsive.** +Setting the NCRA ST bits never produces TI, and NCRA keeps reading back `0x0a` +(ST never clears), for EVERY frame length 8…60 with a full NCRA reset between +attempts. The cause is NOT the EXI side and NOT `tx_bytes` depth (8-byte frames +fail too): the **UART shell's own socket-3 `udp unicast` also hangs** — it +echoes the command and never prints `sent` — so the W5100 bus master is stuck +and `TXFrameDrain` is simply waiting on it. Check the ETH_3V3 rail / GC_ON +(U12 gates the W5100 rail off GC_ON) before suspecting gateware. Note an +earlier contaminated run appeared to transmit at one specific length; that was +leftover state from a previous failed attempt feeding a still-pending drain +request — always NCRA-reset between TX attempts when probing. + +**`id_phase` is a ONE-SHOT.** The device ID is returned for reads of address 0 +only until the first NCRA write with the reset bit, which clears `id_phase` +permanently (`bba_register_file.py:330`); after that address 0 reads back NCRA. +This is correct — it matches the GC's init order — but it means a re-run of any +probe that already issued an NCRA reset will read `00 00 00 00` for the device +ID and look broken. Reconfigure the FPGA (`iceprog -t` is enough) to restore it. + Two bring-up gotchas, both cost real debugging time: - **The master must sample MISO during the CLK-LOW phase.** The FPGA drives MISO on the RISING edge (Mode 3), so sampling just after that edge races the diff --git a/hardware/re-bba-rb/exi_fullpath_rpi.py b/hardware/re-bba-rb/exi_fullpath_rpi.py new file mode 100644 index 0000000..5023ee0 --- /dev/null +++ b/hardware/re-bba-rb/exi_fullpath_rpi.py @@ -0,0 +1,130 @@ +#!/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])}") + if 0x08 in data and 0x06 in data: + print(" -> looks like an ARP frame came back. RX PATH WORKS.") +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()