Skip to main content
Associate
January 4, 2026
Solved

Can not jump to APP from bootloader on STM32G0B0CET6

  • January 4, 2026
  • 5 replies
  • 808 views

Hi I have a question that blocked me for several days, Hopefully someone can help, thanks a lot.

1:MCU:STM32G0B0CET6,Develop tool:STM32CubeIDE

2:I wanna have a test for OTA ,so i create a bootloader project and app project

3:Flash segmentation:

1)Bootloader(32KB),0x08000000~0x08007FFF

    APP(240K):0x08008000~0x08047FFF

    APP BAK(236K):0x08048000~0x08079FFF

   INFO(8K):0x0807A000~x0807FFFF

4:In Bootloader ,try to jump to APP(0x08008000):

5:Attached the source code (bootloader and app) and hex file(in debug directory)

6:Modifed "ld" file in bootloader project and app project, and I confirm  the stack address in 0x08008000 and reset_handler in 0x08008004 is correct.

And have set vector in main entry(first line in main()):SCB->VTOR = FLASH_BASE | 0x8000;

7:If i use stm32CubeProgrammmer and STM32CubeIDE to start ,app will be run ,But app can not run via bootloader software jump.

 

bootloader jump slide:

void RunUserCode( void )
{
 uint32_t* ucaddr;

 // if( ( ( *( uint32_t* )ROM_APP_VECT_START_ADDR ) & 0x2FFC0000 ) == 0x20000000 )
 if( ( *( uint32_t* )APP1ADDR ) == 0x20023FF8)
 {
 	HAL_DeInit();//DeInit The Peripherals Used By The Bootloader
 //Rocky report fault shield first __set_FAULTMASK( 1 );
 __disable_irq();
 __set_MSP( *( __IO uint32_t* )APP1ADDR ); // Get top of stack address and initialize SP
 __set_PSP( *( __IO uint32_t* )APP1ADDR );
 __set_CONTROL( 0 );
 ucaddr = ( uint32_t* )( ROM_APP_VECT_RESET_ADDR ); // Get APP Reset Handler address
 ( *( ( void( * )( void ) )( *ucaddr ) ) )(); // Let's Jump!
 }
}

 app vector set:

int main(void)
{

 /* USER CODE BEGIN 1 */
	/* 第一步:强制设置向量表偏移,优先级最高 */
 //SCB->VTOR = 0x08008000; // 必须放在最前面!
 SCB->VTOR = FLASH_BASE | 0x8000; 
 setMainState(BOOT,3);
 /* USER CODE END 1 */

 /* MCU Configuration--------------------------------------------------------*/

 /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
 HAL_Init();

 /* USER CODE BEGIN Init */

 /* USER CODE END Init */

 /* Configure the system clock */
 SystemClock_Config();

 /* USER CODE BEGIN SysInit */

 /* USER CODE END SysInit */

 /* Initialize all configured peripherals */
 MX_GPIO_Init();

bootloader ld slice: :

/* Memories definition */
MEMORY
{
 RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 144K
 FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 32K
}

/* Sections */
SECTIONS
{
 /* The startup code into "FLASH" Rom type memory */
 .isr_vector :
 {
 . = ALIGN(4);
 KEEP(*(.isr_vector)) /* Startup code */
 . = ALIGN(4);
 } >FLASH

APP ld Slice:

/* Entry Point */
ENTRY(Reset_Handler)

/* Highest address of the user mode stack */

_estack = 0x20023FF8;/*8 bytes alignment*/

_Min_Heap_Size = 0x200; /* required amount of heap */
_Min_Stack_Size = 0x400; /* required amount of stack */

/* Memories definition */
MEMORY
{
 RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 144K 
 
 FLASH (rx) : ORIGIN = 0x08008000, LENGTH = 240K 
}

/* Sections */
SECTIONS
{
 /* The startup code into "FLASH" Rom type memory */
 .isr_vector :
 {
 . = ALIGN(4);
 KEEP(*(.isr_vector)) /* Startup code */
 . = ALIGN(4);
 } >FLASH

flash readback screen shot for boot and app:

sg0993_0-1767521397710.png

sg0993_1-1767521424042.png

 

Best answer by gbm

1. The interrupts are disabled while jumping to the app. They must be enabled.

2. No need to set PSP.

3. The code may not work if compiled with the default -O0. Use -O1 or above.

4.The routine jumping to the app should be invoked in the bootloader right after reset, before initializing peripherals. It should NOT be called from ISR. See some other threads on bootloaders and app starting.

5. See the content of the file, esp. the comments:

https://github.com/gbm-ii/STM32_Inc/blob/main/cm_boot.h

 

5 replies

gbm
gbmBest answer
Principal
January 4, 2026

1. The interrupts are disabled while jumping to the app. They must be enabled.

2. No need to set PSP.

3. The code may not work if compiled with the default -O0. Use -O1 or above.

4.The routine jumping to the app should be invoked in the bootloader right after reset, before initializing peripherals. It should NOT be called from ISR. See some other threads on bootloaders and app starting.

5. See the content of the file, esp. the comments:

https://github.com/gbm-ii/STM32_Inc/blob/main/cm_boot.h

 

My STM32 stuff on github - compact USB device stack and more: https://github.com/gbm-ii/gbmUSBdevice
sg0993Author
Associate
January 5, 2026

Hi gpm,

Thanks for your timely response and the issues has been solved.

I try to modify one by one of suggestions you posted.

The root cause is "The interrupts are disabled while jumping to the app. They must be enabled".

So why the interrupts disabling make jump fail? Hopefully get your response again.

gbm
Principal
January 5, 2026

Your app must execute with interrupts enabled. If you disable interrupts in the bootoader, then some piece of code must enable them - either the bootloader or the app. In ARM-M architecture interrupts are enabled out of reset, so normally when you write software you assume they are enabled and don't enable them explicitly in the app.

My STM32 stuff on github - compact USB device stack and more: https://github.com/gbm-ii/gbmUSBdevice
STOne-32
Technical Moderator
January 5, 2026

Dear @gbm ,

Thank you, a lot, for the key contribution. much appreciated and well done for the great catch!

Dear @sg0993 ,

We have these sources codes from our IAP example:

STM32G0xx_IAP/STM32G0xx_IAP at main · stm32-hotspot/STM32G0xx_IAP · GitHub  

Bootloader project is here: STM32G0xx_IAP/STM32G0xx_IAP/IAP_Main at main · stm32-hotspot/STM32G0xx_IAP · GitHub

and main Application as well.

In case it is helpful for other members and to make aware of it inside our  STMicroelectronics - STM32 Hotspot · GitHub

Have a great day,

STOne-32

sg0993Author
Associate
January 9, 2026

Hi Gpm,

I also have another question:

1)some peripheral device like USART2,if i start Bootloader project or app project using stm32cubeide in debug mode ,the USART2 can work in app ,but if then power off and restart, app is running ,usart1 is working,but usart2 is not working,why?I try to make usart1 and usart2 configuration same in bootloader and app.,but situation is different.

int main(void)
{

 /* USER CODE BEGIN 1 */
 

 /* USER CODE END 1 */

 /* MCU Configuration--------------------------------------------------------*/

 /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
 HAL_Init();

 /* USER CODE BEGIN Init */

 /* USER CODE END Init */

 /* Configure the system clock */
 SystemClock_Config();

 /* USER CODE BEGIN SysInit */

 /* USER CODE END SysInit */

 /* Initialize all configured peripherals */
 MX_GPIO_Init();
 MX_RTC_Init();
 MX_USART1_UART_Init();
 MX_USART2_UART_Init();
 /* USER CODE BEGIN 2 */

 /* USER CODE END 2 */

 /* Infinite loop */
 /* USER CODE BEGIN WHILE */
 while (1)
 {
 /* USER CODE END WHILE */

 /* USER CODE BEGIN 3 */
		//RunUserCode();
		 RunUserCode();
	HAL_Delay(5000);
 }
 /* USER CODE END 3 */
}
gbm
Principal
January 9, 2026

I always recommend to go through reset to invoke the app from the bootloader (or the bootloader from the app). Set some flag not affected by reset, issue reset via NVIC_SystemReset(), then at the very start of the bootloader, before configuring any clocks or peripherals, check the flag and start the app or the bootloader.

This way you may avoid all the possible surprises connected to peripheral setup, and, believe me, there are many of them, like SYSCFG stuff or peripheral clock source selection.

Below is a fragment of my portable STM32 bootloader code I use with 5 different STM32 families:

int main(void)
{
	enable_bootflag_access();
	uint32_t boot_addr = get_boot_flag();
	set_boot_flag(0);

	if (boot_addr == BOOT_ADDR)
	{
		app_start(BOOT_ADDR);	// start ST built-in bootloader
	}

	if (boot_addr != (uint32_t)&g_pfnVectors && !force_boot())	// skip searching for app if SWDIO pulled down
	{
		if (boot_addr < FLASH_BASE || (boot_addr & 0x1ff))
			boot_addr = APP_BASE;

		if (boot_addr > FLASH_BASE)
		{
			// check/search for app
			for (boot_addr &= PAGE_BASE_MASK; boot_addr < APP_END; boot_addr += FLASH_PAGE_SIZE)
			{
				if (app_is_valid(boot_addr))
				{
					// app present, delay for double-reset detection
					set_boot_flag((uint32_t)&g_pfnVectors);	// start bootloader after 2nd reset
					// wait for 0.5 s
					SysTick_Delay(INIT_HCLK_FREQ / 2u);	// if MCU is reset during this delay, bootloader will start

					set_boot_flag(0);	// 2nd reset did not happen - normal start after next reset
					app_invoke(boot_addr);	// start the app
				}
			}
			app_not_found = 1;
		}
	}

 // start the bootloader...

 

My STM32 stuff on github - compact USB device stack and more: https://github.com/gbm-ii/gbmUSBdevice
gbm
Principal
January 13, 2026

In the code above, there is no reason for any USART to start incorrectly.

BUT for the above code to work, .noinit section must be handled correctly in the linker script, which is not always the case with CubeMX-generated linker scripts.

My STM32 stuff on github - compact USB device stack and more: https://github.com/gbm-ii/gbmUSBdevice
sg0993Author
Associate
January 14, 2026

Hi gpm,

I also think there is no reason for any USART to start incorrectly.

I know the link scripts modification and add the below in the ld file:

 .noinit (NOLOAD) :
 {
 . = ALIGN(4);
 *(.noinit) 
 *(.noinit.*) 
 . = ALIGN(4);
 } >RAM

I will try again in another board and get back to you.

gbm
Principal
January 14, 2026

Just make sure that the .noinit is placed in the script AFTER .bss, not between .data and .bss. :)

My STM32 stuff on github - compact USB device stack and more: https://github.com/gbm-ii/gbmUSBdevice