Analysis of stm32 MCU and FreeRTOS, LwIP memory stack usage

Analysis of stm32 MCU and FreeRTOS, LwIP stack memory usage

As an industrial communication product R&D engineer, I often encounter the need to use middleware FreeRTOS and LwIP on the microcontroller. This article mainly sorts out the memory usage methods of various middleware, and the memory management issues that need to be paid attention to when using them in combination.
First of all, the conclusion is that the MCU and middleware FreeRTOS and LwIP stacks are independent of each other. The details are shown in the figure.
Memory usage diagram of each middleware (FreeRTOS, LwIP)Total memory of MCU (hereinafter referred to as RAM) Includes used memory and unused RAM.

The red area represents the used memory, which consists of RW and ZI-Data (including the FreeRTOS stack, LwIP stack, and RAM occupied by other programs) and the microcontroller stack (HEAP, STACK). White areas are unused RAM.

The above information can be confirmed by compiling the generated .map file.
Please add picture description

Analysis of MCU stack memory usage

In the single-chip microcomputer, both the heap and the stack are memory areas, and they both have the function of storing data. The following introduces their functions respectively:

Heap (HEAP): In a microcontroller, the heap acts similarly to the heap of a program running on a computer and is used to dynamically allocate memory. When the program needs to create a new variable or object at runtime, it will apply for a memory space from the heap and return the address of the memory space.

Stack (STACK): In a single-chip microcomputer, the stack is similar to the stack of a program running on a computer, and is used to store information such as local variables, parameters, and return addresses of function calls. When a program executes a function call, it will allocate a section of memory space from the stack to store information such as parameters and local variables of the function. When the function returns, the information in the stack will be popped and the state before the function call will be restored.

Heap memory usage is only associated with the following functions.

malloc();
free();

In other words, the size of the heap memory only needs to meet the memory space allocated by the above functions in the program.

The use of stack memory is relatively simple. After testing, in the case of using FreeRTOS, the stack memory space is basically not used (the reason will be analyzed later).

Taking keil5 as an example, the method of setting the MCU stack size is usually to edit the MDK-ARM/{MCU model}.s file under the project folder. As shown in the picture:
Please add picture description

Analysis of FreeRTOS stack memory usage

There are definitions in FreeRTOSConfig.h as follows.

#if defined(__GNUC__)
 #define configTOTAL_HEAP_SIZE ( ( size_t ) ( 25 * 1024 ) )
#else
 #define configTOTAL_HEAP_SIZE ( ( size_t ) ( 50 * 1024 ) )
#endif

FreeRTOS creates tasks, message queues, and FreeRTOS API functions all apply for memory from configTOTAL_HEAP_SIZE.

The heap space usage of FreeRTOS is shown in the figure.
Please add picture description
It can be seen from the figure that tasks, queues, and space allocated by users using pvPortMalloc() are all divided from the heap.

In addition, the stack memory used by the task is actually in the heap space of FreeRTOS. So no other stack space is needed for storage.

In addition, the function xPortGetFreeHeapSize() can get the size of the free memory in the calling heap, in Byte. The function xPortGetMinimumEverFreeHeapSize() can return the byte number of the smallest unallocated storage space that ever existed after the FreeRTOS application started running (only heap_4 and heap_5 are valid). Using these two functions can effectively reduce memory waste.

LwIP stack memory usage analysis

The default memory strategy of LWIP includes two types: memory pool (POOL) and memory heap (HEAP).

Memory pools refer to memory pools of various sizes (the size of the memory block managed by each memory pool is fixed) in order to meet the needs of specific memory allocation and optimize memory usage. The memory heap is essentially a large block of continuous memory (can be understood as an array). When memory is needed, a memory block of the required size is divided from this array according to a specific algorithm, and the address of this memory is provided; the remaining memory Still stored in the memory heap for continued allocation.

For a detailed explanation of memory pool (POOL) and memory heap (HEAP), please refer to LwIP Series – Detailed Explanation of Memory Management (Memory Pool) and LwIP Series – Detailed Explanation of Memory Management (Heap Memory).

In memory pool mode, various memory pools are defined in the memp_std.h header file.

/*
 * A list of internal pools used by LWIP.
 *
 * LWIP_MEMPOOL(pool_name, number_elements, element_size, pool_description)
 * creates a pool name MEMP_pool_name. description is used in stats.c
 */
#if LWIP_RAW
LWIP_MEMPOOL(RAW_PCB, MEMP_NUM_RAW_PCB, sizeof(struct raw_pcb), "RAW_PCB")
#endif /* LWIP_RAW */

#if LWIP_UDP
LWIP_MEMPOOL(UDP_PCB, MEMP_NUM_UDP_PCB, sizeof(struct udp_pcb), "UDP_PCB")
#endif /* LWIP_UDP */

#if LWIP_TCP
LWIP_MEMPOOL(TCP_PCB, MEMP_NUM_TCP_PCB, sizeof(struct tcp_pcb), "TCP_PCB")
LWIP_MEMPOOL(TCP_PCB_LISTEN, MEMP_NUM_TCP_PCB_LISTEN, sizeof(struct tcp_pcb_listen), "TCP_PCB_LISTEN")
LWIP_MEMPOOL(TCP_SEG, MEMP_NUM_TCP_SEG, sizeof(struct tcp_seg), "TCP_SEG")
#endif /* LWIP_TCP */

#if LWIP_ALTCP & amp; & amp; LWIP_TCP
LWIP_MEMPOOL(ALTCP_PCB, MEMP_NUM_ALTCP_PCB, sizeof(struct altcp_pcb), "ALTCP_PCB")
#endif /* LWIP_ALTCP & amp; & amp; LWIP_TCP */

#if LWIP_IPV4 & & IP_REASSEMBLY
LWIP_MEMPOOL(REASSDATA, MEMP_NUM_REASSDATA, sizeof(struct ip_reassdata), "REASSDATA")
#endif /* LWIP_IPV4 & amp; & amp; IP_REASSEMBLY */
#if (IP_FRAG & amp; & amp; !LWIP_NETIF_TX_SINGLE_PBUF) || (LWIP_IPV6 & amp; & amp; LWIP_IPV6_FRAG)
LWIP_MEMPOOL(FRAG_PBUF, MEMP_NUM_FRAG_PBUF, sizeof(struct pbuf_custom_ref), "FRAG_PBUF")
#endif /* IP_FRAG & amp; & amp; !LWIP_NETIF_TX_SINGLE_PBUF || (LWIP_IPV6 & amp; & amp; LWIP_IPV6_FRAG) */

#if LWIP_NETCONN || LWIP_SOCKET
LWIP_MEMPOOL(NETBUF, MEMP_NUM_NETBUF, sizeof(struct netbuf), "NETBUF")
LWIP_MEMPOOL(NETCONN, MEMP_NUM_NETCONN, sizeof(struct netconn), "NETCONN")
#endif /* LWIP_NETCONN || LWIP_SOCKET */

The content of the memory pool can be increased or decreased by modifying the macro definition.

The implementation of the memory heap is the same, and the two can coexist. The memory space they need (collectively referred to as LwIP stack) is independent, refer to the .map file generated by compilation as follows.

Please add picture description

In addition, LwIP also provides us with other optional memory strategies.

  1. When the macro MEM_LIBC_MALLOC is defined as 1, the code related to the memory heap will not be compiled, and the mem_malloc() and mem_free() in the memory heap will be ANSI
    C compiler comes with malloc () and free () instead. (Equivalent to putting the space occupied by mem.o in the single-chip HEAP)
  2. When the macro MEMP_MEM_MALLOC is defined as 1, the memory pool file memp.c will not be compiled. (Equivalent to putting the space occupied by memp.o in the single-chip HEAP)