cancel
Showing results for 
Search instead for 
Did you mean: 

QSPI nor flash write data with 2-bytes address offset!

MiladChalipa
Senior

I successfully initialize W25Q256 nor flash with STM32F746 MCU through Quad SPI protocol. Every command works fine (write memory, write Enable, erase sector, read and write status registers)

1-But when I debug the code and inspect the memory address 0x90000000, the data is written 2-bytes earlier (from 0x8FFFFFE memory address)

2-And chip-erase command (C7h) is not working. this is weird because the sector-erase command just works fine! I checked the manufacturer datasheet several times but no luck with the erase-command to get to work.

I uploaded the codes and memory address views to maybe experts in the community to help me out. note that the raw code is copied from ST public storage on GitHub with slight changes according to the Winbond flash datasheet.

uint8_t
CSP_QSPI_WriteMemory(uint8_t* buffer, uint32_t address, uint32_t buffer_size) {
 
    QSPI_CommandTypeDef sCommand;
    uint32_t end_addr, current_size, current_addr;
 
    /* Calculation of the size between the write address and the end of the page */
    current_addr = 0;
 
 
    //
    while (current_addr <= address) {
        current_addr += MEMORY_PAGE_SIZE;
    }
    current_size = current_addr - address;
 
    /* Check if the size of the data is less than the remaining place in the page */
    if (current_size > buffer_size) {
        current_size = buffer_size;
    }
 
    /* Initialize the adress variables */
    current_addr = address;
    end_addr = address + buffer_size;
 
    sCommand.InstructionMode = QSPI_INSTRUCTION_1_LINE;
    sCommand.AddressSize = QSPI_ADDRESS_32_BITS;
    sCommand.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE;
    sCommand.DdrMode = QSPI_DDR_MODE_DISABLE;
    sCommand.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY;
    sCommand.SIOOMode = QSPI_SIOO_INST_EVERY_CMD;
    sCommand.Instruction = QUAD_IN_FAST_PROG_CMD;
    sCommand.AddressMode = QSPI_ADDRESS_1_LINE;
    sCommand.DataMode = QSPI_DATA_4_LINES;
    sCommand.NbData = buffer_size;
    sCommand.Address = address;
    sCommand.DummyCycles = 0;
 
    /* Perform the write page by page */
    do {
        sCommand.Address = current_addr;
        sCommand.NbData = current_size;
 
        if (current_size == 0) {
            return HAL_OK;
        }
 
        /* Enable write operations */
        if (QSPI_WriteEnable() != HAL_OK) {
            return HAL_ERROR;
        }
 
        /* Configure the command */
        if (HAL_QSPI_Command(&hqspi, &sCommand, HAL_QPSI_TIMEOUT_DEFAULT_VALUE)
            != HAL_OK) {
 
            return HAL_ERROR;
        }
 
        /* Transmission of the data */
        if (HAL_QSPI_Transmit(&hqspi, buffer, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) {
 
            return HAL_ERROR;
        }
 
        /* Configure automatic polling mode to wait for end of program */
        if (QSPI_AutoPollingMemReady() != HAL_OK) {
            return HAL_ERROR;
        }
 
        /* Update the address and size variables for next page programming */
        current_addr += current_size;
        buffer += current_size;
        current_size =
            ((current_addr + MEMORY_PAGE_SIZE) > end_addr) ?
            (end_addr - current_addr) : MEMORY_PAGE_SIZE;
    } while (current_addr <= end_addr);
 
    return HAL_OK;
 
}
 
uint8_t
CSP_QSPI_Erase_Chip(void) {
    QSPI_CommandTypeDef sCommand;
 
 
    /* Erasing Sequence --------------------------------- */
    sCommand.Instruction = CHIP_ERASE_CMD;
    sCommand.InstructionMode = QSPI_INSTRUCTION_1_LINE;
    sCommand.AddressSize = QSPI_ADDRESS_32_BITS;
    sCommand.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE;
    sCommand.DdrMode = QSPI_DDR_MODE_DISABLE;
    sCommand.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY;
    sCommand.SIOOMode = QSPI_SIOO_INST_EVERY_CMD;
    sCommand.AddressMode = QSPI_ADDRESS_NONE;
    sCommand.Address = 0;
    sCommand.DataMode = QSPI_DATA_NONE;
    sCommand.DummyCycles = 0;
 
 
    if (QSPI_WriteEnable() != HAL_OK) {
        return HAL_ERROR;
    }
 
 
    if (HAL_QSPI_Command(&hqspi, &sCommand, HAL_QPSI_TIMEOUT_DEFAULT_VALUE)
        != HAL_OK) {
        return HAL_ERROR;
    }
 
    if (QSPI_AutoPollingMemReady() != HAL_OK) {
        return HAL_ERROR;
    }
 
    return HAL_OK;
}

0693W000006GuREQA0.png

25 REPLIES 25

@Community member​ would you be a little more precise, cause I am a newbie in QSPI things, what do you mean by modes?

The chips here typically have an assortment of commands, the simplest being 1-bit read and write commands, but also supporting 2 and 4-bit modes of operation. You can also write configurations enabling 4-bit signalling (on the pins), and Quad operation where address, data or commands might always be expected to transfer 4-bits at a time. For memory >16MB(128Mb) one can also switch into a 4-byte addressing mode as the 3-byte mode is not sufficient.

But as I said there are usually more than 2 ways to do most of these things, and different command bytes can achieve a similar outcome.

As I recall the 1-bit read/write don't require additional cycles. In a validation phase you'd expect to be able to read the same data across all modes. If it is losing data bytes in 4-bit mode this should be apparent, and should movable in either direction by adding cycles.

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..

Well, the problem is that it is not obvious whether the write causes the "shift" or the read (or maybe even both). So, I'd suggest you try memory mapped read or indirect read in simple SPI mode (i.e. not QPI) with one line and the basic read command 0x03 with zero dummy clocks. If everything is correct then, the write is ok.

For example DUAL means 2-bit wide, here there are two commands, one where the address has 24-bits (12 cycles) and 32-bit (16 cycles) to deliver.

You'd tell the STM32 side to use 2-bit wide ADDR and DATA transfers.

0693W000006GufLQAS.jpgSo there are likely a dozen ways to access the data and cross-check that all the settings and content are as expected.

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..

Another thing of note would be with 32-bit addressing mode you need to mask the high order bits, as the device's internal array doesn't understand the 0x90000000 range the STM32 assigns.

To manually send commands you need s_command.Address = Addr & 0x0FFFFFFF; // 28-bit (256MB) mask

The memory-mapped operation does this itself based on the Init.FlashSize configuration

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..

@Community member​hm, I've got your points, here is my QSPI workflow: 1-Initialize the QSPI peripherals 2-Reset chip 3-Auto-polling mode (wait for Busy-bit to be cleared to 0 4-Configuration function which consists of 1-Entering 4 byte address mode 2-Sets the QE bit to 1 3-Enter QPI mode 4-Checks the QE bit to be 1 5-checks the ADP bit to be 1 (to ensure that operating 4-byte mode.

all these procedures working without error. Actually, there is no losing data but all the data seems to be shifted 2 bytes back (screenshot attached)!

Is there a way to change the external flash address in the definitions? let's say it is 0x90000000 and changing it to 0x90000002?😉

The default for the memory is 8 dummy cycles

The 2-bit Read looked like this (standard 1-bit instruction clocking, device as powered up)

/**
  * @brief  Reads an amount of data from the QSPI memory.
  * @param  pData: Pointer to data to be read
  * @param  ReadAddr: Read start address
  * @param  Size: Size of data to read
  * @retval QSPI memory status
  */
uint8_t BSP_QSPI_ReadDual(uint8_t* pData, uint32_t ReadAddr, uint32_t Size)
{
  QSPI_CommandTypeDef s_command = {0};
 
  /* Initialize the read command */
  s_command.InstructionMode   = QSPI_INSTRUCTION_1_LINE;
  s_command.AddressMode       = QSPI_ADDRESS_1_LINE;
#ifdef ADR32
  s_command.Instruction       = 0x3C; // READ_DUAL_CMD 4-BYTE
  s_command.AddressSize       = QSPI_ADDRESS_32_BITS;
  s_command.Address           = ReadAddr & 0x0FFFFFFF;
#else
  s_command.Instruction       = 0x3B; // READ_DUAL_CMD 3-BYTE
  s_command.AddressSize       = QSPI_ADDRESS_24_BITS;
  s_command.Address           = ReadAddr & 0xFFFFFF;
#endif
  s_command.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE;
  s_command.DataMode          = QSPI_DATA_2_LINES;
  s_command.DummyCycles       = 8;
  s_command.NbData            = Size;
  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(&QSPIHandle, &s_command, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
  {
    return(QSPI_ERROR);
  }
 
  /* Reception of the data */
  if (HAL_QSPI_Receive(&QSPIHandle, pData, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
  {
    return(QSPI_ERROR);
  }
 
  return(QSPI_OK);
}  // sourcer32@gmail.com

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
MiladChalipa
Senior

@Community member​ @Andreas Bolsch​ Finally worked! thanks for your kindness and information. Here is how to W25Q256FV nor flash problem solved:

according to Mr. Tesla's info, there has to be a "data and address synchronization" between STM32 QSPI peripheral and nor flash chip itself, So in the Write-memory function, I had to set alternateByteMode for data compensation and add 2 lines of code to QSPI write structure routine which I attached below:

It solved all the shifted data problem and everything works like a charm now 😋 (Commented lines is added)

uint8_t
CSP_QSPI_WriteMemory(uint8_t* buffer, uint32_t address, uint32_t buffer_size) {
 
    QSPI_CommandTypeDef sCommand;
    uint32_t end_addr, current_size, current_addr;
 
    /* Calculation of the size between the write address and the end of the page */
    current_addr = 0;
 
 
    //
    while (current_addr <= address) {
        current_addr += MEMORY_PAGE_SIZE;
    }
    current_size = current_addr - address;
 
    /* Check if the size of the data is less than the remaining place in the page */
    if (current_size > buffer_size) {
        current_size = buffer_size;
    }
 
    /* Initialize the adress variables */
    current_addr = address;
    end_addr = address + buffer_size;
 
    sCommand.InstructionMode = QSPI_INSTRUCTION_1_LINE;
    sCommand.AddressSize = QSPI_ADDRESS_32_BITS;
    sCommand.AlternateByteMode = QSPI_ALTERNATE_BYTES_4_LINES;  //Add alternate bytes to 4 lines 
    sCommand.AlternateBytesSize = QSPI_ALTERNATE_BYTES_16_BITS; //2 bytes of alternateByte is solved the problem
    sCommand.DdrMode = QSPI_DDR_MODE_DISABLE;
    sCommand.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY;
    sCommand.SIOOMode = QSPI_SIOO_INST_EVERY_CMD;
    sCommand.Instruction = QUAD_IN_FAST_PROG_CMD;
    sCommand.AddressMode = QSPI_ADDRESS_1_LINE;
    sCommand.DataMode = QSPI_DATA_4_LINES;
    sCommand.NbData = buffer_size;
    sCommand.Address = address & 0x0FFFFFFF;
    sCommand.DummyCycles = 0;
 
    /* Perform the write page by page */
    do {
        sCommand.Address = current_addr & 0x0FFFFFFF;  //add mask 0x0FFFFFFF
        sCommand.NbData = current_size;
 
        if (current_size == 0) {
            return HAL_OK;
        }
 
        /* Enable write operations */
        if (QSPI_WriteEnable() != HAL_OK) {
            return HAL_ERROR;
        }
 
        /* Configure the command */
        if (HAL_QSPI_Command(&hqspi, &sCommand, HAL_QPSI_TIMEOUT_DEFAULT_VALUE)
            != HAL_OK) {
 
            return HAL_ERROR;
        }
 
        /* Transmission of the data */
        if (HAL_QSPI_Transmit(&hqspi, buffer, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) {
 
            return HAL_ERROR;
        }
 
        /* Configure automatic polling mode to wait for end of program */
        if (QSPI_AutoPollingMemReady() != HAL_OK) {
            return HAL_ERROR;
        }
 
        /* Update the address and size variables for next page programming */
        current_addr += current_size;
        buffer += current_size;
        current_size =
            ((current_addr + MEMORY_PAGE_SIZE) > end_addr) ?
            (end_addr - current_addr) : MEMORY_PAGE_SIZE;
    } while (current_addr <= end_addr);
 
    return HAL_OK;

MiladChalipa
Senior

The code will be uploaded to ST QSPI public storage located on GitHub after compilation

Most certainly your solution is NOT correct. The W25Q256 (as most other SPI flash) expects the data to follow right away after the address, hence NO alternate bytes nor dummy clocks are allowed between address and data, check datasheet! Actually your way may *appear* to work, but:

Take a file of *exactly* 33554432 Bytes (capacity of your flash, i.e. 32 Mbytes) of random data and write it into the flash in the way you're proposing. Then read back the whole 32 MBytes and compare the resulting file with the original one ...