modm API documentation
|
lbuild module: modm:platform:heap
Your applicaton is linked against the newlib-nano
libc, which only requires the implementation of the void* sbrk(ptrdiff_t size)
hook to use the heap.
However, the sbrk
mechanism can only grow and shrink forward and backwards in memory and in particular it does not support discontinuous jumps from one memory section to another. The limitation stems from being designed for use with a MMU which would simply map another physical page into the linear virtual address space so that the heap section appears continuous to sbrk
.
Since we do not have a MMU on Cortex-M, this strategy limits the use of the default newlib allocator to one continuous memory region. Therefore this module implements alternative allocators for devices with multiple memory regions with different traits and extends the C++ operator new
to access them.
See the modm:architecture:memory
module for what kind of memory traits exist.
This module is not included by default, and any attempt to use the heap fails with one or multiple linker error messages similiar to this:
This is to prevent you from accidentally using the heap, which may not be desirable for your application. If this occurs you have three choices. You can:
There are several trade-offs to each allocator, however, as a rule of thumb, choose:
newlib
for devices with one large continuous RAM region.block
for devices with one very small RAM region.tlsf
for devices with multiple, different discontinuous RAM regions.newlib
and block
strategies choose the largest continuous memory region, even though unaligned accesses across memory regions may not be supported in hardware and lead to a bus fault! Consider using the TLSF implementation, which does not suffer from this issue.The newlib-nano allocator is a simple linked list, its overhead is therefore low, but the access time may not be good. Due to the limitations of the sbrk
mechanism only the largest memory region is used as heap! Depending on the device memory architecture this can leave large memory regions unused.
Consider using the TLSF allocator for devices with multiple discontinuous memories.
For devices with very small memories, we recommend using the block allocator strategy, which uses a very light-weight and simple algorithm. This also only operates on one continuous memory region as heap.
To use all non-statically allocated memory for heap, use the TLSF strategy, which natively supports multiple memory regions. This implementation treats all internal memories as separate regions, so unaligned access across memory boundaries is not an issue. To request heap memory of different traits, see the modm:architecture:memory
module.
To implement your own allocator do not include this module. Instead initialize your heap in the function __modm_initialize_memory()
, which gets called by the startup script after hardware init, but before static constructors are called (see modm:platform:cortex-m
for details).
The simplest way to do so is to allocate a huge array into one of the heap sections and use this as your heap. Consult modm:platform:core
for what heap sections your target provides!
If you prefer a little more control, include the modm:architecture:memory
module to get access to the internal modm::platform::HeapTable
API, which lists memory regions by traits and sizes.
For example to find the largest continuous memory section with default traits you can use this code:
If you want to know more about the available memory regions, you can iterate over the heap table directly. This gives you full control over where you want to place you heap. You can print this table at runtime to get a feel for it:
To use the builtin allocator from newlib, all you need to provide is an implementation of the sbrk
function. A simple implementation for a [heap_begin
, heap_end
] memory region looks like this:
Unfortunately virtual C++ destructors can emit a call to operator delete
even for classes with static allocation and also in program without a single call to operator new
or malloc
. Therefore if this module is not included, calls to operator delete
are ignored and you must overwrite this behavior with this function that only points to free
.
To use a completely custom allocator, you need to replace the newlib allocator by wrapping the malloc
, calloc
, realloc
and free
functions via the linker by adding this to your project configuration:
And then implement the following functions with your custom allocator:
This is particularly recommended if you need a thread-safe malloc, which you implement here via the _reent
struct. Consult newlib docs for details.
_sbrk_r
is not called anymore, and therefore is thrown away by the linker, thus the linker error disappears. You therefore do not need to implement it, not even as a stub.To also support memory traits, you need to overwrite the default implementation of malloc_traits(size, traits)
which would otherwise just ignore the traits:
Generated with: newlib in [block, newlib, tlsf]