cancel
Showing results for 
Search instead for 
Did you mean: 

How to jump to system bootloader from application code on STM32 microcontrollers

B.Montanari
ST Employee

How to jump to system bootloader from application code on STM32 microcontrollers

Introduction


There are many possible ways to access the System Bootloader in STM32 devices and, in this tutorial, we will cover how to easily perform this jump directly from application code for all our families and product series, except for the STM32F0 and some STM32L0 that have an empty check mechanism in place.
To achieve this goal, we highly recommend following two main sources of documentation to ensure an easy and successful jump. Both the microcontroller’s Reference Manual and the Application Note AN2606 contain bootloader details, such as important addresses, supported peripherals and specific requirements to keep in mind when using the STM32 devices Custom or System Bootloader. In the app note it is possible to see the note explaining the effect of the empty check for the mentioned series:
496.png
This article will not cover the workaround of erasing the first page, but the code will have all STM32s addresses in it to facilitate the implementation, in case the reader wants to use it. We do suggest reading this article that explains how to execute code from SRAM.
Assuming you are using a series that allow you to make the jump from the application into system memory, we need to be aware of some important and necessary steps to be performed and some notes to keep in mind:
 

1. How to find System Memory / Bootloader Start Address value:

Refer to AN2606 “Configuration in System Memory Boot Mode” tables. Each device will have a specific System Memory/Bootloader address, and this value must be known to jump to bootloader correctly.
For example, for the STM32H723ZG microcontroller (a quick article showing the code for this series is available here), the Bootloader doesn’t start from the same address as the System Memory, but this is clearly defined in Table 111.
497.png

2. Disable, deinit and clear all peripherals your application has configured:

Including Clock Structure, Systick timer, ISR, Peripheral initializations and GPIO, every peripheral settings must be set to their default states to avoid interruptions when system is in boot mode. That’s why it is of the most importance to deinit all these functions and prevent an interruption to happen without a proper handler.

3. Re-enable Interrupts:

With the registers cleared, the interrupts can be re-enabled without compromising the application while in Bootloader.
 

4. Set the Bootloader Reset Handler address:

Bootloader Reset Handler address = Bootloader address + 4 bytes offset.

5. Set the Main Stack Pointer (MSP) to the values stored at the Bootloader stack.

6. Call a function pointing to the system bootloader to start execution.


Obs.: If your project uses watchdogs (IWDG and or WWDG), set the time base to the higher value possible to avoid a reset from it while in Boot Mode.

2. Development


Considering all the topics mentioned above, there is defined below a general code where you can simply set the MCU used in your application according to the list in the “enum” structure and the function is ready to be used in your project.
This code works by lighting one of the available User LEDs in the NUCLEO-H723ZG board, and then entering in system bootloader mode by calling the JumpToBootloader function.
Please be aware that all the different parts of code are specifically written between /*USER CODE BEGIN*/ regions so the code will not be erased when regenerating the project *.ioc file.
 

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* Set the enumeration of the STM32 Microcontrollers Series*/
enum{C0, F030x8, F030xC, F03xx, F05, F07, F09, F10xx, F105, F107, F10XL, F2, F3, F4, F7, G0, G4, H503, H563, H573, H7x, H7A, H7B, L0, L1, L4, L5, WBA, WBX, WL, U5};

#define MCU			H7x		//Define here the MCU being used
/* USER CODE END PD */
/* USER CODE BEGIN PFP */
void JumpToBootloader(void);
/* USER CODE END PFP */
/* Infinite loop */
	/* USER CODE BEGIN WHILE */
	while (1)
	{
		HAL_GPIO_TogglePin(LED_GREEN_GPIO_Port, LED_GREEN_Pin);
		HAL_Delay(1000);

		JumpToBootloader();

		/* USER CODE END WHILE */
		/* USER CODE BEGIN 3 */
	}
	/* USER CODE END 3 */
/* USER CODE BEGIN 4 */

void JumpToBootloader (void)
{
uint32_t i=0;
void (*SysMemBootJump)(void);


/* Set a vector addressed with STM32 Microcontrollers names */
/* Each vector position contains an address to the boot loader entry point */

	volatile uint32_t BootAddr[33];

	BootAddr[C0]     = 0x1FFF0000;
	BootAddr[F030x8] = 0x1FFFEC00;
	BootAddr[F030xC] = 0x1FFFD800;
	BootAddr[F03xx]  = 0x1FFFEC00;
	BootAddr[F05]    = 0x1FFFEC00;
	BootAddr[F07]    = 0x1FFFC800;
	BootAddr[F09]    = 0x1FFFD800;
	BootAddr[F10xx]  = 0x1FFFF000;
	BootAddr[F105]   = 0x1FFFB000;
	BootAddr[F107]   = 0x1FFFB000;
	BootAddr[F10XL]  = 0x1FFFE000;
	BootAddr[F2]     = 0x1FFF0000;
	BootAddr[F3]     = 0x1FFFD800;
	BootAddr[F4]     = 0x1FFF0000;
	BootAddr[F7]     = 0x1FF00000;
	BootAddr[G0]     = 0x1FFF0000;
	BootAddr[G4]     = 0x1FFF0000;
	BootAddr[H503]   = 0x0BF87000;
	BootAddr[H563]   = 0x0BF97000;
	BootAddr[H573]   = 0x0BF97000;
	BootAddr[H7x]    = 0x1FF09800;
	BootAddr[H7A]    = 0x1FF0A800;
	BootAddr[H7B]    = 0x1FF0A000;
	BootAddr[L0]     = 0x1FF00000;
	BootAddr[L1]     = 0x1FF00000;
	BootAddr[L4]     = 0x1FFF0000;
	BootAddr[L5]     = 0x0BF90000;
	BootAddr[WBA]    = 0x0BF88000;
	BootAddr[WBX]    = 0x1FFF0000;
	BootAddr[WL]     = 0x1FFF0000;
	BootAddr[U5]     = 0x0BF90000;

	/* Disable all interrupts */
	__disable_irq();

	/* Disable Systick timer */
	SysTick->CTRL = 0;

	/* Set the clock to the default state */
	HAL_RCC_DeInit();

	/* Clear Interrupt Enable Register & Interrupt Pending Register */
	for (i=0;i<5;i++)
	{
		NVIC->ICER[i]=0xFFFFFFFF;
		NVIC->ICPR[i]=0xFFFFFFFF;
	}

	/* Re-enable all interrupts */
	__enable_irq();

	/* Set up the jump to boot loader address + 4 */
	SysMemBootJump = (void (*)(void)) (*((uint32_t *) ((BootAddr[MCU] + 4))));

	/* Set the main stack pointer to the boot loader stack */
	__set_MSP(*(uint32_t *)BootAddr[MCU]);

	/* Call the function to jump to boot loader location */
	SysMemBootJump();

	/* Jump is done successfully */
	while (1)
	{
		/* Code should never reach this loop */
	}
}

/* USER CODE END 4 */


There are different useful ways to test if the code worked successfully, two of them are explained below.
 

1. STM32CubeIDE debugging tab.

You can check whether the PC is set to the Flash or Boot address in the debug tab on the top left corner of the debug perspective. At first, the PC starts in Flash address: 0x08000614 (for this particular demo).
498.png
After entering the Bootloader, the PC is now set to an address inside the Boot region: 0x1FF127BC. You can see the tab below after hitting “pause” button.
499.png
 

2. STM32CubeProgrammer Tool


Once within the Bootloader region, we can use the STM32CubeProg to guarantee that the system is working in system boot mode.
For this tutorial, we will use the USB entry point to perform this, but other interfaces available for the given series could be used as well. Just keep in mind that the USB has higher priority than the others, so if the cable is connected it will be the one selected. The AN2606 covers all of this peripherals and needed settings for all series.
Obs.: Please pay attention to the pins specified in the AN2606, because even if the STM32CubeIDE allows the pin reallocation, to enter in bootloader the pins must be the same as in the Application Note.
To execute this over USB, connect the USB cable on the User USB connector. After that:


- In the right corner, select USB instead of ST-LINK
- Refresh the Port to recognize the USB
- Hit “Connect”


501.png
After connecting,  search for the Bootloader start address and you will see something similar to the following image.
505.png
You have now successfully jumped to bootloader from the application. Well Done!
Hope this article was helpful.
 

Comments
gbm
Lead II

The code shown above is full of errors and unnecessary actions.
- There is no need to dynamically create on the stack a variable vector of addresses while only one address, constant and known a priori, is used.
This causes unnecessary usage of Flash for code and RAM for stack data, which could easily lead to stack overflow.
- There is no reason to declare this unnecessary vector as volatile.
- No. of ICER registers differs between various MCUs and should not be hardcoded to 5.
- Copying of constant addresses to ad-hoc variables is unnecessary.
- The notation used for MSP and PC values stored in system ROM is very cryptic.

How about getting rid of most of the code above and replacing the last piece with the following, much shorter and easier to read:

 

 

/* USER CODE BEGIN 4 */
#define BOOT_ADDR	0x1FFFF000	// my MCU boot code base address
#define	MCU_IRQS	70u	// no. of NVIC IRQ inputs

struct boot_vectable_ {
    uint32_t Initial_SP;
    void (*Reset_Handler)(void);
};

#define BOOTVTAB	((struct boot_vectable_ *)BOOT_ADDR)

void JumpToBootloader(void)
{
	/* Disable all interrupts */
	__disable_irq();

	/* Disable Systick timer */
	SysTick->CTRL = 0;

	/* Set the clock to the default state */
	HAL_RCC_DeInit();

	/* Clear Interrupt Enable Register & Interrupt Pending Register */
	for (uint8_t i = 0; i < (MCU_IRQS + 31u) / 32; i++)
	{
		NVIC->ICER[i]=0xFFFFFFFF;
		NVIC->ICPR[i]=0xFFFFFFFF;
	}

	/* Re-enable all interrupts */
	__enable_irq();

	// Set the MSP
	__set_MSP(BOOTVTAB->Initial_SP);

	// Jump to app firmware
	BOOTVTAB->Reset_Handler();
}

/* USER CODE END 4 */

 

 

rsh.1
Associate II

Hi @B.Montanari @gbm ,
Can we flash hex or bin file in hyperterminal like teraterm, without STMCube Programmer,
I was able to flash using STMcube programmer, by making BOOT pin high with inbuilt bootloader Via UART.
Same i tried with teraterm, Its not flashinh.



Thanks 

Tej

B.Montanari
ST Employee

Hi @gbm ! 

Thanks for your comment and code suggestion, I do agree with you and I'll make the proper adjustments. Also, for some series there is an additional step that is currently missing, for example, for the STM32F4 series we need to remap by hand before making the jump, so SYSCFG->MEMRMP = 0x01; should be added, the same is true for G0, SYSCFG->CFGR1 = 0x01;

Thanks again! I'll make the updates shortly

RhSilicon
Lead

Hi, a while ago, I couldn't get the application to work using DFU via USB HOST. When USB Host was added to the application, it didn't work. I'll try again following these tips. Thanks.

Reference: STM32 USB training - 11.3 USB MSC DFU host labs

Stanford
Associate II

@gbm - I agree w/you.  Thanks for the response to this.  Another way of writing the interrupt clearing loop:

 

    for (i = 0; i < sizeof(NVIC->ICER) / sizeof(NVIC->ICER[0]); i++)
    {
      NVIC->ICER[i] = 0xFFFFFFFF;
      NVIC->ICPR[i] = 0xFFFFFFFF;
    }

 

EThom.3
Associate III

I tried the code in this thread, but couldn't quite make it work on the STM32L4 controller that I'm using.

On my board, I use the USB port as a primary communication port. Using the instructions in the original post, and some modifications in the thread, I did get the microcontroller to enter the bootloader, but the STM32CubeProgrammer software couldn't find a USB DFU port.

I had a hunch that it had something to do with the USB port setup. All the other peripherals were disabled by HAL_xxx_DeInit() functions, but the USB port doesn't have HAL functions. Unable to find a USB DeInit() equivalent, I settled for this:

 

  // Reset USB
  USB->CNTR = 0x0003;

 

It made a huge difference.

Edit: Typo

SBaro.11
Associate III

Hi @B.Montanari ,

Thnaks for the post it's very usefull. I have a question, I'm working with Seed Lora E5 module (STM32WLE5JC) and I want to develop and use my owm firmware, but the problem is Seed doesn't expose BOOT0 pin so adfter firmware erase Ican only program my board with ST-LINK.

My question is do you think I can use your code to jump to bootloader ? Beceause I'd like to update my firmware from UART with UART bootloader for example.

The dev board is here https://wiki.seeedstudio.com/LoRa_E5_Dev_Board/ 

usefull explication coming from Seed :

"The "PB13/SPI_SCK/BOOT" pin on the Wio-E5 module is just a normal GPIO, not the "BOOT0" pin of the MCU. This "PB13/SPI_SCK/BOOT" pin is used in the bootloader of the Factory AT firmware, to decide to jump to APP or stay in bootloader(for DFU). The real "BOOT0" pin doesn't pinout to the module, so users need to be careful when developing the low-power applications."

 

Thanks for help

EThom.3
Associate III

@SBaro.11 

I would certainly think so. Your microcontroller is listed in Application Note 2606, so you should be able to upload through USART1 (pins PA9 and 10) or USART2 (pins PA2 and 3).

SBaro.11
Associate III

@EThom.3thanks for your answer. So for you if I've not access to BOOT0 pin it's not a problem for jump to bootloader from software ?

Because in AN2606 for my microcontroller, it's say The STM32WLE5xx/55xx bootloader is activated by applying Pattern 13, here is the pattern 13

SBaro11_0-1695989934243.png

On this pattern we can see BOOT0...

EThom.3
Associate III

@SBaro.11 

Yes, the main point of jumping to the bootloader from from software is to avoid the need to use BOOT0. I have both options in my latest board. For my customer, it is much more practical to activate the bootloader from software.

The microcontrollers I use are activated by other patterns in that table. But that doesn't mean that it's the only way to activate the bootloader.

In my version of AN2606 (rev. 56), your microcontroller is listed in chapter 72. The bootloader code starts from address 0x1FFF0000. So I guess that you can use the code example in this post the same way as I did. You just don't need to disable the USB port, as I needed to do.

SBaro.11
Associate III

Nice thanks a lot @EThom.3 for explanation, I'll try this 😁, before I'll develop my first simple bootloader tjust to flash a LED after that i'll flash it to 0x1FFF0000 address.

Lavanya1
Associate II

Thank you for the post, code works fine for my application. 

 

Mkotb
Associate II

@EThom.3 

where in the code did you exactly reset the USB ? 

EThom.3
Associate III

@Mkotb 

Just after disabling interrupts, before I de-initialize peripherals.

This my take on it: A mix of the original post, suggestions from others, and the tiny bit I figured out myself.

void JumpToBootloader(void)
{
  #define conBootloadAddress 0x1FFF0000

  void (*SysMemBootJump)(void);
  uint8_t i;

  __disable_irq();
  // Reset USB
  USB->CNTR = 0x0003;

  //De-init all peripherals
  HAL_ADC_DeInit(&hadc1);
  HAL_DMA_DeInit(&hdma_i2c2_rx);
  HAL_DMA_DeInit(&hdma_i2c2_tx);
  HAL_I2C_DeInit(&hi2c2);
  HAL_LPTIM_DeInit(&hlptim1);
  HAL_LPTIM_DeInit(&hlptim2);
  HAL_SPI_DeInit(&hspi1);
  HAL_SPI_DeInit(&hspi2);
  HAL_SPI_DeInit(&hspi3);
  HAL_TIM_PWM_DeInit(&htim1);
  HAL_TIM_Base_DeInit(&htim1);
  HAL_TIM_Base_DeInit(&htim6);
  HAL_TIM_PWM_DeInit(&htim15);
  HAL_TIM_Base_DeInit(&htim15);
  HAL_TIM_PWM_DeInit(&htim16);
  HAL_TIM_Base_DeInit(&htim16);
  HAL_UART_DeInit(&huart1);
  HAL_DMA_DeInit(&hdma_usart2_tx);
  HAL_UART_DeInit(&huart2);

  // Disable Systick
  SysTick->CTRL = 0;
  SysTick->LOAD = 0;
  SysTick->VAL = 0;

  // Reset clock to default
  HAL_RCC_DeInit();

  // Clear all interrupt bits
  for (i = 0; i < sizeof(NVIC->ICER) / sizeof(NVIC->ICER[0]); i++)
  {
    NVIC->ICER[i] = 0xFFFFFFFF;
    NVIC->ICPR[i] = 0xFFFFFFFF;
  }

  __enable_irq();

  SysMemBootJump = (void (*)(void)) (*((uint32_t *) (conBootloadAddress + 4)));
  __set_MSP(*(uint32_t *)conBootloadAddress);
  SysMemBootJump();

  while (1); // Just in case...
}

:information:  Unrelated tip: If you are running the RTC on battery power, and wish to keep it running during firmware upload, then do not run HAL_RTC_DeInit(). Yeah, it is kinda obvious, but that didn't keep me from making that mistake the first time.

Mkotb
Associate II

@EThom.3 Unfortunately, it's still doing the same behavior.
I'm sure that it's Jumping well to the system memory address, but the USB is not recognized.
My controller is : STM32L412 

Mkotb
Associate II

@EThom.3 it does work now, it was only a wrong calling from my side.

Lavanya1
Associate II

Could anyone please suggest to me how to do a user bootloader? I'm trying to erase and program the bank1 while being at bank2. First, I swapped from bank1 to bank2 successfully, verified through the cube programmer too. After erasing the bank1, the status of FLASH_CR.MER1=1, EOPIE=1,ERRIE=1,OPTLOCK=1. FLASH_SR.EOP=1. It's entering into HAL_FLASH_Program_IT(uint32_t TypeProgram, uint32_t Address, uint64_t Data) but exits after HAL_Lock(&pFlash) function. FYI: Custom board using STM32L4P5 controller. 

B.Montanari
ST Employee

Hi @Lavanya1 , there are several ways to implement a user bootloader (the interface to communicate can really vary) and a few fundamentals that are interesting to revisit (you can skip if they are already known to you).

I'd recommend checking this video series for the fundamentals>  How to Create a Super Simple Bootloader - YouTube  and if you want to use an equivalent approach as the native one, I'd suggest getting to know the open bootloader, which is basically a user code implementation of the native system bootloader this article talks briefly about> STMicroelectronics/stm32-mw-openbl: Provides the Open Bootloader library, part of the STM32Cube MCU Component "middleware", for all STM32xx series. (github.com). We provide a video series detailing the OpenBL as well> https://www.youtube.com/watch?v=_gejWsAn5kg

We also have 2 expansion packages covering different approaches, in case you want to look at them as well:

https://www.st.com/en/embedded-software/x-cube-iap-usart.html

https://www.st.com/en/embedded-software/x-cube-iap-sd.html

There is a package related to secure boot and secure firmware update as well, in case your application goes to that route, take a look at this package: https://www.st.com/en/embedded-software/x-cube-sbsfu.html 

I've tried to be generic in the comments, mostly because I believe you should issue an online support ticket (Online Support (st.com) ), so one of our FAEs can help you addressing the particular implementation and specific questions.

Hope this helps

Best Regards

Konami
Senior

Can you expand on when we need to "remap by hand before making the jump, so SYSCFG->MEMRMP = 0x01" and why this is needed?

APrim.2
Associate II

Hello all,

I'm using the STM32F446RE  nucleo board with STM32CubeIde and I would like to use the system bootloader by calling it from within the software.

I implemented the code here suggested by some of you but it doesn't work.

When the code reach the function to jump to the bootloader (BOOTVTAB->Reset_Handler();), the jump doesn't happen but only a reset and the same code that called the bootloader (not the bootloader code) is executed.

Moreover, the DFU device is not enumerated by the PC and the related DFU driver isn't loaded by the PC (when I force the board by the BOOT pin to VCC and reset it all works perfect).

I tried several things also with or without initializing the USB, but nothing worked for me.

Here my code:

 

int main(void)
{
  /* USER CODE BEGIN 1 */
 
  /* USER CODE END 1 */
 
  /* MCU Configuration--------------------------------------------------------*/
 
  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();
 
  /* USER CODE BEGIN Init */
 
  /* USER CODE END Init */
 
  /* Configure the system clock */
  SystemClock_Config();
 
  /* USER CODE BEGIN SysInit */
 
  /* USER CODE END SysInit */
 
  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_USB_DEVICE_Init();
  /* USER CODE BEGIN 2 */
 
  /* USER CODE END 2 */
 
  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
     for (int i=0; i<5; i++)
     {
        HAL_GPIO_TogglePin(LD2_GPIO_Port, LD2_Pin);
        HAL_Delay(1000);
     }
 
  JumpToBootloader();
    /* USER CODE END WHILE */
 
    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}
 
/* USER CODE BEGIN 4 */
#define BOOT_ADDR 0x1FFF0000 // my MCU boot code base address
 
struct boot_vectable_ {
    uint32_t Initial_SP;
    void (*Reset_Handler)(void);
};
 
#define BOOTVTAB ((struct boot_vectable_ *)BOOT_ADDR)
 
int16_t i;
 
void JumpToBootloader(void)
{
// Disable all interrupts
__disable_irq();
 
// Disable Systick timer
SysTick->CTRL = 0;
 
// Set the clock to the default state
HAL_RCC_DeInit();
 
    for (i = 0; i < sizeof(NVIC->ICER) / sizeof(NVIC->ICER[0]); i++)
    {
      NVIC->ICER[i] = 0xFFFFFFFF;
      NVIC->ICPR[i] = 0xFFFFFFFF;
    }
 
    // REMAP
    SYSCFG->MEMRMP = 0x01;
 
// Re-enable all interrupts
__enable_irq();
 
// Set the MSP
__set_MSP(BOOTVTAB->Initial_SP);
 
// Jump to app firmware
BOOTVTAB->Reset_Handler();
}
 
/* USER CODE END 4 */
 
PS: Here in this code it is present the USB but not used, in the final application it will be present to check for a command to jump to the bootloader.

Please help me.

Thanks to you all.

EThom.3
Associate III

@APrim.2

I would find it interesting to see how the compiler interprets this line:

BOOTVTAB->Reset_Handler();

I don't think that it does what you expect it to do, even though that part of your code seems to be similar to the example from @gbm .

APrim.2
Associate II

@EThom.3 

BOOTVTAB->Reset_Handler();

This instruction should point to Initial_SP + 4 bytes (0x1FFF0000 + 4 bytes) that should be correct.

Anyway I tried to substitute the last two instructions with :

void (*SysMemBootJump)(void);

// Set up the jump to boot loader address + 4
SysMemBootJump = (void (*)(void)) (*((uint32_t *) ((BOOT_ADDR + 4))));
 
// Set the main stack pointer to the boot loader stack
__set_MSP(*(uint32_t *)BOOT_ADDR);
 
// Call the function to jump to boot loader location
SysMemBootJump();
 
but the result is the same
EThom.3
Associate III

@APrim.2 

When I am in doubt about what a piece of code actually does, I have a close look at the relevant contents of the .lss file. However, in your case, I'm not sure that would help solve the problem.

I don't think that I can help you. My best guess is that it has something to do with resetting or enabling / disabling the USB port, but I am in no way certain. It might also be a timing issue, that is, the order of things to be carried out. For one, I have no clue when to do the remapping in order not to mess things up.

Sadly, due to the somewhat cryptic nature of the STM32 datasheets and manuals, I frequently need to guess, or see what others have figured out.

I'm sorry that I cannot be more helpful.

Lavanya1
Associate II

@APrim.2 

Connect the board through the Cube Programmer by forcing the BOOT pin to VCC and check for the Option Bytes. For the System Bootloader, BFB2, DBANK/DB1M, and nBoot0 are to be unchecked. The rest of the options are checked. Now you remove the pull-up of Boot0 and try that program, it will enter into boot mode. 

EThom.3
Associate III

@Lavanya1 

Thanks.

The vast amount of nooks and crannies in the STM32s never ceases to amaze me. If one was to make a complete, graphical model of such a microcontroller, at least six dimensions would be necessary...

I just searched for this in the docs, and did find a hint in AN2606, page 29. However, the options that you and AN2606 refer to, don't seem to be mentioned at all in the reference manual or the datasheet. Nor have I found an application note or technical note where this is explained. Do you have any idea where this is documented?

APrim.2
Associate II

@Lavanya1 

Thanks for your suggestion, I will give it a try.

EThom.3
Associate III

@Lavanya1 

Kudos to you, by the way. Manually, because the "Kudos" button isn't available in this thread.

APrim.2
Associate II

@Lavanya1 

The bits you mentioned it doesn't exist on STM32F446RE.

Lavanya1
Associate II

@APrim.2 

Lavanya1_0-1700551879376.png

I'm really sorry. Without checking the reference manual I suggested that one. As most of the STM32 series devices require option bytes changes. Here, what you missed is pulling up the boot0 pin. The condition to enter into system memory is Boot0 pin should be pulled up. 

So, whenever you're trying to jump to system memory, need to pull up the Boot0 pin by software. Please try this and update me. Find the correct GPIO port and pin, and do a pull-up like this. Please add this pull-up condition here.

pull up boot0 pin;

JumpToBootloader();

/* USER CODE END WHILE */

Sample for pull-up PH3 pin:

GPIOH->MODER &= ~(GPIO_MODER_MODE3_1 | GPIO_MODER_MODE3_0);
GPIOH->PUPDR &= ~(GPIO_PUPDR_PUPD3_1 | GPIO_PUPDR_PUPD3_0); 
GPIOH->PUPDR |=  GPIO_PUPDR_PUPD3_0;
APrim.2
Associate II

@Lavanya1 

on the STM32F446RE the boot0 pin is not shared with some port pin so I think there is not the possibility to enable the internal pull up.at least I did not found it, I searched also on the graphical configuration tool but there isn't any possibility to enable the boot0 pull up.

Lavanya1
Associate II

@APrim.2 

Refer RM-0390 section 8.2.1. Using SYSCFG_MEMRMP register you can boot from desired memory location. 

Screenshot_20231122-075300.png

EThom.3
Associate III

@Lavanya1 

Yeah, that's exactly what @APrim.2 is trying to do.

 // REMAP
    SYSCFG->MEMRMP = 0x01;
 
// Re-enable all interrupts
__enable_irq();
 
// Set the MSP
__set_MSP(BOOTVTAB->Initial_SP);
 
// Jump to app firmware
BOOTVTAB->Reset_Handler();
}

 Could it be a problem with the order of events? The manual is more than a little vague on this.

gbm
Lead II

For the compiler, there is only one way to interpret this statement. What's your doubt?

dave4444
Associate

Was trying this on a G0 MCU and ran into problems.  Tracked it down to my calling of JumpToBootloader() from within a FreeRTOS task context.  This has the result of going into the bootloader while SP is using PSP not MSP and the bootloader ends up corrupting the stack in it's initialization.

If you do not exit task mode so MSP is used you must also set PSP prior to jumping to the bootloader.

Adding a __set_PSP(); along with the __set_MSP(); will allow you to call this from any context.

Without setting PSP, the bootloader will corrupt it's stack and cause an exception resulting in the appearance of just executing your normal flashed code.

TDJ
Senior III

@B.Montanari I need to build a bootloader which would allow end user to update STM32U5A9 firmware from pendrive, preferably using encrypted and signed SFI package conveniently created with CubeProgrammer.
How do I go about it? Where to start? Are there any examples available?
Is there any open source software for decoding/decrypting SFI packages available? Probably I can figure it out and write from scratch based on AN4992 and AN5054, but maybe some solution is already available.
I would appreciate any hints/pointers. After all, this task seems to be common.

Reading file from pendrive is out of scope. This part I know how to handle.

Update: This question has been answered here.

pvk
Associate II

Hi @B.Montanari 

This is a great thread for a use case that I think is needed for many many deployment scenarios.

Is the current state of the code example available somewhere on the ST site ?

It would be great if this feature is an option to enable/configure in STM32CubeIDE or any other ST IDE.

/Per

 

 

 

kfrosh
Associate

Posting in case others have run into this. Also, code provided by ST above seems incorrect based on findings below. @B.Montanari would be good to have clarification here.

We just ran into an issue where some of our devices (~10%) were locking up before jumping to the application. This was root caused to the location of disabling systick. If disabling systick was moved AFTER HAL_RCC_DeInit(), the problem devices no longer locked up and were able to jump to the main application.

Inside of HAL_RCC_DeInit(), there are multiple locations where a while loop is protected by Systick timeout. If Systick is disabled before this function, this obviously has no effect.

The source code provided by ST above disables Systick before calling HAL_RCC_DeInit, which seems incorrect based on our findings.

 

Original:

  // Continue from JumpToBootloader()   

  // Disable Systick
  SysTick->CTRL = 0;
  SysTick->LOAD = 0;
  SysTick->VAL = 0;

  // Reset clock to default
  HAL_RCC_DeInit();

  // Clear all interrupt bits
  for (i = 0; i < sizeof(NVIC->ICER) / sizeof(NVIC->ICER[0]); i++)
  {
    NVIC->ICER[i] = 0xFFFFFFFF;
    NVIC->ICPR[i] = 0xFFFFFFFF;
  }

  __enable_irq();

  SysMemBootJump = (void (*)(void)) (*((uint32_t *) (conBootloadAddress + 4)));
  __set_MSP(*(uint32_t *)conBootloadAddress);
  SysMemBootJump();

  while (1); // Just in case...
}

 

Updated:

  // Continue from JumpToBootloader()   

  // Reset clock to default
  HAL_RCC_DeInit();

  // IMPORTANT! Disable Systick AFTER HAL_RCC_Deinit
  // Some devices were locking up inside of HAL_RCC_DeInit 
  // Disable Systick
  SysTick->CTRL = 0;
  SysTick->LOAD = 0;
  SysTick->VAL = 0;

  // Clear all interrupt bits
  for (i = 0; i < sizeof(NVIC->ICER) / sizeof(NVIC->ICER[0]); i++)
  {
    NVIC->ICER[i] = 0xFFFFFFFF;
    NVIC->ICPR[i] = 0xFFFFFFFF;
  }

  __enable_irq();

  SysMemBootJump = (void (*)(void)) (*((uint32_t *) (conBootloadAddress + 4)));
  __set_MSP(*(uint32_t *)conBootloadAddress);
  SysMemBootJump();

  while (1); // Just in case...
}

 

HAL_RCC_DeInit while loop line 15

HAL_StatusTypeDef HAL_RCC_DeInit(void)
{
  uint32_t tickstart;

  /* Get Start Tick*/
  tickstart = HAL_GetTick();

  /* MSI PLL OFF */
  LL_RCC_MSI_DisablePLLMode();

  /* Set MSION bit */
  LL_RCC_MSI_Enable();

  /* Wait till MSI is ready */
  while (LL_RCC_MSI_IsReady() == 0U)
  {
    // Systick timeout detect timeout
    if ((HAL_GetTick() - tickstart) > MSI_TIMEOUT_VALUE)
    {
      return HAL_TIMEOUT;
    }
  }

 

 

Francois Vignon
Associate II

Hello.

I have an issue to access STM32G4 (G431, G474) boot-loader from my app.

The simplest program exhibit this issue is

 

 

 

Vectors: /* to be compiled at 0x0800 0000 */
.word (0x20000000 + 0x1000) /* 0x0000 top stack @ RAM_Base + 0x1000 */
Thumb_Vector Hard_Reset_Handler /* 0x0004 reset vector */

Hard_Reset_Handler:
/* part 1 : to count number of reset */
ldr r4,=0x20008000 /* a RAM address not acceded by bootloader */
ldr r0,[r4]
adds r0,#1
str r0,[r4]

/* part 2 : to jump to bootloader */
ldr r4,=0x1fff0000 /* from AN2606 doc, checked: this address contains SP and BOOT */ 
ldr r1,[r4,#0]
mov r13,r1 /* init sp */
ldr r1,[r4,#4]
mov pc,r1

 

When I run this simple test, RAM location 0x20008000 is well incremented at each reset (value checked with debugger) but the boot-loader is never called and reset occurs permanently ... maybe 1000 times per seconds.
I don't reset anything in RCC etc. because nothing is initiated 😉

An explanation ?

 

Uwe Bonnes
Principal II

Francois, I miss SYSCFG->CFGR1 = 1! And is  "mov r13,r1" good for setting MSP? What about using MSR, reg, MSP?

Francois Vignon
Associate II

Thanks for your answer, Uwe.

I think you mean SYSCFG->MEMRMP = 1 (because it is no usable bit 0 in CFGR1).

I see bit 0 of MEMRMP is set with BOOT0 pin (and BOOT1 option bit) at reset.

after adding the following code

    ldr r4,=0x40010000 /* SYSCFG */
    mov r1,#1
    str r1,[r4,#0] /* SYSCFG_MEMRMP */

All is working like a charm 🙂

Thanks a lot for the solution.

mcasoni
Associate

@B.Montanari 

in our new STM32G474 product we're planning of using a single Flash bank configuration. At the same time we'd like to maintain the possibility of flashing/erasing internal Flash pages runtime. Without the dual bank configuration, and considering we must carefully manage flash drivers execution timing, Option Bytes and interrupts, we initially considered patching the stm32g4xx_hal_flash library and moving the sensitive parts to ram (ramCode). With "sensitive" I obviously mean those moments flash writing or erasing processes become active, and fetching code from the same (single bank) memory is forbidden. A quite usual approach.

 

But recently I reminded that several years ago with a different MCU I asked the bootloader source code, and its memory map. That bootloader was stored in a separate (OTP) memory  mapped and visible in the linear memory map. Knowing the low-level entry points and signature of a few key Flash functions we managed to write and erase all flash pages from the application without any extra code.

 

So, and I get to the point, in your interesting Blog you consider running the entire bootloader, but in our case, if the bootloader structure allows it, we only need entry point and signature of a few functions (Write(), Erase()). Probably it's not even relevant if the "empty check" mechanism is in place, because I guess this will be only implemented once, at the moment the entire bootloader is started.

 

1. Do you think it may work? In case, since the AN2606 does not cover the topic, is ST available disclosing the  necessary internal bootloader details? Should I ask the support of our distributors?

2. Apart the ramCode option you also discussed in another interesting blog, do you see another solution?

3. Is ST considering of releasing soon a CUBE stm32_flash library also able to support single bank configurations?

 

 

tall_guy
Associate

--Removed--

Version history
Last update:
‎2024-03-07 03:25 AM
Updated by:
Contributors