Cortex-M23: Scheduler won't start, HardFault on vRestoreContextOfFirstTask

I am trying to integrate FreeRTOS into a small program running on a Cortex-M23 SAML10 microcontroller. I have found that the scheduler fails to start the first task and a HardFault is generated. From my debugging, it led me to the function: vRestoreContextOfFirstTask but I am still trying to work out how to step through debugging the inline assembly (don’t know how to do this yet). In the meantime, is there anything I can also try that is obvious from the inline assembly that could cause a HardFault on a Cortex-M23 processor? My FreeRTOS config has configENABLE_MPU set to 0.

Have you installed the FreeRTOS interrupt handlers? Would give you a link but not at my computer.

Also, often people mistake a fault occurring after the scheduler starts, so in a task, as a fault occurring in the function that starts the scheduler. Try putting a breakpoint at the very start of the highest priority task (which might be the auto created timer task) to see if it is reached.

I have tried two different methods of setting up the interrupt handlers, but neither work. Each method has a different effect however.

The first method I have done to set it up involves updating the VTOR register in main(). Here is the code I have for it - this is based on the method described in my compiler’s reference manual (XC32):

#define TBL_ALIGN 128
DeviceVectors exn_table __attribute__((aligned(TBL_ALIGN)));
extern DeviceVectors exception_table;

void update_interrupt_handler (void)
{
    memcpy (&exn_table, &exception_table, sizeof (DeviceVectors));
    __disable_irq();
    SCB->VTOR = (uint32_t) &exn_table;

    NVIC_SetVector(SysTick_IRQn, (uint32_t) SysTick_Handler);
    NVIC_SetVector(PendSV_IRQn, (uint32_t) PendSV_Handler);
    NVIC_SetVector(SVCall_IRQn, (uint32_t) SVC_Handler);
    NVIC_SetVector(SERCOM0_0_IRQn, (uint32_t) SERCOM0_I2C_InterruptHandler);
    NVIC_SetVector(SERCOM0_1_IRQn, (uint32_t) SERCOM0_I2C_InterruptHandler);
    NVIC_SetVector(SERCOM0_2_IRQn, (uint32_t) SERCOM0_I2C_InterruptHandler);
    NVIC_SetVector(SERCOM0_OTHER_IRQn, (uint32_t) SERCOM0_I2C_InterruptHandler);
    
    __DSB();
    __enable_irq();

    /* Enable the interrupt sources and configure the priorities */
    NVIC_SetPriority(SERCOM0_0_IRQn, 4);
    NVIC_EnableIRQ(SERCOM0_0_IRQn);
    NVIC_SetPriority(SERCOM0_1_IRQn, 4);
    NVIC_EnableIRQ(SERCOM0_1_IRQn);
    NVIC_SetPriority(SERCOM0_2_IRQn, 4);
    NVIC_EnableIRQ(SERCOM0_2_IRQn);
    NVIC_SetPriority(SERCOM0_OTHER_IRQn, 4);
    NVIC_EnableIRQ(SERCOM0_OTHER_IRQn);
}

void main(void)
{
    sys_initialise ();
    update_interrupt_handler ();

    ...
}

This method results in the HardFault occurring somewhere in vStartFirstTask(). I am assuming this is because I have not set up the SVC_Handler correctly based on the inline assembly for this function. I have included the header files for portasm.c so that main() can find where this is from.

The second method I had for setting up the interrupts involves adding the following source files:

  • interrupts.c - this provides a definition to the variable for the exception handlers, called exception_table.
__attribute__((section(".vectors.default"), weak, externally_visible))
const H3DeviceVectors exception_table=
{
    /* Configure Initial Stack Pointer, using linker-generated symbols */
    .pvStack = &_stack,

    .pfnReset_Handler              = Reset_Handler,
    .pfnNonMaskableInt_Handler     = NonMaskableInt_Handler,
    .pfnHardFault_Handler          = HardFault_Handler,
    .pfnSVCall_Handler             = SVC_Handler,
    .pfnPendSV_Handler             = PendSV_Handler,
    .pfnSysTick_Handler            = SysTick_Handler,
  • exceptions.c, startup_xc32.c - this provides the code for other handlers as defined in interrupts.c

This second method is based on the MPLAB demo using FreeRTOS - which can be found here: https://github.com/Microchip-MPLAB-Harmony/core_apps_sam_l10_l11/tree/master/apps/rtos/freertos/basic_freertos

For this second method, I found that the HardFault is occuring when vPortSVCHandler_C is called, which is called by SVC_Handler. Is it correct to assume that SVC_Handler is called by one of the assembly instructions in vStartFirstTask()?

I am not sure which of either method is (more) correct, as it seems I am not even setting up the handlers correctly at all. I did a read through RTOS for ARM Cortex-M but it seems I have these already configured.

Are you mapping the SysTick_Handler, PendSV_Handler and SVC_Handler functions to the FreeRTOS handlers for these interrupts in a similar manner as described under the red “special note to Arm Cortex-M users” text on this page? https://freertos.org/FAQHelp.html, so something like this in FreeRTOSConfig.h:

#define vPortSVCHandler SVC_Handler
#define xPortPendSVHandler PendSV_Handler
#define xPortSysTickHandler SysTick_Handler

Normally I don’t mess with the vector table and just use it directly out of flash memory as set up by the C start up code, something like this: FreeRTOS/startup_sam3x.c at 202112.00 · FreeRTOS/FreeRTOS · GitHub

I’d propose the second method, too. It’s common practice.
Also when enabling hardware interrupts handled by ISRs using FreeRTOS API you’ve to ensure that all resources used in those ISRs (semaphores, task handles, etc.) are created and initialized before.
If possible you could enable interrupts in the task startup code (right before entering the task forever loop of the task), which post-processes the interrupts signaled by the corresponding ISRs.