cancel
Showing results for 
Search instead for 
Did you mean: 

How to pass back and forth between a bootloader and the application code in an STM32L4 MCU

TReed.1
Associate II

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.

8 REPLIES 8
Paul1
Lead

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

Paul1
Lead

Files as zip as only allowed attach one...

TReed.1
Associate II

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.

Paul1
Lead

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

Paul1
Lead

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.

Paul1
Lead

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
  1. Bootload Flags
    1. 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

TReed.1
Associate II

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.

Paul1
Lead

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