cancel
Showing results for 
Search instead for 
Did you mean: 

Using NVIC_SystemReset() in bootloader<->application jumps

I am working on a project with STM32L432 and trying to get a clean (without necessity to de-init peripherals and disable peripheral interrupts) jump to application from a bootloader and back.

The widely described method to do so is as follows (for a booloader):

  1. Write a predefined key value to the non-initialized RAM predefined key variable.
  2. Call NVIC_SystemReset().
  3. After reset check the key variable.

If it contains the key value, erase it and execute standard jump to the application:

typedef void (*pFunction)(void);
pFunction Jump_To_Application;
 
void Application_jump(void)
{ 
   uint32_t JumpAddress;
 
   /* Get the application stack pointer (First entry in the application vector table) */
   JumpAddress = (uint32_t) *((__IO uint32_t*)APP_START_ADDRESS);
 
   /* Get the application entry point (Second entry in the application vector table) */
   Jump_To_Application = (pFunction) *(__IO uint32_t*) (APP_START_ADDRESS + 4);
 
   /* Reconfigure vector table offset register to match the application location */
   SCB->VTOR = JumpAddress;
 
   /* Set the application stack pointer */
   __set_MSP(JumpAddress);
 
   /* Start the application */
   Jump_To_Application();
}  

My question is:

Is it possible to force the system to start at the application address after reset.

I tried it, but system always starts at a base address. Maybe I've done something wrong.

It it's possible, then how?

And in this case would using only NVIV_SystemReset() call provide a clean jump?

BTW, if system always starts at a base address, would using only NVIC_SystemReset() in application provide a clean jump to a bootloader?

Thank you.

9 REPLIES 9
Piranha
Chief II

First a few words about Your previous combined initialization idea described there:

https://community.st.com/s/question/0D50X0000ADCPB4SQP/custom-bootloader-and-application-initialization

https://community.st.com/s/question/0D50X00009nNERRSA4/interrupt-not-working-after-jump-to-code-from-bootloader

It can be done, but it's a bad solution. Not only it makes application more dependent on bootloader, but it also makes bootloader dependent on application, which is unacceptable. Also it makes code on both sides more complex and that is undesirable.

The same principles apply to Your current question about system start address. You don't want it to start at application address because that's not reliable. If bootloader is interrupted while doing application update, device could be bricked, because it will not be able to start neither application, nor bootloader. So You want a system to always start at bootloader address first.

I'll share my reliable and simple solution. The idea is to mimic system reset conditions for application as close as possible. This is the beginning of bootloader's reset handler before usual initialization:

Reset_Handler:
	BL   Boot_GetApplicationAddress
	CBZ  R0, StartBootloader
	LDR  R1, =0xE000ED00  // SCB
	STR  R0, [R1, #8]  // VTOR
	LDR  SP, [R0, #0]  // Stack pointer
	LDR  R0, [R0, #4]  // Reset handler
	DSB  // Ensure the VTOR and SP operations are complete
	ISB  // Flush the pipeline because of SP change
	BX   R0  // Start the application
StartBootloader:

void* Boot_GetApplicationAddress(void) makes a decision whether to start application or bootloader and returns application base address or NULL respectively. It does the following in this order and uses early returns from steps if bootloader must be started:

  1. Checks special input pin - enables GPIO port clock, reads input value, disables port clock. In L4 series You'll also have to switch mode to input before reading value and back to analog after. This pin can be "recovery mode" button, jumper or some special input. It overrides anything else and therefore it will be possible to start bootloader this way under any circumstances.
  2. Checks signature in non-initialized RAM. I use 0xB00720AD (visual similarity with "BOOTLOAD") and it means that bootloader start is demanded. Don't clear signature it at this stage, because, if unexpected reset happens while bootloader is executing, You want system to start bootloader again.
  3. Checks application integrity. If application is OK, returns application base address.

Starting application from bootloader:

  1. Clear signature.
  2. Call NVIC_SystemReset().

Starting bootloader from application:

  1. Set signature.
  2. Call NVIC_SystemReset().

This is the cleanest and simplest reliable solution I've come up with. Of course, any additional ideas and comments are welcome! =)

>>SCB->VTOR = JumpAddress;

NO, SCB->VTOR = APP_START_ADDRESS; // The BASE of the table

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..

Piranha & Clive,

Thank you so much for your help.

I still need to do a few tweaks (and may ask some questions :) ), but it seems to be working!

KHira.2
Associate II

Hi,

I want to implement a bootloader but facing some issue in jump process. I am using STM32F407 controller. My question is :

1.NVIC_SystemReset() should be used after deinitializing the peripheral. Is it correct ?

2.Also should we set the MSP or not.

I am just setting the vector offset

SCB->VTOR = SECTOR_START_ADDRESS;

Thanks

> 1.NVIC_SystemReset() should be used after deinitializing the peripheral. Is it correct ?

Not necessarily.

A reset puts all internal & peripheral registers back to the default value, as described in the reference manual.

In your application, you should never assume any previous initialization.

@Community member​, you are completely missing the point here, because you haven't read my comment describing a much better solution in which:

  1. There is no deinitialization code.
  2. SP is set by Reset_Handler.

I'm new to this specific area of embedded programming and am currently just using option bytes to switch between the two. I'm working with the L552ZET6Q and am wondering if there were any additional resources you knew of to help me understand this better and implement it on my setup

The Option Bytes are like FLASH, they have a finite life, and also issues if the power fails mid-write. In newer model STM32 the values are latched internally at power-up, not normal resets. This in part has to do with security and attack vectors.

Controlling boot via RAM constants to different loaders, apps, or erasing the app, is perhaps a more consistent method.

RAM on the L5 works much like any other STM32.

Boot Loaders, and Loaders in general are covered in numerous books and papers.

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..

Hence why I'm trying to understand how this solution works and what needs to change to use it on the L5.