Massive stack overflow on task when kernel starts. Any ideas?

Okay so when I load the Vector table directly into RAM (in main.c) then I made it a bit further.

One question, does anyone know why under the “Registers” window, it shows the “Secure” control registers as being used? I don’t have trust zone or any secure functions enabled, I am using a generic Arm M33 device without FPU, DSP and TZ.

This is the call stack:

The second picture shows a call stack that doesn’t make sense, so you should ignore that. The other picture does indicate hard fault. But the EXC_RETURN value 0xFFFFFFBD (in R14 LR) indicates that your FPGA-based M33 does implement the security extension (aka TZ) because the ES bit is set.

But that’s ok - your code should work whether the M33 supports TZ or not.

The first picture shows the running task is UART Test at the time of the hard fault. You could step through that code and see where the hard fault occurs. The tick count is zero so apparently the hard fault happens pretty early on in the task’s work.

I set a break point on the task_uart but it never reaches or enters the task it seems:

int main (void) {
#ifdef LOAD_NVIC_RAM
	uint32_t i;
	for(i = 0; i < VECTORTABLESIZE; i++){
		vectorTable_RAM[i] = __VECTOR_TABLE[i];
	}
	vectorTable_RAM[SysTick_IRQn + 16] = (uint32_t)sysTick_Handler_RAM;
	 /* relocate vector table */ 
  __disable_irq();
    SCB->VTOR = (uint32_t)&vectorTable_RAM;
  __DSB();
  __enable_irq();
	SystemCoreClockUpdate(); 
	SysTick_Config(SystemCoreClock/1000ul);
	xTaskCreate(task_uart, "UART Test", 1024, NULL, 50, NULL);
	

	// Create tasks/threads for application functionality

	// Start the kernel and execute the first thread
	vTaskStartScheduler();

	while(1);
}
void task_uart(void * args){
		    UART_BDR_REG = 0x00000020;
    UART_FBDR_REG = 0x00000037;
	UART_LN_CTL_REG = 0x00000070;
    UART_CTL_REG = 0x00000301;
   int i = 0;
			UART_DATA_REG = 0x41;

	for(;;) {
			UART_DATA_REG = 0x41; 
	}
	vTaskDelete(NULL);

}

If I just let it run, the PC seems to stay at vTaskStartScheduler(), then if I Stop the code execution, the PC jumps to 0x20000A1A which is the beginning of void PendSV_Handler( void ) inside the portasm.c file.

Actually it looks like 0x20000A1A is the hard fault handler. It branches to itself.

The EXC_RETURN value really makes it look like FreeRTOS successfully starts the first task. The PSP was in effect (not MSP) at the time of the exception. Can you try this:

int main (void) {
    SCB->VTOR = &__VECTOR_TABLE;
    xTaskCreate(task_uart, "UART Test", 1024, NULL, 50, NULL);

    // Start the kernel and execute the first thread
    vTaskStartScheduler();
}

void task_uart(void * args){
    while(1);
}

and put a breakpoint on the while(1); line.

Okay, I assume you meant something like this? I tried this and still no luck. The breakpoint never triggers

#include <FreeRTOS.h>
#include <string.h>
#include "bsp_task.h"
#include "ARMCM33.h"
#define VECTORTABLESIZE (496)
#define VECTORTABLE_ALIGNMENT (0x100U)
extern uint32_t __VECTOR_TABLE[VECTORTABLESIZE]; // vector table ROM

int main (void) {
    SCB->VTOR = (uint32_t) &__VECTOR_TABLE[0];
    xTaskCreate(task_uart, "UART Test", 1024, NULL, 50, NULL);

    // Start the kernel and execute the first thread
    vTaskStartScheduler();
}

void task_uart(void * args){
    while(1);
}
`

Yes, that was what I meant. Pretty strange. Unfortunately you probably need to dig into the hard fault. There are bread crumbs and other processor state available to help you see why the CM33 hard faulted. That should give us the next clue. Here is a reference.

But if you want to try one more thing first, here are some breakpoints to place. vStartFirstTask(), SVC_Handler(), and vRestoreContextOfFirstTask(). Once you reach the first breakpoint, do some single stepping at the asm level. Be sure you hit all of those break points. Maybe you’ll see where it goes wrong.

Okay thanks, I will check that out. Are you able to walk through the asm code? In Keil when using the debugger, it does not allow me to step through that.

I’ve never used Keil so don’t know how to single step asm instructions. In other debuggers, you typically select the disassembly window and there is a single stepping button there. Sometimes there is an “instruction” stepping selection that modifies the behavior of the debugging step buttons.

For now it would still be interesting to see which of the three breakpoints you hit.

Hi @tamten, Does the Inline Assembly as described in https://www.keil.com/support/man/docs/uv4cl/uv4cl_db_dbg_disasmwin.htm works for you? How to open the Inline Assembly window is described here.

Can you try setting configRUN_FREERTOS_SECURE_ONLY to 1 in your FreeRTOSConfig.h?

You should be able to single step through the asm just by clicking in the disassembler window to give it focus, and then use the normal debugger controls i.e. F11 to step, F10 to step over.

I tried, this does not help, I am not running in secure part of memory.

I can view the Disassembly but I have no options under View → Trace → (Blank) to turn on Trace Recording.

Debug → Inline Assembly is also greyed out for me in Keil.

I am using J-Link via JTAG as the Debug for target options.

So I set a break point at

port.c 
line:1652        configASSERT( pxVectorTable[ portVECTOR_INDEX_SVC ] == SVC_Handler ); 
...
line:1758        vStartFirstTask();
...  
line:1130        vRestoreContextOfFirstTask();

The first that was hit was line 1652.

A couple observations, when I load the Vector Table into RAM, and start the kernel via vStartScheduler and then stop the code execution, the PC jumps to:

void PendSV_Handler( void ) /* __attribute__ (( naked )) PRIVILEGED_FUNCTION */
PC HERE    {
        __asm volatile
        (
            "   .syntax unified                                 \n"
            "                                                   \n"
            "   mrs r0, psp                                     \n" /* Read PSP in r0. */
            "                                                   \n"
            #if ( ( configENABLE_FPU == 1 ) || ( configENABLE_MVE == 1 ) )
                "   tst lr, #0x10                                   \n" /* Test Bit[4] in LR. Bit[4] of EXC_RETURN is 0 if the Extended Stack Frame is in use. */
                "   it eq                                           \n"
                "   vstmdbeq r0!, {s16-s31}                         \n" /* Store the additional FP context registers which are not saved automatically. */
            #endif /* configENABLE_FPU || configENABLE_MVE */
            "                                                   \n"
            "   mrs r2, psplim                                  \n" /* r2 = PSPLIM. */
            "   mov r3, lr                                      \n" /* r3 = LR/EXC_RETURN. */
            "   stmdb r0!, {r2-r11}                             \n" /* Store on the stack - PSPLIM, LR and registers that are not automatically saved. */
            "                                                   \n"
            "   ldr r2, =pxCurrentTCB                           \n" /* Read the location of pxCurrentTCB i.e. &( pxCurrentTCB ). */
            "   ldr r1, [r2]                                    \n" /* Read pxCurrentTCB. */
            "   str r0, [r1]                                    \n" /* Save the new top of stack in TCB. */
            "                                                   \n"
            "   mov r0, %0                                      \n" /* r0 = configMAX_SYSCALL_INTERRUPT_PRIORITY */
            "   msr basepri, r0                                 \n" /* Disable interrupts upto configMAX_SYSCALL_INTERRUPT_PRIORITY. */
            "   dsb                                             \n"
            "   isb                                             \n"
            "   bl vTaskSwitchContext                           \n"
            "   mov r0, #0                                      \n" /* r0 = 0. */
            "   msr basepri, r0                                 \n" /* Enable interrupts. */
            "                                                   \n"
            "   ldr r2, =pxCurrentTCB                           \n" /* Read the location of pxCurrentTCB i.e. &( pxCurrentTCB ). */
            "   ldr r1, [r2]                                    \n" /* Read pxCurrentTCB. */
            "   ldr r0, [r1]                                    \n" /* The first item in pxCurrentTCB is the task top of stack. r0 now points to the top of stack. */
            "                                                   \n"
            "   ldmia r0!, {r2-r11}                             \n" /* Read from stack - r2 = PSPLIM, r3 = LR and r4-r11 restored. */
            "                                                   \n"
            #if ( ( configENABLE_FPU == 1 ) || ( configENABLE_MVE == 1 ) )
                "   tst r3, #0x10                                   \n" /* Test Bit[4] in LR. Bit[4] of EXC_RETURN is 0 if the Extended Stack Frame is in use. */
                "   it eq                                           \n"
                "   vldmiaeq r0!, {s16-s31}                         \n" /* Restore the additional FP context registers which are not restored automatically. */
            #endif /* configENABLE_FPU || configENABLE_MVE */
            "                                                   \n"
            "   msr psplim, r2                                  \n" /* Restore the PSPLIM register value for the task. */
            "   msr psp, r0                                     \n" /* Remember the new top of stack for the task. */
            "   bx r3                                           \n"
            ::"i" ( configMAX_SYSCALL_INTERRUPT_PRIORITY )
        );
    }

However, if I don’t load the vector table into RAM, and I start the kernel via vStartScheduler() and then stop the execution, the PC is at:

  #if ( configCHECK_HANDLER_INSTALLATION == 1 )
    {
        const portISR_t * const pxVectorTable = portSCB_VTOR_REG;

        /* Validate that the application has correctly installed the FreeRTOS
         * handlers for SVCall and PendSV interrupts. We do not check the
         * installation of the SysTick handler because the application may
         * choose to drive the RTOS tick using a timer other than the SysTick
         * timer by overriding the weak function vPortSetupTimerInterrupt().
         *
         * Assertion failures here indicate incorrect installation of the
         * FreeRTOS handlers. For help installing the FreeRTOS handlers, see
         * https://www.FreeRTOS.org/FAQHelp.html.
         *
         * Systems with a configurable address for the interrupt vector table
         * can also encounter assertion failures or even system faults here if
         * VTOR is not set correctly to point to the application's vector table. */
        configASSERT( pxVectorTable[ portVECTOR_INDEX_SVC ] == SVC_Handler );
PC HERE configASSERT( pxVectorTable[ portVECTOR_INDEX_PENDSV ] == PendSV_Handler );
    }

The green highlight seems to show that the PC is at the beginning of PendSV_Handler(), but it’s not. The PC is actually 0x20000A1A, which branches to itself. When you look at the other screenshot you posted, you can see 0x20000A1A is the hard fault handler. So, when you try to start the OS with the vector table in RAM, you hard fault.

Is that true even if you set SCB->VTOR at the beginning of main to point to the vector table in flash?

I see, that makes more sense, when I run the following and stop code execution, the PC is still pointing to 0x20000A1A:

int main(){
    SCB->VTOR = 0x20000000;
	SystemCoreClockUpdate();
	xTaskCreate(task_uart, "UART Test", 1024, NULL, 50, NULL);
	vTaskStartScheduler();
	while(1);
}

OK great- that means you get the same result whether you copy the vector table to RAM or not. You have solved the configASSERT() failure by setting VTOR to point to the vector table.

So all that remains is to see why the code is experiencing a hard fault when starting the first task (task_uart). Can you set these three break points:

vStartFirstTask()
SVC_Handler()
vRestoreContextOfFirstTask().

Just resume after hitting each one. See if you hit all three.

Both vStartFirstTask and vRestoreContextOfFirstTask() are reached okay, I did not find a SVC_Handler() method, but I saw vPortSVCHandler is defined as that so I set a breakpoint on void PendSV_Handler(void) inside the portasm.c file, i’m not sure if that is the method you’re referring too? If so, that is never reached.

I cannot step through the asm code, but I have confirm the hardfault happens once vStartFirstTask(); is entered in port.c file.

void vStartFirstTask( void ) /* __attribute__ (( naked )) PRIVILEGED_FUNCTION */
{
    __asm volatile
    (
        "   .syntax unified                                 \n"
        "                                                   \n"
        "   ldr r0, =0xe000ed08                             \n" /* Use the NVIC offset register to locate the stack. */
        "   ldr r0, [r0]                                    \n" /* Read the VTOR register which gives the address of vector table. */
        "   ldr r0, [r0]                                    \n" /* The first entry in vector table is stack pointer. */
        "   msr msp, r0                                     \n" /* Set the MSP back to the start of the stack. */
        "   cpsie i                                         \n" /* Globally enable interrupts. */
        "   cpsie f                                         \n"
        "   dsb                                             \n"
        "   isb                                             \n"
        "   svc %0                                          \n" /* System call to start the first task. */
        "   nop                                             \n"
        ::"i" ( portSVC_START_SCHEDULER ) : "memory"
    );
}

vStartFirstTask() executes before vRestoreContextOfFirstTask(), so if you reached the breakpoint in vRestoreContextOfFirstTask(), then vStartFirstTask() is finishing succesfully. Your last message seems to be in conflict on that point.

So just to be double sure, can you put a breakpoint here and make sure it is reached – the C call to vRestoreContextOfFirstTask() : FreeRTOS-Kernel/portable/ARMv8M/non_secure/port.c at 9697f8c9b0ac155093751d9654f39c39b417abff · FreeRTOS/FreeRTOS-Kernel · GitHub

(Regarding SVC_Handler(), my message above includes a link to it. I assume that is the source code you are using. Can you double check?)