I’ve written some code to help exactly measure the ISR stack usage on ARM mbed:
https://github.com/nuket/mbed-memory-status
Update
Usage of the library has also been written up on the mbed website!
https://developer.mbed.org/blog/entry/Tracing-stack-and-heap-overflow-errors/
Notes
Here are some notes on how to fill the stack area with a canary value, and then measure the total ISR stack usage at runtime.
Here I am messing around with an NRF51_DK target, which has a total of 32KB of RAM.
The stack configuration is located in the startup_NRF51822.S
file, which is located in the following folder for GCC ARM:
1 |
mbed-os\targets\TARGET_NORDIC\TARGET_NRF5\TARGET_MCU_NRF51822_UNIFIED\device\TOOLCHAIN_GCC_ARM\startup_NRF51822.S
|
You can find out which assembly file is used for chip startup by running the mbed compile -c -v
command, which then dumps a verbose amount of detail:
Compile [ 70.9%]: startup_NRF51822.S [DEBUG] Compile: C:\tools\gcc-arm-none-eabi-4_9-2014q4-20141203-win32\bin\arm-none-eabi-gcc -x assembler-with-cpp -c -Wall -Wextra -Wno-unused-parameter -Wno-missing-field-initializers -fmessage-length=0 -fno-exceptions -fno-builtin -ffunction-sections -fdata-sections -funsigned-char -MMD -fno-delete-null-pointer-checks -fomit-frame-pointer -Os -mcpu=cortex-m0 -mthumb -D__CORTEX_M0 -DTARGET_MCU_NRF51822 -DTARGET_MCU_NORDIC_32K -DSOFTDEVICE_PRESENT -DNRF51 -DTARGET_NRF51822 -DARM_MATH_CM0 -D__MBED_CMSIS_RTOS_CM -DS130 -DBLE_STACK_SUPPORT_REQD -D__CMSIS_RTOS -DTARGET_MCU_NRF51_32K @.\BUILD\NRF51_DK\GCC_ARM\.includes_7f5e7dfc3f0eeb6c86bfd931d07b1e3f.txt -include .\BUILD\NRF51_DK\GCC_ARM\mbed_config.h -o .\BUILD\NRF51_DK\GCC_ARM\mbed-os\targets\TARGET_NORDIC\TARGET_NRF5\TARGET_MCU_NRF51822_UNIFIED\device\TOOLCHAIN_GCC_ARM\startup_NRF51822.o .\mbed-os\targets\TARGET_NORDIC\TARGET_NRF5\TARGET_MCU_NRF51822_UNIFIED\device\TOOLCHAIN_GCC_ARM\startup_NRF51822.S [DEBUG] Return: 0
In the Reset_Handler
, there’s a segment that calls out and sets up a handful of items before the main program starts.
It is here that the ISR stack memory can be filled with some canary value:
1
2
3
4
5
6
7
8
9
|
.LC0:
LDR R0, =SystemInit
BLX R0
LDR R0, =nrf_reloc_vector_table
BLX R0
LDR R0, =fill_isr_stack_with_canary
BLX R0
LDR R0, =_start
BX R0
|
This fills the stack area with 0xAFFEC7ED
. The cool thing is that the BLX
assembly instruction jumps to this routine w/o pushing anything to stack, so at this point the ARM stack pointer should be pointing to the absolute top of ISR stack memory. The return to caller is handled by the ARM Link Register:
1
2
3
4
5
6
7
8
9
10
|
void fill_isr_stack_with_canary(void)
{
uint32_t * bottom = &__StackLimit;
uint32_t * top = (uint32_t *) GET_SP();
for (; bottom < top; bottom++)
{
*bottom = ISR_STACK_CANARY;
}
}
|
Later, the total stack usage can be calculated by the routine:
1
2
3
4
5
6
7
8
9
10
11
12
|
uint32_t calculate_isr_stack_usage(void)
{
for (const uint32_t * stack = &__StackLimit; stack < &__StackTop; stack++)
{
if (*stack != ISR_STACK_CANARY)
{
return (uint32_t) &__StackTop - (uint32_t) stack;
}
}
return mbed_stack_isr_size;
}
|