Skip to content

EEPROM emulation example - bugs #327

@khevessy

Description

@khevessy

Describe the set-up
The example was ported to our custom board with STM32H735V and there I discovered the bugs.
IDE used is STM32CubeIDE version 1.19.0.

Describe the bug

  1. As mentioned here, there are missing calls to SCB_EnableICache() before exit points in EE_VerifyPageFullWriteVariable().

    SCB_DisableICache();
    /* Check each active page address starting from beginning */
    while (Address < PageEndAddress)
    {
    /* Verify if Address and Address+2 contents are 0xFFFFFFFF */
    if ((*(__IO uint32_t*)Address) == 0xFFFFFFFF)
    {
    /* Set variable data */
    FlashStatus = HAL_FLASH_Program(FLASH_TYPEPROGRAM_FLASHWORD, Address, ((uint32_t)data32));
    /* If program operation was failed, a Flash error code is returned */
    if (FlashStatus != HAL_OK)
    {
    return FlashStatus;
    }
    /* Set variable virtual address */
    FlashStatus = HAL_FLASH_Program(FLASH_TYPEPROGRAM_FLASHWORD, Address + 32, ((uint32_t)VirtAddress1));
    /* Return program operation status */
    return FlashStatus;
    }
    else
    {
    /* Next address location */
    Address = Address + 64;
    }
    }
    SCB_EnableICache();

    Instruction cache then may end up disabled forever.

  2. EE_VerifyPageFullyErased() - if really the whole sector is to be checked, either increments have to be 2-byte or dereferenced value be uint32_t (four bytes). I prefer the former since the value is checked against ERASED which is uint16_t.

    EndAddress = (uint32_t)(Address + (PAGE_SIZE - 4U));
    /* Check each active page address starting from end */
    while (Address <= EndAddress)
    {
    /* Get the current location content to be compared with virtual address */
    AddressValue = (*(__IO uint16_t*)Address);
    /* Compare the read address with the virtual address */
    if (AddressValue != ERASED)
    {
    /* In case variable value is read, reset ReadStatus flag */
    ReadStatus = 0;
    break;
    }
    /* Next address location */
    Address = Address + 4;
    }

    My solution is that EndAddress = (uint32_t)(Address + (PAGE_SIZE - 2U)); and increment Address = Address + 2;.

  3. EE_VerifyPageFullWriteVariable() does not actually check if all the bytes are set to 0xFFFFFFFF. Since there are two 32-byte writes, actually 16 32-bit words should be checked for 0xFFFFFFFF.

    /* Verify if Address and Address+2 contents are 0xFFFFFFFF */
    if ((*(__IO uint32_t*)Address) == 0xFFFFFFFF)
    {
    /* Set variable data */
    FlashStatus = HAL_FLASH_Program(FLASH_TYPEPROGRAM_FLASHWORD, Address, ((uint32_t)data32));
    /* If program operation was failed, a Flash error code is returned */
    if (FlashStatus != HAL_OK)
    {
    return FlashStatus;
    }
    /* Set variable virtual address */
    FlashStatus = HAL_FLASH_Program(FLASH_TYPEPROGRAM_FLASHWORD, Address + 32, ((uint32_t)VirtAddress1));

    The code actually checks only the first word. My fix was along the lines of
    if (is_flashword_erased(Address) && is_flashword_erased(Address + 32))
    with

static inline int is_flashword_erased(uint32_t addr) {
    for (int i = 0; i < 8; ++i) {
        if (*((__IO uint32_t*)(addr + i*4)) != 0xFFFFFFFFU) return 0;
    }
    return 1;
}
  1. This is the gravest one that I think actually caused our problems. It consists of three parts:
    a. Wrong offset in EE_Init() initial check for detecting the variable.
    if (( *(__IO uint16_t*)(PAGE0_BASE_ADDRESS + 6)) == VirtAddVarTab[VarIdx])

    Where does the value 6 come from? I think that the correct should be 128 - 64 for the header and 64 for the address offset. So actually
    if (( *(__IO uint16_t*)(PAGE0_BASE_ADDRESS + PAGE_HEADER_SIZE + EE_ADDRESS_OFFSET)) == VirtAddVarTab[VarIdx]) where PAGE_HEADER_SIZE is 64 and EE_ADDRESS_OFFSET is 64.
    b. Wrong stopping address in EE_ReadVariable() - the last check goes to the page header.
    . I think that the correct code is
    while (Address >= (PageStartAddress + PAGE_HEADER_SIZE))
    or maybe just > since the first four-word actually contains the variable value.
    c. EE_VerifyPageFullWriteVariable() start address - this is the main problem
    Address = (uint32_t)(EEPROM_START_ADDRESS + (uint32_t)(ValidPage * PAGE_SIZE));

    The check actually starts at the sector beginning which should contain the page header. Even though it should not be erased if it is a valid page, this ends up colliding with the header after some time. I think that the correct start address should be Address = (uint32_t)(EEPROM_START_ADDRESS + (uint32_t)(ValidPage * PAGE_SIZE) + PAGE_HEADER_SIZE);

How To Reproduce
With intensive writing, I always end up with both pages containing data with the page header inconsistent which in turn results in deleting of one of the pages and missing data. I think that it was caused by the point 4. which caused header invalidation and subsequent data inconsistency. With the fixes the code now seems to be running reliably.

Aditional Context
Again, also relevant to STM32H743I-EVAL. Not sure for NUCLEO-H7A3ZI-Q, maybe all the offsets I introduce should be halved since the writes are half the size.

Metadata

Metadata

Assignees

Labels

bugSomething isn't workinginternal bug trackerIssue confirmed and logged into the internal bug tracking systemneeds clarificationNeeds clarification or inputs from the userprojectsProjects-related (demos, applications, examples) issue or pull-request.

Type

Projects

Status

Analyzed

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions