Skip to content

sim/ethernet: handle both GMII and bare frames on TX for tap0#2470

Open
theox33 wants to merge 1 commit into
enjoy-digital:masterfrom
theox33:fix-sim-ethernet-fcs
Open

sim/ethernet: handle both GMII and bare frames on TX for tap0#2470
theox33 wants to merge 1 commit into
enjoy-digital:masterfrom
theox33:fix-sim-ethernet-fcs

Conversation

@theox33
Copy link
Copy Markdown

@theox33 theox33 commented Jun 2, 2026

sim/ethernet: strip GMII preamble and FCS before writing to tap0

Problem

The ethernet_tick() function in litex/build/sim/core/modules/ethernet.c accumulates every byte emitted on the source signals and writes them verbatim to the TAP interface via tapcfg_write().

This causes an issue depending on the module connected to the PHY:

  1. LiteEthMAC outputs a complete GMII byte stream on TX (7 bytes preamble 0x55, 1 byte SFD 0xd5, the Ethernet frame, and a 4 bytes FCS). Writing this full stream shifts the frame by 8 bytes on the TAP interface. This produces corrupted Ethernet headers that the Linux kernel silently discards.
  2. Etherbone (when with_preamble_crc = False in LiteEthPHYModel) outputs bare frames without preamble or FCS, which works perfectly with the current code.

Observed symptom on tap0 with LiteEthMAC (tcpdump):
55:d5:ff:ff:ff:ff > 55:55:55:55:55:55, ethertype Unknown (0xffff), length 72
The ARP/ICMP payload was present and correct at a +8 byte offset, but the malformed header prevented any host↔SoC communication.

Fix

This PR introduces a lightweight state machine in the TX path of ethernet_tick() to dynamically support both use cases:

  1. For GMII streams (LiteEthMAC): It detects the preamble and the SFD byte (0xd5). It starts accumulating only from the following byte (first byte of dst MAC) and flags the frame to strip the last 4 bytes (FCS) before calling tapcfg_write().
  2. For bare frames (Etherbone): If the first byte is not a preamble, it assumes a bare frame. It accumulates everything and does not strip any FCS at the end.

The RX path (tap0 → SoC) remains unchanged.

Testing

Verified in Verilator simulation with LiteEthPHYModel on a TAP interface (tap0):

  • Both LiteEthMAC (192.168.1.50) and Etherbone (192.168.1.51) can coexist and operate correctly.
  • ping from host to both IP addresses returns 5/5 replies.
  • tcpdump -i tap0 shows correct ARP request/reply exchanges followed by valid ICMP echo requests/replies, with no 0xffff ethertype corruption.

The ethernet simulation module previously wrote the raw TX byte stream
directly to the TAP interface.

When used with LiteEthMAC, the stream includes a 7-byte preamble (0x55),
an SFD byte (0xd5), and a 4-byte FCS. Because TAP interfaces expect
bare Ethernet frames, this shifted the payload by 8 bytes, producing
malformed headers (e.g., ethertype 0xffff) that the host kernel dropped.
Conversely, when used with Etherbone (with_preamble_crc=False), frames
are already bare and were handled correctly.

This commit updates the TX path in `ethernet_tick()` to dynamically
handle both cases:
- If a preamble and SFD are detected, they are stripped, and the
  trailing 4-byte FCS is removed before calling tapcfg_write().
- If no preamble is detected (e.g., Etherbone), the frame is assumed
  to be bare and is passed through verbatim without stripping an FCS.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant