dnadler wrote on Wednesday, August 21, 2019:
For Cortex M4 (and other MCUs) ISR’s run on a separate stack from FreeRTOS tasks (MSP).
To ensure bulletproof operation, its a good idea to verify all stacks have adequate space
I suggest the following additions:
- In configuration, new #defines:
// DRN ISR (MSP) stack initialization and checking
#define configISR_STACK_SIZE_WORDS (0x100) // DRN in WORDS, must be valid constant for assembler
#define configSUPPORT_ISR_STACK_CHECK 1 // DRN initialize and check ISR stack
- Additional code in port.c (this is for the M4 GCC port). I’ve tried to keep this tight but my ARM-GCC-assembler isn’t super-strong…
#define QUOTE(str) #str
#define EXPAND_AND_QUOTE(str) QUOTE(str)
#if defined(configSUPPORT_ISR_STACK_CHECK) && configSUPPORT_ISR_STACK_CHECK && !defined(configISR_STACK_SIZE_WORDS)
#error "configISR_STACK_SIZE_WORDS must be defined for ISR stack checking (WORDS to reserve for MSP stack)"
#endif
static void prvPortStartFirstTask( void )
{
/* Start the first task. This also clears the bit that indicates the FPU is
in use in case the FPU was used before the scheduler was started - which
would otherwise result in the unnecessary leaving of space in the SVC stack
for lazy saving of FPU registers. */
__asm volatile(
" ldr r0, =0xE000ED08 \n" /* Use the NVIC offset register to locate the initial stack. */
" ldr r0, [r0] \n"
" ldr r0, [r0] \n" /* r0 now has top of stack (actually, word above beginning of stack) */
" msr msp, r0 \n" /* Set the MSP (*ISR* stack register) back to the start of the stack (top of RAM). */
#if defined(configSUPPORT_ISR_STACK_CHECK) && configSUPPORT_ISR_STACK_CHECK
// DRN code: Zero the MSP stack before use, to facilitate stack use check
" mov r1, #0 \n" /* value to store into stack */
" ldr r2, pxMSRstackLen\n" /* remaining words to clear in stack */
"FillZeroMSPstack: \n"
" sub r0, r0, #4 \n" /* next word to zero */
" str r1, [r0] \n" /* store a zero */
" sub r2, r2, #1 \n" /* decrement word count */
" cmp r2, #0 \n" /* finished clearing stack? */
" bne FillZeroMSPstack \n"
#endif // #if defined(configSUPPORT_ISR_STACK_CHECK) && configSUPPORT_ISR_STACK_CHECK
" mov r0, #0 \n" /* Clear the bit that indicates the FPU is in use, see comment above. */
" msr control, r0 \n"
" cpsie i \n" /* Globally enable interrupts. */
" cpsie f \n"
" dsb \n"
" isb \n"
" svc 0 \n" /* System call to start first task. */
" nop \n"
);
#if defined(configSUPPORT_ISR_STACK_CHECK) && configSUPPORT_ISR_STACK_CHECK
__asm volatile (
" .align 4 \n"
"pxMSRstackLen: .word " EXPAND_AND_QUOTE(configISR_STACK_SIZE_WORDS) "\n"
);
#endif // #if defined(configUSE_ISR_STACK_CHECK) && configUSE_ISR_STACK_CHECK
}
/*-----------------------------------------------------------*/
#if defined(configSUPPORT_ISR_STACK_CHECK) && configSUPPORT_ISR_STACK_CHECK
BaseType_t xUnusedISRstackWords( void ) {
register uint32_t *pStackOrigin;
int unusedStackWords = 0;
__asm volatile(
" ldr r0, =0xE000ED08 \n" /* Use the NVIC offset register to locate the initial stack. */
" ldr r0, [r0] \n"
" ldr r0, [r0] \n" /* r0 now has top of stack (actually, word above beginning of stack) */
" mov %[result], r0 \n" : [result] "=r" (pStackOrigin) :: "r0"
);
uint32_t *pStackUseEnd = pStackOrigin - configISR_STACK_SIZE_WORDS; // start testing at stack limit
for(int lim=configISR_STACK_SIZE_WORDS; lim; lim--) {
if(*pStackUseEnd++) break; // break at first used word
unusedStackWords++;
}
return unusedStackWords;
}
#endif // #if defined(configUSE_ISR_STACK_CHECK) && configUSE_ISR_STACK_CHECK
/*-----------------------------------------------------------*/
With this I’m able to verify ISR stack use in our application.
Thanks as always for your excellent work,
Best Regards, Dave
PS: I’ll update heap_useNewlib.c to match the above shortly, on http://www.nadler.com/embedded/newlibAndFreeRTOS.html
PS: I’ve created a pull-request for this change, hope its OK!