cancel
Showing results for 
Search instead for 
Did you mean: 

STM32H743: How to start the system boot loader via software

Werner Mann
Associate II
Posted on July 14, 2018 at 01:29

Hi,

what is the procedure to start the system bootloader in the STM32H743 via software. Even newest AN2606 is not clear on this, since none of the mentioned methods fit the STM32H7 registers etc.

What I mean:

For the F4 the system memory needs to be remapped to 0x00000000, load MSP from value in address 0x00000000 and then a jump to the address stored in address 0x0000004 is done. This works for an F4.

On the F7 (also a Cortex-M7) some option bytes have to be configured correctly and then it is basically MSP from 0x1ff0000 and jump to value of 0x1ff00004.

But what is the procedure on the H7? The system memory is located like on the F7 on 0x1ff00000 but it is not readable, and because of that no loading of MSP and reading the correct jump address. There is no system memory remapping bit in the SYSCFG register which is what AN2606 suggests to use, the only exception mentioned is for the F7. I think there is either no way to achieve this on the STM32H7 or another not yet documented way. In any case, AN2606 is lacking sufficient information, probably forgotten when adding the H7.

Just for reference: When booting with the BOOT pin high, DFU works, no problems here. It is a Rev. Y

Anyone any  idea what works?

Werner

16 REPLIES 16

Hi @Community member​ ,

You're right! The function is considered as a basic set up to allow the jump to bootloader from main application:  CPU is considered in Privileged thread mode and using main stack as a stack pointer.

It does not take into consideration the CPU handler mode nor the MPU protection.

Khouloud.

I believe I'm running into the hypothetical problem you've described. I am executing my own "Jump to Bootloader" code to run the USB DFU on an STM32F405. When executing "Jump to Bootloader" from a bare-metal application, it jumps to the bootloader as expected. When executing from a FreeRTOS version of my application, it gets stuck in the HardFault_Handler. Based on your comment I tried disabling the MPU preemptively, but it did not seem to work. Unfortunately, I wasn't able to determine what provisions I need to take in order for "Jump to Bootloader" to work with an RTOS. Any tips would be appreciated.

I've previously posted the Reset method I used for F2/F4 parts to get them into DFU mode. One of the tricks there was mapping the ROM properly.

https://community.st.com/s/global-search/0xDEADBEEF%20F2%2FF4

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

Thanks, this seemed to work out. Based on your method I'm assuming the system reset automatically returns the peripheral/interrupt registers to their default states while leaving SRAM values intact. Is this correct?

Reset doesn't clear RAM. Anything connected to the async reset will get brought to default states.

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

Hi!

I have basically two questions:

1. How can I jump to user code after exiting DFU on a dual core chip?

I am using NUCLEO-H745ZI-Q board and I can reset to the CM7 core by setting address 0x8000000 and than initiate a 0 download or to CM4 core if I set address 0x81000000. But cannot really start up both cores? Or is there a way to start from one core the other?

So basically my aim is to connect BOOT0 pin to high and the controller always comes up as a DFU device when board starts up and when the USB host is up it can update the firmware if neccessary and than run the user code on both cores?

2. If I am using CM7 core with USB stuffs than I need to disable USB interrupts and reset them to work properly(regarding to the DFU manual):

"When performing a jump from the bootloader to a loaded application code using the USB IP,

the application has to disable all pending USB interrupts and reset the core before enabling

interrupts. Otherwise, a pending interrupt (issued from the bootloader code) may interfere

with the user code and cause a functional failure. This procedure is not needed after exiting

the system memory boot mode"

I am doing this in the beginning of USB_OTG_FS_PCD_Init ()

 hpcd_USB_OTG_FS.Instance = USB_OTG_FS;

 USB_DisableGlobalInt(hpcd_USB_OTG_FS.Instance); //disable USB global interrupt

 USB_CoreReset_User(hpcd_USB_OTG_FS.Instance); //copy of restet USB core code fom LL libraries

but it only works sometimes only once but generally doesnt work. Either my user code USB deivce comes up or the USB bootloader and than I try to reset to bootloader and the USB doesnt come up at all.. and after that I need to power cycle the whole demo board to get any USB back, reset pin doesnt work.. quite odd, probaly USB is getting in a messy state somehow..

Thanks,

Beni

Zed
Associate II

For those whom other solutions didn't help there's clear instructions from Arm which, by the way, helped me a lot.

Direct link to the article: https://developer.arm.com/documentation/ka002218/latest

...When the flash content was updated or is already up-to-date, the bootloader jumps to the user application. This requires a number of steps before the user application can be executed. This is usually done by calling a function such as the example below, BootJump(), which has the aim to basically restore reset conditions for the user application:

static void BootJump( uint32_t *Address )
{

Make sure, the CPU is in privileged mode.

  if( CONTROL_nPRIV_Msk & __get_CONTROL( ) )
  {  /* not in privileged mode */
       EnablePrivilegedMode( ) ;
  }

The function EnablePrivilegedMode( ) triggers a SVC, and enters handler mode (which can only run in privileged mode). The nPRIV bit in the CONTROL register is cleared which can only be done in privileged mode. See ARM: How to write an SVC function about implementing SVC functions.

Disable all enabled interrupts in NVIC.

NVIC->ICER[ 0 ] = 0xFFFFFFFF ;
NVIC->ICER[ 1 ] = 0xFFFFFFFF ;
NVIC->ICER[ 2 ] = 0xFFFFFFFF ;
NVIC->ICER[ 3 ] = 0xFFFFFFFF ;
NVIC->ICER[ 4 ] = 0xFFFFFFFF ;
NVIC->ICER[ 5 ] = 0xFFFFFFFF ;
NVIC->ICER[ 6 ] = 0xFFFFFFFF ;
NVIC->ICER[ 7 ] = 0xFFFFFFFF ;

Disable all enabled peripherals which might generate interrupt requests, and clear all pending interrupt flags in those peripherals. Because this is device-specific, refer to the device datasheet for the proper way to clear these peripheral interrupts.

Clear all pending interrupt requests in NVIC.

NVIC->ICPR[ 0 ] = 0xFFFFFFFF ;
NVIC->ICPR[ 1 ] = 0xFFFFFFFF ;
NVIC->ICPR[ 2 ] = 0xFFFFFFFF ;
NVIC->ICPR[ 3 ] = 0xFFFFFFFF ;
NVIC->ICPR[ 4 ] = 0xFFFFFFFF ;
NVIC->ICPR[ 5 ] = 0xFFFFFFFF ;
NVIC->ICPR[ 6 ] = 0xFFFFFFFF ;
NVIC->ICPR[ 7 ] = 0xFFFFFFFF ;

Disable SysTick and clear its exception pending bit, if it is used in the bootloader, e. g. by the RTX.

SysTick->CTRL = 0 ;
SCB->ICSR |= SCB_ICSR_PENDSTCLR_Msk ;

Disable individual fault handlers if the bootloader used them.

SCB->SHCSR &= ~( SCB_SHCSR_USGFAULTENA_Msk | \ 
                 SCB_SHCSR_BUSFAULTENA_Msk | \ 
                 SCB_SHCSR_MEMFAULTENA_Msk ) ;

Activate the MSP, if the core is found to currently run with the PSP. As the compiler might still use the stack, the PSP needs to be copied to the MSP before this.

if( CONTROL_SPSEL_Msk & __get_CONTROL( ) )
{  /* MSP is not active */
  __set_MSP( __get_PSP( ) ) ;
  __set_CONTROL( __get_CONTROL( ) & ~CONTROL_SPSEL_Msk ) ;
}

Load the vector table address of the user application into SCB->VTOR register. Make sure the address meets the alignment requirements.

SCB->VTOR = ( uint32_t )Address ;

A few device families, like the NXP 4300 series, will also have a shadow pointer to the VTOR, which also needs to be updated with the new address. Review the device datasheet to see if one exists.

The final part is to set the MSP to the value found in the user application vector table and then load the PC with the reset vector value of the user application. This can't be done in C, as it is always possible, that the compiler uses the current SP. But that would be gone after setting the new MSP. So, a call to a small assembler function is done.

BootJumpASM( Address[ 0 ], Address[ 1 ] ) ;

The program flow will never return to this point.

This is the end of the function BootJump( ).

}

The BootJumpASM( ) helper function can also be implemented with the compiler. However, writing assembler is something compiler-specific. So the implementation for the BootJumpASM( ) function looks different for each compiler.

Arm Compiler 5

__asm __attribute__( ( noreturn ) ) void BootJumpASM( uint32_t SP, uint32_t RH )
{
  MSR      MSP,r0
  BX       r1
}

Arm Compiler 6

__attribute__( ( naked, noreturn ) ) void BootJumpASM( uint32_t SP, uint32_t RH )
{
  __asm("MSR      MSP,r0");
  __asm("BX       r1");
}

Define the starting address for the main application, and call the jump function with the address as a parameter:

#define USER_APPLICATION_BASE_ADDRESS 0x00008000  /* as example */
 
BootJump( ( uint32_t * )USER_APPLICATION_BASE_ADDRESS ) ;

Now, flash the device with both applications, to debug the jump. Use the Disassembly dialog to confirm the memory address of the instructions.