From 5f60c011cb5c01449c6a97f6bce48f9431b63ee3 Mon Sep 17 00:00:00 2001 From: Dennis Brentjes Date: Wed, 30 Sep 2026 19:16:51 +0000 Subject: [PATCH] fix(w5100): clear stale Sn_IR bits when INT_N asserts with no RX data RX_RSR_CHK returned to IDLE when RSR==0 without clearing Sn_IR. SEND_OK (bit 4) fires after every successful transmit and the RX path only ever cleared RECV (bit 2), so INT_N stayed asserted; because IDLE tests ~w5100_int_n BEFORE tx_valid, the FSM span IDLE->RX_CHECK->RX_RSR_CHK->IDLE and TX was starved permanently. Observed on hardware as the first frame transmitting and every later one never setting TI while RX kept working. Now clears every Sn_IR bit except RECV, so a frame arriving between the RSR read and the write still raises INT_N and is served next pass. Co-Authored-By: Claude Opus 5 --- exi_bba/w5100_parallel_master.py | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/exi_bba/w5100_parallel_master.py b/exi_bba/w5100_parallel_master.py index 567708b..224e3b9 100644 --- a/exi_bba/w5100_parallel_master.py +++ b/exi_bba/w5100_parallel_master.py @@ -751,10 +751,23 @@ class W5100ParallelMaster(Elaboratable): m.next = "RX_RSR_CHK" with m.State("RX_RSR_CHK"): with m.If(rx_rsr == 0): - m.next = "IDLE" # nothing received + # INT_N is asserted but there is no data. Something other + # than RECV set Sn_IR -- in practice SEND_OK (bit 4), which + # fires after every successful transmit. Returning to IDLE + # without clearing it leaves INT_N low forever, and since + # IDLE tests ~w5100_int_n BEFORE tx_valid, the FSM spins + # IDLE->RX_CHECK->here and TX is starved permanently. That + # showed up on hardware as: first frame transmits, every + # later one never sets TI, while RX keeps working. + m.next = "RX_IR_CLR" with m.Else(): m.next = "RX_RDPTR" + # Clear every Sn_IR bit EXCEPT RECV (0x04). Keeping RECV means a + # frame that lands between the RSR read and this write still raises + # INT_N and gets served on the next pass instead of being lost. + write_reg("RX_IR_CLR", _S0_IR, [0x1B], "IDLE") + with m.State("RX_RDPTR"): # read S0_RX_RD (2 bytes) m.d.sync += [xfer_addr.eq(_S0_RX_RD), xfer_rw.eq(0), xfer_stream.eq(0), xfer_sread.eq(0), xfer_wrap.eq(0),