CM4F Assertion in port.c after update to V11.0.1

Hello,

we’ve updated freertos in our project from 10.x to V11.0.1. After this update, our application doesn’t start anymore. I’ve analyzed the changes and found that kernel commit 30e6b8a5ead added changes to the cortex M4F port that check for correctly installed interrupt handlers (in my case “GCC/ARM_CM4F/port.c” line 343 and 344. In my case, the check in line 344

configASSERT( pxVectorTable[ portVECTOR_INDEX_PENDSV ] == xPortPendSVHandler );
w w w g i t h u b . c o m / FreeRTOS/FreeRTOS-Kernel/blob/0240cd55f20be7100439b606086709b74a105cb1/portable/GCC/ARM_CM4F/port.c#L344

fails. When I remove this line my application starts as expected and run (seemingly) fine. I’ve installed the handlers as explained in this faq for CMSIS: w w w . f r e e r t o s . o r g / FAQHelp.html
FreeRTOSConfig.h
#define xPortPendSVHandler PendSV_Handler

Any idea what I might be doing wrong? I don’t want to just disable the check because it “works for me” but understand why it fails.

Thank you for your help!

ps: sorry for broken links, for some reason posting links as new member is not allowed…

It doesn’t explain why the assert fails, but you can also remove the check by setting configCHECK_HANDLER_INSTALLATION to 0 in FreeRTOSConfig.h.

How are you setting up the vector table? Can you show the code.

You can disable this check by setting configCHECK_HANDLER_INSTALLATION to 0 in your FreeRTOSConfig.h. I am interested to know the value of pxVectorTable[ portVECTOR_INDEX_PENDSV ] and xPortPendSVHandler to see why this assert is failing.

  • Are you using a bootloader?
  • If you comment out only one of the new assert statements, does the other one fail?

Thanks for your replies. As soon as I add debug code, the assertion is gone. This leads me to suspecting that I somehow get memory overwritten, and this is where it gets visible. However, I’m currently out of ideas how I could mess up the interrupt vectors…

  • Bootloader: Yes
  • Asserts: No, but see my other reply.

Just to be thorough, when the assertion failures mysteriously go away, can you put a breakpoint on them to make sure they are actually still compiled in? Often, different build settings make configASSERT() disappear due to different #define definitions of configASSERT().

Does your bootloader provide code to handle PendSV and route control to the application’s PendSV handler? If so, that is what the code comments describe as “indirect routing”. Most Cortex M4 applications don’t use indirect routing because M4 provides a VTOR register. But maybe your bootloader is written to be compatible with M0, which has no VTOR register.

As @jefftenney suggested, please check that the assert is still there in the compiled code. If the memory seems corrupted, you can try to use data breakpoint to find the cause of the corruption.

@mwagner Can you share your vector table setup code, including definition of PendSV Handler, for both bootloader and application so that we can verify this?

Thanks for your replies. Took me a while to get the binary at least partly running with gdb/openocd (wdt, no symbols in release build, …)

  • the assertion also happens when directly flashing binary without bootloader support, so I would rule out bootloader / handover problems.
  • making seemingly random changes to the code fix the assertion.
  • disabling link-time-optimiser fixes the assertion (no idea if by accident or if this is actually a LTO error).
  • breakpoints in gdb seem not to work.

I’m currently debugging the executable, and the furthest I get is that I can pause the target and see it hanging in vPortRaiseBASEPRI (which is part of assert macro). However, gdb doesn’t create a stack trace, so I don’t know the source of the assertion.

My user application doesn’t create any OS objects (except main thread) before starting the scheduler. The vector table is standard from ST (STM32F3)

int main(void)
{
system_init(SYSTEM_OSC_CLOCK_16MHZ, CORE_CLOCK_72MHZ);
wdt_start();

xTaskCreate(main_os, “main”, 500, NULL, 2, NULL);
vTaskStartScheduler();
}


/******************************************************************************
*

  • The minimal vector table for a Cortex-M4. Note that the proper constructs
  • must be placed on this to ensure that it ends up at physical address
  • 0x0000.0000.

******************************************************************************/
.section .isr_vector,“a”,%progbits
.type g_pfnVectors, %object
.size g_pfnVectors, .-g_pfnVectors

g_pfnVectors:
.word _estack
.word Reset_Handler
.word NMI_Handler
.word HardFault_Handler
.word MemManage_Handler
.word BusFault_Handler
.word UsageFault_Handler
.word 0
.word 0
.word 0
.word 0
.word SVC_Handler
.word DebugMon_Handler
.word 0
.word PendSV_Handler
.word SysTick_Handler
.word WWDG_IRQHandler
.word PVD_IRQHandler

.weak UsageFault_Handler
.thumb_set UsageFault_Handler,Default_Handler

.weak SVC_Handler
.thumb_set SVC_Handler,Default_Handler

.weak DebugMon_Handler
.thumb_set DebugMon_Handler,Default_Handler

.weak PendSV_Handler
.thumb_set PendSV_Handler,Default_Handler

.weak SysTick_Handler
.thumb_set SysTick_Handler,Default_Handler

edit: forget this post, I’ve had the wrong elf loaded in gdb…

This is weak definition. You need to share the other definition of PendSV_Handler you have. Are you adding the following line in your FreeRTOSConfig.h:

#define xPortPendSVHandler PendSV_Handler

I use the define (see my first post), this maps to the function in port.c

I’ve fount the line it hangs in the debugger:

configASSERT( xUpdatedTickCount <= xNextTaskUnblockTime );

in tasks.c.

stacktrace:
Thread #1 (Suspended : Signal : SIGINT:Interrupt)
vTaskStepTick() at portmacro.h:217 0x8005d5c
vPortSuppressTicksAndSleep() at port.c:800 0x8005d5c
prvIdleTask.lto_priv.55() at tasks.c:5.902 0x8005d5c
SVC_Handler() at port.c:559 0x8005234

Currently I don’t have any idea why it fails.

It seems this is the real assertion failure:

configASSERT( xUpdatedTickCount <= xNextTaskUnblockTime );

To make sure, as an experiment can you define configUSE_TICKLESS_IDLE to 0 and verify the issue goes away?

Are you using the built-in implementation of vPortSuppressTicksAndSleep() or your own custom version? Are you providing your own custom definition of configPRE_SLEEP_PROCESSING()?

  • disabling tickless idle makes the error go away. But I wouldn’t give to much on that as adding a few lines of code in the main() function also makes the error go away.
  • we use #define configUSE_TICKLESS_IDLE 1, without adding/customizing any functions.

I’ve continued evaluating the problem in the debugger by adding some debug variables. This shows why the assertion fails.

volatile uint32_t test3257 = 5;
volatile uint32_t test3258 = 6;
volatile uint32_t test3259 = 7;

void vTaskStepTick( TickType_t xTicksToJump )
{
    TickType_t xUpdatedTickCount;

    traceENTER_vTaskStepTick( xTicksToJump );

    /* Correct the tick count value after a period during which the tick
     * was suppressed.  Note this does *not* call the tick hook function for
     * each stepped tick. */

    test3257 = xTickCount;
    test3258 = xTicksToJump;

    xUpdatedTickCount = test3257 + test3258;

// xUpdatedTickCount = xTickCount + xTicksToJump;

    test3259 = xNextTaskUnblockTime;
    configASSERT( xUpdatedTickCount <= test3259 );

// configASSERT( xUpdatedTickCount <= xNextTaskUnblockTime );

test3257 is set to a value of 20009FFF₁₆, which is the end of RAM and not the tick count. The other two test variables are correct. This triggers the exception.

the corresponding assembly is

4574 test3257 = xTickCount;
08005d60: ldr r3, [pc, #228] ; (0x8005e48 <prvIdleTask.lto_priv.55+636>) // load addr of variable test3257 to r3
08005d62: ldr r2, [r7, #0] //load value of xTickCount by de-referencing r7
08005d64: str r2, [r3, #0] //store

the line where r7 is set to the address of xTickCount is before entering/leaving sleep mode. Any idea if this might cause the problem?

Another strange thing is, this happens only on power-up / cold boot, not on reset. After a reset the values are correct and the system boots normally.

Does it mean that the value of r7 change as a result of entering/exiting sleep mode? Can you verify that by reading r7 before and after? Also, have you written any ISR in assembly?

From what I see, yes. The following is as close as I get:

vPortSuppressTicksAndSleep()

__asm volatile(“mov %0, r7” : “=r” (test5257));
if( xModifiableIdleTime > 0 )
{
__asm volatile ( “dsb” ::: “memory” );
__asm volatile ( “wfi” );
__asm volatile ( “isb” );
}
//__asm volatile(“mov %0, r7” : “=r” (test5258));

        configPOST_SLEEP_PROCESSING( xExpectedIdleTime );

        __asm volatile ( "cpsie i" ::: "memory" );
        __asm volatile ( "dsb" );
        __asm volatile ( "isb" );
        __asm volatile("mov %0, r7" : "=r" (test5259));

including the “mov” in the middle makes the device boot correctly. r7 (test5257) before sleep points to xTickCount, r7 (test5259) contains 0x0.

At this point in the boot process, my application hasn’t enabled any interrupts yet. Only the ones enabled by default and the rtos should be active. I don’t have any plain assembly ISRs.

That does not make much sense. Which hardware are you using? Asking to see if I can repro this problem.

STM32F302CCT, custom board, toolchain arm-freertos-eabi-gcc (GNU Tools for ARM Embedded Processors 6-2017-q3-update) (standard toolchain modified to have thread-save malloc/newlib). Custom drivers, no stm32cube.