Skip to content

eth_w5500: one frame per interrupt, losing RX notifications until the buffer fills and RX dies #119018

Description

@sjobyt

Describe the bug

drivers/ethernet/eth_w5500.c only services one received frame per interrupt, but Sn_IR is a latch that gets cleared for everything that arrived up to that point. If a second frame lands between the read and the clear of Sn_IR, its notification is gone. The frame itself is fine and sits in the W5500's RX buffer, but there's nothing left pending to tell the driver to go get it.

This piles up over time. Eventually the 16 KB RX buffer fills, the chip stops accepting new frames, and you're left with a node that transmits fine and reports link up, but answers nothing.

Target platform: nRF54L15 + W5500 over SPI @ 8 MHz, nRF Connect SDK v3.4.0 (Zephyr 4.4.0).

Workaround tried: none possible from the application side — by the time anything else can look, the notification for that frame is already gone, and the driver only ever reads RX off the back of S0_IR_RECV.

Regression: not a regression from a known-good version as far as I can tell — checked the same code path against current main and the bug is unchanged there too, so this isn't something that broke recently.

Regression

  • This is a regression.

Steps to reproduce

  • Build and flash a board with the eth_w5500 driver in interrupt mode, with steady incoming traffic (e.g. ping -i 0.1 from another host).
  • Watch round-trip latency over a few minutes — it climbs steadily rather than staying flat.
  • Read Sn_RX_RSR and Sn_IR directly over SPI from the application: Sn_RX_RSR keeps growing while Sn_IR reads 0x00 and INTn is de-asserted, meaning bytes are sitting in the chip's RX buffer with nothing pending to trigger a read.
  • Left long enough (a few minutes on a busy LAN), the 16 KB buffer fills and RX stops entirely. TX keeps working and link status stays up.

Relevant log output

uptime   Sn_RX_RSR   Sn_IR   INTn
28 s     2350 B      0x00    de-asserted
130 s    9741 B      0x00    de-asserted
244 s    15415 B     0x00    de-asserted

(buffer size 16384 B)

Latency vs. inbound rate on one node:
~1.7 pkt/s  -> median ping 13.1 s  (0% loss)
~11.7 pkt/s -> median ping 1.8 s   (0% loss)

With the suggested fix applied, Sn_RX_RSR reads 0 on every sample,
including right after three back-to-back 40-frame bursts.

Impact

Annoyance – Minor irritation; no significant impact on usability or functionality.

Environment

  • OS: MacOS 26.6.2 (build host)
  • Toolchain: nRF Connect SDK v3.4.0 (Zephyr 4.4.0)
  • Board: nRF54L15 + W5500 over SPI @ 8 MHz
  • Confirmed same code path unchanged on current Zephyr main

Additional Context

The relevant code is w5500_check_for_ir():

w5500_spi_read(dev, W5500_S0_IR, &ir, 1);
if (ir != 0U) {
        w5500_spi_write(dev, W5500_S0_IR, &ir, 1);   /* clears everything pending */
        if (ir & S0_IR_RECV) {
                w5500_rx(dev);                        /* but only reads ONE frame */
        }
}

w5500_rx() reads Sn_RX_RSR only to check if it's zero, never loops on it. The
-EAGAIN branch in w5500_thread_interrupt() (monitor-period timeout) only calls
w5500_update_link_status(), so it doesn't drain anything either — no safety net.

Suggested fix:

  • loop w5500_rx() until Sn_RX_RSR == 0 instead of once per interrupt
  • have the monitor-period timeout branch drain too, so a missed notification just
    costs latency instead of eventually killing RX
  • break the service loop if INTn is asserted but Sn_IR reads 0, to avoid spinning

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions