cancel
Showing results for 
Search instead for 
Did you mean: 

[STM32H7] QSPI Flash: Incorrect Data After Reboot (Initial Write/Read looks OK)

eziya76
Associate II

Hello,

I am currently testing the MT25QL512ABB flash memory on an STM32H747I-DISCO board.
The board has two of these flash chips, and I am attempting to operate them in dual mode.

I am using the driver from the official STMicroelectronics repository available at this URL:
https://github.com/STMicroelectronics/stm32-mt25ql512abb


My test code is designed to do the following:

On the first boot, it writes data to a specific region of the flash.

It then immediately reads back the data from that same region to verify that the write operation was successful.

After a reboot, the code reads the same flash region again to ensure the data is still correct.


The initial write and immediate read-back verification works perfectly. However, after I reboot the board, the subsequent read operation fails. The data read from the flash does not match the data that was originally written.

I have already disabled all cache functions, so I don't believe this is a caching issue.

I am looking for advice on what could be causing this problem.

Thanks.

void MX_QUADSPI_Init(void)
{

  /* USER CODE BEGIN QUADSPI_Init 0 */

  /* USER CODE END QUADSPI_Init 0 */

  /* USER CODE BEGIN QUADSPI_Init 1 */
  hqspi.Init.FlashID = QSPI_FLASH_ID_2;
  /* USER CODE END QUADSPI_Init 1 */
  hqspi.Instance = QUADSPI;
  hqspi.Init.ClockPrescaler = 1;
  hqspi.Init.FifoThreshold = 1;
  hqspi.Init.SampleShifting = QSPI_SAMPLE_SHIFTING_HALFCYCLE;
  hqspi.Init.FlashSize = 26;
  hqspi.Init.ChipSelectHighTime = QSPI_CS_HIGH_TIME_3_CYCLE;
  hqspi.Init.ClockMode = QSPI_CLOCK_MODE_0;
  hqspi.Init.DualFlash = QSPI_DUALFLASH_ENABLE;
  if (HAL_QSPI_Init(&hqspi) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN QUADSPI_Init 2 */

  /* USER CODE END QUADSPI_Init 2 */

}

 

#include "main.h"
#include "quadspi.h"
#include "gpio.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "mt25ql512abb.h"
/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */

/* USER CODE END PTD */

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */

/* DUAL_CORE_BOOT_SYNC_SEQUENCE: Define for dual core boot synchronization    */
/*                             demonstration code based on hardware semaphore */
/* This define is present in both CM7/CM4 projects                            */
/* To comment when developping/debugging on a single core                     */
#define DUAL_CORE_BOOT_SYNC_SEQUENCE

#if defined(DUAL_CORE_BOOT_SYNC_SEQUENCE)
#ifndef HSEM_ID_0
#define HSEM_ID_0 (0U) /* HW semaphore 0*/
#endif
#endif /* DUAL_CORE_BOOT_SYNC_SEQUENCE */

/* USER CODE END PD */

/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */

/* USER CODE END PM */

/* Private variables ---------------------------------------------------------*/

/* USER CODE BEGIN PV */

/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP */
int QSPI_Write_Full_Chip(void);
int QSPI_Verify_Full_Chip(void);
int Erase_Test_Flag(void);
int Write_Test_Flag(void);
/* USER CODE END PFP */

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
/* Test configuration defines */
#define QSPI_FLASH_SIZE_128MB   (0x8000000) /* 128 MB */
#define QSPI_SECTOR_SIZE_64KB   (0x10000)   /* 64 KB */
#define QSPI_PAGE_SIZE          (256)       /* 256 Bytes */
#define TEST_BUFFER_SIZE        (256)       /* Test buffer size, same as page size */
#define NUM_SECTORS             (QSPI_FLASH_SIZE_128MB / QSPI_SECTOR_SIZE_64KB)
// Use the LAST sector for the state flag to avoid collision with test data
#define TEST_STATE_FLAG_ADDRESS ((NUM_SECTORS - 1) * QSPI_SECTOR_SIZE_64KB)
#define WRITE_COMPLETE_FLAG     0x5A5A5A5A  // Magic number to indicate write is complete

uint8_t aTxBuffer[TEST_BUFFER_SIZE];
uint8_t aRxBuffer[TEST_BUFFER_SIZE];
MT25QL512ABB_Info_t flashInfo;
uint8_t id[6];

/**
 * @brief Writes a test pattern to 1/10th of the flash memory.
 * @retval 1 on success, 0 on failure.
 */
int QSPI_Write_Full_Chip(void)
{
  // MODIFIED: Test only 1/10th of the data sectors to save time.
  // The last sector is reserved for the flag.
  uint32_t num_data_sectors = (NUM_SECTORS - 1) / 10;
  uint32_t num_pages_per_sector = QSPI_SECTOR_SIZE_64KB / TEST_BUFFER_SIZE;

  // --- Write test pattern to all data sectors ---
  for(uint32_t i = 0;i < num_data_sectors;i++)
  {
    uint32_t sector_base_addr = i * QSPI_SECTOR_SIZE_64KB;
    uint32_t page_index_in_sector = i % num_pages_per_sector;
    uint32_t current_addr = sector_base_addr + (page_index_in_sector * TEST_BUFFER_SIZE);

    // 1. Prepare data with a pattern unique to the address
    for(uint16_t j = 0;j < TEST_BUFFER_SIZE;j++)
    {
      aTxBuffer[j] = j;
    }

    // 2. Erase the entire 64KB sector
    if(MT25QL512ABB_WriteEnable(&hqspi, MT25QL512ABB_QPI_MODE, MT25QL512ABB_DUALFLASH_ENABLE) != MT25QL512ABB_OK)
      return 0;
    if(MT25QL512ABB_BlockErase(&hqspi, MT25QL512ABB_QPI_MODE, MT25QL512ABB_4BYTES_SIZE, sector_base_addr, MT25QL512ABB_ERASE_64K) != MT25QL512ABB_OK)
      return 0;
    if(MT25QL512ABB_AutoPollingMemReady(&hqspi, MT25QL512ABB_QPI_MODE, MT25QL512ABB_DUALFLASH_ENABLE) != MT25QL512ABB_OK)
      return 0;

    // 3. Write data to the calculated page address
    if(MT25QL512ABB_WriteEnable(&hqspi, MT25QL512ABB_QPI_MODE, MT25QL512ABB_DUALFLASH_ENABLE) != MT25QL512ABB_OK)
      return 0;
    if(MT25QL512ABB_PageProgram(&hqspi, MT25QL512ABB_QPI_MODE, MT25QL512ABB_4BYTES_SIZE, aTxBuffer, current_addr, TEST_BUFFER_SIZE) != MT25QL512ABB_OK)
      return 0;
    if(MT25QL512ABB_AutoPollingMemReady(&hqspi, MT25QL512ABB_QPI_MODE, MT25QL512ABB_DUALFLASH_ENABLE) != MT25QL512ABB_OK)
      return 0;

    if(MT25QL512ABB_ReadSTR(&hqspi, MT25QL512ABB_QPI_MODE, MT25QL512ABB_4BYTES_SIZE, aRxBuffer, current_addr, TEST_BUFFER_SIZE) != MT25QL512ABB_OK)
    {
      return 0;
    }

    // 3. Verify data
    for(uint16_t j = 0;j < TEST_BUFFER_SIZE;j++)
    {
      if(aRxBuffer[j] != aTxBuffer[j])
      {
        return 0; // Failure
      }
    }

    HAL_GPIO_TogglePin(LED4_GPIO_Port, LED4_Pin); // Toggle Blue LED during write
  }

  return 1; // Success
}

/**
 * @brief Verifies the test pattern across 1/10th of the data sectors.
 * @retval 1 on success, 0 on failure.
 */
int QSPI_Verify_Full_Chip(void)
{
  // MODIFIED: Verify only 1/10th of the data sectors to save time.
  // The last sector is reserved for the flag.
  uint32_t num_data_sectors = (NUM_SECTORS - 1) / 10;
  uint32_t num_pages_per_sector = QSPI_SECTOR_SIZE_64KB / TEST_BUFFER_SIZE;

  for(uint32_t i = 0;i < num_data_sectors;i++)
  {
    uint32_t sector_base_addr = i * QSPI_SECTOR_SIZE_64KB;
    uint32_t page_index_in_sector = i % num_pages_per_sector;
    uint32_t current_addr = sector_base_addr + (page_index_in_sector * TEST_BUFFER_SIZE);

    // 1. Generate the expected data pattern locally
    for(uint16_t j = 0;j < TEST_BUFFER_SIZE;j++)
    {
      aTxBuffer[j] = j;
    }

    // 2. Read data back from flash
    if(MT25QL512ABB_ReadSTR(&hqspi, MT25QL512ABB_QPI_MODE, MT25QL512ABB_4BYTES_SIZE, aRxBuffer, current_addr, TEST_BUFFER_SIZE) != MT25QL512ABB_OK)
    {
      return 0;
    }

    // 3. Verify data
    for(uint16_t j = 0;j < TEST_BUFFER_SIZE;j++)
    {
      if(aRxBuffer[j] != aTxBuffer[j])
      {
        return 0; // Failure
      }
    }

    HAL_GPIO_TogglePin(LED4_GPIO_Port, LED4_Pin); // Toggle Blue LED during verify
  }
  return 1; // Success
}

/**
 * @brief Writes the 'Write Complete' flag to its dedicated sector.
 * @retval 1 on success, 0 on failure.
 */
int Write_Test_Flag(void)
{
  uint32_t flag_data = WRITE_COMPLETE_FLAG;

  // Erase the flag sector first
  if(MT25QL512ABB_WriteEnable(&hqspi, MT25QL512ABB_QPI_MODE, MT25QL512ABB_DUALFLASH_ENABLE) != MT25QL512ABB_OK)
    return 0;
  if(MT25QL512ABB_BlockErase(&hqspi, MT25QL512ABB_QPI_MODE, MT25QL512ABB_4BYTES_SIZE, TEST_STATE_FLAG_ADDRESS,
      MT25QL512ABB_ERASE_64K) != MT25QL512ABB_OK)
    return 0;
  if(MT25QL512ABB_AutoPollingMemReady(&hqspi, MT25QL512ABB_QPI_MODE, MT25QL512ABB_DUALFLASH_ENABLE) != MT25QL512ABB_OK)
    return 0;

  // Write the flag
  if(MT25QL512ABB_WriteEnable(&hqspi, MT25QL512ABB_QPI_MODE, MT25QL512ABB_DUALFLASH_ENABLE) != MT25QL512ABB_OK)
    return 0;
  if(MT25QL512ABB_PageProgram(&hqspi, MT25QL512ABB_QPI_MODE, MT25QL512ABB_4BYTES_SIZE, (uint8_t*) &flag_data, TEST_STATE_FLAG_ADDRESS,
      sizeof(flag_data)) != MT25QL512ABB_OK)
    return 0;
  if(MT25QL512ABB_AutoPollingMemReady(&hqspi, MT25QL512ABB_QPI_MODE, MT25QL512ABB_DUALFLASH_ENABLE) != MT25QL512ABB_OK)
    return 0;

  return 1;
}

/**
 * @brief Erases the flash sector dedicated to the test state flag.
 * @retval 1 on success, 0 on failure.
 */
int Erase_Test_Flag(void)
{
  if(MT25QL512ABB_WriteEnable(&hqspi, MT25QL512ABB_QPI_MODE, MT25QL512ABB_DUALFLASH_ENABLE) != MT25QL512ABB_OK)
    return 0;
  // Erase the last 64KB sector, which is dedicated for our flag.
  if(MT25QL512ABB_BlockErase(&hqspi, MT25QL512ABB_QPI_MODE, MT25QL512ABB_4BYTES_SIZE, TEST_STATE_FLAG_ADDRESS,
      MT25QL512ABB_ERASE_64K) != MT25QL512ABB_OK)
    return 0;
  if(MT25QL512ABB_AutoPollingMemReady(&hqspi, MT25QL512ABB_QPI_MODE, MT25QL512ABB_DUALFLASH_ENABLE) != MT25QL512ABB_OK)
    return 0;

  return 1;
}

/* USER CODE END 0 */

/**
  * @brief  The application entry point.
  * @retval int
  */
int main(void)
{

  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */
/* USER CODE BEGIN Boot_Mode_Sequence_0 */
#if defined(DUAL_CORE_BOOT_SYNC_SEQUENCE)
  int32_t timeout;
#endif /* DUAL_CORE_BOOT_SYNC_SEQUENCE */
/* USER CODE END Boot_Mode_Sequence_0 */

/* USER CODE BEGIN Boot_Mode_Sequence_1 */
#if defined(DUAL_CORE_BOOT_SYNC_SEQUENCE)
  /* Wait until CPU2 boots and enters in stop mode or timeout*/
  timeout = 0xFFFF;
  while((__HAL_RCC_GET_FLAG(RCC_FLAG_D2CKRDY) != RESET) && (timeout-- > 0))
    ;
  if(timeout < 0)
  {
    Error_Handler();
  }
#endif /* DUAL_CORE_BOOT_SYNC_SEQUENCE */
/* USER CODE END Boot_Mode_Sequence_1 */
  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();
/* USER CODE BEGIN Boot_Mode_Sequence_2 */
#if defined(DUAL_CORE_BOOT_SYNC_SEQUENCE)
  /* When system initialization is finished, Cortex-M7 will release Cortex-M4 by means of
   HSEM notification */
  /*HW semaphore Clock enable*/
  __HAL_RCC_HSEM_CLK_ENABLE();
  /*Take HSEM */
  HAL_HSEM_FastTake(HSEM_ID_0);
  /*Release HSEM in order to notify the CPU2(CM4)*/
  HAL_HSEM_Release(HSEM_ID_0, 0);
  /* wait until CPU2 wakes up from stop mode */
  timeout = 0xFFFF;
  while((__HAL_RCC_GET_FLAG(RCC_FLAG_D2CKRDY) == RESET) && (timeout-- > 0))
    ;
  if(timeout < 0)
  {
    Error_Handler();
  }
#endif /* DUAL_CORE_BOOT_SYNC_SEQUENCE */
/* USER CODE END Boot_Mode_Sequence_2 */

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_QUADSPI_Init();
  /* USER CODE BEGIN 2 */

  int32_t flashStatus = MT25QL512ABB_OK;
  uint32_t test_state_flag = 0;

  /* 1. Enter QPI mode */
  flashStatus = MT25QL512ABB_EnterQPIMode(&hqspi);
  if(flashStatus != MT25QL512ABB_OK)
    Error_Handler();

  /* 2. Read ID and verify */
  flashStatus = MT25QL512ABB_ReadID(&hqspi, MT25QL512ABB_QPI_MODE, id, MT25QL512ABB_DUALFLASH_ENABLE);
  if((flashStatus != MT25QL512ABB_OK) || (id[0] != 0x20) || (id[1] != 0x20) || (id[2] != 0xBA) || (id[3] != 0xBA) || (id[4] != 0x20)
      || (id[5] != 0x20))
  {
    Error_Handler();
  }

  /* 3. Enter 4-byte address mode */
  flashStatus = MT25QL512ABB_Enter4BytesAddressMode(&hqspi, MT25QL512ABB_QPI_MODE);
  if(flashStatus != MT25QL512ABB_OK)
    Error_Handler();

  /* 4. Read the test state flag from its dedicated address */
  if(MT25QL512ABB_ReadSTR(&hqspi, MT25QL512ABB_QPI_MODE, MT25QL512ABB_4BYTES_SIZE, (uint8_t*) &test_state_flag, TEST_STATE_FLAG_ADDRESS,
      sizeof(test_state_flag)) != MT25QL512ABB_OK)
  {
    Error_Handler();
  }

  /* 5. Decide whether to WRITE or VERIFY based on the flag */
  if(test_state_flag != WRITE_COMPLETE_FLAG)
  {
    /*****************************************************************/
    /* PHASE 1: WRITE DATA & REBOOT                                  */
    /*****************************************************************/
    // Turn on Yellow LED to indicate writing phase
    HAL_GPIO_WritePin(LED2_GPIO_Port, LED2_Pin, GPIO_PIN_RESET);

    if(QSPI_Write_Full_Chip() != 1)
      Error_Handler(); // Write data failed

    if(Write_Test_Flag() != 1)
      Error_Handler(); // Write flag failed

    // Write successful, now reboot
    HAL_NVIC_SystemReset();
  }
  else
  {
    /*****************************************************************/
    /* PHASE 2: VERIFY DATA                                          */
    /*****************************************************************/
    if(QSPI_Verify_Full_Chip() == 1)
    {
      // Verification successful: Turn on Green LED
      HAL_GPIO_WritePin(LED1_GPIO_Port, LED1_Pin, GPIO_PIN_RESET);
    }
    else
    {
      // Verification failed: Turn on Red LED
      HAL_GPIO_WritePin(LED3_GPIO_Port, LED3_Pin, GPIO_PIN_RESET);
    }

    // Erase the flag to allow the test to run again on the next power cycle,
    // regardless of success or failure.
    if(Erase_Test_Flag() != 1)
    {
      Error_Handler();
    }
  }

  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while(1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

eziya76_0-1754918710429.png

 

 

 

0 REPLIES 0