0
\$\begingroup\$

I'm trying to erase a single Flash page with the help of the standard HAL library, but it does not clear the page. I've never done that before. More details below.

Resources:

Now, because I want to erase a single flash page, I chose the last page, as declared below. With the IDE I'm using (STM32CubeIDE) I can view the program memory (in hex mode) when debugging. I happened to find out, that at address 0x0801FFFF there are only light gray ????????. I did not touch the read protection. Out of curiosity, I viewed address 0x0800FFFF and it actually is the last address where there is data, in my case a bunch of FFFFFFFF and adjusted the code appropriately.

#define FLASH_USER_START_ADDR   0x0800F800  // not 0x0801F800 
#define FLASH_USER_END_ADDR     0x0800FFFF  // not 0x0801FFFF

This already throws some question marks around in my head... If anyone knows or could help me find out why the technically last address has no data, I'd not say no. If there's no answer to this problem it does not bother me, because the real deal starts below.

I took the code for erasing pretty much 1:1 from the Patch_CubeL4 flash erase example and it looks like the following.

int main(void)
{
  // flash variables
  static FLASH_EraseInitTypeDef EraseInitStruct;
  uint32_t FirstPage = 0;
  uint32_t NbOfPages = 0;
  uint32_t BankNumber = 0;
  uint32_t PAGEError = 0;

// unlock the flash to access control register
  if(HAL_FLASH_Unlock() != HAL_OK)
  {
      // unlock error
      while (1)
      {
          HAL_GPIO_TogglePin(STATUS_GPIO_Port, STATUS_Pin);
          HAL_Delay(20);
      }
  }
  
  // clear OPTVERR register 
  __HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_OPTVERR);
  // get 1st page
  FirstPage = GetPage(FLASH_USER_START_ADDR);
  // get number of pages
  NbOfPages = GetPage(FLASH_USER_END_ADDR) - FirstPage + 1;
  // get bank
  BankNumber = GetBank(FLASH_USER_START_ADDR);
  // EraseInitStruct
  EraseInitStruct.TypeErase   = FLASH_TYPEERASE_PAGES;
  EraseInitStruct.Banks       = BankNumber;
  EraseInitStruct.Page        = FirstPage;
  EraseInitStruct.NbPages     = NbOfPages;

  // erase
  if (HAL_FLASHEx_Erase(&EraseInitStruct, &PAGEError) != HAL_OK)
  {
      // Erase Error
      while (1)
      {
          HAL_GPIO_TogglePin(STATUS_GPIO_Port, STATUS_Pin);  // toggles status LED
          HAL_Delay(100);
      }
  }

  // lock the flash!
  HAL_FLASH_Lock();

  // let's assume here is a main loop
  while(1) 
  {
    // some code...
  }
}

/**
  * @brief  Gets the page of a given address
  * @param  Addr: Address of the FLASH Memory
  * @retval The page of a given address
  */
static uint32_t GetPage(uint32_t Addr)    // from Patch_CubeL4 erase example
{ 
  uint32_t page = 0;

  if (Addr < (FLASH_BASE + FLASH_BANK_SIZE))
  {
    /* Bank 1 */
    page = (Addr - FLASH_BASE) / FLASH_PAGE_SIZE;
  }
  else
  {
    /* Bank 2 */ 
    // page = (Addr - (FLASH_BASE + FLASH_BANK_SIZE)) / FLASH_PAGE_SIZE; -> Bank 2 is nonexistent
  }

  return page;
}


/**
  * @brief  Gets the bank of a given address
  * @param  Addr: Address of the FLASH Memory
  * @retval The bank of a given address
  */
static uint32_t GetBank(uint32_t Addr)    // from Patch_CubeL4 erase example
{
  uint32_t bank = 0;
  // SYSCFG_MEMRMP_FB_MODE
  if (READ_BIT(SYSCFG->MEMRMP, SYSCFG_MEMRMP_MEM_MODE_0) == 0)
  {
    /* No Bank swap */
    if (Addr < (FLASH_BASE + FLASH_BANK_SIZE))
    {
      bank = FLASH_BANK_1;
    }
    else
    {
      // bank = FLASH_BANK_2; -> Bank 2 is nonexistent
    }
  }
  else
  {
    /* Bank swap */
    if (Addr < (FLASH_BASE + FLASH_BANK_SIZE))
    {
      //  bank = FLASH_BANK_2; -> Bank 2 is nonexistent
    }
    else
    {
      bank = FLASH_BANK_1;
    }
  }

  return bank;
}

The program reached my main loop (the HAL_FLASHEx_Erase returns HAL_OK), that's when I started debugging step by step and followed the erase sequence until I found the error.

Here's what I found in the programming manual (page 83/1600):

Page erase
To erase a page (2 Kbyte), follow the procedure below:
1. Check that no Flash memory operation is ongoing by checking the BSY bit in the Flash status register (FLASH_SR).
2. Check and clear all error programming flags due to a previous programming. If not, PGSERR is set.
3. Set the PER bit and select the page you wish to erase (PNB) in the Flash control register (FLASH_CR).
4. Set the STRT bit in the FLASH_CR register.
5. Wait for the BSY bit to be cleared in the FLASH_SR register

The function HAL_FLASHEx_Erase handles all of the points mentioned above. In fact, the code also goes through all points! It reaches the code that clears all errors, sets the PER bit, selects the page (the PNB bits are set to 0x1F after that piece of code) and, finally, sets the STRT bit. (and waits for the BSY bit to be cleared)

That's the point, where I need help. I can't figure out, why the flash is not cleared (every address still set to 0xFFFFFFFF) after the erase sequence.

For completion's sake, this is the HAL_FLASHEx_Erase function and where the ifs turn.

HAL_StatusTypeDef HAL_FLASHEx_Erase(FLASH_EraseInitTypeDef *pEraseInit, uint32_t *PageError)
{
  HAL_StatusTypeDef status;
  uint32_t page_index;

  /* 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)  // is true when debugging
  {
    pFlash.ErrorCode = HAL_FLASH_ERROR_NONE;

    /* Deactivate the cache if they are activated to avoid data misbehavior */
    if(READ_BIT(FLASH->ACR, FLASH_ACR_ICEN) != 0U)  // is true when debugging
    {
      /* Disable instruction cache  */
      __HAL_FLASH_INSTRUCTION_CACHE_DISABLE();

      if(READ_BIT(FLASH->ACR, FLASH_ACR_DCEN) != 0U)  // is true when debugging
      {
        /* Disable data cache  */
        __HAL_FLASH_DATA_CACHE_DISABLE();
        pFlash.CacheToReactivate = FLASH_CACHE_ICACHE_DCACHE_ENABLED;
      }
      else  // never entered
      {
        pFlash.CacheToReactivate = FLASH_CACHE_ICACHE_ENABLED;
      }
    }
    else if(READ_BIT(FLASH->ACR, FLASH_ACR_DCEN) != 0U)  // never entered
    {
      /* Disable data cache  */
      __HAL_FLASH_DATA_CACHE_DISABLE();
      pFlash.CacheToReactivate = FLASH_CACHE_DCACHE_ENABLED;
    }
    else  // never entered
    {
      pFlash.CacheToReactivate = FLASH_CACHE_DISABLED;
    }

    if (pEraseInit->TypeErase == FLASH_TYPEERASE_MASSERASE)  // not entered, MASSERASE not selected
    {
      /* Mass erase to be done */
      FLASH_MassErase(pEraseInit->Banks);

      /* Wait for last operation to be completed */
      status = FLASH_WaitForLastOperation((uint32_t)FLASH_TIMEOUT_VALUE);
// the #if part is ignored
#if defined (STM32L471xx) || defined (STM32L475xx) || defined (STM32L476xx) || defined (STM32L485xx) || defined (STM32L486xx) || \
    defined (STM32L496xx) || defined (STM32L4A6xx) || \
    defined (STM32L4P5xx) || defined (STM32L4Q5xx) || \
    defined (STM32L4R5xx) || defined (STM32L4R7xx) || defined (STM32L4R9xx) || defined (STM32L4S5xx) || defined (STM32L4S7xx) || defined (STM32L4S9xx)
      /* If the erase operation is completed, disable the MER1 and MER2 Bits */
      CLEAR_BIT(FLASH->CR, (FLASH_CR_MER1 | FLASH_CR_MER2));
#else
      /* If the erase operation is completed, disable the MER1 Bit */
      CLEAR_BIT(FLASH->CR, (FLASH_CR_MER1));
#endif
    }
    else  // enters here
    {
      /*Initialization of PageError variable*/
      *PageError = 0xFFFFFFFFU;

      for(page_index = pEraseInit->Page; page_index < (pEraseInit->Page + pEraseInit->NbPages); page_index++)
      {
        FLASH_PageErase(page_index, pEraseInit->Banks);  // enters this function, see below

        /* Wait for last operation to be completed */
        status = FLASH_WaitForLastOperation((uint32_t)FLASH_TIMEOUT_VALUE);

        /* If the erase operation is completed, disable the PER Bit */
        CLEAR_BIT(FLASH->CR, (FLASH_CR_PER | FLASH_CR_PNB));

        if (status != HAL_OK)  // is false when debugging
        {
          /* In case of error, stop erase procedure and return the faulty address */
          *PageError = page_index;
          break;
        }
      }
    }

    /* Flush the caches to be sure of the data consistency */
    FLASH_FlushCaches();
  }

  /* Process Unlocked */
  __HAL_UNLOCK(&pFlash);

  return status;
}

And also the FLASH_PageErase function and the path it takes when debugging.

void FLASH_PageErase(uint32_t Page, uint32_t Banks)
{
  /* Check the parameters */
  assert_param(IS_FLASH_PAGE(Page));

// #if is ignored
#if defined (STM32L471xx) || defined (STM32L475xx) || defined (STM32L476xx) || defined (STM32L485xx) || defined (STM32L486xx) || \
    defined (STM32L496xx) || defined (STM32L4A6xx) || \
    defined (STM32L4P5xx) || defined (STM32L4Q5xx) || \
    defined (STM32L4R5xx) || defined (STM32L4R7xx) || defined (STM32L4R9xx) || defined (STM32L4S5xx) || defined (STM32L4S7xx) || defined (STM32L4S9xx)
// #if is ignored
#if defined (STM32L4P5xx) || defined (STM32L4Q5xx) || defined (STM32L4R5xx) || defined (STM32L4R7xx) || defined (STM32L4R9xx) || defined (STM32L4S5xx) || defined (STM32L4S7xx) || defined (STM32L4S9xx)
  if(READ_BIT(FLASH->OPTR, FLASH_OPTR_DBANK) == 0U)
  {
    CLEAR_BIT(FLASH->CR, FLASH_CR_BKER);
  }
  else
#endif
  {
    assert_param(IS_FLASH_BANK_EXCLUSIVE(Banks));

    if((Banks & FLASH_BANK_1) != 0U)
    {
      CLEAR_BIT(FLASH->CR, FLASH_CR_BKER);
    }
    else
    {
      SET_BIT(FLASH->CR, FLASH_CR_BKER);
    }
  }
#else  // this #else is compiled
  /* Prevent unused argument(s) compilation warning */
  UNUSED(Banks);
#endif

  /* Proceed to erase the page */
  MODIFY_REG(FLASH->CR, FLASH_CR_PNB, ((Page & 0xFFU) << FLASH_CR_PNB_Pos));  
  SET_BIT(FLASH->CR, FLASH_CR_PER);
  SET_BIT(FLASH->CR, FLASH_CR_STRT);  // START bit gets set on this line
}

The flash registers all seem to be what they're supposed to, but if there is something I'm missing out on, below's a picture with their states, right before the START bit is set. And again, the flash has not changed at all after the sequence! The whole page is still all FFFFFFFF's...

Flash register status before clearing

If anyone can help me I'm truly grateful, thanks in advance!

\$\endgroup\$
1
  • \$\begingroup\$ When you look at 0x0801FFFF and see ????????, it's because you're pointing at the very last byte of program memory and your debugger is trying to read a 32-bit word, which results in it trying to read up to 0x08020003 - which doesn't exist. Point your debugger to 0x0801FFFC or lower and it should work fine. \$\endgroup\$
    – brhans
    Jul 17 '20 at 15:17
0
\$\begingroup\$

Erasing Flash means all the bytes gets erased to 0xFF, not 0x00. Flash being programmed means setting bits to 0. The Flash was already unprogrammed and erasing it won't change anything, it still reads all bytes 0xFF.

\$\endgroup\$
1
  • \$\begingroup\$ Oh, thanks. Now I have to figure out how to write to flash. I already tried that and, if I have it correctly in mind, I thought it checks if the data in the specified address is 0x00 and if that's not the case it does not write... \$\endgroup\$
    – rphii
    Jul 17 '20 at 10:28
0
\$\begingroup\$

Try to read memory with variables and step through them. For example try.

uint8_t Readflasharray[pagesize]; Memcpy(ReadFlasharray,flashloc,pagesize) EraseFlase() Memcpy(ReadFlasharray,flashloc,pagesize)

Then breakpoint after after memcopy(), and read the array values in the watch/expression window.

This will make sure you are erasing the memory. For certain reasons, the debugger won't read flash. This way you are bypassing the debugger and reading directly into your application.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.