From e80b7485d580019e6b1d55a274d22c933f1034bd Mon Sep 17 00:00:00 2001 From: Dennis Brentjes Date: Wed, 30 Sep 2026 19:09:25 +0000 Subject: [PATCH] fix(w5100): actually drive RSTn on init so a wedged chip can recover w5100_rst_n was declared and never driven anywhere -- the pin sat at its init value of 1 forever. The only reset the chip ever got was the MR software reset, which arrives over the very bus a wedged chip has stopped answering, so a wedged W5100 could not be recovered at all: not by NCRA reset, not by reflashing, not even by reconfiguring the FPGA (RSTn just floats to its R36 pull-up while unconfigured). Only a 12 V power cycle worked. Init now pulses RSTn low (1 ms, datasheet minimum is ~2 us) and waits the full post-reset settle before touching the bus. RSTn low time is clamped to reset_cycles so the testbenches' small values stay reachable by wait_ctr. Co-Authored-By: Claude Opus 5 --- exi_bba/w5100_parallel_master.py | 37 +++++++++++++++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/exi_bba/w5100_parallel_master.py b/exi_bba/w5100_parallel_master.py index f47cd87..567708b 100644 --- a/exi_bba/w5100_parallel_master.py +++ b/exi_bba/w5100_parallel_master.py @@ -50,6 +50,10 @@ _SUBR0 = 0x0005 # Subnet mask, 4 bytes _SHAR0 = 0x0009 # Source MAC, 6 bytes _SIPR0 = 0x000F # Source IP, 4 bytes _NETLCKR = 0x0071 # W5100S Network Lock Register (WO) — GWR/SUBR/SHAR/SIPR are + +# Default RSTn low time before release (sync cycles). 24_000 @ 24 MHz = 1 ms; +# the W5100S datasheet minimum is ~2 us, so this is generous on purpose. +_RST_LOW_DEFAULT = 24_000 _NETLCK_UNLOCK = 0x3A # writable ONLY while NETLCKR==0x3A (default 0x00 = locked) _IR = 0x0015 # Interrupt register _IMR = 0x0016 # Interrupt mask @@ -150,6 +154,10 @@ class W5100ParallelMaster(Elaboratable): self._strobe = strobe_cycles # MR-reset settle wait; testbench overrides with a small value. self._reset_cycles = reset_cycles + # RSTn low time. Clamped to reset_cycles so the testbenches (which + # pass a tiny reset_cycles to stay fast) keep wait_ctr wide enough to + # actually reach it -- an unreachable target would hang the FSM. + self._rst_low = min(_RST_LOW_DEFAULT, reset_cycles) # ── Optional socket-1 UDP bring-up test ─────────────────────────────── # When enabled, init also configures the W5100's IP stack (GAR/SUBR/ @@ -556,7 +564,8 @@ class W5100ParallelMaster(Elaboratable): with m.If(self.init_req): for i in range(6): m.d.sync += mac_shadow[i].eq(self.par[i*8:(i+1)*8]) - m.next = "MR_RST" + m.d.sync += [self.w5100_rst_n.eq(0), wait_ctr.eq(0)] + m.next = "HW_RST" with m.Elif(~self.w5100_int_n): m.next = "RX_CHECK" with m.Elif(self.tx_valid & self.tx_sof): @@ -582,6 +591,32 @@ class W5100ParallelMaster(Elaboratable): self.dbg_rd_done.eq(1)] m.next = "IDLE" + # Pulse the W5100's RSTn pin, then let it boot, BEFORE touching the + # bus. This used to be missing entirely: w5100_rst_n was declared + # and never driven, so the only reset the chip ever got was the MR + # software reset -- which a wedged chip does not answer, because it + # arrives over the very bus that is stuck. The result was a W5100 + # that could only be recovered by a 12 V power cycle; even + # reconfiguring the FPGA did not help, since RSTn just floats to its + # pull-up (R36) while the FPGA is unconfigured. Driving it here + # makes an NCRA reset able to recover the chip on its own. + # RSTn low: datasheet minimum is ~2 us; _RST_LOW is far longer and + # costs nothing on a path that runs once per init. + with m.State("HW_RST"): + with m.If(wait_ctr == self._rst_low): + m.d.sync += [self.w5100_rst_n.eq(1), wait_ctr.eq(0)] + m.next = "HW_RST_WAIT" + with m.Else(): + m.d.sync += wait_ctr.eq(wait_ctr + 1) + + # RSTn released: the W5100S needs 60.3 ms before it answers at all. + with m.State("HW_RST_WAIT"): + with m.If(wait_ctr == self._reset_cycles): + m.d.sync += wait_ctr.eq(0) + m.next = "MR_RST" + with m.Else(): + m.d.sync += wait_ctr.eq(wait_ctr + 1) + # MR = 0x80 software reset (direct A=00), then settle. # # reset_cycles MUST cover the W5100S's post-reset dead time (the