cancel
Showing results for 
Search instead for 
Did you mean: 

STM32H7 + LwIP: Problems with MPU and Custom Heap at 0x30008000 – No Packets Received

LWI_GUY
Associate II

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

16 REPLIES 16

Hello,

Question: why are you setting the MPU background region size to 256k?

  MPU_InitStruct.Number = MPU_REGION_NUMBER0;
  MPU_InitStruct.BaseAddress = 0x0;
  MPU_InitStruct.Size = MPU_REGION_SIZE_256KB;
  MPU_InitStruct.SubRegionDisable = 0x87;

That region needs to be 4G:

  MPU_InitStruct.Number = MPU_REGION_NUMBER0;
  MPU_InitStruct.BaseAddress = 0x0;
  MPU_InitStruct.Size = MPU_REGION_SIZE_4GB;
  MPU_InitStruct.SubRegionDisable = 0x87;

 

 

 

To give better visibility on the answered topics, please click on "Accept as Solution" on the reply which solved your issue or answered your question.

why are you setting the MPU background region size to 256k?

In that code, region 0 is not the background. It probably catches null pointer references.

 

mƎALLEm
ST Employee

This setting means that the MPU config was used as a background region:

MPU_InitStruct.SubRegionDisable = 0x87;

 

To give better visibility on the answered topics, please click on "Accept as Solution" on the reply which solved your issue or answered your question.

Yes you are right My point was at C B S and XN bits and i didnt noticed the size.

 

LWI_GUY
Associate II

Hi everyone,

I've noticed that I don't need the extra code in ethernetif.c, and I also don't need to adjust the background region of the MPU.

However, for everything to work properly, I currently need to disable the Data Cache entirely.
That said, I would like to enable the Data Cache in my upcoming project, where I plan to use a REST API to control the STM32 and retrieve data.

Unfortunately, I can't get it working properly when I define MPU regions to manage caching behavior. Does anyone know why the STM behaves like this?

I do understand that enabling the Data Cache can lead to inconsistencies between the CPU cache and RAM, especially since the Ethernet DMA won't see the updated data. But why doesn't it help to define MPU regions correctly to disable caching just for the buffers used by the DMA?

Any ideas or experiences with this would be greatly appreciated.


@LWI_GUY wrote:

and I also don't need to adjust the background region of the MPU.


You need to enable the MPU background region to prevent any CM7 read speculation issue .

Also please refer to the AN4839 "Level 1 cache on STM32F7 Series and STM32H7 Series"

To give better visibility on the answered topics, please click on "Accept as Solution" on the reply which solved your issue or answered your question.
Pavel A.
Super User

With a dual core STM32H7 you can handle ETH with the CM4 core as it has no D cache anyway. Do other work with the CM7 core, cache enabled.