cancel
Showing results for 
Search instead for 
Did you mean: 

Flash erase is unreliable on STM32H743 (Code Provided)

AAgar.2
Associate III

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:

0693W00000KaJoHQAV.png 

--------------------------------------------------------------------------------

Reference Manual:

https://www.st.com/resource/en/reference_manual/dm00314099-stm32h742-stm32h743-753-and-stm32h750-value-line-advanced-arm-based-32-bit-mcus-stmicroelectronics.pdf

11 REPLIES 11
  1. // Invalidate data cache
  2. SCB_InvalidateDCache();

The smarter move would be to use the "by_Addr" variant which doesn't have all the nasty side-effects you've ignored.

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
AAgar.2
Associate III

Appreciate your support.

What are the possible nasty effects?

When I did not have SCB_InvalidateDCache(); the issue was still happening.

Couple of follow-up questions:

1) One of my possible theories for this issue is that maybe I am disabling an interrupt that is required for the flash erase, do you think this could be possible? I have read the reference manual but I could not find something like this.

2) I am using Stm32CubeIDE. I am unable to put break points in code which runs on RAM. Is there a way to do this?

3) Is there a variable I could probe to observe the sector erase explicitly?

Pavel A.
Evangelist III

(1) No there's no such interrupt. Ensure that systick interrupt does not occur (or relocate the vectors to RAM along with systick handler)

(2) At least with ST-LINK and JLINK, breakpoints in AXI RAM just work. Are you using ITCM or DTCM RAM?

(3) as in your RAM_FLASH_WaitForLastOperation() - the EOP flag.

To exclude effect of caches and the "speculative execution" issue, can you try this again with all caches disabled? (do not call invalidate cache then, because it may fault when cache is disabled).

AAgar.2
Associate III

Thanks for your response.

(1) I had already moved the NVIC to ram (ITCM) via the following code. The systick interrupt still causes an error if I do not disable interrupts.

#define VECTOR_SIZE 166
uint32_t vectorTable[VECTOR_SIZE] __attribute__ ((aligned(512), section(".INST_RAM")));
 
int main(void)
{
  memcpy(vectorTable, (uint32_t*)0x08000000, sizeof(uint32_t) * VECTOR_SIZE);
  SCB->VTOR = (uint32_t)vectorTable;

(2) All the ram functions go into ITCM ram via the following code

/* Initialized data sections into "ITCMRAM" Ram type memory */
  .data :
  {
    . = ALIGN(4);
    _sdata = .;        /* create a global symbol at data start */
    *(.data)           /* .data sections */
    *(.data*)          /* .data* sections */
    *(.RamFunc)        /* .RamFunc sections */
    *(.RamFunc*)       /* .RamFunc* sections */
 
    . = ALIGN(4);
    _edata = .;        /* define a global symbol at data end */
 
  } >ITCMRAM AT> FLASH

(3) I see so I can check the EOP flag in live expressions?

(4) Do you mean I should call disable cache as the following?

// Disable interrupts
__set_BASEPRI(0001 << __NVIC_PRIO_BITS);
 
// Disable DCache
SCB_DisableDCache();
 
 
// Erase flash
erase_flash();
 
// Invalidate data cache
// SCB_InvalidateDCache();
 
// Enable interrupts
__set_BASEPRI(0);

>>What are the possible nasty effects?

Everything in the cache is destroyed, any pending data, data in memory doesn't reflect what you think you wrote there. So writes to RAM, the STACK, typical consequences are Hard Faults on function returns, or via pointer/variables on the stack (auto/local). Interrupts pushing/popping context onto the stack.

CleanDCache is a FLUSH

InvalidateDCache is a NUKE with no regard to what's touched

You know the areas of FLASH you're erasing, why can't you just contain the damage to those specific areas? ie SCB_InvalidateDCache_by_Addr()

I'm not sure disabling interrupts has any net value, you're a) presumably not erasing active code, b) if anything touches flash the processor is simply going to stall execution by stuffing wait-states.

The FLASH peripheral should flag it is busy with Erase or Write operations.

Before you start the Flash operation you use SCB_CleanDCache() to flush any pending discrepancies between cache and memory

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
Pavel A.
Evangelist III

Well, you've moved the table, but the handlers still are in the flash? or they are in .RamFunc?

To keep it simple, disable all interrupts with __disable_irq().

By the way alignment of vector table must be 1024, I've just learned this in a hard way.

The cache is disabled after reset, simply do not enable it.

When it starts working, add some timeout detection instead of unlimited loops in lines 95, 118,

Check that there's no write protected sectors.

Fascinating. I have added SCB_CleanDCache() as per below and now I cannot reproduce the problem. Kind of annoying since I would like to reproduce it and confirm the fix.

// Invalidate data cache
SCB_CleanDCache();
 
// Disable interrupts
__set_BASEPRI(0001 << __NVIC_PRIO_BITS);
 
// Erase app
erase_flash();
 
// Invalidate data cache
// SCB_InvalidateDCache();
 
// Enable interrupts
__set_BASEPRI(0);

 I will go ahead and add SCB_InvalidateDCache_by_Addr() as well and verify.

Any ideas as to what could have been the root cause?

AAgar.2
Associate III

1) Well, you've moved the table, but the handlers still are in the flash? or they are in .RamFunc?

More likely than not they are in flash. How can I move them to Ram? Would adding the .RamFunc directive be sufficient?

2) By the way alignment of vector table must be 1024.

Do you have a source for this? I've been using 512 for the last little while.

3) The cache is disabled after reset, simply do not enable it.

I have added the following after main(). It always hard faults with this.

__disable_irq();
SCB_DisableDCache();
__enable_irq();

 4) Check that there's no write protected sectors.

Confirmed no RDP or WRP. I do want to add those in soon. Apart from disabling WRP are there any other considerations?

Well like I said, SCB_InvalidateDCache() is a bit indiscriminate, damaging the stack content is a quick way to get Hard Faults and unpredictable behaviour.

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..