cancel
Showing results for 
Search instead for 
Did you mean: 

STM32L021 Jump to Bootloader issue

Steve Krattiger
Associate III
Posted on September 29, 2017 at 01:24

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 #stm32l021
4 REPLIES 4
Posted on September 29, 2017 at 02:12

Do you map the ROM at zero?

I have annotated assembly listings for the parts I use.

Tips, buy me a coffee, or three.. PayPal Venmo Up vote any posts that you find helpful, it shows what's working..
Steve Krattiger
Associate III
Posted on September 29, 2017 at 02:33

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

Posted on September 29, 2017 at 02:36

>>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

Tips, buy me a coffee, or three.. PayPal Venmo Up vote any posts that you find helpful, it shows what's working..
Steve Krattiger
Associate III
Posted on October 20, 2017 at 02:36

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-check

  HAL_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