Skip to main content
Visitor II
July 26, 2021
Question

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

  • July 26, 2021
  • 8 replies
  • 3011 views

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.

    This topic has been closed for replies.

    8 replies

    Visitor II
    July 26, 2021

    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

    Visitor II
    July 26, 2021

    Files as zip as only allowed attach one...

    TReed.1Author
    Visitor II
    July 27, 2021

    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.

    Visitor II
    July 27, 2021

    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

    Visitor II
    July 27, 2021

    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.

    Visitor II
    July 27, 2021

    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.1Author
    Visitor II
    July 27, 2021

    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.

    Visitor II
    August 2, 2021

    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