Hello,
The Nordic folks have explained this many times, and even have a dedicated section in the soft-device spec. However, I can’t connect the dots for some reason. In the happy path, it seems straightforward, but as complexity grows in the application, so does the logic around RAM usage. I added a quick diagram below to give context to the questions. This is mainly about RAM, and the relative position of the stack and heap.
The way I understand RAM usage within nRF52 is that, typically, the RAM_BASE
is defined, growing upwards by the RAM_LENGTH
. This can be seen in the S140 Softdevice spec below:
https://infocenter.nordicsemi.com/pdf/S140_SDS_v1.1.pdf
For example:
In terms of definitions, the address region for RAM has defined in the linker files as seen below:
MEMORY { FLASH (rx) : ORIGIN = 0x27000, LENGTH = 0xd9000 RAM_SPIM3 (rwx) : ORIGIN = 0x20006000, LENGTH = 0x02000 RAM (rwx) : ORIGIN = 0x20008000, LENGTH = 0x38000 }
Based on this, the stack would then live at the RAM end address, meaning STACK_TOP
would be defined as (RAM_BASE + RAM_LENGTH
). Again, this is defined in the linker file as well:
.heap (COPY): { __HeapBase = .; __end__ = .; PROVIDE(end = .); KEEP(*(.heap*)) __HeapLimit = .; } > RAM .stack_dummy (COPY): { KEEP(*(.stack*)) } > RAM __StackTop = ORIGIN(RAM) + LENGTH(RAM); __StackLimit = __StackTop - SIZEOF(.stack_dummy);
As for the complexity mentioned above, now throw an RTOS like FreeRTOS into the picture. Nordic has examples of this as well. But the point is that FreeRTOS has its own stack/heap designations as well. For example:
#define configTOTAL_HEAP_SIZE 131072 // In bytes #define configTIMER_TASK_STACK_DEPTH 200 // In words (800 bytes) #define configMINIMAL_STACK_SIZE 60 // In words (240 bytes)
Similarly, based on how FreeRTOS is configured, you may even have specific implementations of the heap provided by the OS, such as heap_[1-5].
With all of that said, I’ve tried to organize my thoughts into three main questions, all of which are kind of tied together in some way.
-
I can imagine the scenario where someone sets the nRF52 stack size of 2K, either through
__STACK_SIZE
,__STARTUP_CONFIG
, directly in the assembly file, or externally. But then sets the FreeRTOS stack size of 8K. I’m assuming these two would contradict each other. Another scenario would be if the dev didn’t set the stack size at all, which would mean the default would apply from the nRF52 startup assembly(gcc_startup_nrf52840.s), which might be less than what they have set in the FreeRTOS Config. So I guess the question is, how would you handle this scenario, or are the two stack/heap definitions mutually exclusive? -
In terms of the stack/heap allocations. Am I correct in saying that, as I add more data into the statically allocated memory sections, such as
.bss
,.data
, and.noinit
, then there’s a possibility that I will eventually run into the heap/stack sections? I say this because, based on the Softdevice spec referenced above, it looks like the stack/heap typically grow downward toward the base address, however as data gets added to the statically allocated sections like.bss
,.data
, and.noinit
within the application, then it will grow upwards toward the stack/heap. I'm guessing that eventually they will collide? Or, maybe that is the exact reason for Nordics assertion below:/* Set stack top to end of RAM, and stack limit move down by * size of stack_dummy section */ __StackTop = ORIGIN(RAM) + LENGTH(RAM); __StackLimit = __StackTop - SIZEOF(.stack_dummy); PROVIDE(__stack = __StackTop); /* Check if data + heap + stack exceeds RAM limit */ ASSERT(__StackLimit >= __HeapLimit, "region RAM overflowed with stack")
- Lastly, and this is one I find very strange but, when looking at my memory map, I see that my
.heap
section has the same exact address of my.stack
. This seems like a bug introduced by me, especially since both sections have a size of ~16K. I also see a.bss.heap_end.0
section, which seems odd. I was assuming all heap allocations reside in the.heap
section, unless that’s an artifact of FreeRTOS? i.e looking back at question #1, maybe FreeRTOS reserves a section for its own stack and heap within.bss
? So having both the nRF52.stack
/.heap
definitions might be an issue on my end?
.heap 0x20037718 0x4000 0x20037718 __HeapBase = . 0x20037718 __end__ = . 0x20037718 PROVIDE (end = .) *(.heap*) .heap 0x20037718 0x4000 nrf52.a(gcc_startup_nrf52840.S.obj) 0x2003b718 __HeapLimit = . .stack_dummy 0x20037718 0x4000 *(.stack*) .stack 0x20037718 0x4000 rf52.a(gcc_startup_nrf52840.S.obj) 0x20040000 __StackTop = (ORIGIN (RAM) + LENGTH (RAM)) 0x2003c000 __StackLimit = (__StackTop - SIZEOF (.stack_dummy)) 0x20040000 PROVIDE (__stack = __StackTop) 0x00000001 ASSERT ((__StackLimit >= __HeapLimit), region RAM overflowed with stack) 0x00000178 DataInitFlashUsed = (__bss_start__ - __data_start__) 0x00084330 CodeFlashUsed = (__etext - ORIGIN (FLASH)) 0x000844a8 TotalFlashUsed = (CodeFlashUsed + DataInitFlashUsed) 0x00000001 ASSERT ((TotalFlashUsed <= LENGTH (FLASH)), region FLASH overflowed with .data and user data)
I know, it's a lot. But thanks for taking the time to look!