cancel
Showing results for 
Search instead for 
Did you mean: 

Jump issue with custom bootloader (STM32H7)

jean
Senior

Hello,

I've made several bootloader in the past without so much problem in the past with F2, F4, F7, but this time, I have some serious troubles with a STM32H743BI 🙂

The bootloader (using the first sector, 128kb) reads a bin file from the SD card and program the rest of the flash sectors.

Everything works well if : I have flashed the main application one time minimum, thanks to my j-link debugger. Entering the bootloader erase the main app sector, then flash the .bin code, then jump. No issue at all.

But if the main application has never been flashed with "manually" with j-link, the jump fail (no error/hardfault, the uC is just lost after jump).

Also, when everything works (first case), if I flash "manually" the main application starting on the first sector (VECT_TAB_OFFSET = 0, instead of VECT_TAB_OFFSET = 0x020000) : after a bootloader erase/flashing, same issue = jump fails.

It looks like flashing "manually" does configure some hidden registers, and after that the bootloader is able to successfully jump?

This is the code I use for the jump :

void JumpToApplication()
{
  uint32_t JumpAddress = *(__IO uint32_t*)(app_address);
  pFunction Jump = (pFunction)JumpAddress;
 
  // __disable_irq(); // tested, do not change anything
  SysTick->CTRL = 0;
  SysTick->LOAD = 0;
  SysTick->VAL = 0;
  SCB->VTOR = app_address;
 
  HAL_RCC_DeInit();
  // HAL_DeInit(); // tested, do not change anything
  
  // tested, do not change anything
  // for(uint8_t i = 0; i < 5; i++) 
  // {
  //   NVIC->ICER[i] = 0xFFFFFFFF;
  //   NVIC->ICPR[i] = 0xFFFFFFFF;
  // }
 
  // __enable_irq(); //tested, do not change anything
  __set_MSP(*(__IO uint32_t*)app_address);
  Jump();
}

On the side of the main application :

#define VECT_TAB_OFFSET  0x020000UL

Do you have idea on what is going wrong?

Thanks!

Jean

1 ACCEPTED SOLUTION

Accepted Solutions
jean
Senior

Hi everybody,

Thanks a lot for your ideas!

Unfortunately any of them did work...

But I found the problem : for the erase, I was using FLASH_VOLTAGE_RANGE_4 instead of FLASH_VOLTAGE_RANGE_3

The final code is :

FLASH_EraseInitTypeDef pEraseInit { 
      .TypeErase { FLASH_TYPEERASE_MASSERASE },
      .Banks { FLASH_BANK_2 },
      .Sector { FLASH_SECTOR_0 },
      .NbSectors { 8 },
      .VoltageRange { FLASH_VOLTAGE_RANGE_3 },
};

Now, everything works well in any condition!

View solution in original post

7 REPLIES 7

Check the BOOT0 pin state

Turn off all your random interrupts, this isn't "__disable_irq()", this is you turning off your peripheral interrupt sources, AT THE SOURCE.

Print out the Option Bytes content.

Print out the addresses in the vector table.

I would avoid changing SCB->VTOR here, your app side SystemInit() code should set this properly. Make sure it is. Ideally use linker symbols, and not ST's #define nonsense.

App side code can also explicitly set the Stack Point when it arrives in Reset_Handler, this would work more safely as the auto-variables are in the stack frame.

Make sure the Segger isn't erasing the first sector by accident, otherwise the processor will enter the System Loader, and not yours. Inspect the memory in the failing cases.

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

Hi,

I had many troubles on that topic too, the jump function I ended up with is the following:

void vSetupAndJumpToAddr(uint32_t flashStartAddr)
{
    __disable_irq();
 
    HAL_MPU_Disable();
  
    // Deactivate the used peripherals
    HAL_DeInit();
    HAL_RCC_DeInit();
  
    //Disable ALL enabled peripherals
    MX_GPIO_DeInit();
    MX_DMA_DeInit();
    
    HAL_I2C_MspDeInit(&hi2c5);
    HAL_OSPI_MspDeInit(&hospi1);
    HAL_TIM_Base_MspDeInit(&htim12);
    HAL_TIM_Base_MspDeInit(&htim15);
    HAL_TIM_Base_MspDeInit(&htim23);
    HAL_UART_MspDeInit(&huart4);
    HAL_UART_MspDeInit(&huart5);
    HAL_UART_MspDeInit(&huart9);
    HAL_USART_MspDeInit(&husart10);
    HAL_TIM_Base_MspDeInit(&htim6);
    HAL_CRC_MspDeInit(&hcrc);
    HAL_RTC_MspDeInit(&hrtc);
    
    __HAL_DBGMCU_FREEZE_IWDG1();
    __HAL_DBGMCU_FREEZE_TIM1 ();
    __HAL_DBGMCU_FREEZE_TIM4 ();
    __HAL_DBGMCU_FREEZE_TIM6 ();
    __HAL_DBGMCU_FREEZE_TIM12 ();
    __HAL_DBGMCU_FREEZE_TIM15 ();
    __HAL_DBGMCU_FREEZE_TIM16 ();
    __HAL_DBGMCU_FREEZE_TIM23 ();
    __HAL_DBGMCU_FREEZE_TIM3 ();
    __HAL_DBGMCU_FREEZE_I2C5 ();
    __HAL_DBGMCU_FREEZE_WWDG1 ();
    __HAL_DBGMCU_FREEZE_IWDG1 ();
  
    // Disable I-Cache
    SCB_DisableICache();
    // Disable D-Cache
    SCB_DisableDCache();
    
    // Make sure, the CPU is in privileged mode.
       if( CONTROL_nPRIV_Msk & __get_CONTROL( ) )
    {  /* not in privileged mode */
      EnablePrivilegedMode( ) ;
    }
   
    for (int i = 0; i < 8; i++)
    {
      NVIC->ICER[i] = 0xFFFFFFFF; //Disable all enabled interrupts in NVIC.
      NVIC->ICPR[i] = 0xFFFFFFFF; //Clear all pending interrupt requests in NVIC.
    }
    
    // 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 ) ;
    }
    
    // SCB->VTOR is setup in the appl startup
    // "VECT_TAB_OFFSET = 0x40400" setup into keil > option for target > C/C++ > Preprocessor symbols
    
    // !!! Attention !!!
    // re-enable IRQ with _enable_irq() in the application
    // AFTER the initialization of all the peripherals!
   
    // Jump to user application
    JumpAddress = *(__IO uint32_t*)(flashStartAddr+4);
    Jump_To_Adress = (pFunction)JumpAddress;
        
   // re-init stack pointer (first entry of the vector table)
    __set_MSP(*(__IO uint32_t*) flashStartAddr);
    Jump_To_Adress();
  
    // *** codeline never reached ***
    Error_Handler();
}

Many of these lines may or may not be useful, but now it works fine for me. The VECT_TAB_OFFSET of the VTOR as written in the comments is configured inside keil in my case.

With this procedure I'm able to directly jump to the application after programming Application sectors.

If this is not enough, compare the binary and the flash area when it might be flashed in, and then also the flashing procedure.

GLASS
Senior

Be aware of asm code produced by C compiler​, you can single step asm to identify what happen.

Another trap: @jean​  consider allocating "jump" as a global Var and not a local var. Because just before jumping the stack pointer is forced with set MSP!

So depending of generated code​ the stack relative address of local Var is no more guaranted!

Prefetching and out of order execution​ (due to compiler optim and/or Cortex M7) can also change what we are usually expecting with previous STM32 generations...

jean
Senior

Hi everybody,

Thanks a lot for your ideas!

Unfortunately any of them did work...

But I found the problem : for the erase, I was using FLASH_VOLTAGE_RANGE_4 instead of FLASH_VOLTAGE_RANGE_3

The final code is :

FLASH_EraseInitTypeDef pEraseInit { 
      .TypeErase { FLASH_TYPEERASE_MASSERASE },
      .Banks { FLASH_BANK_2 },
      .Sector { FLASH_SECTOR_0 },
      .NbSectors { 8 },
      .VoltageRange { FLASH_VOLTAGE_RANGE_3 },
};

Now, everything works well in any condition!

Good information, but order of de-initializing seems wrong. Shouldn't it be something like that:

HAL_UART_MspDeInit(&huart4); // de-init peripherals first
HAL_RCC_DeInit();
HAL_DeInit(); // everything is in reverse order compared to initialization stage

In my opinion, best approach is to place bootloader in sector 0 and keep it simple, with only one UART initialized. In this case de-iniatilization is very simple:

HAL_UART_DMAStop(&huart1);
HAL_UART_DeInit(&huart1);
HAL_DeInit();
 
JumpAddress=0x24000000;
JumpAddress = *(__IO uint32_t*) (JumpAddress + 4);
Jump_To_Application = (pFunction) JumpAddress;
 
__set_MSP(*(__IO uint32_t*) JumpAddress);
Jump_To_Application();

I always keep sector 0 write protected, where simple bootloader is located. It jumps to main application after power-up if no special sequence of bytes is received. Bootloader is started from main application by calling HAL_NVIC_SystemReset(); and then sending specific bytes to stay in bootloader. This way it is nearly impossible to brick the device, and you always can get to bootloader through power cycling even if something is messed up within main application.

Piranha
Chief II

Your copy-paste procedure has bug... 😉 The initial code lacks the 4 byte offset for reading the Reset_Handler:

  uint32_t JumpAddress = *(__IO uint32_t*)(app_address);
  pFunction Jump = (pFunction)JumpAddress;

Also it amazes me how people just copy any code and don't think for themselves even for a second. There is no need for an intermediate useless JumpAddress variable:

  pFunction Jump = (pFunction)(*(__IO uint32_t*)(app_address + 4));

And ARM clearly says that changing the VTOR requires a DSB after it and changing the stack pointer requires an ISB after it...

jean
Senior

Hi @Piranha​ 

Thanks for your comment and sorry for the typo, indeed it was a copy paste error from my side!

Never heard of DSB / ISB, do you have an example of use in this case?