Issue After Bootloader on STM32F072RBT6: Stack Pointer Not Jumping to Application Address
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Email to a Friend
- Report Inappropriate Content
‎2024-10-01 09:39 PM - last edited on ‎2024-10-04 03:00 AM by Amel NASRI
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();
}
Solved! Go to Solution.
- Labels:
-
Bootloader
-
Flash
-
STM32F0 Series
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Email to a Friend
- Report Inappropriate Content
‎2024-10-04 06:14 AM
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.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Email to a Friend
- Report Inappropriate Content
‎2024-10-04 06:18 AM
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 }
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Email to a Friend
- Report Inappropriate Content
‎2024-10-04 06:31 AM - edited ‎2024-10-04 06:38 AM
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.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Email to a Friend
- Report Inappropriate Content
‎2024-10-04 06:43 AM
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
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Email to a Friend
- Report Inappropriate Content
‎2024-10-04 07:15 AM
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.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Email to a Friend
- Report Inappropriate Content
‎2024-10-05 01:49 AM - edited ‎2024-10-05 01:53 AM
@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!
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Email to a Friend
- Report Inappropriate Content
‎2024-10-05 12:58 PM
Use the </> to post code so that it's properly formatted and readable.
If you find my solution useful, please click the Accept as Solution so others see the solution.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Email to a Friend
- Report Inappropriate Content
‎2024-10-06 12:44 AM
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.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Email to a Friend
- Report Inappropriate Content
‎2024-10-06 09:29 PM
@MM..1 Yes, it's work. I have tested the code execution at the address 0x08004000 as per your instructions.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Email to a Friend
- Report Inappropriate Content
‎2024-10-07 05:35 AM
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...