cancel
Showing results for 
Search instead for 
Did you mean: 

STM32H7 Bootloader jumps as clean as possible to application

Jack3
Senior II

Hi,

for this experiment, I use a NUCLEO-H743ZI (with 'Y' version MCU).

I have created two different versions of an application and two versions of a bootloader.

They are just very simple.

One (let's call it version A) is just a blinking a LED and printing on a UART.

The other (version B) is likewise, but also drives an OLED display.

The bootloader prints 'b' to the serial port every 500mS, the app prints 'a' to the serial port every 500mS.

When pressing the user button during reset, the bootloader enters a while loop (printing b's),

and only jumps to the application if the user button is released and pressed again.

If the user button is released during a reset the bootloader jumps to the application directly.

Bootloader A works fine with application A, and B with B.

Somehow I cannot let bootloader A have app B started, or let bootloader B have app A started.

I thought these could be independent programs.

I searched and read a lot about this topic, but I can't seem to find a solution.

What can I check?

My current JumpToApplication program in the bootloader:

void Bootloader_JumpToApplication(uint32_t AppAddress)
{
  typedef void (*pFunction)(void);
 
  /* Set the address of the entry point to application */
  volatile uint32_t BootAddr = AppAddress;
 
  printmsg("BL_DEBUG_MSG: Bootloader_JumpToApplication\n");
 
  uint32_t MSPValue = *(__IO uint32_t*)BootAddr;
  printmsg("BL_DEBUG_MSG: MSP value: 0x%08x\n", MSPValue);
 
  uint32_t  JumpAddress = *(__IO uint32_t*)(BootAddr + 4);
 
  pFunction JumpToApplication = (pFunction)JumpAddress;
  printmsg("BL_DEBUG_MSG: JumpAddress: 0x%08x\n", JumpAddress);
 
  f_mount(NULL, SDPath, 1);
  HAL_SD_MspDeInit(&hsd1);
  FATFS_UnLinkDriver(SDPath);
 
  __HAL_RCC_USART1_FORCE_RESET();
  __HAL_RCC_USART3_FORCE_RESET();
  HAL_Delay(5);
  __HAL_RCC_USART1_RELEASE_RESET();
  __HAL_RCC_USART3_RELEASE_RESET();
  HAL_Delay(5);
 
  HAL_SPI_MspDeInit(&hspi1);
  HAL_SPI_MspDeInit(&hspi2);
  HAL_SPI_MspDeInit(&hspi4);
  HAL_ETH_MspDeInit(&heth);
  HAL_UART_MspDeInit(&huart3);
 
 
//  HAL_SD_MspDeInit(&hsd1);
 
  SCB_DisableDCache();
  SCB_DisableICache();
  HAL_MPU_Disable();
  HAL_SuspendTick();
  /* Make sure you disable the RCC in your bootloader code *before* you call the application */
  /* Set the clock to the default state */
  HAL_RCC_DeInit();
  // Modify the HAL_DeInit function:
  //  HAL_StatusTypeDef HAL_DeInit(void)
  //  {
  //    /* Reset of all peripherals */
  //  //  __HAL_RCC_AHB3_FORCE_RESET();     // This causes a CPU reset
  //    RCC->AHB3RSTR &= 0x7fffffff;        // have bit 31 set to 0
  //    __HAL_RCC_AHB3_RELEASE_RESET();
  HAL_DeInit();
//  HAL_SuspendTick();
 
  /* Disable all interrupts */
  __disable_irq();
 
  // Disable SysTick and clear its exception pending bit, if it is used in the bootloader, e. g. by the RTX.
  /* Disable Systick timer */
  SysTick->CTRL = 0;
  SysTick->LOAD = 0;
  SysTick->VAL  = 0;
 
  /* Clear Interrupt Enable Register & Interrupt Pending Register */
  for (uint8_t i = 0; i < 8; i++)
  {
    // Disable all enabled interrupts in NVIC
    NVIC->ICER[i] = 0xFFFFFFFF;
    // Clear all pending interrupt requests in NVIC
    NVIC->ICPR[i] = 0xFFFFFFFF;
  }
 
  /* Re-enable all interrupts */
  __enable_irq();
 
  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.
  if( CONTROL_SPSEL_Msk & __get_CONTROL( ) )
  {  /* MSP is not active */
    __set_CONTROL( __get_CONTROL( ) & ~CONTROL_SPSEL_Msk ) ;
  }
 
  __HAL_RCC_SYSCFG_CLK_ENABLE();
 
  SCB->VTOR = BootAddr;
 
  SystemClock_Config();
  __enable_irq();
  HAL_ResumeTick();
 
  // Configure the Main Stack Pointer. This function comes from CMSIS.
  __set_MSP(*(__IO uint32_t*) BootAddr);
 
  __set_CONTROL(0);
 
  Boot_Delay(200);
 
  JumpToApplication();
}

Thanks for helping!

6 REPLIES 6

>>What can I check?

Review the code generated? Following the failing branch down the rabbit-hole, and see where it goes, and the mechanism by which it fails.

If JumpToApplication is a stacked variable, moving the stack could be an issue.

Interrupts (SysTick) will have entirely different ideas about RAM usage, and no memory on the far side initialized properly (pointers/instances).

Running code in BootAddr context, not your own.

HAL_ResumeTick()? SystemClock_Config()? Why?

Use a delay based on processor ticks, not SysTick.

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

Hi Clive,

thank you very much! The app seems to hang on HAL_Delay, which uses SysTick.

I can use a delay based on processor ticks, but don't I need Systick for HAL to be working anyway, like the SDMMC library?

Tomorrow I will try to get closer to what is causing the issue.

I will check the JumpToApplication variable too.

What exactly to you mean with "running code in BootAddr context, not your own"?

Did I do something wrong? I can post the source on github tomorrow, if that helps.

Btw, for the app, I only changed the FLASH location in the linker (ld) file so the app (its vector table) was located at 0x08080000, instead of 0x0800000. Were there more changes required?

It would be my ultimate goal to make a bootloader, which can load and flash some app of choice (from SD card) and jump to it. But it seems picky on my app.

If I use that app, add the JumpToApplication function and make it a bootloader, it seems it better starts the app it was based on. But then I have stuff for the app in it, which I don't need for the bootloader itself. I hope you understand the problem, I try to describe.

Rgds,

Jack.

>>What exactly to you mean with "running code in BootAddr context, not your own"?

SCB->VTOR = BootAddr; <<< This, you're on the wrong side of the looking glass, fine for A=A or B=B

>>I can use a delay based on processor ticks, but don't I need Systick for HAL to be working anyway, like the SDMMC library?

Not saying you can't do that, just saying you can't flip context and expect uwTick for A and B to reside in the same/interchangeable spot.

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

>>Btw, for the app, I only changed the FLASH location in the linker (ld) file so the app (its vector table) was located at 0x08080000, instead of 0x0800000. Were there more changes required?

If SystemInit() set SCB->VTOR to a linker symbol that would be fine, ST has some other stupidity.

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

Hi Clive, thank you so much for bearing with me. For both apps, I changed SystemInit:

Original:

SCB->VTOR = FLASH_BANK1_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal FLASH */

Modified to:

SCB->VTOR = 0x08080000; /* Vector Table Relocation in Internal FLASH */

Now, I can use both bootloaders (1 & 2) can they can jump to either app (1 & 2)!

I will dive a bit more into this, to see if this was all that was needed, or there may be other pitfalls.

Hi Clive, I try to learn from you. I don't exactly understand how translate "Not saying you can't do that, just saying you can't flip context and expect uwTick for A and B to reside in the same/interchangeable spot." to point to the problem in the code, right now.

What am I missing?

This is what seems to work well (using STM32CubeMX, TrueStudio, and HAL) for Nucleo-H743ZI ('Y' version MCU):

In system_stm32h7xx.c:

Original and for the bootloader:

#define VECT_TAB_OFFSET 0x00000000UL /*!< Vector Table base offset field.
                   This value must be a multiple of 0x200. */

For the application:

#define VECT_TAB_OFFSET 0x00080000UL

In STM32H743ZI_FLASH.ld:

Original:

FLASH (rx)   : ORIGIN = 0x8000000, LENGTH = 2048K

For the bootloader:

FLASH (rx)   : ORIGIN = 0x08000000, LENGTH = 512K /* 0x80000 */

For the application:

FLASH (rx)   : ORIGIN = 0x08080000, LENGTH = 1536K /* (1536 = 2048 - 512 */

My JumpToApplication function:

/**
 * \brief           Jump To Application
 * \param[in]       AppAddress: The memory address of the app (0x08080000)
 * \return          None
 */
void Bootloader_JumpToApplication(uint32_t AppAddress) {
    typedef void (*pFunction)(void);
 
    /* Set the address of the entry point to application */
    volatile uint32_t BootAddr = AppAddress;
 
    uint32_t MSPValue = *(__IO uint32_t*) BootAddr;
 
    uint32_t JumpAddress = *(__IO uint32_t*) (BootAddr + 4);
 
    pFunction JumpToApplication = (pFunction) JumpAddress;
 
    /* Unmount SD Card, and UnLink driver */
    f_mount(NULL, SDPath, 1);
    FATFS_UnLinkDriver(SDPath);
 
    __HAL_RCC_USART3_FORCE_RESET();
    HAL_Delay(5);
    __HAL_RCC_USART3_RELEASE_RESET();
    HAL_Delay(5);
 
    /* DeInit used peripherals */
    HAL_SD_MspDeInit(&hsd1);
    HAL_SPI_MspDeInit(&hspi1);
    HAL_SPI_MspDeInit(&hspi2);
    HAL_SPI_MspDeInit(&hspi4);
    HAL_ETH_MspDeInit(&heth);
    HAL_UART_MspDeInit(&huart3);
 
    SCB_DisableDCache();
    SCB_DisableICache();
    HAL_MPU_Disable();
    HAL_SuspendTick();
 
    /*
     * Modify the HAL_DeInit function:
     *  HAL_StatusTypeDef HAL_DeInit(void)
     *  {
     *    // Reset of all peripherals
     *  //  __HAL_RCC_AHB3_FORCE_RESET();      This causes a CPU reset 
     *    RCC->AHB3RSTR &= 0x7fffffff;        // clear bit 31
     *    __HAL_RCC_AHB3_RELEASE_RESET();
     */
    HAL_DeInit();
 
    /* Disable all interrupts */
    __disable_irq();
 
    /*
     * Disable SysTick timer and clear its exception pending bit,
     * if it is used in the bootloader, e. g. by the RTX.
     */
    SysTick->CTRL = 0;
    SysTick->LOAD = 0;
    SysTick->VAL = 0;
 
    /*
     * Make sure you disable the RCC in your bootloader
     * code *before* you call the application
     * Set the clock to the default state
     */
    HAL_RCC_DeInit();
 
    /* Clear Interrupt Enable Register & Interrupt Pending Register */
    for (uint8_t i = 0; i < 7; i++) {
        /* Disable all enabled interrupts in NVIC */
        NVIC->ICER[i] = 0xFFFFFFFF;
        /* Clear all pending interrupt requests in NVIC */
        NVIC->ICPR[i] = 0xFFFFFFFF;
    }
 
    /* Re-enable all interrupts */
    __enable_irq();
 
    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. */
    if ( CONTROL_SPSEL_Msk & __get_CONTROL()) {
        /* MSP is not active */
        __set_CONTROL(__get_CONTROL() & ~CONTROL_SPSEL_Msk);
    }
 
    __HAL_RCC_SYSCFG_CLK_ENABLE() ;
 
    /*
     * Configure the Main Stack Pointer.
     * This function comes from CMSIS.
     */
    __set_MSP(*(__IO uint32_t*) BootAddr);
 
    __set_CONTROL(0);
 
    JumpToApplication();
}

I hope this helps others.

Comments are welcome.

I was able to check the SD card (using 4-bit SDMMC and FATFS) for an updated version of the application, if it exists, check md5 checksum, erase sectors, load the file and flash it in 4kB chunks, delete that file, and jump to the updated version of the application.