2022-02-23 2:40 PM
Hello All,
I am working with an STM32H743 and am having issues reliably erasing flash (unreliable in that it fails ~95% of the time). The code which erases flash runs from RAM (functions are allocated in RAM). I disable many of the interrupts before entering the code. I am also invalidating the data cache after erasing the application.
Essentially the code executes without any errors reported. However, afterwards when I connect to STLINK Utility and read the memory contents, some of the sectors are not erased, which were intended to be erased.
Couple of points and observations:
-> Erasing takes maybe 30 to 40 seconds
-> If I do not disable interrupts then while erase code is running I get a hard fault because the Systick interrupt fired.
What is going on here or what am I missing? Appreciate your insights on how to resolve this. A means of debugging the issue would be helpful as well.
Reference manual for this MCU is linked at the bottom of the page.
--------------------------------------------------------------------------------
Below is the starting point for the erasing flash code.
Note I disable the interrupts with priority >= 1.
Interrupt priority listing is available at the bottom of the page.
// Disable interrupts
__set_BASEPRI(0001 << __NVIC_PRIO_BITS);
// Erase flash
erase_flash();
// Invalidate data cache
SCB_InvalidateDCache();
// Enable interrupts
__set_BASEPRI(0);--------------------------------------------------------------------------------
Here are the function calls which contain the erase code.
#define __RAM_FUNC __attribute__((section(".RamFunc")))
__RAM_FUNC tsb_err_t erase_flash()
{
// Setup to erase last 5 sectors of bank 1
uint32_t error_bank_1 = 0;
FLASH_EraseInitTypeDef erase_conf_bank_1;
erase_conf_bank_1.TypeErase = FLASH_TYPEERASE_SECTORS;
erase_conf_bank_1.Banks = FLASH_BANK_1;
erase_conf_bank_1.Sector = FLASH_SECTOR_3;
erase_conf_bank_1.NbSectors = 5;
erase_conf_bank_1.VoltageRange = FLASH_CR_PSIZE_1;
// Setup to erase base 2
uint32_t error_bank_2 = 0;
FLASH_EraseInitTypeDef erase_conf_bank_2;
erase_conf_bank_2.TypeErase = FLASH_TYPEERASE_SECTORS;
erase_conf_bank_2.Banks = FLASH_BANK_2;
erase_conf_bank_2.Sector = FLASH_SECTOR_0;
erase_conf_bank_2.NbSectors = 8;
erase_conf_bank_2.VoltageRange = FLASH_CR_PSIZE_1;
// Unlock Flash
if (RAM_FLASH_Unlock() != HAL_OK) { goto error; }
// Erase flash
if (RAM_FLASHEx_Erase(&erase_conf_bank_1, &error_bank_1) != HAL_OK) { goto error; }
if (RAM_FLASHEx_Erase(&erase_conf_bank_2, &error_bank_2) != HAL_OK) { goto error; }
// Lock Flash
if (RAM_FLASH_Lock() != HAL_OK) { goto error; }
return TSB_OK;
error:
RAM_FLASH_Lock();
return TSB_ERR_FLASH_ERASE;
}
__RAM_FUNC HAL_StatusTypeDef RAM_FLASH_Unlock(void)
{
if(READ_BIT(FLASH->CR1, FLASH_CR_LOCK) != 0U)
{
/* Authorize the FLASH Bank1 Registers access */
WRITE_REG(FLASH->KEYR1, FLASH_KEY1);
WRITE_REG(FLASH->KEYR1, FLASH_KEY2);
/* Verify Flash Bank1 is unlocked */
if (READ_BIT(FLASH->CR1, FLASH_CR_LOCK) != 0U)
return HAL_ERROR;
}
if(READ_BIT(FLASH->CR2, FLASH_CR_LOCK) != 0U)
{
/* Authorize the FLASH Bank2 Registers access */
WRITE_REG(FLASH->KEYR2, FLASH_KEY1);
WRITE_REG(FLASH->KEYR2, FLASH_KEY2);
/* Verify Flash Bank2 is unlocked */
if (READ_BIT(FLASH->CR2, FLASH_CR_LOCK) != 0U)
return HAL_ERROR;
}
return HAL_OK;
}
__RAM_FUNC HAL_StatusTypeDef RAM_FLASH_Lock(void)
{
/* Set the LOCK Bit to lock the FLASH Bank1 Control Register access */
SET_BIT(FLASH->CR1, FLASH_CR_LOCK);
/* Verify Flash Bank1 is locked */
if (READ_BIT(FLASH->CR1, FLASH_CR_LOCK) == 0U)
return HAL_ERROR;
/* Set the LOCK Bit to lock the FLASH Bank2 Control Register access */
SET_BIT(FLASH->CR2, FLASH_CR_LOCK);
/* Verify Flash Bank2 is locked */
if (READ_BIT(FLASH->CR2, FLASH_CR_LOCK) == 0U)
return HAL_ERROR;
return HAL_OK;
}
__RAM_FUNC HAL_StatusTypeDef RAM_FLASH_WaitForLastOperation(uint32_t Timeout, uint32_t bank)
{
if ((bank != FLASH_BANK_1) && (bank != FLASH_BANK_2)) { return HAL_ERROR; }
if (bank == FLASH_BANK_1)
{
uint32_t errorflag = FLASH->SR1 & FLASH_FLAG_ALL_ERRORS_BANK1;
while (FLASH->SR1 & FLASH_FLAG_QW_BANK1) {}
/* In case of error reported in Flash SR1 or SR2 register */
if((errorflag & 0x7FFFFFFFU) != 0U)
{
/*Save the error code*/
pFlash.ErrorCode |= errorflag;
/* Clear error programming flags */
FLASH->CCR1 = errorflag;
return HAL_ERROR;
}
if (FLASH->SR1 & FLASH_FLAG_EOP_BANK1)
{
FLASH->SR1 = FLASH_FLAG_EOP_BANK1;
}
}
else if (bank == FLASH_BANK_2)
{
uint32_t errorflag = FLASH->SR2 & FLASH_FLAG_ALL_ERRORS_BANK2;
while (FLASH->SR2 & FLASH_FLAG_QW_BANK2) {}
/* In case of error reported in Flash SR1 or SR2 register */
if((errorflag & 0x7FFFFFFFU) != 0U)
{
/*Save the error code*/
pFlash.ErrorCode |= errorflag;
/* Clear error programming flags */
FLASH->CCR2 = errorflag;
return HAL_ERROR;
}
if (FLASH->SR2 & FLASH_FLAG_EOP_BANK2)
{
FLASH->SR2 = FLASH_FLAG_EOP_BANK2;
}
}
return HAL_OK;
}
__RAM_FUNC void RAM_FLASH_Erase_Sector(uint32_t Sector, uint32_t Banks, uint32_t VoltageRange)
{
assert_param(IS_VOLTAGERANGE(VoltageRange));
if((Banks & FLASH_BANK_1) == FLASH_BANK_1)
{
/* Reset Program/erase VoltageRange and Sector Number for Bank1 */
FLASH->CR1 &= ~(FLASH_CR_PSIZE | FLASH_CR_SNB);
FLASH->CR1 |= (FLASH_CR_SER | VoltageRange | (Sector << FLASH_CR_SNB_Pos) | FLASH_CR_START);
}
if((Banks & FLASH_BANK_2) == FLASH_BANK_2)
{
/* Reset Program/erase VoltageRange and Sector Number for Bank2 */
FLASH->CR2 &= ~(FLASH_CR_PSIZE | FLASH_CR_SNB);
FLASH->CR2 |= (FLASH_CR_SER | VoltageRange | (Sector << FLASH_CR_SNB_Pos) | FLASH_CR_START);
}
}
__RAM_FUNC HAL_StatusTypeDef RAM_FLASHEx_Erase(FLASH_EraseInitTypeDef *pEraseInit, uint32_t *SectorError)
{
HAL_StatusTypeDef status = HAL_OK;
uint32_t sector_index;
/* Check the parameters */
assert_param(IS_FLASH_TYPEERASE(pEraseInit->TypeErase));
assert_param(IS_FLASH_BANK(pEraseInit->Banks));
/* Process Locked */
__HAL_LOCK(&pFlash);
/* Reset error code */
pFlash.ErrorCode = HAL_FLASH_ERROR_NONE;
/* Wait for last operation to be completed on Bank1 */
if((pEraseInit->Banks & FLASH_BANK_1) == FLASH_BANK_1)
{
if(RAM_FLASH_WaitForLastOperation((uint32_t)50000U, FLASH_BANK_1) != HAL_OK)
{
status = HAL_ERROR;
}
}
#if defined (DUAL_BANK)
/* Wait for last operation to be completed on Bank2 */
if((pEraseInit->Banks & FLASH_BANK_2) == FLASH_BANK_2)
{
if(RAM_FLASH_WaitForLastOperation((uint32_t)50000U, FLASH_BANK_2) != HAL_OK)
{
status = HAL_ERROR;
}
}
#endif /* DUAL_BANK */
if(status == HAL_OK)
{
/*Initialization of SectorError variable*/
*SectorError = 0xFFFFFFFFU;
/* Erase by sector by sector to be done*/
for(sector_index = pEraseInit->Sector; sector_index < (pEraseInit->NbSectors + pEraseInit->Sector); sector_index++)
{
RAM_FLASH_Erase_Sector(sector_index, pEraseInit->Banks, pEraseInit->VoltageRange);
if((pEraseInit->Banks & FLASH_BANK_1) == FLASH_BANK_1)
{
/* Wait for last operation to be completed */
status = RAM_FLASH_WaitForLastOperation((uint32_t)50000U, FLASH_BANK_1);
/* If the erase operation is completed, disable the SER Bit */
FLASH->CR1 &= (~(FLASH_CR_SER | FLASH_CR_SNB));
}
#if defined (DUAL_BANK)
if((pEraseInit->Banks & FLASH_BANK_2) == FLASH_BANK_2)
{
/* Wait for last operation to be completed */
status = RAM_FLASH_WaitForLastOperation((uint32_t)50000U, FLASH_BANK_2);
/* If the erase operation is completed, disable the SER Bit */
FLASH->CR2 &= (~(FLASH_CR_SER | FLASH_CR_SNB));
}
#endif /* DUAL_BANK */
if(status != HAL_OK)
{
/* In case of error, stop erase procedure and return the faulty sector */
*SectorError = sector_index;
break;
}
}
}
/* Process Unlocked */
__HAL_UNLOCK(&pFlash);
return status;
}--------------------------------------------------------------------------------
Here is the listing of the NVIC:
--------------------------------------------------------------------------------
Reference Manual:
2022-02-26 5:16 PM
Once I enabled WRP. I am getting the hard fault above on the SCB_CleanDCache() execution. Note the sectors which are being erased do not have WRP. But where this code is executing from does have WRP.
I would assume the next step is to use SCB_CleanDCache_by_Addr().
The function description for this method is below for reference.
/**
\brief D-Cache Clean by address
\details Cleans D-Cache for the given address
D-Cache is cleaned starting from a 32 byte aligned address in 32 byte granularity.
D-Cache memory blocks which are part of given address + given size are cleaned.
\param[in] addr address
\param[in] dsize size of memory block (in number of bytes)
*/
__STATIC_FORCEINLINE void SCB_CleanDCache_by_Addr (uint32_t *addr, int32_t dsize)Suppose the sector I want to clean is defined as follows.
#define ADDR_FLASH_BANK_1_SECTOR_3_START ((uint32_t)0x8060000) /* Base @ of SECTOR 3, 128KByte */
#define ADDR_FLASH_BANK_1_SECTOR_3_END ((uint32_t)0x807FFFF) /* Base @ of SECTOR 3, 128KByte */If I wanted to use this SCB_CleanDCache_by_Addr method on an entire sector which starts at 0x8060000 would I use this method like so?
SCB_CleanDCache_by_Addr(0x8060000, 131071)
Where, 131071 = 0x807FFFF - 0x8060000
2022-02-27 9:12 AM
(1) For simplicity sake, just disable interrupts and do not tinker with the vectors at all.
(2) see this thread. last message from @piranha
(3) Just do not enable Dcache or Icache
> Apart from disabling WRP are there any other considerations?
There can be some other complications such as proprietary code protected areas, but unlikely that you have that.
We’re moving the ST Community to a new platform to give you a better and more reliable community experience.