Skip to main content
Graduate II
September 18, 2025
Solved

Jump from Openbootloader to the Application

  • September 18, 2025
  • 8 replies
  • 845 views

Greetings! 
So, I used the OpenBootloader project, and then flashed a GPIO_IOToggle firmware in other area of the flash, close to the example in the video Introduction to Open Bootloader, Part 3: Loading an Application.  In that example the CubeProgrammer flashes through the DFU and check "run after programming". When I unplug the board and turn it back on, the OpenBootloader starts (as expected). 
What I want to do is, as soon as I flash the application, when I turn off the board and turn it on again, the application starts to run and not the bootloader, OR, it starts the bootloader, it checks if there's any application installed, and if yes, it jumps to the application immediately. 
Any clue on how to do that?
Thank you! 

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

    Hello @j_filipe 

    I provided an example of a linker file for an IAR project. If you are working with an STM32CubeIDE project, you should have a .ld linker script file instead.

    STM32CubeU5/Projects/B-U585I-IOT02A/Examples/GPIO/GPIO_IOToggle/STM32CubeIDE/STM32U585AIIXQ_FLASH.ld at main · STMicroelectronics/STM32CubeU5

    8 replies

    Technical Moderator
    September 19, 2025

    Hello @j_filipe 

     

    You can implement a simple check in your bootloader code. Here’s how you can proceed:


    1. Define the Application Start Address

    Decide on the flash address where your application will be located (for example, 0x08008000). Make sure your application is built to start from this address by configuring your linker script or project settings accordingly.


    2. Add Application Check in Bootloader

    In your bootloader’s main function, add code to verify if a valid application is present at the specified address. If so, the bootloader can jump to the application’s entry point. Here’s a typical example for STM32 devices:

    #define APP_ADDRESS 0x08008000U
    
    typedef void (*pFunction)(void);
    
    void jumpToApplication(void) {
     uint32_t appStack = *(__IO uint32_t*)APP_ADDRESS;
     uint32_t appEntry = *(__IO uint32_t*)(APP_ADDRESS + 4);
    
     // Check if stack pointer is in RAM (valid application)
     if ((appStack >= RAM_START) && (appStack < RAM_END)) {
     __disable_irq();
     __set_MSP(appStack);
     pFunction appMain = (pFunction)appEntry;
     appMain();
     }
    }
    
    int main(void) {
     HAL_Init();
     // Bootloader initialization...
    
     // Check for valid application and jump if present
     jumpToApplication();
    
     // If no valid application, stay in bootloader
     while (1) {
     // Bootloader main loop
     }
    }
    j_filipeAuthor
    Graduate II
    September 22, 2025

    I still need to test it... 
    What would it be the RAM_START and RAM_END for STM32U585? Why do you do the appEntry = APP_ADDRESS+4 ? 
    What is appMain, the main function? 
    Thank you! 

     

    Technical Moderator
    September 24, 2025

    Hello @j_filipe 

    The line appEntry = *(uint32_t*)(APP_ADDRESS + 4) retrieves the address of the application's reset handler (the entry point), which is always stored at offset +4 from the application start. The appMain variable is a function pointer to this reset handler, so when you call appMain(), you are transferring control to the application.

    j_filipeAuthor
    Graduate II
    October 21, 2025

    @Saket_Om thank you for your response. 
    One more thing, what is RAM_START and RAM_END? I'm using a STM32U585 MCU. 
    Thank you! 

    Technical Moderator
    October 21, 2025

    The RAM_START and RAM_END are defined in your linker file. For example, it in the stm32u585xx_flash.icf file on the B-U585I-IOT02A examples it is defined as below.

    define symbol __ICFEDIT_region_RAM_start__ = 0x20000000;
    define symbol __ICFEDIT_region_RAM_end__ = 0x200BFFFF;

    Saket_Om_0-1761043265713.png

     

     

    j_filipeAuthor
    Graduate II
    October 21, 2025

    @Saket_Om  
    I do not have any stm32u585xx_flash.icf  file in my project... 
    Can I set:
    define symbol __ICFEDIT_region_RAM_start__ = 0x20000000;
    define symbol __ICFEDIT_region_RAM_end__ = 0x200BFFFF;
    in my main.c file? 

    j_filipeAuthor
    Graduate II
    October 21, 2025

    Thank you @Saket_Om 

    j_filipeAuthor
    Graduate II
    October 24, 2025

    @Saket_Om 
    So tried to do the function you showed me previously, I did it more or less like this, but it doesn't give the jump, it stays in while(1)...

    #define APP_START_ADDRESS ((uint32_t)0x0800C000) 
    #define RAM_START	 ((uint32_t)0x20000000)	
    #define RAM_END		 ((uint32_t)0x200C0000)
    
    typedef void (*pFunction)(void);
    
    void jumpToApplication(void)
    {
    	uint32_t appStack = *(__IO uint32_t*)APP_START_ADDRESS;
    	uint32_t appEntry = *(__IO uint32_t*)(APP_START_ADDRESS + 4);
    
    	//Check if stack pointer is in RAM (valid application)
    	if((appStack >= RAM_START) && (appStack < RAM_END))
    	{
    		__disable_irq();
    		__set_MSP(appStack);
    		pFunction appMain = (pFunction)appEntry;
    		appMain();
    	}
    }
    
    // Called the function before the main while(1)
     jumpToApplication();
    Technical Moderator
    October 24, 2025

    Hello @j_filipe 


    @j_filipe wrote:

    @Saket_Om 
    So tried to do the function you showed me previously, I did it more or less like this, but it doesn't give the jump, it stays in while(1)...


    Which while(1)?

    While debugging, did the execution enter the if statement: if ((appStack >= RAM_START) && (appStack < RAM_END))?

    j_filipeAuthor
    Graduate II
    October 24, 2025

    @Saket_Om 
    The while(1) inside main function...
    No, the execution did not enter the if statement... 

    j_filipeAuthor
    Graduate II
    October 27, 2025

    @Saket_Om 
    So, what is the alternative? 

    ST Employee
    October 28, 2025

    Hello @j_filipe 

    Thank you for sharing your experience with the OpenBootloader project.

    To do this, you need to modify the openbootloader code so that at startup it checks if a valid user application is present in the designated flash memory area. If an application is found, the bootloader jumps directly to it; otherwise, it stays in bootloader mode.

    Here is how you can implement this:

    1/add the following declarations:

    typedef void (*pFunction)(void);
    pFunction JumpToApplication;
    uint32_t JumpAddress;

    2/Define the application start address (for example, 0x0800C000), which corresponds to where your user firmware is flashed.

    3/In the bootloader’s main() function, before entering the bootloader loop, add the following check:

    /* Test if user code is programmed starting from address 0x0800C000 */
     if (((*(__IO uint32_t *) 0x0800C000) & 0x2FFC0000) ==
     0x20000000)
     {
     /* Jump to user application */
     JumpAddress = *(__IO uint32_t *) (0x0800C000 + 4);
     JumpToApplication = (pFunction) JumpAddress;
    
     /* Initialize user application's Stack Pointer */
     __set_MSP(*(__IO uint32_t *) 0x0800C000);
     JumpToApplication();
     }

     

    T_Hamdi_1-1761653378679.png

    This address (0x0800C000 ) corresponds to the start address of your application, which is defined in your application and must match the address used when generating the application.

    After this modification of your OpenBootloader code, the OpenBootloader checks if there is a valid application present; if yes, it jumps to the application, otherwise it enters bootloader mode.

    thank you 

    Hamdi 

    j_filipeAuthor
    Graduate II
    October 29, 2025

    @Saket_Om  @T_Hamdi 
    Thank you for your patience so far. 
    So I dropped the Openbootloader solution and I implemented an own bootloader. I am using the classic USB device middleware as communication protocol and I followed this tutorial, and so, I increased the minimum heap size for 0x300 and minimum stack size for 0x500. I am using some sort of xmodem communication to receive the .bin file, that passes 128 bytes per packet and after receives the 128bytes, the bootloader flashes the 128bytes. I tried to implement both of your solutions and I am not able to jump to the main application after flashing the firmware. There is something that the protocol does, if the last packet only has for example 1byte of firmware, it sends that 1byte followed by 127byte of a padding pattern (only 0x1A), I don't know if that is messing with the flash and not allowing the firmware to start. I compared the flash memory, (after flashing with my protocol and flashing the same .bin with STM32CubeProgrammer) using the 'cmp' command in bash, and only differences that I found were the following:
    9847 32 ^Z 377 M-^?
    9848 32 ^Z 377 M-^?
    9849 32 ^Z 377 M-^?
    9850 32 ^Z 377 M-^?
    9851 32 ^Z 377 M-^?
    9852 32 ^Z 377 M-^?
    9853 32 ^Z 377 M-^?
    9854 32 ^Z 377 M-^?
    9855 32 ^Z 377 M-^?
    9856 32 ^Z 377 M-^?

    I guess that happens because of the 0x1A pattern that my protocol writes...
    So, what I want to do in my bootloader is:
    1 - Start, and checks if there's a valid firmware to run on a specific flash address
    2 - If yes, jumps into the application, if no, starts the protocol to receive the firmware
    3 - After flash finishes, jumps into the main application. 

    I guess that's all... 
    Thank you! 



    ST Employee
    October 29, 2025

    hello @j_filipe 

    Thank you for your effort and work on the bootloader implementation.

    I would like to suggest considering the use of the   USB DFU (Device Firmware Upgrade) class   instead of the CDC class for your firmware update process. Here are some reasons why DFU might be a better fit:

    • Purpose-built for firmware updates: The USB DFU class is specifically designed for device firmware upgrade operations, providing a standardized and reliable protocol.

    • Simplifies implementation: Using DFU can reduce the complexity of your bootloader communication protocol compared to implementing a custom XMODEM over CDC.

    • Better integration: DFU is widely supported by tools such as STM32CubeProgrammer, making flashing and recovery easier.

    Additionally, for practical implementation, you can refer to the official STM32U5 DFU example application available on GitHub: STM32U5 USB Device DFU Standalone Example.

    I would like to try the first solution using the Open Bootloader and kindly ask for your feedback on its effectiveness in this context.

    Thank you!

     

     

    j_filipeAuthor
    Graduate II
    October 29, 2025

    @T_Hamdi 
    Thank you for your response, but I did not ask for the DFU solution... I already was able to flash the application correctly. I changed the FLASH.ld file: 
    FLASH (rx) : ORIGIN = 0x0800C000, LENGTH = 10K
    and also the system_stm32u5xx.c file: 
    #define VECT_TAB_OFFSET 0xC000UL
    and generated the bin file. 

    Instead of the padding pattern, I placed 0xFF and now the application is flashed correctly, I just need all the steps to make the application jump from the bootloader to the application. 
    Thank you! 



    j_filipeAuthor
    Graduate II
    October 29, 2025

    @T_Hamdi @Saket_Om 
    I figured it out. 
    I managed to flash the firmware and then run it:

    typedef void (application_t)(void);
    static void jumpToApp(uint32_t address);
    typedef struct
    {
    	uint32_t 		stack_addr;
    	application_t* 	func_p;
    } JumpStruct;
    
    int main(void)
    {
     // Initializes flash, etc.
     HAL_Init();
    
     // Make all the flashing firmware process
    
     // finally:
     jumpToApp(APP_START_ADDRESS);
     
     while(1){}
    }
    
    
    static void jumpToApp(const uint32_t address)
    {
    	const JumpStruct* vector_p = (JumpStruct*)address;
    
    	DeInitEverything();
    
    	asm("msr msp, %0; bx %1;" : : "r"(vector_p->stack_addr), "r"(vector_p->func_p));
    }
    
    
    static void DeInitEverything(void)
    {
    	USBD_DeInit(&hUsbDeviceFS);
    	HAL_PCD_DeInit(&hpcd_USB_OTG_FS);
    
    	HAL_ICACHE_Disable();
    	HAL_GPIO_DeInit(MIC_SDIN0_GPIO_Port, MIC_SDIN0_Pin);
    	HAL_GPIO_DeInit(USB_UCPD_CC2_GPIO_Port, USB_UCPD_CC2_Pin);
    	HAL_GPIO_DeInit(WRLS_SPI2_NSS_GPIO_Port, WRLS_SPI2_NSS_Pin);
    	HAL_GPIO_DeInit(USB_VBUS_SENSE_GPIO_Port, USB_VBUS_SENSE_Pin);
    	HAL_GPIO_DeInit(OCTOSPI_R_IO6_GPIO_Port, OCTOSPI_R_IO6_Pin);
    	HAL_GPIO_DeInit(WRLS_WKUP_B_GPIO_Port, WRLS_WKUP_B_Pin);
    	HAL_GPIO_DeInit(MIC_CCK1_GPIO_Port, MIC_CCK1_Pin);
    	HAL_GPIO_DeInit(OCTOSPI_R_IO4_GPIO_Port, OCTOSPI_R_IO4_Pin);
    	HAL_GPIO_DeInit(USER_Button_GPIO_Port, USER_Button_Pin);
    	HAL_GPIO_DeInit(OCTOSPI_R_IO7_GPIO_Port, OCTOSPI_R_IO7_Pin);
    	HAL_GPIO_DeInit(OCTOSPI_R_DQS_GPIO_Port, OCTOSPI_R_DQS_Pin);
    	HAL_GPIO_DeInit(UCPD_PWR_GPIO_Port, UCPD_PWR_Pin);
    	HAL_GPIO_DeInit(PH3_BOOT0_GPIO_Port, PH3_BOOT0_Pin);
    	HAL_GPIO_DeInit(OCTOSPI_R_IO5_GPIO_Port, OCTOSPI_R_IO5_Pin);
    	HAL_GPIO_DeInit(OCTOSPI_F_NCS_GPIO_Port, OCTOSPI_F_NCS_Pin);
    	HAL_GPIO_DeInit(USB_UCPD_CC1_GPIO_Port, USB_UCPD_CC1_Pin);
    
    	__HAL_RCC_GPIOG_CLK_DISABLE();
    	__HAL_RCC_GPIOC_CLK_DISABLE();
    	__HAL_RCC_GPIOA_CLK_DISABLE();
    	__HAL_RCC_GPIOI_CLK_DISABLE();
    	__HAL_RCC_GPIOH_CLK_DISABLE();
    	__HAL_RCC_GPIOB_CLK_DISABLE();
    	__HAL_RCC_GPIOD_CLK_DISABLE();
    	__HAL_RCC_GPIOE_CLK_DISABLE();
    	__HAL_RCC_GPIOF_CLK_DISABLE();
    
    	HAL_RCC_DeInit();
    	HAL_DeInit();
    	SysTick->CTRL = 0;
    	SysTick->LOAD = 0;
    	SysTick->VAL = 0;
    }
    

     
    This way worked for me! 
    What is missing: 
    At the beginnig of the program, check if there's an available application to run, and if yes, jump right next to it, skipping all the flashing process. 


    ST Employee
    October 30, 2025

    Hello @j_filipe 

    you can add a simple check before entering the bootloader. If the application address is valid, the system jumps to it. Otherwise, the system enters bootloader mode and later jumps to the application.

    int main(void)
    {
     // Initializes flash, etc.
     HAL_Init();
     // Simple MSP validity check
     if (((*(__IO uint32_t *) APP_START_ADDRESS) & 0x2FFC0000) == 0x20000000)
     {
     jumpToApp(APP_START_ADDRESS);
     }
     else
     {
     // Make all the flashing firmware process
     
     // finally:
     jumpToApp(APP_START_ADDRESS);
     }
     
     while(1){}
    }