2022-02-23 02: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 05: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 09: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.