cancel
Showing results for 
Search instead for 
Did you mean: 

Issue After Bootloader on STM32F072RBT6: Stack Pointer Not Jumping to Application Address

shyamparmar
Associate III

Dear STM32 Community,

I am currently working on a project using the STM32F072RBT6 microcontroller, where I have implemented a custom bootloader to load an application program into flash memory. However, after the bootloader successfully writes the application binary, the stack pointer does not correctly jump to the application’s address, and the application fails to execute. 

MCU Model: STM32F072RBT6 Bootloader

Functionality: The bootloader receives the application binary and writes it to flash memory at the designated application address.

Application Flash Address: 0x08004000

Problem: After the bootloader completes, I attempt to jump to the application by setting the stack pointer and program counter accordingly, but the application does not start. It appears that the stack pointer is not correctly set to the application's address.

Jump Code in Bootloader:

 

static void goto_application(void){

void (* app_reset_handler)(void) = (void *)(*(volatile uint32_t *) (0x08004000 + 4));

__disable_irq();

__set_MSP((*(volatile uint32_t *)0x08004000));

__enable_irq();

app_reset_handler();

}

23 REPLIES 23

Look at the code I linked. You must disable the UART interrupt before jumping so it isn't called when the bootloader is running. This is what clearing the ICER/ICPR registers does. You should not globally disable interrupt.

If you feel a post has answered your question, please click "Accept as Solution".

I understand the theory, but in practice in STM32CubeIDE the code works in both default Debug and Release configurations. Nothing uses the stack pointer after it the __set_MSP instruction. This is the disassembly in Debug:

          jump_to_bootloader:
080004dc:   push    {r7, lr}
080004de:   sub     sp, #8
080004e0:   add     r7, sp, #0
 62           void (* app_reset_handler)(void) = (void *)(*(volatile uint32_t *) (0x1FFF0000 + 4));
080004e2:   ldr     r3, [pc, #40]   @ (0x800050c <jump_to_bootloader+48>)
080004e4:   ldr     r3, [r3, #0]
080004e6:   str     r3, [r7, #4]
 962        __ASM volatile ("cpsid i" : : : "memory");
080004e8:   cpsid   i
 963      }
080004ea:   nop     
 66           __set_MSP((*(volatile uint32_t *)0x1FFF0000));
080004ec:   ldr     r3, [pc, #32]   @ (0x8000510 <jump_to_bootloader+52>)
080004ee:   ldr     r3, [r3, #0]
080004f0:   str     r3, [r7, #0]
1155        __ASM volatile ("MSR msp, %0" : : "r" (topOfMainStack) : );
080004f2:   ldr     r3, [r7, #0]
080004f4:   msr     MSP, r3
1156      }
080004f8:   nop     
 951        __ASM volatile ("cpsie i" : : : "memory");
080004fa:   cpsie   i
 952      }
080004fc:   nop     
 70           app_reset_handler();
080004fe:   ldr     r3, [r7, #4]
08000500:   blx     r3
 72       }

 

If you feel a post has answered your question, please click "Accept as Solution".
Pavel A.
Evangelist III

Yes but this depends on the compiler. Not robust. Also, what if __set_MSP moves the stack pointer up  and then some interrupts occur and clobber the data pointed by r7. Not robust, won't pass code review in a decent project.

By default, Debug is -g3 and -O0, Release is None and -Os.

Switching to -g3 and -Og changed the code a bit but didn't affect the takeaway.

          jump_to_bootloader:
080004dc:   push    {r3, lr}
 62           void (* app_reset_handler)(void) = (void *)(*(volatile uint32_t *) (0x1FFF0000 + 4));
080004de:   ldr     r3, [pc, #16]   @ (0x80004f0 <jump_to_bootloader+20>)
080004e0:   ldr     r2, [r3, #4]
 962        __ASM volatile ("cpsid i" : : : "memory");
080004e2:   cpsid   i
 66           __set_MSP((*(volatile uint32_t *)0x1FFF0000));
080004e4:   ldr     r3, [r3, #0]
1155        __ASM volatile ("MSR msp, %0" : : "r" (topOfMainStack) : );
080004e6:   msr     MSP, r3
 951        __ASM volatile ("cpsie i" : : : "memory");
080004ea:   cpsie   i
 70           app_reset_handler();
080004ec:   blx     r2

 

If you feel a post has answered your question, please click "Accept as Solution".
Pavel A.
Evangelist III

TDK, this is good optimization, the pointer is stored in r2 early and is not affected by stack. This should work. But what is your point, I don't get. Yes the OP's problem can be somewhere else.

 

 

@Pavel A.  Thank you for your input. I understand your point about the pointer being stored in r2 and not being affected by the stack. That makes sense.

As for the issue I'm facing, I'm trying to receive an application .bin file over UART3 ( STM32F0RBT6 )and write it into flash memory. However, the problem arises when trying to execute the application code using app_reset_handler The code seems to get stuck somewhere, and I'm unable to determine the cause.

 

this is brief overview of bootloader code (I am receiving application .bin file using uart interrupt)::(

HAL_UART_Transmit(&huart3, (uint8_t *)"Bootloader started...\r\n", 23, HAL_MAX_DELAY);

HAL_UART_Receive_IT(&huart3,(uint8_t *)buffer,8024);

 

Here's a brief overview of the relevant code:

static void goto_application(void){

void (* app_reset_handler)(void) = (void *)(*(volatile uint32_t *) (0x8004000 + 4));

__disable_irq();

__set_MSP((*(volatile uint32_t *)0x8004000));

app_reset_handler();

}

 

UART INTERRUPT : 

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)

{

HAL_UART_Transmit(&huart3,(uint8_t *)buffer,8024,HAL_MAX_DELAY);

if(Flash_Erase() == HAL_OK) {

HAL_UART_Transmit(&huart3, (uint8_t *)"Flash erase succes!\r\n", 21, HAL_MAX_DELAY);

} else {

HAL_UART_Transmit(&huart3, (uint8_t *)"Flash erase failed!\r\n", 21, HAL_MAX_DELAY);

}

if (Flash_Write(address,(uint8_t *)buffer, 8024) != HAL_OK) {

HAL_UART_Transmit(&huart3, (uint8_t *)"Flash write failed!\r\n", 21, HAL_MAX_DELAY);

}

HAL_UART_Transmit(&huart3, (uint8_t *)"Flashing complete.\r\n", 20, HAL_MAX_DELAY);

__disable_irq();

goto_application();

HAL_UART_Transmit(&huart3, (uint8_t *)"uart int\r\n", 10, HAL_MAX_DELAY);

}

 

 

I'm wondering if the issue is related to the way the application is being loaded into flash or if it's something else. Do you have any suggestions for what might be going wrong here or what I could investigate further?

Thanks for your help!

Use the </> to post code so that it's properly formatted and readable.

Tips and Tricks with TimerCallback https://www.youtube.com/@eebykarl
If you find my solution useful, please click the Accept as Solution so others see the solution.

Primary have you check code working on 8004000? Simply erase flash and load only hex onto 08004000. Then start code from this addr with command go over STLINK.

 

@MM..1  Yes, it's work. I have tested the code execution at the address 0x08004000 as per your instructions.

Then next step is place in bootloader main optimaly total first line ...

		// Jump to user application
		JumpAddress = *(__IO uint32_t*) (USER_FLASH_START_ADDRESS + 4);
		Jump_To_Application = (pFunction) JumpAddress;
		// Initialize user application's Stack Pointer
		__set_MSP(*(__IO uint32_t*) USER_FLASH_START_ADDRESS);
		Jump_To_Application();

after this test go next as your call , but with uninit all conflict peripheral before. I preffer WWDG or IWDG reboot and detect in bootloader main first lines. Then jump to app without init some conflicts...