Memory Management in Embedded Development with FreeRTOS

1. Concept of Memory Management

Memory management is a critical function in computer systems, responsible for managing memory resources within the system. The goal of memory management is to ensure that all programs in the system can access the required memory without issues such as memory conflicts or memory leaks. Memory management includes operations such as memory allocation, memory reclamation, memory protection, and memory mapping, which need to be managed by the operating system or application. The quality of memory management directly affects the performance and stability of the system.

In embedded programming, memory allocation should be determined based on the characteristics of the designed system, choosing between dynamic memory allocation or static memory allocation algorithms. Systems with very high reliability requirements should opt for static allocation, while ordinary business systems can use dynamic allocation to improve memory usage efficiency. Static allocation ensures device reliability but requires consideration of memory limits, leading to lower memory usage efficiency, while dynamic allocation is the opposite.

Moreover, in embedded real-time operating systems, the timing requirements for memory allocation are more stringent; the time taken to allocate memory must be deterministic. Generally, memory management algorithms search for a free memory block that fits the length of the data to be stored. However, the time taken to find such a free memory block is uncertain, which is unacceptable for real-time systems. Real-time systems must ensure that the memory block allocation process is completed within a predictable and deterministic time; otherwise, the response of real-time tasks to external events will also become uncertain.

To avoid this issue, FreeRTOS keeps the memory allocation API in its portable layer. The portable layer allows for specific application implementations suitable for the real-time system being developed, outside the source files that implement core RTOS functions. When the RTOS kernel needs RAM, it does not call malloc(), but instead calls pvPortMalloc(). When freeing RAM, the RTOS kernel calls vPortFree() instead of free().

Memory Management in Embedded Development with FreeRTOS

FreeRTOS provides several heap management schemes, each with varying complexity and functionality. You can also provide your own heap implementation and even use two heap implementations simultaneously. Using two heap implementations allows task stacks and other RTOS objects to be placed in internal RAM while application data can be placed in slower external RAM.

Memory Management in Embedded Development with FreeRTOS

2. heap_1.c

heap_1 is not very useful because FreeRTOS has added support for static allocation. heap_1 is the simplest implementation. Once memory is allocated, it does not allow the memory to be freed. Nevertheless, heap_1.c is suitable for many embedded applications. This is because many small and deeply embedded applications create all the required tasks, queues, semaphores, etc., at system startup and use all these objects throughout the program’s lifecycle (until the application is closed or restarted). Nothing is deleted.

This implementation simply subdivides a single array into smaller blocks when RAM is requested. The total size of the array (the total size of the heap) is set by configTOTAL_HEAP_SIZE (defined in FreeRTOSConfig.h).

It provides

configAPPLICATION_ALLOCATED_HEAP

FreeRTOSConfig.h configuration constant, allowing the heap to be placed at a specific address in memory.

xPortGetFreeHeapSize() API function returns the total amount of unallocated heap space,

allowing optimization of the configTOTAL_HEAP_SIZE setting.

When using heap_1, the memory allocation process is illustrated as follows:

A: The entire array is free before creating tasks.

B: After creating the first task, the blue area is allocated.

C: Memory usage after creating three tasks.

Memory Management in Embedded Development with FreeRTOS

3. heap_2.c

heap_2 is now considered outdated, with heap_4 being preferred. heap_2 uses the best-fit algorithm and, unlike scheme 1, it allows previously allocated blocks to be freed, but it does not combine adjacent free blocks into a larger block. For the implementation that does not merge free blocks, see heap_4.c.

The total amount of available heap space is set by configTOTAL_HEAP_SIZE (defined in FreeRTOSConfig.h).

It provides

configAPPLICATION_ALLOCATED_HEAP

in FreeRTOSConfig.h configuration constant, allowing the heap to be placed at a specific address in memory.

xPortGetFreeHeapSize() API function returns the total amount of unallocated heap space, (allowing optimization of

configTOTAL_HEAP_SIZE setting), but does not provide information on how unallocated memory is fragmented into small blocks.

pvPortCalloc() function has the same signature as the standard library calloc function. It allocates memory for an array of objects and initializes all bytes in the allocated storage space to zero. If allocation is successful, it returns a pointer to the lowest byte of the allocated memory block. If allocation fails, it returns a null pointer.

When using heap_2, the memory allocation process is illustrated as follows:

A: Three tasks have been created.

B: One task has been deleted, leaving three parts of free memory: the top level, the TCB space of the deleted task, and the stack space of the deleted task.

C: A new task is created, and since the TCB and stack sizes are consistent with those of the previously deleted task, it perfectly allocates the original memory.

Memory Management in Embedded Development with FreeRTOS

4. heap_3.c

heap_3.c scheme simply wraps the malloc() and free() functions from the standard C library and can satisfy common compilers. The rewrapped malloc() and free() functions have protective features, where the scheduler is suspended before memory operations and resumed afterward.

heap_3.c scheme has the following characteristics:

1. Requires the linker to set a heap, with malloc() and free() functions provided by the compiler.

2. Has uncertainty.

3. Likely increases the code size of the RTOS kernel.

It is important to note that when using the heap_3.c scheme, the configTOTAL_HEAP_SIZE macro definition in FreeRTOSConfig.h does not take effect. In STM32 series projects, this heap defined by the compiler is set in the startup file, measured in bytes.

Memory Management in Embedded Development with FreeRTOS

5. heap_4.c

This scheme uses the first-fit algorithm and, unlike scheme 2, it does combine adjacent free memory blocks into a single large memory block (it indeed includes a merging algorithm).

The total amount of available heap space is set by configTOTAL_HEAP_SIZE (defined in FreeRTOSConfig.h). It provides

configAPPLICATION_ALLOCATED_HEAP

FreeRTOSConfig.h configuration constant, allowing the heap to be placed at a specific address in memory.

xPortGetFreeHeapSize() API function returns the total amount of unallocated heap space when called, and xPortGetMinimumEverFreeHeapSize() API function returns the minimum free heap space that has existed in the system since the FreeRTOS application started. Both functions do not provide information on how unallocated memory is fragmented into small blocks.

6. heap_5.c

This scheme uses the same first-fit and memory merging algorithm as heap_4, allowing the heap to span multiple non-adjacent (non-contiguous) memory regions. Heap_5 is initialized by calling vPortDefineHeapRegions(), and it must not be used until vPortDefineHeapRegions() has been executed. Creating RTOS objects (tasks, queues, semaphores, etc.) will implicitly call pvPortMalloc(), so it is crucial to call vPortDefineHeapRegions() before creating any such objects when using heap_5.

vPortDefineHeapRegions() takes a single parameter, which is an array of HeapRegion_t structures. HeapRegion_t is defined in portable.h as

typedef struct HeapRegion{ /* The start address of a block of memory that will be part of the heap.*/ uint8_t *pucStartAddress; /* The size of the block of memory in bytes. */ size_t xSizeInBytes;} HeapRegion_t;

The array is terminated by defining a region of zero size, and the memory regions defined in the array must be in address order, from low address to high address.

xPortGetFreeHeapSize() API function returns the total amount of unallocated heap space when called,

xPortGetMinimumEverFreeHeapSize() API function returns the minimum free heap space that has existed in the system since the FreeRTOS application started. Both functions do not provide information on how unallocated memory is fragmented into small blocks.

pvPortCalloc() function has the same signature as the standard library calloc function. It allocates memory for an array of objects and initializes all bytes in the allocated storage space to zero. If allocation is successful, it returns a pointer to the lowest byte of the allocated memory block. If allocation fails, it returns a null pointer.

vPortGetHeapStats() API function provides additional information about the heap status.

7. Functions Related to Heap

pvPortMalloc/vPortFree

Functionality: Allocate memory, free memory. If memory allocation fails, the return value is NULL.

Function prototype

void *pvPortMalloc( size_t xWantedSize ){  void *pvReturn = NULL;  static uint8_t *pucAlignedHeap = NULL;  /* Ensure byte alignment */  #if( portBYTE_ALIGNMENT != 1 )  {if( xWantedSize & portBYTE_ALIGNMENT_MASK ){  /* Needs byte alignment */  xWantedSize += (portBYTE_ALIGNMENT-(xWantedSize&portBYTE_ALIGNMENT_MASK));  }  }  #endif  vTaskSuspendAll();// Suspend the task scheduler, the memory allocation process cannot be interrupted by other tasks  {if( pucAlignedHeap == NULL ){  /* Ensure the starting address of the memory heap is byte-aligned */  pucAlignedHeap = (uint8_t *)(((portPOINTER_SIZE_TYPE) &ucHeap[portBYTE_ALIGNMENT]) & (~((portPOINTER_SIZE_TYPE)portBYTE_ALIGNMENT_MASK)));  }/* Check if there is enough memory for allocation and if it is out of bounds */if(((xNextFreeByte + xWantedSize) < configADJUSTED_HEAP_SIZE) &&((xNextFreeByte + xWantedSize) > xNextFreeByte)){  pvReturn = pucAlignedHeap + xNextFreeByte;// Return the address of the allocated memory  xNextFreeByte += xWantedSize;  }traceMALLOC( pvReturn, xWantedSize );  }  ( void ) xTaskResumeAll();// Resume the task scheduler  #if( configUSE_MALLOC_FAILED_HOOK == 1 )// If the memory allocation failure hook function is enabled  {if( pvReturn == NULL ){  extern void vApplicationMallocFailedHook(void);// Hook function, user-defined  vApplicationMallocFailedHook();  }  }  #endif  return pvReturn;// Return the address of the allocated memory on success, NULL on failure}  void vPortFree( void *pv ){  /* It can be seen that there is no specific release process, i.e., once memory is successfully allocated, it cannot be freed */  ( void ) pv;  configASSERT( pv == NULL );}

xPortGetFreeHeapSize

Functionality:Used to optimize memory usage. For example, when all kernel objects are allocated, executing this function returns 2000, then configTOTAL_HEAP_SIZE can be reduced by 2000.

Function prototype

size_t xPortGetFreeHeapSize( void );

Note: Cannot be used in heap_3.

xPortGetMinimumEverFreeHeapSize

Function prototype

size_t xPortGetMinimumEverFreeHeapSize( void );

Function return value: The minimum value of free memory capacity during program execution.

Note: Only heap_4 and heap_5 support this function.

Memory allocation failure hook function

void * pvPortMalloc( size_t xWantedSize ){    ......    #if ( configUSE_MALLOC_FAILED_HOOK == 1 )        {            if( pvReturn == NULL )            {                extern void vApplicationMallocFailedHook( void );                vApplicationMallocFailedHook();            }        }    #endif    return pvReturn;       }

Inside the pvPortMalloc function, so if you want to use this hook function: define configUSE_MALLOC_FAILED_HOOK as 1 in FreeRTOSConfig.h. Provide the vApplicationMallocFailedHook function.

This function is only called when pvPortMalloc fails.

Memory Management in Embedded Development with FreeRTOSMemory Management in Embedded Development with FreeRTOS

Leave a Comment