October 13, 2026

First experiences with STM32 CubeMX -- CubeMX and network/ethernet - part 3

First a well deserved rant. ST and CubeMX have let me down. I have been struggling for 2 days, now going on three trying to get network code running and responding to a ping. This should just work! Nobody should know better than ST what the gpio pin assignments are for the PHY chip, but I have to dig out information and fix incorrect setup. This is not at all what a person would expect. They could and should do better.

Do I have the correct schematic

There are several versions of the STM32H743 Nucleo board. Mine is labeled MB1364E on the silkscreen and a sticker says NUCLEO-H743ZI2. Their website does have two schematics (look under CAD resources):
MB1364-H743-ZI-C01
MB1364-H743-ZI-E01
The schematics seem to agree on these changes to the CubeMX settings:
TX_EN on PB11  (should be PG11)
TXD0 on PB12  (should be PG13)
TXD1 on PB13  (correct)
A search suggested that TXD1 was on PG14, but this was bogus information.

Is the LAN8742 PHY address set properly?

I never did find a place to set/change this in CubeMX. And all of the online info about where to change this are out of date.

So we will just start with MX_LWIP_Init() in LWIP/App/lwip.c and see where we end up. I ran ctags -R . at the top level to aid the process of exploring the code. Remember Ctrl-] follows an item and Ctrl-t goes back.

I find the code I am interested in in LWIP/Target/ethernetif.c in the routine low_level_init(). This is static and is called by ethernetif_init() in the same file. The only reference to this is a line in LWIP/App/lwip.c

netif_add(&gnetif, &ipaddr, &netmask, &gw, NULL, ðernetif_init, ðernet_input);

The LAN8742 driver is provided by these two files:

Drivers/BSP/Components/lan8742/lan8742.c
Drivers/BSP/Components/lan8742/lan8742.h
A look at this code shows the function LAN8742_Init() scanning all possible addresses and looking for one that responds. I add a printf to ethernetif.c that shows me this. 0 is the OK status.
8742 Init status: 0

In LWIP/Target/ethernetif.c is a routine ethernet_link_check_state(). I add printf to this. I get continuous output. It shows the state as 1 for a while, then it changes to 2.

#define  LAN8742_STATUS_LINK_DOWN             ((int32_t) 1)
#define  LAN8742_STATUS_100MBITS_FULLDUPLEX   ((int32_t) 2)
So this is all good and as it ought to be. I also print the value returned by netif_is_link_up(netif) -- and it is "1" -- so the link is up.

The check_state() routine is only called from Ethernet_Link_Periodic_Handle() in App/lwip.c. It gets called at 10 Hz. This gets called from MX_LWIP_Process() much more often, but it throttles the status checks to 10 Hz.

What about those DMA addresses

This is handled with undue tricky programming. Not to mention that part of this is not handled correctly at all.

We can start in LWIP/Target/ethernetif.c with these lines.

ETH_DMADescTypeDef DMARxDscrTab[ETH_RX_DESC_CNT] __attribute__((section(".RxDescripSection")));
ETH_DMADescTypeDef DMATxDscrTab[ETH_TX_DESC_CNT] __attribute__((section(".TxDescripSection")));
__attribute__((section(".Rx_PoolSection"))) extern u8_t memp_memory_RX_POOL_base[];
The above is specific to the Gnu "gcc" compiler. The incorrect/missing part is that the following lines need to be added to the linker script STM32H743xx_FLASH.ld
.lwip_sec (NOLOAD) : {
    . = ABSOLUTE(0x30000000);
    *(.RxDescripSection)

    . = ABSOLUTE(0x30000080);
    *(.TxDescripSection)

    . = ABSOLUTE(0x30000100);
    *(.Rx_PoolSection)
  } >RAM_D2
The above gives us 3 pointers to DMA eligible memory. The first two (the Rx and Tx descriptors) are handled by placing the values into a struct, then passing that struct to the ethernet initializtion routine in Drivers/STM32H7xx_HAL_Driver/Src/stm32h7xx_hal_eth.c
  heth.Init.TxDesc = DMATxDscrTab;
  heth.Init.RxDesc = DMARxDscrTab;
  HAL_ETH_Init(&heth)
The third is handled in a more devious way
LWIP_MEMPOOL_DECLARE(RX_POOL, ETH_RX_BUFFER_CNT, sizeof(RxBuff_t), "Zero-copy RX PBUF pool");
This is actually a macro define in Middlewares/Third_Party/LwIP/src/include/lwip/memp.h
In that file, a line like this generates the name memp_memory_RX_POOL_base --
LWIP_DECLARE_MEMORY_ALIGNED(memp_memory_ ## name ## _base, ((num) * (MEMP_SIZE + MEMP_ALIGN_SIZE(size))));

Conclusion

The goal here was to understand how the pointers to DMA eligible memory were handled and to verify that things were being handled correctly. It looks like they are, but at this point my demo code still does not respond to ping (ICMP) requests.


Have any comments? Questions? Drop me a line!

Tom's software pages / tom@mmto.org