From 3d37e4ba6d5653112bbdc9b16d0c77ffe9178622 Mon Sep 17 00:00:00 2001 From: Dennis Brentjes Date: Sun, 20 Sep 2026 19:10:07 +0000 Subject: [PATCH] Hardware bring-up: first UDP packet transmitted and captured The W5100S ethernet back-end now works end-to-end on real hardware: a complete, correctly-checksummed UDP datagram (10.0.0.99:40000 -> 10.0.0.50:6464, 32-byte payload) was transmitted by the FPGA-driven W5100S and captured on a PC, after a full bidirectional ARP handshake. Records the three W5100S-specific findings in CLAUDE.md (NETLCKR unlock, the 15-bit IDM address space putting the buffers at 0x4000/0x6000, and the 60.3 ms reset-to-ready time), and notes that UDP unicast is the reliable bring-up path while a 255.255.255.255 send sets SEND_OK without appearing on the wire. w5100_udptest.py is the self-contained configure+send+readback probe that isolated all of this. Co-Authored-By: Claude Opus 5 --- CLAUDE.md | 21 ++++++++++++++++++--- exi_bba/w5100_udptest.py | 34 ++++++++++++++++++++++++++-------- 2 files changed, 44 insertions(+), 11 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 383698e..cd11231 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -330,9 +330,24 @@ the `BBATop` default. IMR 0x16, RMSR/TMSR 0x1A/0x1B), socket 0 at 0x0400 (S0_MR/CR/IR, TX_WR 0x424, RX_RSR 0x426, RX_RD 0x428), TX buffer 0x4000, RX buffer 0x6000. MACRAW mode. - **Status:** init/TX/RX (with wrap) verified vs a bus model; BBATop full - W5100→SPRAM→GC RX loop passes byte-for-byte; synth PASS (slow ~32≥24, capture - ~56≥54, 42% LC). Register addresses/MR bits are from the datasheet (from - memory) — **confirm at hardware bring-up**. + W5100→SPRAM→GC RX loop passes byte-for-byte; synth PASS. **Hardware bring-up + 2026-09-20: a real UDP datagram was transmitted and captured on a PC** — the + ethernet back-end is proven end-to-end. Register addresses confirmed on silicon, + with three W5100S-specific gotchas the from-memory map got wrong: + - **NETLCKR (0x0071) must be unlocked with 0x3A** before GWR/SUBR/SHAR/SIPR are + writable (default 0x00 = locked). The original W5100 has no such register, so + without this every network-config write is silently dropped and the chip + transmits with a zero source MAC/IP. + - **The parallel/indirect (IDM) bus decodes only a 15-bit offset**, so the TX/RX + buffers are at the original **0x4000 / 0x6000** — NOT the 0x8000/0xC000 the + W5100S datasheet pseudo-code quotes. Using 0x8000 makes payload writes alias + onto 0x0000+ and overwrite SHAR/SIPR. Proven with an address-aliasing probe + (0xC000 and 0x4000 read back the same cell). Do not "fix" this again. + - **The W5100S needs 60.3 ms after RSTn** before it answers; polling sooner reads + all-zeros and looks like a dead chip. + - UDP **unicast** works (the chip ARPs the peer first, per Sn_MR FARP). A send to + 255.255.255.255 sets SEND_OK but was not observed on the wire — prefer unicast + or a subnet-directed broadcast for bring-up tests. ### `rebbarb/` — LED blink demo (unchanged) - `rebbarb.py` — blinks LEDs via a PLL (36 MHz), demonstrates `IceBreakerPlatform` diff --git a/exi_bba/w5100_udptest.py b/exi_bba/w5100_udptest.py index 1ea8db3..af14f95 100644 --- a/exi_bba/w5100_udptest.py +++ b/exi_bba/w5100_udptest.py @@ -30,27 +30,45 @@ _MR_IND, _MR_AI, _MR_RST = 0x01, 0x02, 0x80 _SOCK = 3 _SBASE = 0x0400 + _SOCK * 0x0100 # socket-3 register block = 0x0700 -_TXBUF = 0x8000 + _SOCK * 0x0800 # socket-3 TX buffer (W5100S) = 0x9800 +_TXBUF = 0x4000 + _SOCK * 0x0800 # socket-3 TX buffer (IDM 15-bit) = 0x5800 _PAYLEN = 32 # >= 18 so the frame clears 64B minimum -# ── address-aliasing probe: distinct value to each candidate address ──────── +# ── phase 1: unlock + network config + open socket 3 as UDP ────────────────── _CFG1 = [ - (0x4000, 0x11), (0x6000, 0x22), (0x8000, 0x33), - (0x9800, 0x44), (0xC000, 0x55), + (0x0071, 0x3A), # NETLCKR unlock + (0x0009, 0x00), (0x000A, 0x09), (0x000B, 0xBF), # SHAR 00:09:BF:00:00:01 + (0x000C, 0x00), (0x000D, 0x00), (0x000E, 0x01), + (0x0005, 0xFF), (0x0006, 0xFF), (0x0007, 0xFF), (0x0008, 0x00), # SUBR /24 + (0x000F, 10), (0x0010, 0), (0x0011, 0), (0x0012, 99), # SIPR 10.0.0.99 + (0x001A, 0x55), (0x001B, 0x55), # RMSR / TMSR + (_SBASE + 0x00, 0x02), # Sn_MR = UDP + (_SBASE + 0x04, 0x9C), (_SBASE + 0x05, 0x40), # Sn_PORT = 40000 + (_SBASE + 0x01, 0x01), # Sn_CR = OPEN +] + +# ── phase 2: destination + payload + length + SEND ─────────────────────────── +_CFG2 = [ + (_SBASE + 0x0C, 10), (_SBASE + 0x0D, 0), # DIPR 10.0.0.50 (PC) + (_SBASE + 0x0E, 0), (_SBASE + 0x0F, 50), + (_SBASE + 0x10, 0x19), (_SBASE + 0x11, 0x40), # DPORT = 6464 +] + [(_TXBUF + i, 0x41 + (i % 26)) for i in range(_PAYLEN)] + [ + (_SBASE + 0x24, (_PAYLEN >> 8) & 0xFF), # Sn_TX_WR = len + (_SBASE + 0x25, _PAYLEN & 0xFF), + (_SBASE + 0x01, 0x20), # Sn_CR = SEND ] -_CFG2 = [(0x0030, 0x66)] _SCRIPT = _CFG1 + _CFG2 _N1 = len(_CFG1) # registers read back, in report order -_READS = [("A40", 0x4000), ("A60", 0x6000), ("A80", 0x8000), - ("A98", 0x9800), ("AC0", 0xC000), ("A30", 0x0030)] +_READS = [("B00", _TXBUF + 0), ("B01", _TXBUF + 1), ("B02", _TXBUF + 2), + ("C00", 0x4000), ("C01", 0x4001), + ("SIR", _SBASE + 0x02), ("TRl", _SBASE + 0x23)] class W5100UdpTest(Elaboratable): def __init__(self, *, strobe_cycles=3, rst_cycles=48_000, - boot_cycles=2_040_000, settle_cycles=240_000): + boot_cycles=2_040_000, settle_cycles=12_000_000): self._strobe = strobe_cycles self._rst = rst_cycles self._boot = boot_cycles # >= 60.3 ms W5100S init