Getting HardFault and IBUSERR when jumping from bootloader to UART flashed application on STM32H7
Hello.
I am developing a UART bootloader. Currently developing it on the Nucleo H7S3L8 that sports the STM32H7S3L8 microcontroller, 65K flash, ~450K something RAM.
I have implemented the ol' JumpToApplication pattern that can be found all over the internet:
typedef void (*pFunction)(void);
#define FLASH_APP_START_ADDRESS ((uint32_t)0x8008000U)
#define BOOTLOADER_START_ADDRESS ((uint32_t)0x8000000U) // Just for testing
void JumpToApp(void)
{
const uint32_t jump_address = *(__IO uint32_t*) (FLASH_APP_START_ADDRESS + 4U);
const pFunction jump_to_application = (pFunction) jump_address;
SCB->VTOR = FLASH_APP_START_ADDRESS;
uint32_t sp_addr = *(__IO uint32_t*)FLASH_APP_START_ADDRESS;
__set_MSP(sp_addr);
__enable_irq();
jump_to_application();
}
If I use the BOOTLOADER_START_ADDRESS as jump_address, it will (just as expected) jump back to the ResetHandler of the bootloader, that resides in the startup.s file (generated from CubeIDE). So I have verified that my "jumping" works, and it can jump arbitrarily many times.
If I use the FLASH_APP_START_ADDRESS however, it does not work. Rather, while stepping in Instruction Stepping Mode, I can successfully jump to the address where my App ResetHandler resides (which happens to be 0x080086C5) so that I hit the first assembly instruction:
/* Address 080086c4 will be hit when stepping from "jump_to_application" */
080086c4: ldr r0, [pc, #60] @ (0x8008704)
080086c6: mov sp, r0
080086c8: ldr r4, [pc, #60] @ (0x8008708) /* Debug dummy instruction, not in originally generated startup file */
080086ca: ldr r4, [pc, #64] @ (0x800870c) /* Debug dummy instruction */
080086cc: ldr r4, [pc, #64] @ (0x8008710) /* Debug dummy instruction */
080086ce: bl 0x8008698
080086d2: ldr r0, [pc, #64] @ (0x8008714)
080086d4: ldr r1, [pc, #64] @ (0x8008718)
And I have verified the assembly instructions are the same here where I have flashed the app, comparing the same memory area when I flash the app through CubeIDE with SWD.
However, when I step to the next instruction, everything goes haywire, the PC goes to some invalid address 0xFFFFFFFF and the assembly view is broken. Looking at the registers, I see the following
- The lowest 2 bit of xPSR is set to 0b11: This indicates a HardFault if I understand things correctly
- In SCB->HFSR, bit 1 is set, which indicates the following: "VECTTBL - Indicates a fault occurred because of an issue reading from an address in the vector table. This is pretty atypical but could happen if there is a bad address in the vector table and an unexpected interrupt fires.". So I have screwed up the interrupt vector table somehow, but when I tested with jumping to my bootloader ResetHandler, I purposely set the VTOR field to random memory address, and jumping still worked
- Control - CFSR, bit 8 or the IBUSERR bit is set. Sound pretty serious, but I have no idea what it means.
Here is the start of the linker script from my bootloader
/* Entry Point */
ENTRY(Reset_Handler)
/* Highest address of the user mode stack */
_estack = ORIGIN(DTCM) + LENGTH(DTCM); /* end of Ram type memory */
_Min_Heap_Size = 0x200; /* required amount of heap */
_Min_Stack_Size = 0x400; /* required amount of stack */
__FLASH_BEGIN = 0x08000000;
__FLASH_SIZE = 64K;
__RAM_BEGIN = 0x24000000;
__RAM_SIZE = 0x71C00;
__RAM_NONCACHEABLEBUFFER_SIZE = 0x400;
/* Memories definition */
MEMORY
{
RAM (xrw) : ORIGIN = __RAM_BEGIN, LENGTH = __RAM_SIZE
RAM_NONCACHEABLEBUFFER (xrw) : ORIGIN = __RAM_BEGIN + __RAM_SIZE, LENGTH = __RAM_NONCACHEABLEBUFFER_SIZE
ITCM (xrw) : ORIGIN = 0x00000000, LENGTH = 0x00010000
DTCM (rw) : ORIGIN = 0x20000000, LENGTH = 0x00010000
SRAMAHB (rw) : ORIGIN = 0x30000000, LENGTH = 0x00008000
BKPSRAM (rw) : ORIGIN = 0x38800000, LENGTH = 0x00001000
FLASH (xrw) : ORIGIN = __FLASH_BEGIN, LENGTH = __FLASH_SIZE
}
/* Sections */
SECTIONS
{
/* The startup code into "FLASH" Rom type memory */
.isr_vector :
{
. = ALIGN(4);
KEEP(*(.isr_vector)) /* Startup code */
. = ALIGN(4);
} >FLASH
/* The program code and other data into "FLASH" Rom type memory */
.text :
{
. = ALIGN(4);
*(.text) /* .text sections (code) */
*(.text*) /* .text* sections (code) */
*(.glue_7) /* glue arm to thumb code */
*(.glue_7t) /* glue thumb to arm code */
*(.eh_frame)
KEEP (*(.init))
KEEP (*(.fini))
. = ALIGN(4);
_etext = .; /* define a global symbols at end of code */
} >FLASH
etc...
And here's the linker script of my application:
ENTRY(Reset_Handler)
/* Highest address of the user mode stack */
_estack = ORIGIN(DTCM) + LENGTH(DTCM); /* end of Ram type memory */
_Min_Heap_Size = 0x200; /* required amount of heap */
_Min_Stack_Size = 0x400; /* required amount of stack */
__FLASH_BEGIN = 0x08008000;
__FLASH_SIZE = 32K;
__RAM_BEGIN = 0x24000000;
__RAM_SIZE = 0x71C00;
__RAM_NONCACHEABLEBUFFER_SIZE = 0x400;
/* Memories definition */
MEMORY
{
RAM (xrw) : ORIGIN = __RAM_BEGIN, LENGTH = __RAM_SIZE
RAM_NONCACHEABLEBUFFER (xrw) : ORIGIN = __RAM_BEGIN + __RAM_SIZE, LENGTH = __RAM_NONCACHEABLEBUFFER_SIZE
ITCM (xrw) : ORIGIN = 0x00000000, LENGTH = 0x00010000
DTCM (rw) : ORIGIN = 0x20000000, LENGTH = 0x00010000
SRAMAHB (rw) : ORIGIN = 0x30000000, LENGTH = 0x00008000
BKPSRAM (rw) : ORIGIN = 0x38800000, LENGTH = 0x00001000
FLASH (xrw) : ORIGIN = __FLASH_BEGIN, LENGTH = __FLASH_SIZE
}
/* Sections */
SECTIONS
{
/* The startup code into "FLASH" Rom type memory */
.isr_vector :
{
. = ALIGN(4);
KEEP(*(.isr_vector)) /* Startup code */
. = ALIGN(4);
} >FLASH
/* The program code and other data into "FLASH" Rom type memory */
.text :
{
. = ALIGN(4);
*(.text) /* .text sections (code) */
*(.text*) /* .text* sections (code) */
*(.glue_7) /* glue arm to thumb code */
*(.glue_7t) /* glue thumb to arm code */
*(.eh_frame)
KEEP (*(.init))
KEEP (*(.fini))
. = ALIGN(4);
_etext = .; /* define a global symbols at end of code */
} >FLASH
etc...
In many of the resources online about this "jump to application" pattern states that you should Deinit HAL, RCC, disable interrupts, write certain bits to control memory locations etc., which is probably true for doing it robustly, but jumping back to my bootloader ResetHandler seems to work just fine without doing all that stuff, so I figured that for now it should work without it (and believe me, I have tried with all that stuff too and it still doesn't work).
And I have disabled the MPU (or rather, I have not enabled it, and it didn't work with it either). The only thing I had to keep in my bootloader main was "SCB_EnableDCache", because without it writing to flash wasn't working for whatever reason.
So what could I be missing?
