2021-10-28 09:01 AM
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
Solved! Go to Solution.
2021-11-03 04:45 AM
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!
2021-10-28 09:27 AM
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.
2021-10-29 07:08 AM
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.
2021-10-29 12:38 PM
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...
2021-11-03 04:45 AM
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!
2021-11-03 08:08 PM
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.
2021-11-06 06:04 PM
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...
2021-11-08 02:25 AM
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?