Skip to main content
Visitor II
July 13, 2018
Solved

STM32H743: How to start the system boot loader via software

  • July 13, 2018
  • 5 replies
  • 8917 views
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

    This topic has been closed for replies.
    Best answer by Tesla DeLorean
    Posted on July 14, 2018 at 05:17

    0x1FF09800

    5 replies

    Graduate II
    July 14, 2018
    Posted on July 14, 2018 at 05:17

    0x1FF09800

    Graduate II
    July 14, 2018

    Original Posted on July 14, 2018 at 07:33

    Definitely gets into ROM

    startup.s (Keil)
     
    Reset_Handler PROC
     EXPORT Reset_Handler [WEAK]
     IMPORT SystemInit
     IMPORT __main
     
    ; STM32H7 Boot-to-ROM sourcer32@gmail.com 13-July-2018
     
     LDR R0, =0x2001FFFC ; Safe End-of-RAM location (for demonstration)
     LDR R1, =0xBEEFBEEF ; Special Key
     LDR R2, [R0, #0]
     CMP R1, R2
     BNE NotDFuse
     
     STR R0, [R0, #0] ; Invalidate Key
     LDR R1, =0xE000ED00 ; SCB
     LDR R0, =0x1FF09800 ; ROM BASE
     STR R0, [R1, #8] ; VTOR
     LDR SP,[R0, #0] ; ROM Stack Pointer
     LDR R0,[R0, #4] ; ROM Program Counter
     BX R0
     
    NotDFuse
     
     LDR R0, =SystemInit
     BLX R0
     
     LDR R0, =__main
     BX R0
     
     ENDP Reset_Handler
     
    main.c
     
    void DFUBoot(void)
    {
     *((uint32_t *)0x2001FFFC) = 0xBEEFBEEF; // Special Key to End-of-RAM
     
     SCB_CleanDCache();
     NVIC_SystemReset();
    }

    Formatting broken in forum transition, edited to try and fix that

    Technical Moderator
    January 21, 2019

    Hello,

    In STM32H7, the base address of the system memory is different from the entry point of the bootloader.

    Thus, in order to jump to the bootloader, address "0x1FF09800" should be used instead of "0x1FFF0000".

    You may have a look on FAQ "Jump to Bootloader from application on STM32H7 devices".

    Link:

    https://community.st.com/s/article/STM32H7-bootloader-jump-from-application

    Khouloud.

    Visitor II
    January 21, 2019

    Thanks for pointing the FAQ page out to me. While the first answer was already good enough for my purpose, it is good to see some sample code which includes the proper deactivation procedure.

    Werner

    Visitor II
    January 21, 2019

    Indeed the caches and MPU should be considered. In my bootloader I simply do not activate any of these (for exactly the above reason) but in general you're right. You may be right about the interrupt context and protected mode (at least for the later I hope you are!).

    Visitor II
    March 10, 2020

    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

    Graduate
    August 6, 2022

    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.