Missing data when reading from the NAND Flash (SPIM3 32MHz) to USB MSC

Hello we are using custom board nrf52840 with nRF SDK 15.2.0 on SEGGER Embedded Studio

I hope someone can help me with this issue. I am using SPIM3 to communicate with the NAND Flash AS5F18G04SND-10LIN (1GB). The read/write functions for communicating with the flash work very well, reading and writing data accurately. Recently, I wanted to add the USB MSC feature, so I wrote a custom block device (I referred to the block devices for SD card and RAM). My NAND flash has a write block size of 4096, so I configured my USB MSC as NRF_BLOCK_DEV_SPI_CONFIG(4096, 4096*64), and #define MSC_WORKBUFFER_SIZE (4096).

However, for some reason, when the USB MSC calls the read/write functions via SPIM3, the interrupt does not trigger EVENTS_END, causing the read/write functions to hang. I referred to the nRF52840 Errata [193] SPIM: SPIM3 does not generate EVENTS_END and halts if suspended during the last byte. I also tried restarting SPIM3 after a countdown if EVENTS_END did not trigger. Now, my program does not hang anymore. On my PC (Windows 10), I can see the files on my NAND flash.

However, the real issue starts here. After checking the data in the files from the PC, I discovered that data is lost periodically. I noticed that the data loss occurs whenever SPIM3 hangs. The solution for Errata [193] only prevents my read/write functions from hanging but does not truly resolve the issue of data not being read correctly or other errors. I also tried referring to Errata [198] and enabled #define NRFX_SPIM3_NRF52840_ANOMALY_198_WORKAROUND_ENABLED 1, but it was not effective. As soon as USB power is applied, the program dumps.

My read/write functions work well on their own; however, they have issues when used inside block_dev_spi_read_req or block_dev_spi_write_req. I am unsure if the USB MSC backend affects SPIM3.

Do you have any suggestions or solutions for this issue? Thanks

  • Hi there,

    Can you describe in more detail how you have implemented the USB MSC write and read functions with the external flash through SPIM?

    Code examples would be great,

    Also, have you tried changing the SPIM instance and see if the issue is still reproducible?

    regards

    Jared 

  • Thanks for your support!

    You can see my implementation for spi_block_dev and usb_msc in this folder link. My flash block write size is 4096 bytes

    In the block_dev_spi_read_req() and block_dev_spi_write_req(), I use my custom flash interface disk_read() and disk_write()

    The disk_read() and disk_write() functions will be ported to the Dhara library as shown below:

    int dhara_map_read(struct dhara_map * m, dhara_sector_t s, uint8_t * data, dhara_error_t * err)
    {
        const struct dhara_nand * n = m->journal.nand;
        dhara_error_t my_err;
        dhara_page_t p;
    
        if (dhara_map_find(m, s, &p, &my_err) < 0)
        {
            if (my_err == DHARA_E_NOT_FOUND)
            {
                memset(data, 0xff, 1 << n->log2_page_size);
                return 0;
            }
    
            dhara_set_error(err, my_err);
            return -1;
        }
    
        return dhara_nand_read(n, p, 0, 1 << n->log2_page_size, data, err);
    }
    
    int dhara_map_write(
        struct dhara_map * m, dhara_sector_t dst, const uint8_t * data, dhara_error_t * err)
    {
        for (;;)
        {
            uint8_t meta[DHARA_META_SIZE];
            dhara_error_t my_err;
            const dhara_sector_t old_count = m->count;
    
            if (prepare_write(m, dst, meta, err) < 0)
                return -1;
    
            if (!dhara_journal_enqueue(&m->journal, data, meta, &my_err))
                break;
    
            m->count = old_count;
    
            if (try_recover(m, my_err, err) < 0)
                return -1;
        }
    
        return 0;
    }
    

    Below is the part of the program where I communicate with the flash via SPIM3:

    const nrfx_spim_t spi_flash = NRFX_SPIM_INSTANCE(3);
    void spi_flash_init()
    {
        nrfx_spim_config_t spi_config = NRFX_SPIM_DEFAULT_CONFIG;
        spi_config.ss_pin = NRFX_SPIM_PIN_NOT_USED;
        spi_config.miso_pin = FLASH_SPI_MISO_PIN;
        spi_config.mosi_pin = FLASH_SPI_MOSI_PIN;
        spi_config.sck_pin = FLASH_SPI_SCK_PIN;
        spi_config.frequency = NRF_SPIM_FREQ_32M;
        APP_ERROR_CHECK(nrfx_spim_init(&spi_flash, &spi_config, spi_event_handler, NULL));
        nrf_gpio_cfg_output(FLASH_SPI_SS_PIN);
        nrf_gpio_pin_set(FLASH_SPI_SS_PIN);
    }
    void restart_spim3(void)
    {
        *(volatile uint32_t *)0x4002FFFC = 0; 
        *(volatile uint32_t *)0x4002FFFC; 
        *(volatile uint32_t *)0x4002FFFC = 1; 
    }
    /*
     * Function:      spi_flash_write
     * Arguments:	  _spi,       spi instance
     *                addr,      address to be written to.
     * 		  byte_count, number of byte to write.
     *                wr_buff,     Pointer to a data buffer where the write data
     * will be stored. wr_cmd,     write command code to be written to the flash.
     * Return Value:  SUCCESS.
     *                FAILURE.
     * Description:   This function prepares the data to be written and put them
     * into data buffer, then call nrf_drv_spi_transfer function to start a write
     * data transfer.
     */
    
    ret_code_t spi_flash_write(nrfx_spim_t const * const _spi, uint32_t addr, uint32_t byte_count,
        const uint8_t * wr_buff, uint8_t wr_cmd)
    {
        restart_spim3();
        uint8_t * ptr = write_buffer;
        uint8_t iter, rem, i;
        uint32_t len_inst;
        uint32_t transfer_bytes;
        uint32_t count = 3500;
        ret_code_t status;
        len_inst = len_cmd + len_addr;
        write_buffer[0] = wr_cmd;
        addr_to_cmd(addr, write_buffer);
        memcpy(write_buffer + len_inst, wr_buff, byte_count);
        transfer_bytes = byte_count + len_inst;
        iter = transfer_bytes / SPI_MAX_DATA_SIZE;
        rem = transfer_bytes % SPI_MAX_DATA_SIZE;
        //NRF_LOG_INFO("To Transfer %u Iter %u Rem %u",transfer_bytes,iter,rem);
        nrf_gpio_pin_clear(FLASH_SPI_SS_PIN);
        for (i = 0; i <= iter; i++)
        {
            spi_xfer_done = false;
            xfer_desc.p_rx_buffer = read_buffer;
            xfer_desc.p_tx_buffer = ptr;
            if (i == iter)
            {
                xfer_desc.rx_length = rem;
                xfer_desc.tx_length = rem;
                status = nrfx_spim_xfer(_spi, &xfer_desc,NULL);
            }
            else
            {
                xfer_desc.rx_length = SPI_MAX_DATA_SIZE;
                xfer_desc.tx_length = SPI_MAX_DATA_SIZE;
                status =
                    nrfx_spim_xfer(_spi, &xfer_desc,NULL);
                ptr = ptr + SPI_MAX_DATA_SIZE;
            }
            msg("status: %d", status);
    #if 0
            while(!spi_xfer_done)
            {
                __WFE();
            }
    #else
            while (!spi_xfer_done && count > 0)
            {
                //__WFE();
                count--;
            }
            if(count == 0)
            {
                restart_spim3();
                nrf_gpio_pin_set(FLASH_SPI_SS_PIN);
            }
    #endif
            if (status != NRF_SUCCESS)
                return FAILURE;
        }
        nrf_gpio_pin_set(FLASH_SPI_SS_PIN);
        return SUCCESS;
    }
    
    /*
     * Function:      spi_flash_read
     * Arguments:	  _spi,       spi instance
     *                addr,      address to be written to.
     * 		  byte_count, number of byte to write.
     *                rd_buff,     Pointer to a data buffer where the write data
     * will be stored. rd_cmd,     write command code to be written to the flash.
     * Return Value:  SUCCESS.
     *                FAILURE.
     * Description:   This function calls nrf_drv_spi_transfer to start a read data
     * transfer and puts the data into data buffer
     */
    
    ret_code_t spi_flash_read(nrfx_spim_t const * const _spi, uint32_t addr, uint32_t byte_count,
        uint8_t * rd_buff, uint8_t rd_cmd)
    {
        restart_spim3();
        uint32_t len_inst;
        uint32_t transfer_bytes;
        uint16_t iter, rem;
        uint32_t count = 3500;
        ret_code_t status;
        uint8_t * ptr = read_buffer;
        len_inst = len_cmd + len_addr + len_dummy;
        write_buffer[0] = rd_cmd;
        addr_to_cmd(addr, write_buffer);
        if (len_dummy)
            write_buffer[1 + len_addr] = 0xFF;
        transfer_bytes = byte_count + len_inst;
        iter = transfer_bytes / SPI_MAX_DATA_SIZE;
        rem = transfer_bytes % SPI_MAX_DATA_SIZE;
        //NRF_LOG_INFO("To Transfer %u Iter %u Rem %u",transfer_bytes,iter,rem);
        nrf_gpio_pin_clear(FLASH_SPI_SS_PIN);
        for (uint8_t i = 0; i <= iter; i++)
        {
            spi_xfer_done = false;
            //NRF_LOG_INFO("Iter %d",i);
            xfer_desc.p_rx_buffer = ptr;
            xfer_desc.p_tx_buffer = write_buffer;
            if (i == iter)
            {
                xfer_desc.rx_length = rem;
                xfer_desc.tx_length = rem;
                status = nrfx_spim_xfer(_spi, &xfer_desc,NULL);
                
            }
            else
            {
                xfer_desc.rx_length = SPI_MAX_DATA_SIZE;
                xfer_desc.tx_length = SPI_MAX_DATA_SIZE;
                status = nrfx_spim_xfer(_spi, &xfer_desc,NULL);
                ptr = ptr + SPI_MAX_DATA_SIZE;
                
            }
            msg("status: %d", status);
    #if 0
            while(!spi_xfer_done)
            {
                __WFE();
            }
    #else
            while (!spi_xfer_done && count > 0)
            {
                //__WFE();
                count --;
            }
            if(count == 0)
            {
                restart_spim3();
                nrf_gpio_pin_set(FLASH_SPI_SS_PIN);
                //return FAILURE;
            }
    #endif
            if (status != NRF_SUCCESS)
                return FAILURE;
        }
        nrf_gpio_pin_set(FLASH_SPI_SS_PIN);
        memcpy(rd_buff, read_buffer + len_inst, byte_count);
       // msg("spi_flash_read: 0x%x 0x%x\n", rd_buff[0], rd_buff[1]);
        return SUCCESS;
    }

    When running USB MSC, there are times when error code 17 (NRFX_ERROR_BUSY) is returned from nrfx_spim_xfer() in spi_flash_read / spi_flash_write  This error code prevents me from reading data from the flash. This issue occurs periodically. Every time I test, the error occurs at the same fixed blocks (so all the times I miss data, it is always at the same spots). The data misses only change when I adjust the clock frequency of SPIM3 or when I increase the MSC_WORKBUFFER_SIZE. Please note that SPIM3 only encounters error 17 when called by USB MSC, and it works very well outside of USB MSC. I also retried reading up to 10 times when there was an error, but all of my retries returned error code 17(NRFX_ERROR_BUSY).

    This is my sdk_config.h for SPI:

    // <e> SPI0_ENABLED - Enable SPI0 instance
    //==========================================================
    #ifndef SPI0_ENABLED
    #define SPI0_ENABLED 1
    #endif
    // <q> SPI0_USE_EASY_DMA  - Use EasyDMA
     
    
    #ifndef SPI0_USE_EASY_DMA
    #define SPI0_USE_EASY_DMA 1
    #endif
    
    // </e>
    
    // <e> SPI1_ENABLED - Enable SPI1 instance
    //==========================================================
    #ifndef SPI1_ENABLED
    #define SPI1_ENABLED 0
    #endif
    // <q> SPI1_USE_EASY_DMA  - Use EasyDMA
     
    
    #ifndef SPI1_USE_EASY_DMA
    #define SPI1_USE_EASY_DMA 1
    #endif
    
    
    
    // </e>
    
    // <e> SPI2_ENABLED - Enable SPI2 instance
    //==========================================================
    #ifndef SPI2_ENABLED
    #define SPI2_ENABLED 1
    #endif
    // <q> SPI2_USE_EASY_DMA  - Use EasyDMA
     
    
    #ifndef SPI2_USE_EASY_DMA
    #define SPI2_USE_EASY_DMA 1
    #endif
    
    
    // <e> NRFX_SPIM_ENABLED - nrfx_spim - SPIM peripheral driver
    //==========================================================
    #ifndef NRFX_SPIM_ENABLED
    #define NRFX_SPIM_ENABLED 1
    #endif
    // <q> NRFX_SPIM0_ENABLED  - Enable SPIM0 instance
     
    
    #ifndef NRFX_SPIM0_ENABLED
    #define NRFX_SPIM0_ENABLED 0
    #endif
    
    // <q> NRFX_SPIM1_ENABLED  - Enable SPIM1 instance
     
    
    #ifndef NRFX_SPIM1_ENABLED
    #define NRFX_SPIM1_ENABLED 0
    #endif
    
    // <q> NRFX_SPIM2_ENABLED  - Enable SPIM2 instance
     
    
    #ifndef NRFX_SPIM2_ENABLED
    #define NRFX_SPIM2_ENABLED 2
    #endif
    
    // <q> NRFX_SPIM3_ENABLED  - Enable SPIM3 instance
     
    
    #ifndef NRFX_SPIM3_ENABLED
    #define NRFX_SPIM3_ENABLED 3
    #endif
    
    // <q> NRFX_SPIM_EXTENDED_ENABLED  - Enable extended SPIM features
     
    
    #ifndef NRFX_SPIM_EXTENDED_ENABLED
    #define NRFX_SPIM_EXTENDED_ENABLED 0
    #endif

  • Hi,

    The code looked ok. I can't see anything that would result in the loss of data. 

    What frequency are you using and how far down in frequency do you need to decrease it to not return NRFX_ERROR_BUSY 

    regards

    Jared 

Related