STM32 Flash Write causes multiple HardFault Errors

1

I am trying to write several bytes of data onto the STM32F410CBT3 Flash Memory Sector 4 (size of 64KB), and I chose this sector and assume it is safe to use as the code is approximately 30KB (which would probably reside in sectors 1 and 2). The microcontroller's clock speed is clocked at 100MHz (through PLL).

Here is my Flash Write code:

/* Programming in 16-bit words, so offset address of 0x04 should be enough */
#define FLASH_SECTOR4_BASEADDRESS                       ((uint32_t)0x8011000)
#define OFFSET                                          ((uint8_t)0x04)

#define ADDR0               FLASH_SECTOR4_BASEADDRESS                                           
#define ADDR1               ((uint32_t)(ADDR0 + OFFSET))
#define ADDR2               ((uint32_t)(ADDR1 + OFFSET))
#define ADDR3               ((uint32_t)(ADDR2 + OFFSET))
/* and so on... */


void FLASH_Init(void)
{
    /* Only use FLASH Sector 4 for storing configuration/calibration data. s_EraseInit is stored as a static variable. This function called first because s_EraseInit needs to have values before any FLASH functions/routines are called. */
    s_EraseInit.TypeErase = TYPEERASE_SECTORS;
    s_EraseInit.Sector = FLASH_SECTOR_4;
    s_EraseInit.NbSectors = 1;
    s_EraseInit.VoltageRange = VOLTAGE_RANGE_4; /* Input voltage to mcu is around 3.3V */
}


void FLASH_Write(void)
{
    /* Stop LPTIM1 interrupts prior to modifying FLASH region */
    HAL_LPTIM_Counter_Stop_IT(&hlptim1);

    /* Assign temp_x values of struct members stored globally. temp_x will be the variable used for the FlashProgram function */
    uint16_t temp0 = GlobalStruct[0].Member1;
    uint16_t temp1 = GlobalStruct[0].Member2;
    uint16_t temp2 = GlobalStruct[0].Member3;
    uint16_t temp3 = GlobalStruct[1].Member1;
    uint16_t temp4 = GlobalStruct[1].Member2;
    uint16_t temp5 = GlobalStruct[1].Member3;
    uint16_t temp6 = GlobalStruct[2].Member1;
    uint16_t temp7 = GlobalStruct[2].Member2;
    uint16_t temp8 = GlobalStruct[2].Member3;
    uint16_t temp9 = GlobalStruct[3].Member1;
    uint16_t temp10 = GlobalStruct[3].Member2;
    uint16_t temp11 = GlobalStruct[3].Member3;

    /* Unlock FLASH peripheral and clear FLASH status register (error) flags */
    HAL_FLASH_Unlock();
    __HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_EOP|FLASH_FLAG_OPERR|FLASH_FLAG_WRPERR|FLASH_FLAG_PGAERR|FLASH_FLAG_PGSERR);
    
    /* Mass erase FLASH sector 4 */
    FlashStatus[12] = HAL_FLASHEx_Erase(&s_EraseInit, &s_SectorError);

    /* Write into FLASH sector 4 and lock the FLASH peripheral after using it */
    FlashStatus[0] = HAL_FLASH_Program(FLASH_TYPEPROGRAM_HALFWORD, ADDR0, temp0);
    FlashStatus[1] = HAL_FLASH_Program(FLASH_TYPEPROGRAM_HALFWORD, ADDR1, temp1);   
    FlashStatus[2] = HAL_FLASH_Program(FLASH_TYPEPROGRAM_HALFWORD, ADDR2, temp2);   
    FlashStatus[3] = HAL_FLASH_Program(FLASH_TYPEPROGRAM_HALFWORD, ADDR3, temp3);
    FlashStatus[4] = HAL_FLASH_Program(FLASH_TYPEPROGRAM_HALFWORD, ADDR4, temp4);
    FlashStatus[5] = HAL_FLASH_Program(FLASH_TYPEPROGRAM_HALFWORD, ADDR5, temp5);
    FlashStatus[6] = HAL_FLASH_Program(FLASH_TYPEPROGRAM_HALFWORD, ADDR6, temp6);
    FlashStatus[7] = HAL_FLASH_Program(FLASH_TYPEPROGRAM_HALFWORD, ADDR7, temp7);
    FlashStatus[8] = HAL_FLASH_Program(FLASH_TYPEPROGRAM_HALFWORD, ADDR8, temp8);
    FlashStatus[9] = HAL_FLASH_Program(FLASH_TYPEPROGRAM_HALFWORD, ADDR9, temp9);
    FlashStatus[10] = HAL_FLASH_Program(FLASH_TYPEPROGRAM_HALFWORD, ADDR10, temp10);
    FlashStatus[11] = HAL_FLASH_Program(FLASH_TYPEPROGRAM_HALFWORD, ADDR11, temp11);

    HAL_FLASH_Lock();
    
    /* Resume LPTIM1 interrupts after modifying FLASH region */
    HAL_LPTIM_Counter_Start_IT(&hlptim1, LPTIM1_AUTORELOAD_NUMBER);     
}

/*********************** Relevant code from the HAL Library *******************************/

/**
  * @brief  Program byte, halfword, word or double word at a specified address
  * @param  TypeProgram  Indicate the way to program at a specified address.
  *                           This parameter can be a value of @ref FLASH_Type_Program
  * @param  Address  specifies the address to be programmed.
  * @param  Data specifies the data to be programmed
  * 
  * @retval HAL_StatusTypeDef HAL Status
  */
HAL_StatusTypeDef HAL_FLASH_Program(uint32_t TypeProgram, uint32_t Address, uint64_t Data)
{
  HAL_StatusTypeDef status = HAL_ERROR;
  
  /* Process Locked */
  __HAL_LOCK(&pFlash);
  
  /* Check the parameters */
  assert_param(IS_FLASH_TYPEPROGRAM(TypeProgram));
  
  /* Wait for last operation to be completed */
  status = FLASH_WaitForLastOperation((uint32_t)FLASH_TIMEOUT_VALUE);
  
  if(status == HAL_OK)
  {
    if(TypeProgram == FLASH_TYPEPROGRAM_BYTE)
    {
      /*Program byte (8-bit) at a specified address.*/
      FLASH_Program_Byte(Address, (uint8_t) Data);
    }
    else if(TypeProgram == FLASH_TYPEPROGRAM_HALFWORD)
    {
      /*Program halfword (16-bit) at a specified address.*/
      FLASH_Program_HalfWord(Address, (uint16_t) Data);
    }
    else if(TypeProgram == FLASH_TYPEPROGRAM_WORD)
    {
      /*Program word (32-bit) at a specified address.*/
      FLASH_Program_Word(Address, (uint32_t) Data);
    }
    else
    {
      /*Program double word (64-bit) at a specified address.*/
      FLASH_Program_DoubleWord(Address, Data);
    }
    
    /* Wait for last operation to be completed */
    status = FLASH_WaitForLastOperation((uint32_t)FLASH_TIMEOUT_VALUE);
    
    /* If the program operation is completed, disable the PG Bit */
    FLASH->CR &= (~FLASH_CR_PG);  
  }
  
  /* Process Unlocked */
  __HAL_UNLOCK(&pFlash);
  
  return status;
}

About the code above, the global struct's members' are either 8-bit or 16-bit. I stopped the LPTIM1 interrupts before going into the FLASH Write sequence because I thought that the LPTIM1 interrupts could cause issues if it occurs exactly when the microcontroller is overwritting its Flash memory.

The Issue

I have been having HardFault errors on this code even though a really similar code (with the difference being the data written) works on a different STM32F410CBT3 microcontroller. I also matched the Linker Settings on MDK-ARM IROM1: Start 0x8000000, Size 0x1C000, previously it was at IROM1: Start 0x8000000, Size 0x20000. The common pattern that I am observing is, based on MDK-ARM's Memory Viewer, the Flash gets erased where ?? appears on all the memory block. Afterwards, it goes to 0xFF again indicating that none of the blocks/sections were written. At this point, the code is already in its HardFault Handler. If the code supposedly works, the sectors would turn from ?? to whatever value was written to the Flash memory.

Regarding the HardFault Error, it sometimes shows as a Memory Manage Fault, where the IACCVIOL (Instruction access violation flag) was raised, and SCB->MMFAR not containing any address. On most times, it shows up as a Bus Fault, where SCB->BFAR = 0xFFFFFFFF with the BFARVALID flag high, and PRECISERR flag was also raised. I looked through the STM32F410 Memory Map and the address 0xFFFFFFFF points to a 512-Mbyte block 7 Cortex-M4's internal peripherals. On rare occasions, it shows as a Usage Fault, where the UNDEFINSTR bit is high. Sometimes, the Flash Write sequence does work, but only if breakpoints were used.

One Example of Bus Fault Error

What I tried

  • Initially, I immediately placed the struct members inside the HAL_FLASH_Program APIs. Based on the UsageFault error, I thought that the struct member access was too slow, so I declared those unsigned 16-bit tempX variables to hold the struct members' values. The error still persists, and maybe, it is not the cause as the compiler would probably optimize that anyways. I also tried using delays and the error still persists.
  • I changed the 16-bit tempX variable declarations to uint32_t because I read somewhere that the input needs to be either 32-bits or 64-bits. I changed it to uint32_t declarations and the error still persists.
  • I also tried using FLASH_TYPEPROGRAM_WORD instead of FLASH_TYPEPROGRAM_HALFWORD, and the error still persists. On the different microcontroller, I noticed that this should not have much effect, because if I were to write a 16-bit value (for ex. 0xAAAA), in the Flash section itself, it would either appear as 0x0000AAAA if FLASH_TYPEPROGRAM_WORD was used, or 0xFFFFAAAA if FLASH_TYPEPROGRAM_HALFWORD was used because the left 16-bits were cleared to 0xFFFF but not re-written to 0x0000 since only the least 16-bits were overwritten.
  • Initially, I thought that writing a 0xFFFF would cause an issue, but after I ensured that all the struct members were nonzero or nonFFFF, the error still persists. On the different microcontroller, I could also still write 0xFFFF to the Flash without causing any errors.
  • I also tried using a different location (although still located in Sector 4), and the error still persists.
  • I verified that whenever a HardFault error occurs, FlashStatus[x], x = 0...12 contains only HAL_OK. The FLASH->SR register usually shows nothing too (no error status).
  • The variable s_SectorError usually has 0xFFFFFFFF, indicating that all the sectors (Sector 4) was successfully erased

Question What am I missing here? Any help would be greatly appreciated. I also don't know how to debug this issue deeper and any tips on debugging this problem would be appreciated too. This is the Keil uVision's page on Fault Reporting Guide, which I used as a reference for identifying the HardFault Errors. Thank you!

memory
arm
embedded
stm32
microcontroller
asked on Stack Overflow May 7, 2021 by Cimory • edited May 7, 2021 by Cimory

1 Answer

1

I was looking through the HAL files and I made a mistake of using VOLTAGE_RANGE_4 or FLASH_VOLTAGE_RANGE_4 instead of FLASH_VOLTAGE_RANGE_3. The HAL files showed that both options are for microcontrollers powered by a 2.7V - 3.6V, but the difference between the two options are the external Vpp for voltage range 4. Using FLASH_VOLTAGE_RANGE_3 actually fixed my issue, meaning that the original problem I had was caused by erasing the Flash memory incorrectly, and not because I programmed the Flash Memory incorrectly.

Voltage Range Options in HAL Code

I initially chose VOLTAGE_RANGE_4 because I misunderstood the voltage range when reading this page found in the Reference Manual:

Old STM32F410 Ref Manual on FLASH

While this section below actually tells me which voltage range I should be using since the voltage range referred in the HAL libraries corresponds to FLASH_CR->PSIZE bits:

New STM32F410 Ref Manual on FLASH

It is still a mystery to me as to why different HardFault errors can be raised at each tries, and why erasing the Flash Memory needs to be done in a certain way (64-bit erase/32-bit erase/16-bit erase/8-bit erase) depending on the supply voltage. It is also weird how it can work sometimes, but not work most of the time with the wrong configuration.

answered on Stack Overflow May 8, 2021 by Cimory

User contributions licensed under CC BY-SA 3.0