cancel
Showing results for 
Search instead for 
Did you mean: 

CubeIDE generates wrong/hard-fault parameters for LWiP on Nucleo-F746ZG

KMill
Senior

Steps to reproduce:

Start a new STM32 Project in STM32CubeIDE v1.17.0
Select NUCLEO-F746ZG board, and initailize all peripherals.
Now in the IOC editor, under Middleware select LwIP and enable it.
Set the PHY to LAN8742 in the "Platform Settings" tab.
On the Key Options tab set NO_SYS to OS Not Used.
In General Settings tab, disable DHCP, disable the TCP module and enable UDP.
Go back to Key Options tab and note that the "LWIP_RAM_HEAP_POINTER" is set to 0x30004000
Save, Build an run the project in debug mode.

The code will hard-fault during MX_LWiP_Init() because the RAM HEAP POINTER is wrong.

Now open the file at LWIP/Target/lwipopts.h
Note that at approximately line 58/59 there is:

 

 

 

 

/*----- Default Value for F7/H7 devices: 0x30044000 -----*/
#define LWIP_RAM_HEAP_POINTER 0x30004000

 

 

 

 

FIrst of all the comment doesn't match the value given, and even if you change the value given to the same as the comment (this is an F7 device after all) the code will still hard-fault.

If you comment out the #define the code will now build and run, but the number of ETH_RX_BUFFER_CNT buffers you can set in the IOC editor is very limited.

Also, if you make any changes at all to anything in the IOC editor, this line will get put back i and your code will hard-fault again.

 

8 REPLIES 8
Sarra.S
ST Employee

Hello @KMill, you are right 

The default value causes a Hardfault as the address is in a reserved region:

SarraS_0-1736433773036.png

I'm creating an internal ticket to correct this (Internal ticket number: 200083) 

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.

OWLchan
Associate III

thanks for saving my day, I'm using STM32CubeMX version '6.13.0' to generate code and got the error as you said above, I wonder if ST has fixed that error or has a guide because it's very inconvenient every time I generate code again.

and one thing I just discovered is that previous versions of STM32CubeMX don't seem to generate anything similar to #define LWIP_RAM_HEAP_POINTER 0x30004000

Screenshot 2025-01-10 150757.png

 


UPDATE: I found a way to fix this problem, just change the address of LWIP_RAM_HEAP_POINTER to sram
Screenshot 2025-01-10 153448.png

 

I too changed the value in that field to 0x20010000 (sram) but I was not confident in the fix, having drilled through the macros that LWiP uses.

As far as I could figure out, if LWIP_RAM_HEAP_POINTER is not defined, it will just allocate space for a variable of the required size, plus alignment. Thus leaving it up to the compiler to allocate scalar storage space from the heap. However if I define this to be the start of ram (where the heap would normally grow from) how do I know that there is nothing else using that location, or that the compiler hasn't alreay allocated space at that address?

Clearly I could be mis-reading this, but to me it looks like an arbitrarily chosen ram pointer, which the compiler would not be aware of. Please tell me I'm wrong so that I can have confidence in this!

your comment is right but that is actually what is going on, i looked a little at :

 

 

  lwip_init();

 

 

it seems that function is using malloc to allocate memory on sram and using mutex to handle:

 

 

void
mem_init(void)
{
  struct mem *mem;

  LWIP_ASSERT("Sanity check alignment",
              (SIZEOF_STRUCT_MEM & (MEM_ALIGNMENT - 1)) == 0);

  /* align the heap */
  ram = (u8_t *)LWIP_MEM_ALIGN(LWIP_RAM_HEAP_POINTER);
  /* initialize the start of the heap */
  mem = (struct mem *)(void *)ram;
  mem->next = MEM_SIZE_ALIGNED;
  mem->prev = 0;
  mem->used = 0;
  /* initialize the end of the heap */
  ram_end = ptr_to_mem(MEM_SIZE_ALIGNED);
  ram_end->used = 1;
  ram_end->next = MEM_SIZE_ALIGNED;
  ram_end->prev = MEM_SIZE_ALIGNED;
  MEM_SANITY();

  /* initialize the lowest-free pointer to the start of the heap */
  lfree = (struct mem *)(void *)ram;

  MEM_STATS_AVAIL(avail, MEM_SIZE_ALIGNED);

  if (sys_mutex_new(&mem_mutex) != ERR_OK) {
    LWIP_ASSERT("failed to create mem_mutex", 0);
  }
}

 

 

my hypothesis is that allocating ram using pointer is only possible on F7|H7 line because they have MPU mechanism. not sure if i am right but they say this helps prevent fragmentation and is faster than heap:

/**
 * @file
 * Dynamic memory manager
 *
 * This is a lightweight replacement for the standard C library malloc().
 *
 * If you want to use the standard C library malloc() instead, define
 * MEM_LIBC_MALLOC to 1 in your lwipopts.h
 *
 * To let mem_malloc() use pools (prevents fragmentation and is much faster than
 * a heap but might waste some memory), define MEM_USE_POOLS to 1, define
 * MEMP_USE_CUSTOM_POOLS to 1 and create a file "lwippools.h" that includes a list
 * of pools like this (more pools can be added between _START and _END):
 *
 * Define three pools with sizes 256, 512, and 1512 bytes
 * LWIP_MALLOC_MEMPOOL_START
 * LWIP_MALLOC_MEMPOOL(20, 256)
 * LWIP_MALLOC_MEMPOOL(10, 512)
 * LWIP_MALLOC_MEMPOOL(5, 1512)
 * LWIP_MALLOC_MEMPOOL_END
 */

 

Yes it's the code at lines 10 and 12 in your post above above that worry me.

They take an abitrarily allocated address (typed in by the user, or taken from a #define) and immediately write to it. How is that safe? How can the memory manager or the compiler, or the garbage collector (if there is one) know about this?

As far as I know, wlip doesn't directly tell the compiler where the location will be a pointer, instead it will initialize and allocate data for the pointer inside the heap and will cause an error if the address entered is not surrounded by the heap. and to protect that area, wlip will use mutex (if there is an RTOS) to block threads accessing that address or use arch (architecture) to handle it at the hardware level (interrupt, malloc...). that's what I understand, I don't even know "arch". but it seems like wlip's library has very strong hardware intervention capabilities.

in code generated by older cubemx versions, wlip uses LWIP_RAM_HEAP_POINTER as a regular global variable:

/** Allocates a memory buffer of specified size that is of sufficient size to align
 * its start address using LWIP_MEM_ALIGN.
 * You can declare your own version here e.g. to enforce alignment without adding
 * trailing padding bytes (see LWIP_MEM_ALIGN_BUFFER) or your own section placement
 * requirements.\n
 * e.g. if you use gcc and need 32 bit alignment:\n
 * \#define LWIP_DECLARE_MEMORY_ALIGNED(variable_name, size) u8_t variable_name[size] \_\_attribute\_\_((aligned(4)))\n
 * or more portable:\n
 * \#define LWIP_DECLARE_MEMORY_ALIGNED(variable_name, size) u32_t variable_name[(size + sizeof(u32_t) - 1) / sizeof(u32_t)]
 */
#ifndef LWIP_DECLARE_MEMORY_ALIGNED
#define LWIP_DECLARE_MEMORY_ALIGNED(variable_name, size) u8_t variable_name[LWIP_MEM_ALIGN_BUFFER(size)]
#endif
/** If you want to relocate the heap to external memory, simply define
 * LWIP_RAM_HEAP_POINTER as a void-pointer to that location.
 * If so, make sure the memory at that location is big enough (see below on
 * how that space is calculated). */
#ifndef LWIP_RAM_HEAP_POINTER
/** the heap. we need one struct mem at the end and some room for alignment */
LWIP_DECLARE_MEMORY_ALIGNED(ram_heap, MEM_SIZE_ALIGNED + (2U * SIZEOF_STRUCT_MEM));
#define LWIP_RAM_HEAP_POINTER ram_heap
#endif /* LWIP_RAM_HEAP_POINTER */

Thanks again @OWLchan 

I think I will just need to test, test, test to satisfy my doubtful mind!

 

no problem, if it wasn't for your post I probably would have stuck with it until next week, thanks a lot :))))