Beware that this post is related to an SDK in maintenance mode
More Info: Consider nRF Connect SDK for new designs
This discussion has been locked.
You can no longer post new replies to this discussion. If you have a question you can start a new discussion

Understanding Write then Read SPI Transfers (External Flash)

Hi all,

I'm currently trying to interface an nRF52832 with an external Flash chip via SPI

I have tried with both NAND and NOR types, specifically the MT29F2G01 from Micron and the S25FS064S from Cypress. Both of these chips implement a pretty simple Command protocol over SPI and I have had success reading and writing to both of them however I've had to implement a work around that I'm sure isn't the correct way to do things.

My problem is that for every byte I write to the device I have to read an extra byte at the start of the read. For example if I write 4 bytes to read a page (ie. the READ (0x03) command and a 3 byte address to read from) I will need to increase the number of read bytes by 4. An example can be seen in this FLASH_READ function I'm using for the Cypress device. I'm using the nrf_drv_spi driver in blocking mode (i.e. no callback function provided at init time)

static const nrf_drv_spi_t m_spi_instance = NRF_DRV_SPI_INSTANCE(1);

static uint8_t m_tx_buffer[512];
static uint8_t m_rx_buffer[512];

static nrf_drv_spi_xfer_desc_t m_spi_xfer = {
    .p_rx_buffer = m_rx_buffer,
    .p_tx_buffer = m_tx_buffer,
    .rx_length = 0,
    .tx_length = 0,
};

SLLD_STATUS FLASH_READ(BYTE device_num, BYTE command, ADDRESS sys_addr, BYTE *data_buffer, int Number_Of_Read_Bytes)
{
    uint8_t tx_buffer_index = 0;

    //Write command byte to bus
    m_tx_buffer[0] = command;
    tx_buffer_index += 1;

    //Write the address
    if(sys_addr != ADDRESS_NOT_USED)
    {
        // Shift out the address one byte at a time in 3-bytes addressing scheme
        m_tx_buffer[tx_buffer_index] = (uint8_t)((sys_addr >> 16) & 0x000000FF);
        tx_buffer_index += 1;
        m_tx_buffer[tx_buffer_index] = (uint8_t)((sys_addr >>  8) & 0x000000FF);
        tx_buffer_index += 1;
        m_tx_buffer[tx_buffer_index] = (uint8_t)(sys_addr         & 0x000000FF);
        tx_buffer_index += 1;
    }
    
    
    //Write the correct number of dummy bytes
    uint8_t number_of_dummy_bytes = 0;
    switch (command)
    {
        case SPI_FAST_READ_CMD:
        case SPI_FAST_READ_4B_CMD:
        case SPI_OTPR_CMD:
        case SPI_READ_SFDP_CMD:
        {
            number_of_dummy_bytes = 1;
            break;
        }

        case SPI_RES_CMD:
        {
            number_of_dummy_bytes = 3;
            break;
        }
    }

    for(int i = 0; i < number_of_dummy_bytes; i++)
    {
        m_tx_buffer[tx_buffer_index] = 0x00;
        tx_buffer_index += 1;
    }

    if (Number_Of_Read_Bytes > 0)
    {
        //Read Number_Of_Read_Bytes off the SPI bus into data_buffer
        
        //Increase number of bytes to read by the number of bytes written
        m_spi_xfer.rx_length = Number_Of_Read_Bytes + tx_buffer_index;
        m_spi_xfer.tx_length = tx_buffer_index;
        nrf_drv_spi_xfer(&m_spi_instance, &m_spi_xfer, 0);
        
        //memcpy is needed as the raw number of read bytes will be longer then expected by caller
        memcpy(data_buffer, &m_rx_buffer[tx_buffer_index], Number_Of_Read_Bytes);
    }

    return SLLD_OK;
}

This work around is giving me the results I expect but it seems quite inefficient (esp. the memcpy at the end to move the read data into the buffer provided by the caller ideally I would read directly into the provided buffer) and I feel like I'm completely missing something with how the SPI transfer should be done.

Any help or explanation on how best to deal with this behaviour would be greatly appreciated.

Parents
  • Hi

     Chapter 11 of the nRF52832 is very specific on how to do writing to flash.

    "When write is enabled, full 32-bit words can be written to word-aligned addresses in flash memory. As illustrated in Memory on page 20, the flash is divided into multiple pages. The same 32-bit word in flash memory can only be written n WRITE number of times before a page erase must be performed.

    The NVMC is only able to write 0 to bits in flash memory that are erased (set to 1). It cannot rewrite a bit back to 1. Only full 32-bit words can be written to flash memory using the NVMC interface. To write less than 32 bits, write the data as a full 32-bit word and set all the bits that should remain unchanged in the word to 1. The restriction on the number of writes (nWRITE) still applies in this case.

    Only word-aligned writes are allowed. Byte or half-word-aligned writes will result in a hard fault."

    This means that you have to write in 1 full word (or 4 bytes at a time) I'm afraid.

    Best regards,

    Simon

Reply
  • Hi

     Chapter 11 of the nRF52832 is very specific on how to do writing to flash.

    "When write is enabled, full 32-bit words can be written to word-aligned addresses in flash memory. As illustrated in Memory on page 20, the flash is divided into multiple pages. The same 32-bit word in flash memory can only be written n WRITE number of times before a page erase must be performed.

    The NVMC is only able to write 0 to bits in flash memory that are erased (set to 1). It cannot rewrite a bit back to 1. Only full 32-bit words can be written to flash memory using the NVMC interface. To write less than 32 bits, write the data as a full 32-bit word and set all the bits that should remain unchanged in the word to 1. The restriction on the number of writes (nWRITE) still applies in this case.

    Only word-aligned writes are allowed. Byte or half-word-aligned writes will result in a hard fault."

    This means that you have to write in 1 full word (or 4 bytes at a time) I'm afraid.

    Best regards,

    Simon

Children
  • Hi Simon,

    Thank you for your reply. I think I might not have made it clear in my question but I am not working with the Internal Flash of the nRF52832. This question is more about what I thought was odd behaviour from the SPI driver when reading data from external chips.

    However after doing some more reading about SPI I think it's actually my misunderstanding about the duplex nature of SPI. I expected all of the writing to happen first and then the reading to happen after, however because there are dedicated input and output lines what I now realise is that a byte is written and a byte is read at the same time. 

    The solution for me will be to run two seperate SPI transfers, the first one writing the required data and ignoring any data that comes over the MISO line and then another transfer reading the correct amount data off the MISO line and outputting 0 on the MOSI line for each byte read.

    I will also need to manage the CS pin myself now as it looks like the SPI driver automatically sets it high after a transfer but I need to keep it low so that I can perform the second read.

    After realising this it seems very obvious now but this was my first real exposure to SPI. I was used to the back and forth nature of I2C and expected SPI to work in a similar way.

    Hopefully this can serve as a record in case someone else is making the same silly mistake I have.

    Cheers,

    -Elliott

Related