memoryflashspi

Winbond W25Q01 (1G-BIT) Flash erases 256-bytes not 4K and fails write-read-verify after address 0x07000000


I am writing STM32 firmware to interface with a Winbond W25Q01JV flash memory chip. The chip provides 1GBit memory in two dies of 512M-bit each, with each die having it's own status registers. The flash chip is being used as two non-volatile FIFO buffers (one on each 512M-bit die) writing and verifying 256-byte pages.

Whilst most commands use 3-byte addressing, the chip may be placed into 4-byte address mode and there are 4-byte address commands for erase, write and read (regardless of whether the chip is in 3-byte or 4-byte address mode).

Command 0x21 is supposed to erase a 4K sector starting from the given 4-byte address. It must be preceded by write enable command 0x06. Instead I find that command 0x21 only erases a 256-byte page regardless of how I try to use it.

I also find that when using an Erase, Write, Read and Verify cycle all 256-byte pages from 0x00000000 to 0x06FFFFFF verify, but pages 0x07000000 fail. I have tried this on two identical development boards. Both fail from the same address of 0x07000000 with on failing to the end of the address range and the other failing from 0x07000000 to 0x7000D00. These locations fail even after write, read verify attempts after full-chip erase.

I thought that write protection might have been set on the chip. Both chips have the three registers clear except register #3 SRV1 is set (Output Driver Strength = 01 which is the default of 75%).

I have run performed extensive testing, code revisions and re-reading of the datasheet. The results remain the same. The code example is the latest iteration given as a monolithic function for brevity.

Can anyone please tell me what I am doing wrong?

/********************************************************************
 * NVR - DATA WRITE
 ********************************************************************
 * Write array of up to 256 bytes to memory page specified by Addr.
 * Assume that the memory page has already been erased.
 * Addr should be the start of a 256-byte memory page.
 * Return HAL_OK if success.
 *
 * Declared above
 * const uint8_t NVR_TIMEOUT = 100;
 * uint8_t NvrHead[5];
 * uint8_t NvrData[252];
 */
HAL_StatusTypeDef nvrDataWrite(uint32_t Addr, const uint8_t * Data, uint16_t DataLen) {
    HAL_StatusTypeDef LocalResult = HAL_OK;
    uint8_t LocalEraseFlag = 0;



    /* Are we starting 4K sector */
    if((Addr & 0x00000FFF) == 0) {
        LocalEraseFlag = 1;
    }


    /* Prepare SPI header with reverse address */
    NvrBuff[4] = ((Addr >> 24) & 0xFF);
    NvrBuff[3] = ((Addr >> 16) & 0xFF);
    NvrBuff[2] = ((Addr >> 8) & 0xFF);
    NvrBuff[1] = ((Addr >> 0) & 0xFF);


    /* Set pin to mark transaction for Logic Analyser */
    HAL_GPIO_WritePin(GPIOF, GPIO_PIN_2, GPIO_PIN_SET);


    /**
     * Software Die Select
     * Explicity select the Die to ensure that the correct status
     * register is polled for busy.
     */
    if(Addr < 0x04000000) {
        NvrBuff[1] = 0;
    } else {
        NvrBuff[1] = 1;
    }
    NvrBuff[0] = 0xC2;
    CS_LO
    HAL_SPI_Transmit(&hspi1, NvrBuff, 2, NVR_TIMEOUT);
    CS_HI


    /**
     * Erase
     * This is supposed to erase a 4K sector starting at the given
     * address. Test show that only 256-bytes is being erased.
     */
    if(LocalEraseFlag > 0) {

        /* Wait until memory chip is not busy */
        do {
            /* Read Status Register-1 (05h) */
            NvrBuff[0] = 0x05;
            CS_LO;
            HAL_SPI_Transmit(&hspi1, NvrBuff, 1, NVR_TIMEOUT);
            HAL_SPI_Receive(&hspi1, NvrData, 1, NVR_TIMEOUT);
            CS_HI;
        } while((NvrData[0] & 0b00000001) > 0);

        /* 8.5.1 Write Enable (06h) */
        CS_LO;
        NvrBuff[0] = CMD_BWRITE_ENABLE;
        HAL_SPI_Transmit(&hspi1, NvrBuff, 1, NVR_TIMEOUT);
        CS_HI;

        /* Erase 4K sector (21h) */
        NvrBuff[0] = 0x21;
        CS_LO;
        LocalResult = HAL_SPI_Transmit(&hspi1, NvrBuff, 5, NVR_TIMEOUT);
        CS_HI;
        HAL_Delay(63); /* tSE = 50 to 400 ms. Measured at 62ms by logic analyser */

    }


    /* Wait until memory chip is not busy */
    do {
        /* 8.5.4 Read Status Register-1 (05h) */
        NvrBuff[0] = 0x05;
        CS_LO;
        HAL_SPI_Transmit(&hspi1, NvrBuff, 1, NVR_TIMEOUT);
        HAL_SPI_Receive(&hspi1, NvrData, 1, NVR_TIMEOUT);
        CS_HI;
    } while((NvrData[0] & 0b00000001) > 0);


    /* Write Enable 8.5.1 */
    NvrBuff[0] = 0x06;
    CS_LO
    HAL_SPI_Transmit(&hspi1, NvrBuff, 1, NVR_TIMEOUT);
    CS_HI


    /* 8.6.1 WRITE or Page Program 1 to 256-bytes with 4-byte address (12h) */
    CS_LO;
    NvrBuff[0] = 0x12;
    HAL_SPI_Transmit(&hspi1, NvrBuff, 5, NVR_TIMEOUT);
    HAL_SPI_Transmit(&hspi1, Data, DataLen, NVR_TIMEOUT);
    CS_HI;
    HAL_Delay(3); /* tPP = 0.7 to 3.5 ms from Datasheet */


    /* Wait until memory chip is not busy */
    do {
        /* 8.5.4 Read Status Register-1 (05h) */
        NvrBuff[0] = 0x05;
        CS_LO;
        HAL_SPI_Transmit(&hspi1, NvrBuff, 1, NVR_TIMEOUT);
        HAL_SPI_Receive(&hspi1, NvrData, 1, NVR_TIMEOUT);
        CS_HI;
    } while((NvrData[0] & 0b00000001) > 0);


    /* READ 1 to 256-bytes with 4-byte address (13h) */
    NvrBuff[0] = 0x13;
    CS_LO;
    HAL_SPI_Transmit(&hspi1, NvrBuff, 5, NVR_TIMEOUT);
    HAL_SPI_Receive(&hspi1, NvrData, DataLen, NVR_TIMEOUT);
    CS_HI;


    /* Set pin to mark transaction for Logic Analyser */
    HAL_GPIO_WritePin(GPIOF, GPIO_PIN_2, GPIO_PIN_SET);


    /* Verify payload and report result */
    if(memcmp(Data, NvrData, DataLen) != 0) {
        LocalResult = HAL_ERROR;
    }

    return LocalResult;

}

For the record I am using STM32H733ZGT6 MCU with regular SPI at 12.5MBit/S and no enhanced features such as CRC, automatic CS, etc.


Solution

  • You incorectly format address in the erase transaction:

    /* Prepare SPI header with reverse address */
    NvrBuff[4] = ((Addr >> 24) & 0xFF);
    NvrBuff[3] = ((Addr >> 16) & 0xFF);
    NvrBuff[2] = ((Addr >> 8) & 0xFF);
    NvrBuff[1] = ((Addr >> 0) & 0xFF);
            ↑ 
            │
            └──── Errors Here
    

    Address should be encoded in big-endian format, but you format it in little-endian.

    enter image description here

    You should reverse indexes in NvrBuff when assembly address. LSB of address should be in 4th byte of buffer and MSB should be in 1st byte of buffer.

    /* Prepare SPI header */
    NvrBuff[1] = ((Addr >> 24) & 0xFF);
    NvrBuff[2] = ((Addr >> 16) & 0xFF);
    NvrBuff[3] = ((Addr >> 8) & 0xFF);
    NvrBuff[4] = ((Addr >> 0) & 0xFF);