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 <noreply@anthropic.com>
This commit is contained in:
@@ -50,6 +50,10 @@ _SUBR0 = 0x0005 # Subnet mask, 4 bytes
|
|||||||
_SHAR0 = 0x0009 # Source MAC, 6 bytes
|
_SHAR0 = 0x0009 # Source MAC, 6 bytes
|
||||||
_SIPR0 = 0x000F # Source IP, 4 bytes
|
_SIPR0 = 0x000F # Source IP, 4 bytes
|
||||||
_NETLCKR = 0x0071 # W5100S Network Lock Register (WO) — GWR/SUBR/SHAR/SIPR are
|
_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)
|
_NETLCK_UNLOCK = 0x3A # writable ONLY while NETLCKR==0x3A (default 0x00 = locked)
|
||||||
_IR = 0x0015 # Interrupt register
|
_IR = 0x0015 # Interrupt register
|
||||||
_IMR = 0x0016 # Interrupt mask
|
_IMR = 0x0016 # Interrupt mask
|
||||||
@@ -150,6 +154,10 @@ class W5100ParallelMaster(Elaboratable):
|
|||||||
self._strobe = strobe_cycles
|
self._strobe = strobe_cycles
|
||||||
# MR-reset settle wait; testbench overrides with a small value.
|
# MR-reset settle wait; testbench overrides with a small value.
|
||||||
self._reset_cycles = reset_cycles
|
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 ───────────────────────────────
|
# ── Optional socket-1 UDP bring-up test ───────────────────────────────
|
||||||
# When enabled, init also configures the W5100's IP stack (GAR/SUBR/
|
# 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):
|
with m.If(self.init_req):
|
||||||
for i in range(6):
|
for i in range(6):
|
||||||
m.d.sync += mac_shadow[i].eq(self.par[i*8:(i+1)*8])
|
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):
|
with m.Elif(~self.w5100_int_n):
|
||||||
m.next = "RX_CHECK"
|
m.next = "RX_CHECK"
|
||||||
with m.Elif(self.tx_valid & self.tx_sof):
|
with m.Elif(self.tx_valid & self.tx_sof):
|
||||||
@@ -582,6 +591,32 @@ class W5100ParallelMaster(Elaboratable):
|
|||||||
self.dbg_rd_done.eq(1)]
|
self.dbg_rd_done.eq(1)]
|
||||||
m.next = "IDLE"
|
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.
|
# MR = 0x80 software reset (direct A=00), then settle.
|
||||||
#
|
#
|
||||||
# reset_cycles MUST cover the W5100S's post-reset dead time (the
|
# reset_cycles MUST cover the W5100S's post-reset dead time (the
|
||||||
|
|||||||
Reference in New Issue
Block a user