2017-09-28 04:24 PM
About 6 months ago, I wrote a serial GUI under Windows to upgrade firmware on an STM32L052 via the UART2 port, where the firmware does a JumpToBootloader() to get things started. This took a lot of work and troubleshooting to perfect, but I know my way around the bootloader now. (or so I thought...)
I'm now porting that code to an STM32L021, and it just doesn't behave the same way. I see the code jump to the bootloader at 0x1FF00000, I can single step through, see it clear out SRAM, do a couple compares, then the bootloader jumps back to the main code. Location 0x1FF00236 is final compare and branch that jumps back (resets) to main code.
I haven't decrypted what it's checking, but was wondering if the C or Assembly source is available for the bootloader in the STM32L021?
Or, if anyone has a workaround on the L021, that would sure help.
Thanks!
- SteveK
#firmware-update #bootloader #bootloader-firmware-upgrade #uart2 #embedded-bootloader #stm32l0212017-09-28 05:12 PM
Do you map the ROM at zero?
I have annotated assembly listings for the parts I use.
2017-09-28 05:33 PM
I've tried it both ways. Jumping to 0x1FF00000, and also remapping to 0x00000000.
Since the bootloader code uses absolute addressing, when I remap and jumps to 0x00000000 it grabs the address and runs at 0x1FF00000, so they both behave the same.
After single-stepping through the disassembled code with Keil, I see a compare performed on FLASH_OPTR where it seems to look at bit 30 (assembly location 0x1FF00202) by doing a left shift, this OPTR bit-30 isn't defined in the RM. But after this compare the code puts the RCC back to init state then jumps to my main code.
Can you tell me what's going on in this compare?
Steve
2017-09-28 07:36 PM
>>Since the bootloader code uses absolute addressing, when I remap and jumps to 0x00000000 it grabs the address and runs at 0x1FF00000, so they both behave the same.
Except the vector table doesn't. The Cortex-M0+ has a SCB->VTOR, the ROM might not use it.
You'll need to send me a .BIN or .HEX of the ROM for the STM32L021 in question.
mailto:sourcer32@gmail.com
2017-10-19 05:36 PM
Hi,
Just wanted to follow up and close out this issue. After about a weeks' worth of work, I was able to solve the bootloader issue with some help from ST-Micro's STM32 support team.
Here is the issue. The STM32L021 (and other smaller STM32 chips) now include a 'main flash empty' check (see AN2606, Table 2, Pattern6), and will jump to the bootloader if the FLASH is empty. However, if there's already code in the Flash, and you jump to the bootloader from your code, the flash-empty check fails and the bootloader simply goes back to the reset vector.
To fix this, we need to detect that we really want to stay in the bootloader, so I set a magic number in SRAM then jump back into it just past the flash-empty assy routine. Finding this exact 'second-jump' location took a lot of signle-stepping through assembly language! But the following code works beautifully on the STM32L021 (DevID 0x457 Rev Z).
This is the C source that jumps to the bootloader. It has 3 key Steps:
- De-initialize peripherals and IRQ's - Set a magic-number in the last location in SRAM - Do a system reset ------------------------------------------------- // Disable used PLL HAL_RCC_DeInit(); // Disable Interrupts __disable_irq(); // Initialize SysTick for the Bootloader SysTick->CTRL = 0; // Reset the Systick Timer SysTick->LOAD = 0; SysTick->VAL = 0;// goto the bootloader, we won't be coming back from here...
*(__IO uint32_t *)FWUG_MAGIC_NUMBER_ADDR = 0x12348765; // Set the magic number for when we fail empty-checkHAL_NVIC_SystemReset();
while (1) { }----------------------------------------------------Note: The following assembly routine will need to be inserted into your 'startup_stm32xxxx.s' file. Typically the Reset_Handler routine has 5 lines of code (top 2, and the bottom 3 below), however you 'must' insert the double-jump to bootloader shown here into the assembly code as shown (if you try to do this double-jump in main(), your SRAM gets cleared between the first and second jumps causing the bootloader to completely fail. This double-jump has to be in the assembly code)
So here's how it works. When you perform HAL_NVIC_SystemReset() from your C routine, it will reset and run this code. If we see the magic-number stored in SRAM, we jump to 'FirstJumpToBootloader', which swaps bytes in the magic-number, then jumps to the bootloader code.
When the bootloader fails the empty-check, we fall back into this Reset routine, and check the magic-number again and it matches our second pattern, so we jump to the 'SecondJumpToBootloader'. Here, we erase the magic-number then jump to the middle of the bootloader code, location 0x1FF0023D just after the empty-check. From here, the bootloader works as expected.
You will have to find this bootloader 'second-jump' location for your device, or check with ST-Micro tech support.
----------------------------------------------------
ApplicationStart LDR R0, =SystemInit BLX R0 CheckIfBootloaderJumpRequired LDR R0, =0x200007FC ; This is our FWUG_MAGIC_NUMBER_ADDRESS LDR R1, =0x12348765 LDR R2, [R0,#00] CMP R2, R1 BEQ FirstJumpToBootloader LDR R1, =0x65873412 CMP R2, R1 BEQ SecondJumpToBootloader LDR R0, =JumpToMain BX R0 FirstJumpToBootloader REV R1,R1 ; stir up the bytes for the next time thru STR R1,[R0,#00] LDR R0, =0x1FF00000 ; Load SP with bootloader SP LDR R0,[R0,#00] MOV SP, R0 LDR R0, =0x1FF00004 ; This is the First Bootloader jump address LDR R0,[R0,#00] BX R0 SecondJumpToBootloader LDR R1, =0x00 STR R1,[R0,#00] LDR R0, =0x1FF00000 ; Load SP with bootloader SP, again... LDR R0,[R0,#00] MOV SP, R0 LDR R0, =0x1FF0023D ; This is the second Bootloader jump address BX R0 JumpToMain LDR R0, =__main BX R0 ENDP----------------------------------------------------
With these two pieces of code in place, the bootloader works perfectly every time via the UART.
-SteveK