cancel
Showing results for 
Search instead for 
Did you mean: 

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

eziya76
Associate III

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

 

 

 

1 ACCEPTED SOLUTION

Accepted Solutions
eziya76
Associate III

The root cause of this issue was a misunderstanding of the erase operation.
When an erase is performed in dual mode, it clears an area twice the size of the actual library region.
Therefore, the addresses must be managed to account for this behavior. You can find the answer in the follow-up post

https://community.st.com/t5/stm32-mcus-products/stm32h747i-mt25ql512abb-blockerase-function-erases-previous/m-p/831109#M284197


 

View solution in original post

4 REPLIES 4
KDJEM.1
ST Employee

Hello @eziya76 ;

 

Are you respecting the QUADSPI internal timing criticality mentioned in the errata sheet?

The code below have to be executed upon reset and upon switching from memory-mapped to any other mode.

// Save QSPI_CR and QSPI_CCR values if necessary
QSPI->QSPI_CR = 0; // ensure that prescaling factor is not at maximum, and disable the perip
heral
while(QSPI->QSPI_SR & 0x20){}; // wait for BUSY flag to fall if not already low
QSPI->QSPI_CR = 0xFF000001; // set maximum prescaling factor, and enable the peripheral
QSPI->QSPI_CCR = 0x20000000; // activate the free-running clock
QSPI->QSPI_CCR = 0x20000000; // repeat the previous instruction to prevent a back-to-back dis
able
// The following command must complete less than 127 kernel clocks after the first write to the QSPI_CCR register
QSPI->QSPI_CR = 0; // disable QSPI
while(QSPI->QSPI_SR & 0x20){}; // wait for busy to fall
// Restore CR and CCR values if necessary

 

Thank you.

Kaouthar

To give better visibility on the answered topics, please click on Accept as Solution on the reply which solved your issue or answered your question.

Thank you for your reply.

I believed that the suggested code needed to be executed during the reboot sequence, so I placed it after the QSPI initialization. However, an error occurred from the very first operation, MT25QL512ABB_EnterQPIMode().

While searching online, I found some libraries for the MT25TLxx series (though not specifically for the MT25QL512ABB) that use functions like Reset_Memory() and QSPI_DummyCyclesCfg(). I am now further investigating whether my issue might be caused by missing such configuration steps.



static int32_t QSPI_ResetMemory(uint32_t Instance)
{
  int32_t ret = HAL_OK;

  /* Send RESET ENABLE command in QPI mode (QUAD I/Os, 4-4-4) */
  if(MT25TL01G_ResetEnable(&hqspi, MT25TL01G_QPI_MODE) != MT25TL01G_OK)
  {
    ret =HAL_ERROR;
  }/* Send RESET memory command in QPI mode (QUAD I/Os, 4-4-4) */
  else if(MT25TL01G_ResetMemory(&hqspi, MT25TL01G_QPI_MODE) != MT25TL01G_OK)
  {
    ret = HAL_ERROR;
  }/* Wait Flash ready */
  else if(MT25TL01G_AutoPollingMemReady(&hqspi, QSPI_Ctx[Instance].InterfaceMode) != MT25TL01G_OK)
  {
    ret = HAL_ERROR;
  }/* Send RESET ENABLE command in SPI mode (1-1-1) */
  else if(MT25TL01G_ResetEnable(&hqspi, QSPI_SPI_MODE) != MT25TL01G_OK)
  {
    ret = HAL_ERROR;
  }/* Send RESET memory command in SPI mode (1-1-1) */
  else if(MT25TL01G_ResetMemory(&hqspi, QSPI_SPI_MODE) != MT25TL01G_OK)
  {
    ret = HAL_ERROR;
  }
  else
  {
    QSPI_Ctx[Instance].IsInitialized = QSPI_ACCESS_INDIRECT;  /* After reset S/W setting to indirect access   */
    QSPI_Ctx[Instance].InterfaceMode = QSPI_SPI_MODE;     /* After reset H/W back to SPI mode by default  */
    QSPI_Ctx[Instance].TransferRate  = QSPI_STR_TRANSFER; /* After reset S/W setting to STR mode          */

  }

  /* Return BSP status */
  return ret;
}

/**
  * @brief  This function configure the dummy cycles on memory side.
  *         Dummy cycle bit locate in Configuration Register[7:6]
  * @PAram  Instance  QSPI instance
  * @retval BSP status
  */
static int32_t QSPI_DummyCyclesCfg(uint32_t Instance, QSPI_Interface_t Mode)
{
    int32_t ret= HAL_OK;
    QSPI_CommandTypeDef s_command;
  uint16_t reg=0;

  /* Initialize the read volatile configuration register command */
  s_command.InstructionMode   = (Mode == MT25TL01G_QPI_MODE) ? QSPI_INSTRUCTION_4_LINES : QSPI_INSTRUCTION_1_LINE;
  s_command.Instruction       = MT25TL01G_READ_VOL_CFG_REG_CMD;
  s_command.AddressMode       = QSPI_ADDRESS_NONE;
  s_command.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE;
  s_command.DataMode          = (Mode == MT25TL01G_QPI_MODE) ? QSPI_DATA_4_LINES : QSPI_DATA_1_LINE;
  s_command.DummyCycles       = 0;
  s_command.NbData            = 2;
  s_command.DdrMode           = QSPI_DDR_MODE_DISABLE;
  s_command.DdrHoldHalfCycle  = QSPI_DDR_HHC_ANALOG_DELAY;
  s_command.SIOOMode          = QSPI_SIOO_INST_EVERY_CMD;

  /* Configure the command */
  if (HAL_QSPI_Command(&hqspi, &s_command, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
  {
    return HAL_ERROR;
  }

  /* Reception of the data */
  if (HAL_QSPI_Receive(&hqspi, (uint8_t *)(&reg), HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
  {
    return HAL_ERROR;
  }

  /* Enable write operations */
  if (MT25TL01G_WriteEnable(&hqspi, Mode) != MT25TL01G_OK)
  {
    return HAL_ERROR;
  }

  /* Update volatile configuration register (with new dummy cycles) */
  s_command.Instruction = MT25TL01G_WRITE_VOL_CFG_REG_CMD;
  MODIFY_REG(reg, 0xF0F0, ((MT25TL01G_DUMMY_CYCLES_READ_QUAD << 4) |
                               (MT25TL01G_DUMMY_CYCLES_READ_QUAD << 12)));

  /* Configure the write volatile configuration register command */
  if (HAL_QSPI_Command(&hqspi, &s_command, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
  {
    return HAL_ERROR;
  }

  /* Transmission of the data */
  if (HAL_QSPI_Transmit(&hqspi, (uint8_t *)(&reg), HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
  {
    return HAL_ERROR;
  }

  /* Return BSP status */
  return ret;
}

....

MX_QUADSPI_Init ();

        /* QSPI memory reset */
        if(QSPI_ResetMemory(Instance) != HAL_OK)
        {
          ret = HAL_ERROR;
        }

        /* Force Flash enter 4 Byte address mode */
        else if(MT25TL01G_Enter4BytesAddressMode(&hqspi, QSPI_Ctx[Instance].InterfaceMode) != MT25TL01G_OK)
        {
          ret = HAL_ERROR;
        }

      	/* Configuration of the dummy cycles on QSPI memory side */
        else if(MT25TL01G_AutoPollingMemReady(&hqspi, QSPI_Ctx[Instance].InterfaceMode) != MT25TL01G_OK)
        {
          ret = HAL_ERROR;
        }

        else if(QSPI_DummyCyclesCfg(Instance, QSPI_Ctx[Instance].InterfaceMode) != HAL_OK)
        {
          ret = HAL_ERROR;
        }
        else
        {
          /* Configure Flash to desired mode */
          if(QSPI_ConfigFlash(Instance, Init->InterfaceMode, Init->TransferRate) != HAL_OK)
          {
            ret = HAL_ERROR;
          }
        }

 

eziya76
Associate III
eziya76
Associate III

The root cause of this issue was a misunderstanding of the erase operation.
When an erase is performed in dual mode, it clears an area twice the size of the actual library region.
Therefore, the addresses must be managed to account for this behavior. You can find the answer in the follow-up post

https://community.st.com/t5/stm32-mcus-products/stm32h747i-mt25ql512abb-blockerase-function-erases-previous/m-p/831109#M284197