Hello, I am getting hardfaults from the PendSV interrupt. I am trying to get FreeRTOS 8.2.3 running on a SAMD51 microcontroller (cortex m4). I have reduced the scope of my problem down to purely FreeRTOS territory, Segger code, and Cortex m4 standard features. I still don’t understand enough about interrupt masking, recovery, and cortex hardfaults to understand what my next debugging step needs to be.
I got the code from Atmel Start (so hopefully it came with sane defaults), and I then added the Segger Systemview patch (see the segger wiki, article FreeRTOS_with_SystemView v8, with some adjustments that are hopefully correct for 8.2.3 patch not aligning). I also increased configQUEUE_REGISTRY_SIZE, set configCHECK_FOR_STACK_OVERFLOW to 2, and enabled xTaskGetIdleTaskHandle. I also configured RTT to hopefully mask correctly:
#define SEGGER_RTT_MAX_INTERRUPT_PRIORITY (0x80) // Interrupt priority to lock on SEGGER_RTT_LOCK on Cortex-M3/4
Initially, I thought that I was messing up NVIC priorities, so first I did things like
NVIC_SetPriority(ADC1_0_IRQn, configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY);
NVIC_SetPriority(ADC1_1_IRQn, configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY);
NVIC_SetPriority(EIC_15_IRQn, configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY+2);
but as I kept running into issues, I eventually commented out all peripheral initialization and just created 2 tasks:
#define debug_printf(ch, ...) do {UBaseType_t saved = taskENTER_CRITICAL_FROM_ISR(); SEGGER_RTT_printf(ch, __VA_ARGS__); taskEXIT_CRITICAL_FROM_ISR(saved); } while (0)
void rtos_printing_tester(void * arg)
{
int i = 0;
while(true) {
debug_printf(0, "testing %d\n", i++);
vTaskDelay(pdMS_TO_TICKS(1000));
}
}
When I run one task, it works fine, printing to the Segger RTT console once per second. And I can see in the systemview all the interrupts/context switches/etc, and it looks fine.
When I start a second of the same task, I get a hardfault. I added code to hopefully retrieve the PC where the hardfault happened, and I believe it’s happening in PendSV based on looking up the address I found in the dissassembly:
void prvGetRegistersFromStack( uint32_t *pulFaultStackAddress )
{
/* These are volatile to try and prevent the compiler/linker optimising them
away as the variables never actually get used. If the debugger won't show the
values of the variables, make them global my moving their declaration outside
of this function. */
volatile uint32_t r0;
volatile uint32_t r1;
volatile uint32_t r2;
volatile uint32_t r3;
volatile uint32_t r12;
volatile uint32_t lr; /* Link register. */
volatile uint32_t pc; /* Program counter. */
volatile uint32_t psr;/* Program status register. */
r0 = pulFaultStackAddress[ 0 ];
r1 = pulFaultStackAddress[ 1 ];
r2 = pulFaultStackAddress[ 2 ];
r3 = pulFaultStackAddress[ 3 ];
r12 = pulFaultStackAddress[ 4 ];
lr = pulFaultStackAddress[ 5 ];
pc = pulFaultStackAddress[ 6 ];
psr = pulFaultStackAddress[ 7 ];
SEGGER_RTT_printf(0, "pc=%x\n", pc);
/* When the following line is hit, the variables contain the register values. */
for( ;; );
}
void HardFault_Handler(void)
{
SEGGER_SYSVIEW_RecordEnterISR();
IRQn_Type actirqn = ((int32_t)__get_IPSR()) - 16; /* -16 see IPSR bit assignments */ \
SEGGER_RTT_printf(0, "hardfault interrupt prio %d\n", NVIC_GetPriority(actirqn)); \
__asm volatile
(
" tst lr, #4 \n"
" ite eq \n"
" mrseq r0, msp \n"
" mrsne r0, psp \n"
" ldr r1, [r0, #24] \n"
" ldr r2, handler2_address_const \n"
" bx r2 \n"
" handler2_address_const: .word prvGetRegistersFromStack \n"
);
}
I’m not sure where to go from here: there are no interrupts running that could call any FreeRTOS code, so I think I’ve ruled out the NVIC configuration. I don’t understand how just running 2 tasks could be resulting in a fault. I would really appreciate advice as to what I should investigate next.