STM32H7 + LwIP: Problems with MPU and Custom Heap at 0x30008000 – No Packets Received
Hi everyone,
I'm working on a project using an STM32H743 and LwIP (v2.1.2) without an RTOS (NO_SYS=1), and I've been debugging persistent issues related to LwIP's heap allocation and MPU configuration. After days of digging, I’d really appreciate the community's input.
What I'm trying to do:
I want to place the LwIP heap in RAM_D2 at address 0x30008000, enable MPU regions for protection, and successfully use Ethernet and ping using LwIP.
My configuration:
.ld file (.lwip_heap section):
.lwip_heap (NOLOAD) :
{
. = ABSOLUTE(0x30008000);
KEEP(*(.lwip_heap))
. = ALIGN(4);
} >RAM_D2
In mem.c:
attribute((section(".lwip_heap"))) attribute((used))
u8_t lwip_heap[MEM_SIZE];
#define LWIP_RAM_HEAP_POINTER ((void*)lwip_heap)
In lwipopts.h:
#define NO_SYS 1
#define MEM_ALIGNMENT 4
#define MEM_SIZE (64*1024)
#define MEM_LIBC_MALLOC 0
#define LWIP_SUPPORT_CUSTOM_PBUF 1
The symptoms:
-
With MPU disabled: I can receive ARP requests and the STM seems to process them correctly by trying to send a broadcasting ARP Response. But the only thing I can see in Wireshark is the following entry:
02:00:00:00:00:01 SchneiderEle_a5:92:d0 0x4d35 60 Ethernet II
The first MAC Address makes sense as I gave it to the STM via config, but the rest doesn't make sense at all. -
With MPU enabled: the heap at 0x30008000 is not written properly, mem_init() fails silently, and LwIP crashes or behaves unexpectedly.
I got it to work once and i could receive and respond to arp packages and ping requests, but not any more, which seems quite weird.
MPU Configuration:
I configured two regions:
Region 1: 0x30000000, 32 KB for DMA descriptors & pbuf pool
Region 2: 0x30008000, 64 KB for the LwIP heap
My MPU Config with Region 2 commented out:
void MPU_Config(void)
{
MPU_Region_InitTypeDef MPU_InitStruct = {0};
/* Disables the MPU */
HAL_MPU_Disable();
/** Initializes and configures the Region and the memory to be protected
*/
MPU_InitStruct.Enable = MPU_REGION_ENABLE;
MPU_InitStruct.Number = MPU_REGION_NUMBER0;
MPU_InitStruct.BaseAddress = 0x0;
MPU_InitStruct.Size = MPU_REGION_SIZE_256KB;
MPU_InitStruct.SubRegionDisable = 0x87;
MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL0;
MPU_InitStruct.AccessPermission = MPU_REGION_NO_ACCESS;
MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_DISABLE;
MPU_InitStruct.IsShareable = MPU_ACCESS_SHAREABLE;
MPU_InitStruct.IsCacheable = MPU_ACCESS_NOT_CACHEABLE;
MPU_InitStruct.IsBufferable = MPU_ACCESS_NOT_BUFFERABLE;
HAL_MPU_ConfigRegion(&MPU_InitStruct);
/** Initializes and configures the Region and the memory to be protected
*/
MPU_InitStruct.Number = MPU_REGION_NUMBER1;
MPU_InitStruct.BaseAddress = 0x30000000;
MPU_InitStruct.Size = MPU_REGION_SIZE_32KB;
MPU_InitStruct.SubRegionDisable = 0x0;
MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL1;
MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_DISABLE;
MPU_InitStruct.IsShareable = MPU_ACCESS_SHAREABLE;
MPU_InitStruct.IsCacheable = MPU_ACCESS_NOT_CACHEABLE;
MPU_InitStruct.IsBufferable = MPU_ACCESS_NOT_BUFFERABLE;
HAL_MPU_ConfigRegion(&MPU_InitStruct);
/*
MPU_InitStruct.Number = MPU_REGION_NUMBER2;
MPU_InitStruct.BaseAddress = 0x30008000;
MPU_InitStruct.Size = MPU_REGION_SIZE_64KB;
MPU_InitStruct.SubRegionDisable = 0x0;
MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
MPU_InitStruct.IsCacheable = MPU_ACCESS_NOT_CACHEABLE;
MPU_InitStruct.IsBufferable = MPU_ACCESS_BUFFERABLE;
MPU_InitStruct.IsShareable = MPU_ACCESS_SHAREABLE;
MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_DISABLE;
MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL1;
HAL_MPU_ConfigRegion(&MPU_InitStruct);
*/
/* Enables the MPU */
HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT);
}
My MwIP Stackup looks as follows:
RxDescripSection 0x30000000
TxDescripSection 0x30000080
RxBufferSection 0x30000100 (RxBufferLength 1524 and RxBufferCNT 12)
LwIP HEAP 0x30008000 (MEM_SIZE 0x10000)
What I’ve tried so far:
-
Disabling/enabling D-Cache and I-Cache
-
Using attribute((used)) and KEEP() in the linker script
-
Verifying .map file shows lwip_heap placed at 0x30008000
-
Debugging mem pointer after mem_init()
-
Checking Ethernet TX via ethernet_output() and linkoutput()
-
Confirming headers are correct before linkoutput(), but incorrect on wire
The issue:
As soon as I enable the MPU region for 0x30008000, writing to lwip_heap no longer works. mem becomes invalid, allocations fail, and Ethernet frames on the wire have corrupted headers. No ICMP replies, no ARP responses. With the MPU region disabled, I can receive packages but cannot respond to them correctly.
My question:
What is the correct MPU configuration for 0x30008000 to safely use it as non-cacheable memory for lwip_heap with LwIP?
Also:
-
Do I need to align anything else?
-
Is there a required TEX/C/B/S config for LwIP memory?
-
Am I missing something obvious regarding MPU or memory attributes?
Bonus Info:
STM32CubeIDE 1.18.0, default HAL drivers, no RTOS.
Thanks in advance for any help – this has been a very tricky issue to debug!
Or is there any other solution besides LwIP to establish communication over ethernet.
P.S.: I am using a custom PCB but I used the layout of the STM32H743VIH6 Nucleo Board for the basic functionality including a LAN8742A PHY IC.
Best regards,
Tobias
