This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

Exiting QSPI DPM mode when opening QSPI (memory has been put into DPM previously before QSPI was closed)

Using the nrfx_qspi driver, it is possible to put a QSPI memory into DPM to save power, this all works fine and well. We want to keep the memory in DPM when uninitiating the QSPI hardware on the nRF52840 which seems to work OK until we attempt to re-open the QSPI driver later and it causes a module reset because the nrfx_qspi_init function returns a timeout. Even toggling the CS line from high to low and back to high before opening doesn't seem to resolve the issue. The memory being used is a Macronix MX25R6435F (same as used on the nRF52840 development board). Is there a way to prevent this from failing?

Parents
  • I need a scope of the SPI lines from when you put the ext flash into DPM and uninitialize the driver, when you try to re-initialize the driver, as well as when you try to wake up the ext flash by toggling the CS line manually. 

  • Whilst getting a trace, I noticed that it seems that when it tries to re-open it, the module doesn't seem to be toggling the clock line, here is a trace of the initial opening and when I attempt to re-open it, the end of the top trace includes where it is being put into DPM:

    In the re-open, you can see where I drive the CS low then high to reset the module. For test purposes I am using nrf_delay_ms() with a period of 2ms for testing, the datasheet specifies a minimum of 35us so this should be more than sufficient.

    I can get more detailed scope traces if necessary, I stopped here as the clock line remaining low seems to be the issue.

  • The CS line seems to be pulled low at random intervals throughout the transaction.

    1. Is your CS line susceptible to EMI?

    2. Do you have anything else connected to your CS line? 

    3. Is this a measurement artifact?


    More detailed scopes might help, as well as the decoded data.

  • 1) With the oscilloscope cables attached yes, there are thin wires on the nRF52840 board connecting the QSPI pads to pin headers, and the pin headers are attached to the oscilloscope via long cables, see setup picture:

    So I put this down to noise from the surrounding environment (maybe 2.4GHz devices and sub-1GHz devices transmitting)

    2) No, nothing else is connected (this is being tested on an nRF52840-DK with wires added to the QSPI, no other changes to the hardware have been made)

Reply
  • 1) With the oscilloscope cables attached yes, there are thin wires on the nRF52840 board connecting the QSPI pads to pin headers, and the pin headers are attached to the oscilloscope via long cables, see setup picture:

    So I put this down to noise from the surrounding environment (maybe 2.4GHz devices and sub-1GHz devices transmitting)

    2) No, nothing else is connected (this is being tested on an nRF52840-DK with wires added to the QSPI, no other changes to the hardware have been made)

Children
  • It seems to be EMI induced into the attached logic analyzer probes. 

    Do you encounter this issue when the probes are removed? If yes, then I'll try to reproduce the issue and include the developers in my findings. Can you provide the code necessary to reproduce your issue?

  • Yes, I fitted the wires after seeing this issue. Change the code in the sample app in examples\peripheral\qspi\main.c (using SDK v15.2.0) to this:

    #include <stdio.h>
    #include <string.h>
    #include <stdlib.h>
    #include "nrf_drv_qspi.h"
    #include "nrf_delay.h"
    #include "app_util_platform.h"
    #include "app_error.h"
    #include "boards.h"
    
    #include "nrf_log.h"
    #include "nrf_log_ctrl.h"
    #include "nrf_log_default_backends.h"
    #include "sdk_config.h"
    
    #define QSPI_STD_CMD_WRSR   0x01
    #define QSPI_STD_CMD_RSTEN  0x66
    #define QSPI_STD_CMD_RST    0x99
    
    #define QSPI_TEST_DATA_SIZE 256
    
    #define WAIT_FOR_PERIPH() do { \
            while (!m_finished) {} \
            m_finished = false;    \
        } while (0)
    
    static volatile bool m_finished = false;
    static uint8_t m_buffer_tx[QSPI_TEST_DATA_SIZE];
    static uint8_t m_buffer_rx[QSPI_TEST_DATA_SIZE];
    
    static void qspi_handler(nrf_drv_qspi_evt_t event, void * p_context)
    {
        UNUSED_PARAMETER(event);
        UNUSED_PARAMETER(p_context);
        m_finished = true;
    }
    
    static void configure_memory()
    {
        uint8_t temporary = 0x40;
        uint32_t err_code;
        nrf_qspi_cinstr_conf_t cinstr_cfg = {
            .opcode    = QSPI_STD_CMD_RSTEN,
            .length    = NRF_QSPI_CINSTR_LEN_1B,
            .io2_level = true,
            .io3_level = true,
            .wipwait   = true,
            .wren      = true
        };
    
        // Send reset enable
        err_code = nrf_drv_qspi_cinstr_xfer(&cinstr_cfg, NULL, NULL);
        APP_ERROR_CHECK(err_code);
    
        // Send reset command
        cinstr_cfg.opcode = QSPI_STD_CMD_RST;
        err_code = nrf_drv_qspi_cinstr_xfer(&cinstr_cfg, NULL, NULL);
        APP_ERROR_CHECK(err_code);
    
        // Switch to qspi mode
        cinstr_cfg.opcode = QSPI_STD_CMD_WRSR;
        cinstr_cfg.length = NRF_QSPI_CINSTR_LEN_2B;
        err_code = nrf_drv_qspi_cinstr_xfer(&cinstr_cfg, &temporary, NULL);
        APP_ERROR_CHECK(err_code);
    }
    
    
    int main(void)
    {
        uint32_t i;
        uint32_t err_code;
    
        err_code = NRF_LOG_INIT(NULL);
        APP_ERROR_CHECK(err_code);
    
        NRF_LOG_DEFAULT_BACKENDS_INIT();
    
        NRF_LOG_INFO(""
                     "QSPI write and read example using 24bit addressing mode");
    
        srand(0);
        for (i = 0; i < QSPI_TEST_DATA_SIZE; ++i)
        {
            m_buffer_tx[i] = (uint8_t)rand();
        }
    
        nrf_drv_qspi_config_t config = NRF_DRV_QSPI_DEFAULT_CONFIG;
    
        config.phy_if.dpmen = false;
        config.prot_if.dpmconfig = true;
        err_code = nrf_drv_qspi_init(&config, qspi_handler, NULL);
        APP_ERROR_CHECK(err_code);
        NRF_LOG_INFO("QSPI example started.");
    
        configure_memory();
    
        m_finished = false;
    
        err_code = nrf_drv_qspi_read(m_buffer_rx, QSPI_TEST_DATA_SIZE, 0);
        WAIT_FOR_PERIPH();
        NRF_LOG_INFO("Data read");
    
        uint32_t nConfigOld = NRF_QSPI->IFCONFIG1;
        uint32_t nConfig = NRF_QSPI->IFCONFIG1;
        nConfig |= 1U << QSPI_IFCONFIG1_DPMEN_Pos;
        NRF_QSPI->IFCONFIG1 = nConfig;
    
        nrf_drv_qspi_uninit();
        NRF_LOG_INFO("QSPI closed in DPM.");
        nrf_delay_ms(500);
    
        NRF_QSPI->IFCONFIG1 = nConfigOld;
    
        err_code = nrf_drv_qspi_init(&config, qspi_handler, NULL);
        APP_ERROR_CHECK(err_code);
        NRF_LOG_INFO("QSPI opened again.");
    
        configure_memory();
    
        err_code = nrf_drv_qspi_read(m_buffer_rx, QSPI_TEST_DATA_SIZE, 0);
        WAIT_FOR_PERIPH();
        NRF_LOG_INFO("Data read 2");
    
        for (;;)
        {
        }
    }
    
    /** @} */
    

    This is the output I get:

    <info> app: QSPI write and read example using 24bit addressing mode

    <info> app: QSPI example started.

    <info> app: Data read

    <info> app: QSPI closed in DPM.

    <error> app: Fatal error

    <warning> app: System reset

  • I think I've found the problem, the timing requirements for DPM were not set. 

    Add 'NRF_QSPI->DPMDUR = (0x3 << 16) | 0x3;' before the first nrf_drv_qspi_init.

    This sets the time it takes for the external memory to enter/exit DPM. From its spec i gather that

    tDP + tDPDD = 35µs and tRDP = 30µs. Closest viable value of DPMDUR.ENTER and .EXIT will be 0x3. See chapter 10-24 Deep Power-down and Table 17. AC Charatceristics, see also DPMDUR

    Strangely there's no HAL API for setting this register. I'll ask the devs what's up with that. 

Related