cancel
Showing results for 
Search instead for 
Did you mean: 

Getting HardFault and IBUSERR when jumping from bootloader to UART flashed application on STM32H7

DanielPi
Associate III

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?

7 REPLIES 7
DanielPi
Associate III

So a quick update, when flashing both projects at the same time (and of course not transferring code using UART) using a debug configuration in STM32CUBEIDE, (i.e. adding both .elf files into the configuration, but entering on the bootloader elf), stepping through the app code works just fine.

DanielPi
Associate III

Another update, comparing the application memory region (starting at 0x0800_8000, length 32K), it's identical when comparing the bytes there, whether the region was populated by the bootloader from reading UART transmission of the app, or when the app was flashed together with the bootloader from CubeIDE over SWD... So the code is the same, and still it doesn't work? =/

DanielPi
Associate III

Another update, I set up so that both bootloader and application are flashed through CubeIDE SWD, I then copy all the bytes of the application memory area (0x08008000, length 8232 bytes) to some memory I allocated on the heap using malloc, I then flash the application through UART (still during the same session), then compare the application memory area with the memory copied to my heap buffer, and they are IDENTICAL!!!

I don't get it, I put the exact same bytes in the application memory area, and then it doesn't work...

DanielPi
Associate III

Yet another update, copying the SWD downloaded app flash to a malloc'ed buffer, then erasing the app flash, then writing to the app flash again makes things not working (all this in the same run session). So even if I copy the data, then write the exact same data back, it doesn't work. Seems like I can't execute on flash memory that has been erased/written to...

Have I missed "releasing" some sort of flash write resource? I use HAL_FLASH_Lock after every manipulation of the flash memory... Or is there some memory protection mechanism that prevents me from executing on flash sectors that have been erased/written to?

DanielPi
Associate III

Update 5/x: Tried adding SCB_InvalidateDCache before jumping to application, didn't work...

Blind use of SCB_InvalidateDCache() can be highly destructive, like throwing away pending writes to RAM / Stack Frame/Context. It's the Nuke from Orbit method.

Always use the byAddr form to avoid collateral damage.

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

Yeah sounds like a bad idea to use it...

Any idea on what could be wrong with my original problem? Not being able to execute from flash that I have erased and then written to...