Skip to content

Stm32h7 ethernet - #19930

Open
JorgeGzm wants to merge 2 commits into
apache:masterfrom
JorgeGzm:stm32h7_ethernet
Open

Stm32h7 ethernet#19930
JorgeGzm wants to merge 2 commits into
apache:masterfrom
JorgeGzm:stm32h7_ethernet

Conversation

@JorgeGzm

Copy link
Copy Markdown
Contributor

Summary

Two independent fixes to the STM32H7 Ethernet driver, split out of #19911
at review request.

Never transmit a reply into a full TX ring

stm32_receive() replies to ARP, IPv4 and IPv6 straight from the RX path
without checking whether a TX descriptor is free. Under sustained load the
reply overwrites a descriptor the DMA still owns and the link stalls until
the interface is reset.

All three reply paths now check stm32_txringfull() first. That check has
to consider des0 as well as the OWN bit: a descriptor already consumed by
the DMA but not yet reclaimed still has its buffer pointer set, and
treating it as free is what corrupts the ring.

Allow restricting autonegotiation to 10BASE-T full duplex

New CONFIG_STM32_AUTONEG_10FD_ONLY, default n. On a board whose display
generates enough noise to corrupt 100BASE-TX, throughput collapses with
whatever is on the screen. Advertising only 10BASE-T full duplex makes both
ends negotiate a link that survives the interference.

It advertises rather than forcing the MCR, so the two ends still agree. A
forced setting on one side produces a duplex mismatch, which looks exactly
like a bad cable and is much harder to diagnose.

Impact

No functional change for existing users. Both are confined to
arch/arm/src/stm32h7/stm32_ethernet.c and the shared
arch/arm/src/common/stm32/Kconfig.eth; the new option defaults to n, so
nothing changes unless a board opts in.

The TX ring guard costs one descriptor read per received packet that gets
a reply.

Testing

Host: Linux, arm-none-eabi-gcc 13.2.
Board: linum-stm32h753bi (STM32H753BI, 1024x600 RGB panel), netnsh.

TX ring: sustained ping flood and TCP transfer while the panel was being
redrawn. Before the change the link stalled and only a reset recovered it.
After, the interface stayed up for the whole run.

Autonegotiation: measured throughput with the panel showing static content
against animated content.

nsh> ifconfig
eth0  Link encap:Ethernet HWaddr 00:e0:de:ad:be:ef at RUNNING mtu 1486
      inet addr:192.168.15.4 DRaddr:192.168.15.1 Mask:255.255.255.0

stm32_eth: link full-duplex 100 Mbps      (default)
stm32_eth: link full-duplex 10 Mbps       (CONFIG_STM32_AUTONEG_10FD_ONLY=y)

With 100BASE-TX the transfer rate varied by up to 200x depending on screen
content. With the option enabled the link is slower on paper and steady in
practice.

…duplex

On the linum-stm32h753bi, Ethernet throughput collapses in proportion to
what the display panel is showing. With the LTDC scanning a black screen
a 1.2 MiB TCP transfer to a wired peer takes 0.7 s; solid white takes 20
to 55 s and noise over 130 s, all at the same negotiated 100BASE-TX full
duplex, with the same bytes read from the same SDRAM. The display's
switching couples into the PHY hard enough to corrupt 100BASE-TX
signalling, and TCP grinds through the losses at whatever rate survives.

10BASE-T signals at 2.5 V with Manchester coding at a tenth of the
frequency, and does not care: black, white and noise all move at the
link rate. The same transfer that took five minutes with the display
rendering takes 5.6 seconds.

Restricting the ANAR advertisement is deliberately not the same as
disabling autonegotiation. A forced MCR leaves the partner to parallel
detection, which cannot sense duplex and picks half, a genuine
mismatch, verified here to stall bulk traffic completely. Advertising
only 10BASE-T full duplex keeps the negotiation and lands both ends on
the same mode.

Also fix the never-compiled !CONFIG_STM32_AUTONEG path, which still
called stm32_phywrite(); this driver has only ever had mdio_write().
And say what was negotiated at link-up: a duplex mismatch looks exactly
like a bad cable, and nothing else reports which of the two it is.

The vnc configuration of the linum board enables the new option, and its
packet pool sizing from a few commits ago stays: at any link speed, 24
buffers of 196 bytes was never going to stream a display.


Assisted-by: Claude:opus-5
Signed-off-by: Jorge Guzman <jorge.gzm@gmail.com>
The receive path hands incoming packets to the stack and transmits
whatever reply comes back, without checking that a TX descriptor is
free, though the poll path checks exactly that. Under sustained
bidirectional load the reply lands on a descriptor the DMA still owns:
with assertions built in, a panic from the RX work queue
(DEBUGASSERT(des3 & RD_OWN), reproduced under a VNC pointer flood);
without them, corruption of a frame in flight.

A reply to received data is almost always an acknowledgement, and a
peer that misses one retransmits; overwriting a frame the DMA owns
recovers from nothing. Drop the reply when the ring is full, using the
same descriptor test the poll path already trusts.

Assisted-by: Claude:opus-5
Signed-off-by: Jorge Guzman <jorge.gzm@gmail.com>
@github-actions github-actions Bot added Arch: arm Issues related to ARM (32-bit) architecture Size: M The size of the change in this PR is medium labels Aug 22, 2026
@github-actions

Copy link
Copy Markdown

MemBrowse Memory Report

No memory changes detected for:

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

Labels

Arch: arm Issues related to ARM (32-bit) architecture Size: M The size of the change in this PR is medium

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants