Skip to main content
Visitor II
July 26, 2025
Solved

Jump to Freertos application

  • July 26, 2025
  • 7 replies
  • 1407 views

Hi all,

I have developed a bootloader for STM32F412, the bootloader works perfectly, however, when I change the user app to be FreeRTOS based, it ceases to work.

I added a blink led in the Hard Fault in both the bootloader and app, but both were not triggered.

Any idea what is going on?

void jump_to_application(void) {
 void (*app_reset_handler)(void);
 uint32_t msp_value = *(volatile uint32_t*)(APP_START_ADDRESS + APP_HEADER_SIZE);
 uint32_t reset_handler_address = *(volatile uint32_t*)(APP_START_ADDRESS + APP_HEADER_SIZE + 4);

 __disable_irq();

 HAL_RCC_DeInit();
 HAL_DeInit();

 SysTick->CTRL = 0;
 SysTick->LOAD = 0;
 SysTick->VAL = 0;


 for (uint32_t i = 0; i < 8; i++)
 {
 NVIC->ICER[i] = 0xFFFFFFFF;
 NVIC->ICPR[i] = 0xFFFFFFFF;
 }

 __enable_irq();

 __set_MSP(msp_value);
 __set_PSP(msp_value);
 __set_CONTROL(0);
 app_reset_handler = (void*)reset_handler_address;
 app_reset_handler();
} 



Thanks in advance.

Regards, 

    This topic has been closed for replies.
    Best answer by Qusai

    After investigating the binaries of both the bare-metal and FreeRTOS applications, I discovered the following: although both apps use the same linker script and startup code, FreeRTOS enforces a stricter alignment on memory sections. This caused the .text section to shift by three words, leaving a gap of three words filled with 0x00 after the .isr_vector section, which ultimately led to the PRECISERR fault.

    Adding the linker option --gap-fill=0xFF in the post-build steps resolved the issue. By default, .bin and .hex files do not include gap-filling, whereas flashing the memory via the debugger automatically fills these gaps. This explains why the application worked when flashed through the debugger but not when loaded by my bootloader.

    Additionally, in the bare-metal build, the .text section starts immediately after the .isr_vector section, whereas in the FreeRTOS build, a 16-byte alignment is enforced instead of the 4-byte alignment used in bare metal.

    I haven’t yet identified exactly where does this alignment requirement come from, but the issue is now resolved, and the bootloader is running smoothly.

    For context, I am using FreeRTOS CMSIS V2, and the files are generated using STM32CubeMX.

    7 replies

    Super User
    July 26, 2025

    Nothing wrong with the code posted. If app doesn't work, I wouldn't suspect an issue with the bootloader.

    General rules:

    • Don't jump to application from within an interrupt.
    QusaiAuthor
    Visitor II
    July 27, 2025

    I am not jumping from an interrupt, after flashing is done, the bootloader should jump to the app immediately.

     

    Super User
    July 27, 2025

    FreeRTOS uses unprivileged access level for tasks. Are you sure the the MCU always has privileged acces level when your bootloader is executed?

    hth

    KnarfB

    QusaiAuthor
    Visitor II
    July 27, 2025

    I am unware of this, enlighten me please. Does it have to do anything with __set_CONTROL(0); ??

    Super User
    July 27, 2025

    Yes, and more like setting MSP, manipulating NVIC, ... All details are in the Cortex-M4 Technical Reference Manual.

    This only matters if you enter the bootloader from a FreeRTOS thread for example. After reset, everything should work as expected. 

    Super User
    July 27, 2025

    Note that FreeRTOS needs VTOR to point to app interrupt vectors, and the 1st word of the vectors be the MSP for interrupts. Does your bootloader sets the MSP for the app to something else?

     

    QusaiAuthor
    Visitor II
    July 27, 2025

    Yes. The bootloader is setting the MSP to the app. 

    Moreover, I have changed the VECT_TAB_OFFSET on the application side. Still not working.

    I suspect, cleaning and de-initialization is the problem here. But not able to figure out where exactly is the problem.

    Looks like FreeRTOS is not having a fresh start.

    QusaiAuthor
    Visitor II
    July 30, 2025

    Hi all,

    Few updates:

    I did many experiments, unfortunetly none of them worked.

    1- Now the FreeRTOS applications start at 0x08040000
    2- I use GDB to debug the application, and it shows that the jump was done, but stuck in the hard fault.

    3- These are my registers

    (gdb) info registers
    r0 0x1 1
    r1 0xe000e100 -536813312
    r2 0x8041e04 134487556
    r3 0xbe1fc 778748
    r4 0xbe1fd 778749
    r5 0x0 0
    r6 0x0 0
    r7 0x2003ffe0 537133024
    r8 0x0 0
    r9 0x0 0
    r10 0x0 0
    r11 0x0 0
    r12 0x80000000 -2147483648
    sp 0x2003ffe0 0x2003ffe0
    lr 0xfffffff9 -7
    pc 0x804075c 0x804075c <HardFault_Handler+4>
    xPSR 0x1000003 16777219
    fpscr 0x0 0
    msp 0x2003ffe0 0x2003ffe0
    --Type <RET> for more, q to quit, c to continue without paging--
    psp 0x0 0x0
    primask 0x1 1
    basepri 0x0 0
    faultmask 0x0 0
    control 0x0 0
    (gdb) x/x $pc
    0x804075c <HardFault_Handler+4>: 0xb480e7fd
    (gdb) 

    4- The final version of my jump function

    void jump_to_application(void) {
    void (*app_reset_handler)(void);
    uint32_t app_msp = *(volatile uint32_t*)(APP_START_ADDRESS + APP_HEADER_SIZE);
    uint32_t reset_handler = *(volatile uint32_t*)(APP_START_ADDRESS + APP_HEADER_SIZE + 4);
    
    __disable_irq();
    
    // Stop SysTick
    SysTick->CTRL = 0;
    SysTick->LOAD = 0;
    SysTick->VAL = 0;
    
    HAL_RCC_DeInit();
    HAL_DeInit();
    
    for (uint32_t i = 0; i < 8; i++) {
    NVIC->ICER[i] = 0xFFFFFFFF;
    NVIC->ICPR[i] = 0xFFFFFFFF;
    }
    
    // Optionally clear DMA/USB specific IRQs
    __HAL_RCC_USB_OTG_FS_CLK_DISABLE();
    HAL_NVIC_DisableIRQ(OTG_FS_IRQn);
    
    __HAL_RCC_DMA1_FORCE_RESET();
    __HAL_RCC_DMA1_RELEASE_RESET();
    
    // Set vector table to application
    SCB->VTOR = APP_START_ADDRESS + APP_HEADER_SIZE;
    __DSB();
    __ISB();
    
    __set_MSP(app_msp);
    __set_CONTROL(0); // Privileged + use MSP
    __DSB();
    __ISB();
    
    app_reset_handler = (void (*)(void))reset_handler;
    app_reset_handler();
    }

    5- VTOR value is 0x08040000
    6- My linker script is

    MEMORY
    {
     RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 256K
     FLASH (rx) : ORIGIN = 0x8040000, LENGTH = 128K
    }

     

    Super User
    July 31, 2025

    > the jump was done, but stuck in the hard fault

    in the next step or somewhere later? Can you find our where? Call stack?

    Segger has a nice kb article about fault analysis and a diagnostic fault handler: https://kb.segger.com/Cortex-M_Fault

    hth

    KnarfB

    QusaiAuthorAnswer
    Visitor II
    November 13, 2025

    After investigating the binaries of both the bare-metal and FreeRTOS applications, I discovered the following: although both apps use the same linker script and startup code, FreeRTOS enforces a stricter alignment on memory sections. This caused the .text section to shift by three words, leaving a gap of three words filled with 0x00 after the .isr_vector section, which ultimately led to the PRECISERR fault.

    Adding the linker option --gap-fill=0xFF in the post-build steps resolved the issue. By default, .bin and .hex files do not include gap-filling, whereas flashing the memory via the debugger automatically fills these gaps. This explains why the application worked when flashed through the debugger but not when loaded by my bootloader.

    Additionally, in the bare-metal build, the .text section starts immediately after the .isr_vector section, whereas in the FreeRTOS build, a 16-byte alignment is enforced instead of the 4-byte alignment used in bare metal.

    I haven’t yet identified exactly where does this alignment requirement come from, but the issue is now resolved, and the bootloader is running smoothly.

    For context, I am using FreeRTOS CMSIS V2, and the files are generated using STM32CubeMX.

    Super User
    November 13, 2025

    > SCB->VTOR = APP_START_ADDRESS + APP_HEADER_SIZE

    > 5- VTOR value is 0x08040000

    So APP_START_ADDRESS is 0x08040000... Then what is the value of APP_HEADER_SIZE? 

     

    QusaiAuthor
    Visitor II
    November 14, 2025

    I was doing a few experiments, so the addresses were not finalized yet. The current configuration is as follows:
    The header has a size of 0x200 bytes starting at 0x08020000, followed by the application binary starting at 0x08020200.