This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

Clarification on configuration of FreeRTOS after hardfault in xPortPendSVHandler()

I need to include RTOS support in my NRF52840-based system.  In order to obtain a good FreeRTOSConfig.h I've looked at the four examples in NRF5 that include FreeRTOS and, aside from choice of heap size (4 kbytes or 8 kbytes) and, in the case of usbd_ble_uart_freertos, use of the idle hook, they are identical, so I just picked 8 kbytes and no idle hook.

However, as soon as I call vTaskDelay() I end up in a hardfault in xPortPendSVHandler(), similar to this ticket:

https://devzone.nordicsemi.com/f/nordic-q-a/49587/freertos-context-switch-hardfault

Unfortunately I can't divine an answer from that ticket, so I'd like to ask for clarification on how interrupt priorities, which seem a likely cause of my problem, should be set.  All four example FreeRTOSConfig.h files say the same thing on this topic: 

/* The lowest interrupt priority that can be used in a call to a "set priority" function. */
#define configLIBRARY_LOWEST_INTERRUPT_PRIORITY 0xf

/* The highest interrupt priority that can be used by any interrupt service
routine that makes calls to interrupt safe FreeRTOS API functions. DO NOT CALL
INTERRUPT SAFE FREERTOS API FUNCTIONS FROM ANY INTERRUPT THAT HAS A HIGHER
PRIORITY THAN THIS! (higher priorities are lower numeric values. */
#define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY _PRIO_APP_HIGH


/* Interrupt priorities used by the kernel port layer itself. These are generic
to all Cortex-M ports, and do not rely on any particular library functions. */
#define configKERNEL_INTERRUPT_PRIORITY configLIBRARY_LOWEST_INTERRUPT_PRIORITY
/* !!!! configMAX_SYSCALL_INTERRUPT_PRIORITY must not be set to zero !!!!
See http://www.FreeRTOS.org/RTOS-Cortex-M3-M4.html. */
#define configMAX_SYSCALL_INTERRUPT_PRIORITY configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY

/* Definitions that map the FreeRTOS port interrupt handlers to their CMSIS
standard names - or at least those used in the unmodified vector table. */

#define vPortSVCHandler SVC_Handler
#define xPortPendSVHandler PendSV_Handler

...where app_util_platform.h sets _PRIO_APP_HIGH to 2 for a Cortex M4.  The NRF5 v16 documentation says:

configLIBRARY_LOWEST_INTERRUPT_PRIORITY must be set to the same value as NRF_APP_PRIORITY_LOW.
The syscall priority must be higher than or equal to the lowest application priority. To ensure this, set configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY to a value equal to or higher than NRF_APP_PRIORITY_LOW.
It has not been tested if starting any interrupt before the FreeRTOS initialization is safe. Interrupts that use FreeRTOS functions from a priority level higher or equal to NRF_APP_PRIORITY_HIGH might break system initialization, because this level is higher than the SVC level. Therefore, start the system as quickly as possible and do the rest of the initialization in the thread.

...but I've searched the entire NRF5 directory and I can't find NRF_APP_PRIORITY_LOW in it.  Could this really mean _PRIO_APP_LOWEST, which I believe app_util_platform.h sets to 7 for a Cortex M, or maybe _PRIO_THREAD, which is 15 for a Cortex M and happens to match the value for configLIBRARY_LOWEST_INTERRUPT_PRIORITY in the example FreeRTOSConfig.h files (though setting an "interrupt" priority value to a thread priority value seems odd)?

Can anyone advise whether the example FreeRTOS configuration files are correct and I should look elsewhere for my hardfault resolution, or whether there is something I need to change, maybe connected with NRF_APP_PRIORITY_LOW?

In case it is of use the hardfault details are:

<error> hardfault: HARD FAULT at 0x0000F69A
<error> hardfault:   R0:  0x00000000  R1:  0x20001B34  R2:  0x20001B30  R3:  0x20002124
<error> hardfault:   R12: 0x20001378  LR:  0xFFFFFFE9  PSR: 0x6100080E
<error> hardfault: Cause: Data bus error (return address in the stack frame is not related to the instruction that caused the error).

This is in the FreeRTOS xPortPendSVHandler( void )
{
    /* This is a naked function. */

    __asm volatile
    (
    "   mrs r0, psp                         \n"
    "   isb                                 \n"
    "                                       \n"
    "   ldr r3, =pxCurrentTCB               \n" /* Get the location of the current TCB. */
    "   ldr r2, [r3]                        \n"
    "                                       \n"
    "   tst r14, #0x10                      \n" /* Is the task using the FPU context?  If so, push high vfp registers. */
    "   it eq                               \n"
    "   vstmdbeq r0!, {s16-s31}             \n"  <--- THIS IS 0x0000F69A

Rob

Parents Reply Children
  • OK, I'd completely misunderstood how much NRF5 was doing underneath me, thought I was already in the scheduler.  Thanks for your swift response, closing this ticket,

  • Confirmed that if I do what you're meant to do, i.e. put all my code into a function named void myTask(void **ppParamUnused) and then do something like:

    int main()
    {
        TaskHandle_t taskHandle = NULL;
    
    #if configTICK_SOURCE == FREERTOS_USE_RTC
        // If the clock has not already been started, start it
        nrf_drv_clock_init();
    #endif
    
        // Create the test task
        assert(xTaskCreate(myTask, "MyTask", MY_TASK_STACK_SIZE_BYTES / 4,
                           NULL, 15 /* Low priority */,
                           &taskHandle) == pdPASS);
    
        // Start the scheduler.
        vTaskStartScheduler();
        
        // Should never get here
        assert(false);
    }


    ...then I no longer get a hardfault.  Thanks for putting me right!

Related