Skip to main content
Associate
March 4, 2026
Solved

Unable to erase sector of internal flash using HAL

  • March 4, 2026
  • 4 replies
  • 265 views

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, &sector_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.

Best answer by CCNConner

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?

4 replies

gbm
Principal
March 4, 2026

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.

My STM32 stuff on github - compact USB device stack and more: https://github.com/gbm-ii/gbmUSBdevice
CCNConnerAuthor
Associate
March 4, 2026

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, &sector_error) != HAL_OK) {
		HAL_FLASH_Lock();
		return HAL_FLASH_GetError();
	}

 

TDK
Super User
March 4, 2026

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.

"If you feel a post has answered your question, please click ""Accept as Solution""."
gbm
Principal
March 4, 2026

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

 

My STM32 stuff on github - compact USB device stack and more: https://github.com/gbm-ii/gbmUSBdevice
CCNConnerAuthorBest answer
Associate
March 4, 2026

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?