From 2b210dc5e38c4fe8b38c19b763eddf8cea2f6e0f Mon Sep 17 00:00:00 2001 From: Dennis Brentjes Date: Wed, 30 Sep 2026 18:42:16 +0000 Subject: [PATCH] EXI bring-up: full Swiss init runs on hardware; W5100 TX blocked The whole GC init sequence now runs over EXI on the real board: device ID, NCRA reset self-clear, PAR0-5, MAR, ANALOG, NWAYC, IMR, NCRA SR, NWAYS 0x17. GC->wire TX does not complete. Not an EXI fault and not tx_bytes depth -- 8-byte frames fail too, and the UART shell's own UDP send hangs the same way, so the W5100 bus master is stuck and TXFrameDrain is waiting on it. Also records that id_phase is a one-shot cleared by the first NCRA reset, so a re-run probe reads 00 00 00 00 for the device ID and looks broken. Co-Authored-By: Claude Opus 5 --- CLAUDE.md | 24 +++++ hardware/re-bba-rb/exi_fullpath_rpi.py | 130 +++++++++++++++++++++++++ 2 files changed, 154 insertions(+) create mode 100644 hardware/re-bba-rb/exi_fullpath_rpi.py 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()