"""Self-contained W5100S UDP send + register readback (bring-up diagnostic). One bitstream does EVERYTHING — reset, configure socket 3 for UDP, send a broadcast, then read back what the chip made of it — so there is no reflash gap and no "did the W5100 get reset in between?" ambiguity that made the read-only w5100_regdump probe inconclusive. It streams the verdict on the UART (115200 8N1): U: SIP=XX SSR=XX SIR=XX TRD=XXXX TWR=XXXX SIP SIPR0 read back. C0 (=192) => NETLCKR unlock worked and the network config actually took. 00 => the write was still being dropped. SSR Sn_SR after OPEN. 22 => SOCK_UDP, the socket really opened. 00 => never opened (OPEN rejected) — SEND can then do nothing. SIR Sn_IR after SEND. bit4 (10) => SEND_OK, the chip completed the send. TRD/TWR Sn_TX_RD / Sn_TX_WR. TWR should equal the queued byte count; TRD catching up to TWR means the chip actually drained/transmitted it. TWR=0000 => nothing was ever queued. Everything is driven from a flat (addr, data) script executed over the indirect bus, so the whole configure+send is a table rather than a hand-rolled FSM. Build/flash: python -m exi_bba.w5100_udptest --flash """ from amaranth import * _A_MR, _A_AR0, _A_AR1, _A_DR = 0b00, 0b01, 0b10, 0b11 _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 _PAYLEN = 32 # >= 18 so the frame clears 64B minimum # ── address-aliasing probe: distinct value to each candidate address ──────── _CFG1 = [ (0x4000, 0x11), (0x6000, 0x22), (0x8000, 0x33), (0x9800, 0x44), (0xC000, 0x55), ] _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)] class W5100UdpTest(Elaboratable): def __init__(self, *, strobe_cycles=3, rst_cycles=48_000, boot_cycles=2_040_000, settle_cycles=240_000): self._strobe = strobe_cycles self._rst = rst_cycles self._boot = boot_cycles # >= 60.3 ms W5100S init self._settle = settle_cycles # ~10 ms waits after OPEN and after SEND self.bus_addr = Signal(2) self.bus_data_o = Signal(8); self.bus_data_oe = Signal() self.bus_data_i = Signal(8) self.cs_n = Signal(init=1); self.rd_n = Signal(init=1) self.wr_n = Signal(init=1); self.rst_n = Signal(init=1) self.uart_tx = Signal(init=1) self.vals = Array([Signal(8, name=f"v{i}") for i in range(len(_READS))]) self.done = Signal() def elaborate(self, platform): m = Module() S = self._strobe # ── bus engine ─────────────────────────────────────────────────────── go=Signal(); rw=Signal(); ba=Signal(2); wd=Signal(8); rdv=Signal(8) bdone=Signal(); bctr=Signal(range(S+2)); rw_r=Signal() a_o=Signal(2); d_o=Signal(8); d_oe=Signal() cs=Signal(init=1); rdn=Signal(init=1); wrn=Signal(init=1); rstn=Signal(init=1) m.d.comb += [self.bus_addr.eq(a_o), self.bus_data_o.eq(d_o), self.bus_data_oe.eq(d_oe), self.cs_n.eq(cs), self.rd_n.eq(rdn), self.wr_n.eq(wrn), self.rst_n.eq(rstn)] m.d.sync += bdone.eq(0) with m.FSM(domain="sync", name="bus"): with m.State("IDLE"): m.d.sync += [cs.eq(1), rdn.eq(1), wrn.eq(1), d_oe.eq(0)] with m.If(go): m.d.sync += [a_o.eq(ba), rw_r.eq(rw), cs.eq(0), bctr.eq(0)] with m.If(rw): m.d.sync += [d_o.eq(wd), d_oe.eq(1), wrn.eq(0)] with m.Else(): m.d.sync += rdn.eq(0) m.next="STROBE" with m.State("STROBE"): m.d.sync += bctr.eq(bctr+1) with m.If(bctr == S-1): with m.If(~rw_r): m.d.sync += rdv.eq(self.bus_data_i) m.d.sync += [rdn.eq(1), wrn.eq(1)] m.next="FINISH" with m.State("FINISH"): m.d.sync += [cs.eq(1), d_oe.eq(0), bdone.eq(1)] m.next="IDLE" m.d.comb += [go.eq(0), rw.eq(0), ba.eq(0), wd.eq(0)] def bw(a,d): m.d.comb += [go.eq(1), rw.eq(1), ba.eq(a), wd.eq(d)] def br(a): m.d.comb += [go.eq(1), rw.eq(0), ba.eq(a)] saddr = Array([Const(a,16) for a,_ in _SCRIPT]) sdata = Array([Const(d,8) for _,d in _SCRIPT]) raddr = Array([Const(a,16) for _,a in _READS]) si = Signal(range(len(_SCRIPT)+1)) ri = Signal(range(len(_READS)+1)) ctr = Signal(range(max(self._rst,self._boot,self._settle)+2)) send_phase = Signal() # 0 = running CFG1, 1 = running CFG2 with m.FSM(domain="sync", name="seq"): with m.State("RST"): m.d.sync += [rstn.eq(0), ctr.eq(ctr+1)] with m.If(ctr == self._rst-1): m.d.sync += ctr.eq(0); m.next="BOOT" with m.State("BOOT"): m.d.sync += [rstn.eq(1), ctr.eq(ctr+1)] with m.If(ctr == self._boot-1): m.d.sync += ctr.eq(0); m.next="MRMODE" with m.State("MRMODE"): # MR = indirect + AI bw(_A_MR, _MR_IND | _MR_AI); m.next="MRMODE_W" with m.State("MRMODE_W"): with m.If(bdone): m.d.sync += si.eq(0); m.next="W_AR0" # generic script executor: indirect write saddr[si] = sdata[si] with m.State("W_AR0"): bw(_A_AR0, saddr[si][8:16]); m.next="W_AR0_W" with m.State("W_AR0_W"): with m.If(bdone): m.next="W_AR1" with m.State("W_AR1"): bw(_A_AR1, saddr[si][0:8]); m.next="W_AR1_W" with m.State("W_AR1_W"): with m.If(bdone): m.next="W_DR" with m.State("W_DR"): bw(_A_DR, sdata[si]); m.next="W_DR_W" with m.State("W_DR_W"): with m.If(bdone): m.d.sync += si.eq(si+1) with m.If((si+1 == _N1) & ~send_phase): m.d.sync += [ctr.eq(0), send_phase.eq(1)] m.next="SETTLE1" # let OPEN take effect with m.Elif(si+1 == len(_SCRIPT)): m.d.sync += ctr.eq(0); m.next="SETTLE2" # let SEND finish with m.Else(): m.next="W_AR0" with m.State("SETTLE1"): m.d.sync += ctr.eq(ctr+1) with m.If(ctr == self._settle-1): m.d.sync += ctr.eq(0); m.next="W_AR0" # continue with CFG2 with m.State("SETTLE2"): m.d.sync += ctr.eq(ctr+1) with m.If(ctr == self._settle-1): m.d.sync += [ctr.eq(0), ri.eq(0)]; m.next="R_AR0" # generic readback: indirect read raddr[ri] -> vals[ri] with m.State("R_AR0"): bw(_A_AR0, raddr[ri][8:16]); m.next="R_AR0_W" with m.State("R_AR0_W"): with m.If(bdone): m.next="R_AR1" with m.State("R_AR1"): bw(_A_AR1, raddr[ri][0:8]); m.next="R_AR1_W" with m.State("R_AR1_W"): with m.If(bdone): m.next="R_DR" with m.State("R_DR"): br(_A_DR); m.next="R_DR_W" with m.State("R_DR_W"): with m.If(bdone): m.d.sync += self.vals[ri].eq(rdv) with m.If(ri == len(_READS)-1): m.next="DONE" with m.Else(): m.d.sync += ri.eq(ri+1); m.next="R_AR0" with m.State("DONE"): m.d.comb += self.done.eq(1) # ── UART report ────────────────────────────────────────────────────── DIV = round(24_000_000/115_200) parts = b"U:"; pos={} for name,_ in _READS: parts += b" "+name.encode()+b"="; pos[name]=len(parts); parts += b"00" parts += b"\r\n" tmpl=list(parts); MSGLEN=len(tmpl) rom=Array([Const(b,8) for b in tmpl]) def hexch(n): return Mux(n<10, 0x30+n, 0x37+n) cur=Signal(8); uidx=Signal(range(MSGLEN+1)) m.d.comb += cur.eq(rom[uidx]) with m.Switch(uidx): for i,(name,_) in enumerate(_READS): p=pos[name] with m.Case(p): m.d.comb += cur.eq(hexch(self.vals[i][4:8])) with m.Case(p+1): m.d.comb += cur.eq(hexch(self.vals[i][0:4])) shift=Signal(10, init=0x3FF); nb=Signal(range(11)); bd=Signal(range(DIV)) gap=Signal(range(DIV*30+1)) m.d.comb += self.uart_tx.eq(shift[0]) with m.FSM(domain="sync", name="uart"): with m.State("IDLE"): with m.If(self.done): m.d.sync += uidx.eq(0); m.next="LOAD" with m.State("LOAD"): with m.If(uidx==MSGLEN): m.d.sync += gap.eq(DIV*30); m.next="GAP" with m.Else(): m.d.sync += [shift.eq(Cat(C(0,1),cur,C(1,1))), nb.eq(10), bd.eq(DIV-1)] m.next="SHIFT" with m.State("SHIFT"): with m.If(bd==0): m.d.sync += bd.eq(DIV-1) with m.If(nb==1): m.d.sync += uidx.eq(uidx+1); m.next="LOAD" with m.Else(): m.d.sync += [nb.eq(nb-1), shift.eq(Cat(shift[1:],C(1,1)))] with m.Else(): m.d.sync += bd.eq(bd-1) with m.State("GAP"): m.d.sync += gap.eq(gap-1) with m.If(gap==0): m.d.sync += uidx.eq(0); m.next="LOAD" return m class W5100UdpTestTop(Elaboratable): def elaborate(self, platform): m = Module() m.domains += ClockDomain("sync") m.submodules.hfosc = Instance("SB_HFOSC", p_CLKHF_DIV="0b01", i_CLKHFEN=Const(1,1), i_CLKHFPU=Const(1,1), o_CLKHF=ClockSignal("sync")) m.submodules.dut = dut = W5100UdpTest() w = platform.request("w5100", 0); u = platform.request("uart", 0) m.d.comb += [ w.addr.o.eq(dut.bus_addr), w.data.o.eq(dut.bus_data_o), w.data.oe.eq(dut.bus_data_oe), dut.bus_data_i.eq(w.data.i), w.cs_n.o.eq(dut.cs_n), w.rd_n.o.eq(dut.rd_n), w.wr_n.o.eq(dut.wr_n), w.rst_n.o.eq(dut.rst_n), u.tx.o.eq(dut.uart_tx), ] return m if __name__ == "__main__": import sys if "--build" in sys.argv or "--flash" in sys.argv: from exi_bba.synth import IceBreakerPlatform IceBreakerPlatform().build(W5100UdpTestTop(), do_program="--flash" in sys.argv, name="w5100_udptest", build_dir="build_w5100test") print("[built] build_w5100test/w5100_udptest.bin") raise SystemExit(0) print(f"script={len(_SCRIPT)} writes (CFG1={_N1}), payload={_PAYLEN}B at {_TXBUF:#06x}") print("use --build or --flash (hardware probe)")