2026-03-04 2:56 AM
Problem:
HAL_FLASHEx_Erase returns error 0x20000, and HAL_FLASH_Program does return HAL_OK but does not actually write any data to the flash.
Goal: Erase and install new firmware on flash on its own inside the bootloader.
I am developing a bootloader for the STM32H533RET6. I have TrustZone enabled, and configured the secure and non-secure regions. I am trying to erase and write to the non-secure part of the flash, from the bootloader which is secure (and separate from the actual application). I have disabled any write protection that I am aware of inside the option bytes. I also use the Memory Management Tool inside CubeMX to configure the secure and non-secure regions. For the non-secure region of the flash I have set "Access Permissions" to "RW by any privilege level". I do have a Flash watermark set on bank 1 but this does not cover any of the non-secure flash regions. No watermark is set on bank 2.
Below is the code I use to erase and write to a sector on the flash:
uint32_t Erase_And_Write_Data(uint32_t start_address, uint32_t* data, uint32_t words_size)
{
FLASH_EraseInitTypeDef erase_init;
uint32_t sector_error;
HAL_FLASH_Unlock();
// Determine sectors and bank based on start_address
uint32_t first_sector = (start_address - 0x08000000) / INTERNAL_FLASH_SECTOR_SIZE;
uint32_t last_sector = ((start_address - 0x08000000) + (words_size*4)) / INTERNAL_FLASH_SECTOR_SIZE;
uint32_t sector_count = last_sector - first_sector + 1;
uint32_t bank = start_address < 0x08040000? FLASH_BANK_1 : FLASH_BANK_2;
// Erase sector
erase_init.TypeErase = FLASH_TYPEERASE_SECTORS;
erase_init.Sector = first_sector;
erase_init.NbSectors = sector_count;
erase_init.Banks = bank;
if(HAL_FLASHEx_Erase(&erase_init, §or_error) != HAL_OK) {
HAL_FLASH_Lock();
return HAL_FLASH_GetError();
}
// Write to sector
uint32_t written = 0;
while(written < words_size) {
if(HAL_FLASH_Program(FLASH_TYPEPROGRAM_QUADWORD, start_address + (written * 4), (uint32_t)&data[written]) != HAL_OK) {
HAL_FLASH_Lock();
return HAL_FLASH_GetError();
}
written += 4;
}
HAL_FLASH_Lock();
return 0;
}
The STM32H533 flash consists of 2 banks each being 256Kbytes in size. This then is divided into sectors of 8KB making up 64 total sectors, where 0-31 is bank1 and 32-63 is bank2. I have validated that putting in a start_address results in the correct sectors, sector count and bank. I have also just tried hardcoding a sector and a bank, but this all results in the same 0x20000 error.
To test the HAL_FLASH_Program I disabled the HAL_FLASHEx_Erase. I then called the HAL_FLASH_Program on an address of a sector that I knew was empty, as I erased it using the STM32CubeProgrammer. After executing the HAL_FLASH_Program, it did infact return a HAL_OK, but after checking the data on the address I wrote to, nothing was written and everything was still 0xFFFFFFFF.
Any help or advice is appreciated.
Solved! Go to Solution.
2026-03-04 6:38 AM
Thanks for the replies.
By experimenting and stepping through the code I saw that inside the HAL_FLASHEx_Erase() the IS_FLASH_SECURE_OPERATION() is evaluated to determine whether the secure or non-secure control register should be used. This resulted always in the secure control registers being used even though I am erasing a sector inside a non-secure region.
To check if this is why it is failing I added the FLASH_NON_SECURE_MASK flag to the TypeErase property of the EraseInitTypeDef struct like this:
FLASH_EraseInitTypeDef erase_init;
erase_init.TypeErase = FLASH_TYPEERASE_SECTORS | FLASH_NON_SECURE_MASK;
erase_init.Sector = first_sector;
erase_init.NbSectors = sector_count;
erase_init.Banks = bank;This prevented the error and also actually erased the sector, so it worked.
I also added the flag to the HAL_FLASH_Program() function and this made it actually write the data to the sector aswell:
// Write to sector
uint32_t written = 0;
while(written < words_size) {
if(HAL_FLASH_Program(FLASH_TYPEPROGRAM_QUADWORD | FLASH_NON_SECURE_MASK, start_address + (written * 4), (uint32_t)&data[written]) != HAL_OK) {
HAL_FLASH_Lock();
return HAL_FLASH_GetError();
}
written += 4;
}
However this is probably not the proper way to implement this, and is definitely a result of me doing something wrong somewhere else.
I believe it has something to do with calling this from the secure world when I need to erase from the non-secure region.
Is there a function that switches the context of the flash or something I need to implement to specify that I want the flash to use the non-secure control registers instead?
2026-03-04 5:21 AM
Your sector number formula is probably incorrect for the second bank. The sector numbers start from 0 IN EACH bank, so that the sector at BASE + 256 KiB is sector 0 of the second bank. See figures 25..28 in the RefMan.
2026-03-04 5:31 AM
Thank you for the reply.
I have unfortunately tried that already by hardcoding the values in, with the same error as result. Additionally erasing sectors from bank 1 also fails, so the problem is not just limited to the second bank.
Hard coded example that fails:
// Erase sector
erase_init.TypeErase = FLASH_TYPEERASE_SECTORS;
erase_init.Sector = 28;
erase_init.NbSectors = 1;
erase_init.Banks = FLASH_BANK_2;
if(HAL_FLASHEx_Erase(&erase_init, §or_error) != HAL_OK) {
HAL_FLASH_Lock();
return HAL_FLASH_GetError();
}
2026-03-04 5:57 AM - edited 2026-03-04 5:58 AM
WRPERR indicates write protection is not disabled.
Edit: The forum software ate the screenshots I posted along with this. I'm sick of this forum software. Fix the bugs ST.
2026-03-04 6:23 AM
Have you set ALL the fields of the structure? Start with this:
FLASH_EraseInitTypeDef erase_init = {0};Or, better yet, that:
FLASH_EraseInitTypeDef erase_init = {
.TypeErase = FLASH_TYPEERASE_SECTORS,
.Sector = 28,
.NbSectors = 1,
.Banks = FLASH_BANK_2
};
2026-03-04 6:38 AM
Thanks for the replies.
By experimenting and stepping through the code I saw that inside the HAL_FLASHEx_Erase() the IS_FLASH_SECURE_OPERATION() is evaluated to determine whether the secure or non-secure control register should be used. This resulted always in the secure control registers being used even though I am erasing a sector inside a non-secure region.
To check if this is why it is failing I added the FLASH_NON_SECURE_MASK flag to the TypeErase property of the EraseInitTypeDef struct like this:
FLASH_EraseInitTypeDef erase_init;
erase_init.TypeErase = FLASH_TYPEERASE_SECTORS | FLASH_NON_SECURE_MASK;
erase_init.Sector = first_sector;
erase_init.NbSectors = sector_count;
erase_init.Banks = bank;This prevented the error and also actually erased the sector, so it worked.
I also added the flag to the HAL_FLASH_Program() function and this made it actually write the data to the sector aswell:
// Write to sector
uint32_t written = 0;
while(written < words_size) {
if(HAL_FLASH_Program(FLASH_TYPEPROGRAM_QUADWORD | FLASH_NON_SECURE_MASK, start_address + (written * 4), (uint32_t)&data[written]) != HAL_OK) {
HAL_FLASH_Lock();
return HAL_FLASH_GetError();
}
written += 4;
}
However this is probably not the proper way to implement this, and is definitely a result of me doing something wrong somewhere else.
I believe it has something to do with calling this from the secure world when I need to erase from the non-secure region.
Is there a function that switches the context of the flash or something I need to implement to specify that I want the flash to use the non-secure control registers instead?