cancel
Showing results for 
Search instead for 
Did you mean: 

Need for invalidating cache when executing from flash that has been erased and written to

DanielPi
Associate III

Hi!

I am writing a bootloader for an STM32H7S3L8, that received the application binary over UART and places it into flash, specifically on address 0x08008000, and I am getting hard faults when jumping to the address.

If I "install" the application AND bootloader through STM32CubeIDE with SWD, I can successfully jump from the bootloader to the application at runtime. Even if I copy all the application bytes from flash to a RAM section, I can successfully jump to the application and execute it there (from RAM).

But as soon as I am trying to execute from flash that during that runtime session has been erased and then written to, I get hard faults. I even tried just copying the application bytes to ram, erase the flash and copy the application bytes back to the same memory location, and execution from there doesn't work.

From googling around, I suspect that the D and/or I cache might be the problem. I can disable I cache, but I can't disable D cache, then I'm not able to write and read from flash properly (i.e. I don't even reach the line where I am trying to jump to the application).

I have also tried various combinations of invalidating D cache (using SCB_CleanInvalidateDCache_by_Addr/SCB_InvalidateDCache_by_Addr) after erasing and writing, but to no avail.

So to put it simply, how do I robustly execute from flash that I have erased and then written to? What do you need to do/set up before you try to execute from flash memory that you have once erased and written to?

Code is something along the lines of this

typedef void (*pFunction)(void);

#define FLASH_APP_START_ADDRESS ((uint32_t)0x8008000U)

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();
}

int main()
{
  SCB_EnableDCache();
  EraseFlash();

  uint8_t* application_data = (uint8_t*)malloc(...);
  ReceiveApplication(application_data);

  // 'application_data' now contains application bytes
  const flash_status status = FlashWriteBytes(FLASH_APP_START_ADDRESS, application_data, application_size);


  JumpToApp();
}

 

11 REPLIES 11
STOne-32
ST Employee

Dear @DanielPi ,

Hope you have already solved the issue from our direct support channel and now it works 😉 . I would very grateful if you could share back to our Community members the Solution including this thread as well

Re: Getting HardFault and IBUSERR when jumping fro... - STMicroelectronics Community

Thank you again.

STOne-32.

DanielPi
Associate III

Yes of course @STOne-32 !

So using the ST online help I was provided with the solution. Basically is was cache invalidation that was missing, which I had tried before to some extent, but not in the correct order, or type of cache invalidation. The flow now is basically:

EraseFlash(); // Erase the relevant flash sectors before writing to flash

SCB_CleanInvalidateDCache();  // Clean and Invalidate D Cache now that flash has been erased

// Write application data to flash (i.e. the application binary)
FlashWrite(FLASH_APP_START_ADDRESS, application_data, application_size);

SCB_CleanInvalidateDCache();
// Invalidate instruction cache, so new code is visible to CPU
SCB_InvalidateICache();

JumpToApplication();

 

Now it works, though my understanding of the cache is very limited, so I can't say WHY this worked, as opposed to all the other various combinations that I tried. Should add that I used the Clean/InvalidateD/ICache_by_Addr functions, instead of the non address functions, which turned out to be the correct solution.