cancel
Showing results for 
Search instead for 
Did you mean: 

Sector Erasing in STM32--chip crashes on 256kB erase

rsoc
Associate III

I am working on erasing sectors sequentially in my MCU, from Sector 2 through Sector 7 (the size of flash where my firmware will live). I have found that sequential sector erases will occasionally fail, and any attempt to erase a sector of size 256kB will crash the MCU.

My chip is fed 1.8V steady, so it is in the HAL VOLTAGE_RANGE_1, erasing bytes. My code is below, with the HAL function for sector erasing directly out of the zip file. Am I missing something at a software level? Or is it related to the 1.8V level. Erasing a sector of 256 kB should be possible with that voltage level correct? Also happy to clarify if need be.

uint8_t Bootloader_Erase(void)
{
    HAL_StatusTypeDef FlashStatus = HAL_OK;
    HAL_FLASH_Unlock();
    FLASH_EraseInitTypeDef pEraseInit;
    pEraseInit.TypeErase = TYPEERASE_SECTORS;
    pEraseInit.Sector = 5;
    pEraseInit.NbSectors = 1;
    pEraseInit.VoltageRange = FLASH_VOLTAGE_RANGE_1;
    FlashStatus = HAL_FLASHEx_Erase(&pEraseInit, &SectorError);
    HAL_FLASH_Lock();
    if (FlashStatus != HAL_OK)
    {
        return 0;
    }
    else {return 1;}
}
 
HAL_StatusTypeDef HAL_FLASHEx_Erase(FLASH_EraseInitTypeDef *pEraseInit, uint32_t *SectorError)
{
  HAL_StatusTypeDef status = HAL_ERROR;
  uint32_t index = 0;
  
  /* Process Locked */
  __HAL_LOCK(&pFlash);
 
  /* Check the parameters */
  // assert_param(IS_FLASH_TYPEERASE(pEraseInit->TypeErase));
 
  /* Wait for last operation to be completed */
  status = FLASH_WaitForLastOperation((uint32_t)FLASH_TIMEOUT_VALUE);
 
  if(status == HAL_OK)
  {
    /*Initialization of SectorError variable*/
    *SectorError = 0xFFFFFFFFU;
    
    /* Check the parameters */
    // assert_param(IS_FLASH_NBSECTORS(pEraseInit->NbSectors + pEraseInit->Sector));
 
    /* Erase by sector by sector to be done*/
    for(index = pEraseInit->Sector; index < (pEraseInit->NbSectors + pEraseInit->Sector); index++)
    {
      FLASH_Erase_Sector(index, (uint8_t) pEraseInit->VoltageRange);
 
      /* Wait for last operation to be completed */
      status = FLASH_WaitForLastOperation((uint32_t)FLASH_TIMEOUT_VALUE);
      
      /* If the erase operation is completed, disable the SER Bit and SNB Bits */
      CLEAR_BIT(FLASH->CR, (FLASH_CR_SER | FLASH_CR_SNB)); 
 
      if(status != HAL_OK) 
      {
        /* In case of error, stop erase procedure and return the faulty sector*/
        *SectorError = index;
        break;
      }
    }
  }
  /* Process Unlocked */
  __HAL_UNLOCK(&pFlash);
  return status;
}
 
HAL_StatusTypeDef FLASH_Erase_Sector(uint32_t Sector, uint8_t VoltageRange)
{
  uint32_t tmp_psize = 0;
  
  if(VoltageRange == FLASH_VOLTAGE_RANGE_1)
  {
     tmp_psize = FLASH_PSIZE_BYTE;
  }
  else if(VoltageRange == FLASH_VOLTAGE_RANGE_2)
  {
    tmp_psize = FLASH_PSIZE_HALF_WORD;
  }
  else if(VoltageRange == FLASH_VOLTAGE_RANGE_3)
  {
    tmp_psize = FLASH_PSIZE_WORD;
  }
  else
  {
    tmp_psize = FLASH_PSIZE_DOUBLE_WORD;
  }
 
  /* If the previous operation is completed, proceed to erase the sector */
  FLASH->CR &= CR_PSIZE_MASK;
  FLASH->CR |= tmp_psize;
  FLASH->CR &= SECTOR_MASK;
  FLASH->CR |= FLASH_CR_SER | (Sector << POSITION_VAL(FLASH_CR_SNB));
  FLASH->CR |= FLASH_CR_STRT;
  
  /* Data synchronous Barrier (DSB) Just after the write operation
     This will force the CPU to respect the sequence of instruction (no optimization).*/
  __DSB();
}

4 REPLIES 4

F7 I presume, please state exactly what "STM32" is being discussed when opening a new thread, it saves confusion.

>> Or is it related to the 1.8V level.

Perhaps try the same functionality on parts where this isn't the case to eliminate it as a possible reason.

>>chip crashes on 256kB erase

If you stop it in a debugger, where is it? Are you ending up in the Hard Fault Handler, or some other while(1) loop some where.

Caching or stacking could be issues?

You have a watchdog?

Use the form

FLASH_EraseInitTypeDef pEraseInit = {0};

to ensure auto/local variable doesn't contain random junk.

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

And again...

FLASH->CR &= CR_PSIZE_MASK;
FLASH->CR &= SECTOR_MASK;

From Your previous answer: "Wouldn't the premise of that line be to clear the bit before the next is set?"

That is what You intended to do, but it's not what these lines do. Execute these operations on example data in Your mind or on paper and look what You get!

This library code, if I'm not mistaken.

Really though, if there is a problem, a minimal, compilable, example illustrating the issue is what is needed. Not something I'm going to debug through a key-hole.

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

OK, I looked more carefully and realized that CR_PSIZE_MASK and SECTOR_MASK are binary inverted masks. Apparently there was some "genius" thinking that making few masks inverted and different from all the other code base is a good idea and doesn't make code more ambiguous. And in a current F7 HAL version now they have this:

FLASH->CR &= CR_PSIZE_MASK;
FLASH->CR |= tmp_psize;
CLEAR_BIT(FLASH->CR, FLASH_CR_SNB);
FLASH->CR |= FLASH_CR_SER | (Sector << FLASH_CR_SNB_Pos);
FLASH->CR |= FLASH_CR_STRT;

Yes, even more chaos! And will it be impolite to remind ST's code monkeys that multiple unnecessary writes to volatile variable (register) is plain stupid? In a few minutes programmer with a brain can rewrite all of that junk code as simple and effective as this:

uint32_t rCR = FLASH->CR;
 
rCR &= ~(FLASH_CR_PSIZE | FLASH_CR_SNB);
...
rCR |= FLASH_PSIZE_***;
...
rCR |= FLASH_CR_STRT | (Sector << FLASH_CR_SNB_Pos) | FLASH_CR_SER;
FLASH->CR = rCR;