I'd previously tried to get networking up on the GD32H7. Since the chip was never used in the final product, it was only evaluated and then shelved.
What I observed at the time was this: I used exactly the same libraries, peripheral configuration, call sequence, and operating methods as the official example — the only difference being that I converted the official Keil example into a cmake-gcc project. The problem was that the official example compiled and flashed without any trouble, while my project, no matter what, simply couldn't be pinged after compiling.
My knowledge of the lwIP network stack and the ETH peripheral is honestly quite limited, and even GD support couldn't find the cause.
The Cause
In short, the GD32H7 requires the entire addressing region starting from 0x0000 to be covered with MPU protection as a background setting, following a configuration method like this, and then the MPU configuration blocks that follow make the actual configuration.
In the GD32H7 manual, section 1.3 Memory Map shows that 0x0000 0000~0x0007 FFFF is allocated as ITCM RAM (from shared RAM), but this region is also designated as the code region. I don't quite understand the intent behind this at the moment. In any case, during kernel operation the region is treated as RAM, so reads and writes are possible.
According to the reference materials, neither GD's official errata nor any manual describes this. Many of the official examples don't account for it either — otherwise, my earlier switch to a gcc build should have worked perfectly. Only ST's official documentation contains such a description — and even then, in the touchgfx graphics library manual rather than the chip manual:
On M7 processors, speculative access to external memory must be prevented, otherwise it can cause high latency or system errors. For the STM32H7R/S, this affects the AXI masters that access memory and significantly reduces graphics performance.
The MPU can prevent speculative read accesses by controlling the address ranges that can be accessed. The simplest approach is to use a background region that covers the entire memory region, restricting access by setting it to "strongly ordered, never execute".
The background region should be defined in default region ID-1, because all other regions will have higher priority than it. Then, additional MPU regions with appropriate settings should be defined for the memory regions that need to be accessed. Up to 16 regions can be defined on the STM32H7R/S.
In other words, this is a flaw inherent in the M7 core's original design. The STM32H7 I've used only escaped it because ST either worked around the problem by some means when producing the chip, or simply never happened to run into it.