cancel
Showing results for 
Search instead for 
Did you mean: 

[solved][STM32H7-33] HardFault_Handler in application jumping from custom bootloader

simosilva
Senior

Hi!

I'm currently having troubles jumping from custom bootloader to application.

Working on STM32H733 MCU, the boot code that makes the jump is the following:

void vSetupAndJumpToAddr(uint32_t flashStartAddr)
{
    uint32_t i=0;
  
  // Disable all interrupts
   __disable_irq(); //correspond to assembly cmd: CPSID i
    HAL_MPU_Disable();
 
    
    HAL_RCC_DeInit();
    HAL_DeInit();
    
    /* Clear Interrupt Enable Register & Interrupt Pending Register */
    for (i = 0 ; i < 8 ; i ++)
    {
	    NVIC->ICER[i] = 0xFFFFFFFF;
	    NVIC->ICPR[i] = 0xFFFFFFFF;
    }
    
    // Disable I-Cache
    SCB_DisableICache();
    // Disable D-Cache
    SCB_DisableDCache();
  
    __enable_irq();
    // Jump to user application
    uint32_t JumpAddress = *(__IO uint32_t*)(flashStartAddr+4);
    pFunction 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();
}

After that, I correctly jump to application with start address 0x0804 0200.

In the Application project, the define "VECT_TAB_OFFSET=0x40200" was inserted to offset the Interrupt Vector Table.

The application starts correctly (I checked both the entrance in the startup code startup_stm32h723xx.s, the execution of SCB->VTOR = FLASH_BANK1_BASE | VECT_TAB_OFFSET; that correctly setup the Application VIT address at 0x08040200), but then the same application that worked alone placed at 0x0800 0000 enter in hard fault.

Hard fault is reached when main.c is initializing stuffs, when HAL_TIM_PeriodElapsedCallback(htim); is hit by the htim7, wich in my case is set as the Timebase Source of the SYS in the CubeMX project.

the Application main.c init code looks like this:

int main(void)
{
  /* USER CODE BEGIN 1 */
  
  __HAL_DBGMCU_FREEZE_IWDG1();		// must be declared using debug session
  
  __HAL_DBGMCU_FREEZE_IWDG1();		// must be declared using debug session
  __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 ();
  
  
  /* USER CODE END 1 */
 
  /* MPU Configuration--------------------------------------------------------*/
  MPU_Config();
 
  /* Enable I-Cache---------------------------------------------------------*/
  SCB_EnableICache();
 
  /* Enable D-Cache---------------------------------------------------------*/
  SCB_EnableDCache();
 
  /* MCU Configuration--------------------------------------------------------*/
 
  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();
 
  /* USER CODE BEGIN Init */
 
memset ((void *)0x30004000, 0xEE, 0x00004000); //LWIP_RAM_HEAP_POINTER area
 
memset ((void *)0x30004640, 0xAA, 0x000039C0); //LWIP_RAM_HEAP_POINTER area
 
  /* USER CODE END Init */
 
  /* Configure the system clock */
  SystemClock_Config();
 
/* Configure the peripherals common clocks */
  PeriphCommonClock_Config();
 
  /* USER CODE BEGIN SysInit */
 
  /* USER CODE END SysInit */
 
  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_DMA_Init();
  MX_ADC1_Init();
  MX_ADC2_Init();
  MX_ADC3_Init();
  MX_FDCAN3_Init();
  MX_I2C5_Init();
  MX_OCTOSPI1_Init();
  MX_TIM1_Init();
  MX_TIM4_Init();
  MX_TIM12_Init();
  MX_TIM15_Init();
  MX_TIM16_Init();
  MX_TIM23_Init();
  MX_UART4_Init();
  MX_UART5_Init();
  MX_UART9_Init();
  MX_USART3_Init();
  MX_USART10_Init();
  MX_TIM3_Init();
  MX_DAC1_Init();
  MX_TIM6_Init();
  MX_CRC_Init();
  MX_RTC_Init();
  /* USER CODE BEGIN 2 */
 
  //__enable_irq();
 
	// HW initialization 
  
  HAL_GPIO_WritePin([blablabla]_GPIO_Port, [blablabla]_Pin, GPIO_PIN_RESET);
   
  HAL_TIM_Base_Start (&htim6);                                                          // Start TIM6.
 
  BSP_Capture_Timers_Init ();
  BSP_Analog_Init();			// 	HW board support package for Analog Components	
 
 
[ ... other code never reached before the error ... ]

Any clue on the problem?

I enabled and disabled in different ways irqs without success, it might be something small I cannot see.

It is for sure an interrupt problem, _irq_disable() is used in the begin of the code, the hard fault is never reached.

Thanks 🙏

14 REPLIES 14
Artem.Moroz
Associate II

Hi there:

I have implemented a bootloader for STM32F746 MCU successfully. But I am not using Keil, but OpenSTM32.

My advice asides that was proposed here is to initialize as minimum peripherals as possible for the bootloader.

Why? Because

1) not all peripherals can be reverted to the initial state easily and

2) main application assumes that it is starting from power-up thus initializing peripherals icorrectly as well.

Here is my bootloader code (not all, just the essential part):

#define FLASH_USER_START_ADDR 0x08010000
 
void _binExec (void * l_code_addr)
{
    __asm__ (
    "mov   r1, r0        \n"
    "ldr   r0, [r1, #4]  \n"
    "ldr   sp, [r1]      \n"
    "blx   r0"
    );
}
 
void GoToUserApp(void)
{
	int i;
	__disable_irq();
 
	__DSB();
    __ISB();
	// Disable IRQs
    for (i = 0; i < 8; i ++) NVIC->ICER[i] = 0xFFFFFFFF;
    // Clear pending IRQs
    for (i = 0; i < 8; i ++) NVIC->ICPR[i] = 0xFFFFFFFF;
 
    // -- Modify vector table location
    // Barriers
    __DSB();
    __ISB();
    // Change the vector table
    SCB->VTOR = (FLASH_USER_START_ADDR & SCB_VTOR_TBLOFF_Msk);
    // Barriers
    __DSB();
    __ISB();
 
    __enable_irq();
 
 
    _binExec((void *)FLASH_USER_START_ADDR);
}
 
 
 
int main(void)
{
  /* USER CODE BEGIN 1 */
 
  /* USER CODE END 1 */
  
 
  /* MCU Configuration--------------------------------------------------------*/
 
  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();
 
  /* USER CODE BEGIN Init */
  SCB_DisableDCache();
 
  SCB_DisableICache();
  /* USER CODE END Init */
 
  /* Configure the system clock */
  SystemClock_Config();
 
  /* USER CODE BEGIN SysInit */
 
  /* USER CODE END SysInit */
 
  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_SPI2_Init();
  MX_FATFS_Init();
  MX_UART4_Init();
  MX_TIM12_Init();
  /* Initialize interrupts */
  MX_NVIC_Init();
  /* USER CODE BEGIN 2 */
  ///^^^^^^^
  ///Remove    MX_FMC_Init(),  MX_LTDC_Init(), MX_DMA2D_Init(). MX_QUADSPI_Init();
 
  BSP_QSPI_Init();
 
  /* Initialize the LCD */
  BSP_LCD_Init();
  /* Initialize the LCD Layers */
  BSP_LCD_LayerDefaultInit(LTDC_ACTIVE_LAYER, LCD_FRAME_BUFFER);
  BSP_LCD_SelectLayer(LTDC_ACTIVE_LAYER);
  LCD_LOG_Init();
  /* Show Header text */
  LCD_LOG_SetHeader(g_szBootHeader);
 
  
 
  HAL_TIM_Base_Start_IT(&htim12);
 
  uint8_t bMainFirmwareGood = 1, bMustEraseBinFile = 0;
 
  // FLASH MAIN FIRMWARE CODE
 
 
  FATFS_UnLinkDriverEx("0:", 0);
  HAL_TIM_Base_Stop_IT(&htim12);
 
 
 
 
 
 
  if (bMainFirmwareGood)
  {
	  LCD_UsrLog("Jumping to main Firmware %08lX, %08lX\n", *((uint32_t*)FLASH_USER_START_ADDR), *((uint32_t*)FLASH_USER_START_ADDR+4));
	  HAL_Delay(1000);
	  BSP_LCD_DisplayOff();
	  LCD_LOG_DeInit();
	  BSP_LCD_DeInit();
	  BSP_SDRAM_DeInit();
	  BSP_QSPI_DeInit();
	  HAL_UART_DeInit(&huart4);
	  HAL_SPI_DeInit(&hspi2);
	  HAL_TIM_Base_DeInit(&htim12);
	  HAL_RCC_DeInit();
	  HAL_SuspendTick();
	  HAL_DeInit();
	  GoToUserApp();
  }
  else
  {
	  LCD_UsrLog("Error!\n");
	  HAL_Delay(10000);
	  NVIC_SystemReset();
  }
 
  /* USER CODE END 2 */
 
  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */
 
    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

Thanks for the reply @Artem.Moroz​ !

I see, anyway digging in my debugger, the program enter in the TIM3_IRQ_Handler, without having enabled the corresponding timer in the application nor into the bootloader, so just uninitializing the peripherals or not initializing them at all (also tried as you suggested) do not solve my problem.

In the snippet of your code, i see a comment

"///^^^^^^^
///Remove    MX_FMC_Init(),  MX_LTDC_Init(), MX_DMA2D_Init(). MX_QUADSPI_Init();

for removing initializations done by the CubeMX, in my experience but most of based on the suggestion of Seniorer colleagues, is prefereable to deinit such peripherals in the USER CODE sections so that CubeMX will never change any of the changes made also in the case of regenerating the code, but this is just a recommendation 🙂

simosilva
Senior

EUREKA!

The problem was in the offset of the VTOR.

Looking to the _Vectors size for the STM32H733. the lenght is 0x2CC, so i have the last idea to try different addressing of the application, and moved it from 0x08040200 to 0x08040400, AND IT WORKED FIRST TRY! (obviously changed accordingly the jump in the boot and all the scatter files).

note: the vector table is charged FROM the specified address, so it has all the needed space in memory to fillupp the complete _Vectors with no problem, and it was on multiple of 0x200 as specified in the manuals, so I was following all the rules written in manuals.

The big issue is that in the ST reference/programming manuals the reserved area is always on 9 bits, not 10!

Into the PM0253, chapter 4.3.4 "The table alignment requirements mean that bits [8:0] of the table offset are always zero." so multiple of 0x200! not 0x400!

@Community member​ maybe you know some good ST employee to tag here to correct this precious info?

I surfed so many different blogs and forum searching the solution, but no-one have the same issue simply because tipically the application starts in addresses like 0x08###000 with last 3 words at 0, so this issue just did't appear...

PM0253 section 2.4.4 explains it:

The silicon vendor must configure the top range value, which is dependent on the number of interrupts implemented. The minimum alignment is 32 words, enough for up to 16 interrupts. For more interrupts, adjust the alignment by rounding up to the next power of two. For example, if the user requires 21 interrupts, the alignment must be on a 64-word boundary because the required table size is 37 words, and the next power of two is 64, see Vector table offset register on page 197.

Together with section 4.3.4:

When setting TBLOFF, the user must align the offset to the number of exception entries in the vector table.

Though the description could be better...

Hi @Piranha​ !

You're right, but the text is really contradditory, in many rm and pm i found "The table alignment requirements mean that bits [8:0] of the table offset are always zero.".

This is probably a copy-paste error without any correction on the specific architecture of the STM32H733..

This sayd, yes, digging more the correct offset is multiple of 0x400, but at the same time I had as reference a project based on STM32H745 using 0x08040200 working fine.

All's well that ends well..!