How to pass back and forth between a bootloader and the application code in an STM32L4 MCU
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Email to a Friend
- Report Inappropriate Content
‎2021-07-26 9:09 AM
The bootloader checks the CRC of the application code and passes control to the application code it matches a saved value. If it doesn't match, it waits for commands from the host system to update the application firmware. The application will clear the saved CRC when commanded from the host system. The host system requests a reset which is supposed to run the bootloader. I have already verified the bootloader and application code separately. Combining them is my problem. I think it's related to the .isr_vector definition in the linker files but I'm not certain. I am redefining VTOR, but that alone isn't working. I've been trying to find what registers or where the isr and reset pointers are kept.
- Labels:
-
Bootloader
-
STM32L4 series
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Email to a Friend
- Report Inappropriate Content
‎2021-07-26 9:30 AM
Hi,
For our STM32L4x6 products with a custom bootloader project we use attached linker files:
- STM32L496RGTX_FLASH.ld.BL.txt = For Bootloader
- STM32L476RGTX_FLASH.ld.App.txt = For App
- At this time memory division is simply 50% BL and 50% App, though that will change for projects with larger Apps.
Memory Definitions:
- MCU_STM32L476xG.h
- MCU_STM32L496xG.h
The following command in App code to switch to its vector table before BEFORE any interrupts enabled:
- vSetVectorTableOffset(D1_FLASH_AF_ADDR_START); //Set location of the Vector Table to Bootloader, first thing so following code uses matching vectors
- inserted between HAL_Init() and SystemClock_Config() in initilization.
Paul
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Email to a Friend
- Report Inappropriate Content
‎2021-07-26 9:34 AM
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Email to a Friend
- Report Inappropriate Content
‎2021-07-27 6:45 AM
Thank you.
It looks like what I'm doing is similar but, I'm not specifying separate memory blocks for the vector table and BL flash. I'm also trying to use the reset to jump from the application to the bootloader. My AP linker has a section for sending the reset vector to the BL Flash which keeps me from loading the BL first then debugging AP. If I remove it then the reset restarts the AP. Seems like I can ditch using the reset and make my own function to jump to BL from the AP.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Email to a Friend
- Report Inappropriate Content
‎2021-07-27 6:54 AM
We use a location in the DataFLASH block we defined.
To force BL the App zeros that location and reset's MCU (Zeroing + reset means minimal code in App).
On startup the BL checks that location for a specific value (your choice),
and if finds it then does a CRC on the App block,
and if CRC passes then jumps to App block.
If anything fails stays in BL mode.
When BL successfully receives a new app it updates both the value and App CRC in DataFlash, then resets.
Feel free to implement BL any way you wish, to the BL it is just updating FLASH data.
You can have the BL's Vector table in the BL block, that is also choice, Just ensure it is at the MCU's default vector table location for proper reset.
Paul
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Email to a Friend
- Report Inappropriate Content
‎2021-07-27 6:58 AM
Default should be BL call App (not App call BL).
If the App load is interrupted then you wouldn't be able to recover if:
Reset > Incomplete App > Can't start BL
That's also why we added a CRC for the App load, bit of paranoia doesn't hurt when self programming the FLASH.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Email to a Friend
- Report Inappropriate Content
‎2021-07-27 9:07 AM
RAM Flags instead of FLASH (Bonus of can return to App if BL didn't happen)
- By defining a section of RAM that is not zeroed on reset, it is possible to fill it with useful data that can be checked by the MCU on reset
- Ensure use long codes for flags (Long code similar to a CRC check), not just True/False, as RAM may be Random on a normal powerup.
- Hold data such as
- Bootload Flags
- Reset reasons
- Note: it is important to take into account that the bootloader may clear any data set by the app if its uninit section is smaller than the app's
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Email to a Friend
- Report Inappropriate Content
‎2021-07-27 11:15 AM
Thank you again.
I have the same thing as the DataFLASH block as you mentioned, and I'm not really calling the BL from AP code. It's called with a reset as you mentioned, poor choice of words on my part. I changed my AP linker file to remove the reset vector pointer (which I didn't need) and now I think I've narrowed my issue down to either how I'm transferring from the BL to the AP or how I'm disabling stuff in the AP before allowing the reset. I've tried a couple of ways dummy loop in the BL put at the address of the start of AP code that gets over written when the AP is installed and a goto the starting address. Both send me to hard_fault_interrupt jail. The good part is I can see in the general registers that the BL was running.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Email to a Friend
- Report Inappropriate Content
‎2021-08-02 5:22 AM
Careful in jump from BL to App
- Must disable all Irq and must De-Init all modules that BL inited or they may cause random vector jumps while preparing App
i.e.:
void vStartApp(void)
{
if (!BL_App::bCheckCRC()) // App CRC invalid?
{
dprintf(EV_LOWVERBOSE, context(CON_BL | CON_ERROR), "CRC verification failed, entering bootloader\n");
vRebootToBootloader(); // App invalid, reboot to bootloader
}
dprintf(EV_UNVERBOSE, CON_BL, "Starting App...\n");
vUartForceStdAll("BOOTINGa");
// Begin app booting process
__disable_irq();
HAL_DeInit(); // Uninitialize all configured devices
for(int i = 0;i < 8;i++) NVIC->ICER[i] = 0xFFFFFFFF;// Disable all interrupts @suppress("C-Style cast instead of C++ cast") @suppress("Field cannot be resolved")
for(int i = 0;i < 8;i++) NVIC->ICPR[i] = 0xFFFFFFFF;// @suppress("C-Style cast instead of C++ cast") @suppress("Field cannot be resolved")
__set_CONTROL(0); // Reset all control
__set_MSP(*(__IO uint32_t*)D1_FLASH_AF_ADDR_START); // Set stack pointer to new program @suppress("C-Style cast instead of C++ cast")
//uint32_t JumpAddress = *((__IO uint32_t*) (D1_FLASH_AF_ADDR_START + 4)); // Jump address pointer @suppress("C-Style cast instead of C++ cast")
uint32_t JumpAddress = (((uint32_t*)D1_FLASH_AF_ADDR_START)[1]); // Jump address pointer @suppress("C-Style cast instead of C++ cast")
__ISB();//Before Jump to Unknown Code: Instruction Synchronization Barrier == Empty CPU Instruction Pipeline
__DSB();//Before Jump to Unknown Code: Data Synchronization Barrier == Wait till all CPU Data accesses completed
SysTick->CTRL &= ~(SysTick_CTRL_CLKSOURCE_Msk | SysTick_CTRL_ENABLE_Msk); // @suppress("C-Style cast instead of C++ cast") @suppress("Field cannot be resolved")
reset_handler Jump = (reset_handler)JumpAddress; // Cast address pointer to a function pointer @suppress("C-Style cast instead of C++ cast") @suppress("Field cannot be resolved")
Jump(); // Execute Jump
// End app booting process. Function should never get past this point unless there was a failure
dprintf(EV_UNVERBOSE, context(CON_BL | CON_ERROR), "Failed to jump to app, rebooting to Bootloader...\n");
vRebootToBootloader();
}
Paul
