Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Using Multiple Memory Regions With the FreeRTOS Heap

DZone's Guide to

Using Multiple Memory Regions With the FreeRTOS Heap

Here is how to enable FreeRTOS to use multiple memory blocks for a virtual combined memory heap, giving you access to non-contiguous memory regions.

· IoT Zone ·
Free Resource

ARM Cortex-M microcontrollers can have multiple memory controllers. This is a good thing, as it allows the hardware to do multiple parallel memory read/writes. However, this makes the memory map more complicated for the software: It divides the memory into different regions and memory segments. This article is about how to enable FreeRTOS to use multiple memory blocks for a virtual combined memory heap:

Image title

FreeRTOS with Segmented Heap Memory

Segmented RAM

For example, an NPX Kinetis K64F has 256KByte of SRAM, but the biggest memory object or array I can use is only 192 KBytes. How is that?

Because the device has two memory controllers, one for the UPPER and one for the LOWER memory area, with a division at address 0x2000'0000. This manifests in two memory areas in the linker file:

SRAM_UPPER (rwx) : ORIGIN = 0x20000000, LENGTH = 0x30000 /* 192K bytes (alias RAM) */ 
SRAM_LOWER (rwx) : ORIGIN = 0x1fff0000, LENGTH = 0x10000 /* 64K bytes (alias RAM2) */


One might think that these two areas could be combined, as they are adjacent. Wrong, as this will very likely will result in a hard fault. I cannot have memory objects crossing that memory boundary at 0x2000'0000. For a discussion on that topic, see https://mcuoneclipse.com/2013/07/10/freertos-heap-with-segmented-kinetis-k-sram/

So, how do you use more than one-half of the memory? FreeRTOS has the solution with the Heap_5 memory scheme.

heap_5

FreeRTOS offers different memory management schemes, and one of them is heap_5.

From http://www.freertos.org/a00111.html:

"This scheme ... allows the heap to span multiple non adjacent (non-contiguous) memory regions. "

To use Scheme 5, make sure heap_5.c is used in the application:

Image title

If using the McuOnEclipse FreeRTOS port for Processor Expert, select Scheme 5:

Image title

FreeRTOS Heap_5.c in Processor Expert

Memory Variables

Next, define memory arrays like below. I'm using the industry standard GNU toolchain for ARM microcontroller with __attribute__ to allocate variables in a specific segment (see Defining Variables at Absolute Addresses with gcc):

static __attribute__ ((used,section(".noinit.$SRAM_LOWER_Heap5"))) uint8_t heap_sram_lower[50*1024]; /* placed in in no_init section inside SRAM_LOWER */
static __attribute__ ((used,section(".noinit_Heap5"))) uint8_t heap_sram_upper[128*1024]; /* placed in in no_init section inside SRAM_UPPER */


The above reserves one block of 50 KByte and one block of 128 KByte.

The section name I have used heavily depends on the linker file used. For the above case, I have used the Eclipse-based NXP MCUXpresso IDE 10.0.2 with GNU Linker file. That linker file uses two 'no-init' memory placements:

.noinit_RAM2 (NOLOAD) : ALIGN(4)
{
    *(.noinit.$RAM2*)
    *(.noinit.$SRAM_LOWER*)
    . = ALIGN(4) ;
} > SRAM_LOWER


and

.noinit (NOLOAD): ALIGN(4)
{
    _noinit = .;
    *(.noinit*) 
    . = ALIGN(4) ;
    _end_noinit = .;
} > SRAM_UPPER


'No-init' means that the startup code will not initialize the variable/RAM inside that section (see GNU Linker, can you NOT Initialize my Variable?). Initialization of heap memory is not needed, and it only would slow down the startup of the program.

HeapRegion_t

Next, I list the heap regions in a table of type HeapRegion_t:

static HeapRegion_t xHeapRegions[] =
{
    { 
        &heap_sram_lower[0], sizeof(heap_sram_lower)
    },
    { 
        &heap_sram_upper[0], sizeof(heap_sram_upper)
    },
    { 
        NULL, 0 // << Terminates the array.
    } 
};


That table lists the regions, starting with the lower address block. Each entry has a pointer to the start of the memory block and its size. The list is terminated by a NULL sentinel.

Defining FreeRTOS Regions

To tell FreeRTOS about the memory regions, I have to call vPortDefineHeapRegions() with a pointer to the description table:

vPortDefineHeapRegions(xHeapRegions); // Pass the array into vPortDefineHeapRegions(). Must be called first!


The important thing is that this has to be called before any FreeRTOS memory allocation (task, semaphore, mutex, queue, ...) is performed! I usually call it right after main() is called.

Using Multiple FreeRTOS Memory Regions

After that, FreeRTOS can be used with multiple regions as if it would be only a single heap memory region:

Image title

FreeRTOS with Segmented Heap Memory

The memory is still divided into pieces, but the total amount of memory available is now the sum of the pieces. Of course, memory is still fragmented, and this defines the largest piece of memory I can allocate.

Summary

Using the heap_5 allows me to use a FreeRTOS heap to span multiple memory regions: exactly what I need for devices which have multiple RAM memory controllers or that have gaps between the SRAM regions. With heap_5, I still cannot have single objects larger than the largest memory region, but it allows me to use multiple non-contiguous areas in a safe way.

I have posted the example project used in this article on GitHub.

Happy Heaping!

Links

Topics:
iot ,microcontrollers ,arm cortex-m ,memory management ,tutorial ,freertos

Published at DZone with permission of

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}