Beware that this post is related to an SDK in maintenance mode
More Info: Consider nRF Connect SDK for new designs

Cannot exit deep power down mode (QSPI flash)

We designed a board based on the nRF52840, and are using nrf5 SDK 17.1.0 with S140 SoftDevice. We are using a QSPI flash, MX25U51245G (https://www.macronix.com/Lists/Datasheet/Attachments/8736/MX25U51245G%2054,%201.8V,%20512Mb,%20v1.2.pdf), we are using the 8-WSON version with no Reset pin

I have followed the QSPI example and the information provided here  Exiting QSPI DPM mode when opening QSPI (memory has been put into DPM previously before QSPI was closed) I can successfully access the QSPI flash, and I can put it in deep power down mode using the code provided, or sending the deep power down command as a QSPI command (CMD_DEEP_PWRDOWN 0xB9).

The problem is that this chip, unlike the one in the DK, does not exit deep power down by holding CS high for the proper time, but requires a command (RDP = 0xAB), see 10-4. Release from Deep Power-down (RDP). When I re-initialize the QSPI peripheral with the flash chip in power down, I get a NRF_ERROR_TIMEOUT because the chip doesn't seem to respond to whatever command the QSPI peripheral is using to wake up the device. nrfx_qspi_init fails when executing  NRFX_WAIT_FOR(nrf_qspi_event_check(NRF_QSPI, NRF_QSPI_EVENT_READY), QSPI_DEF_WAIT_ATTEMPTS, QSPI_DEF_WAIT_TIME_US, result);

I'm not sure I understand what nrf_qspi_task_trigger(NRF_QSPI, NRF_QSPI_TASK_ACTIVATE); is doing, but when my flash chip is in deep power down, that never completes and blocks the QSPI peripheral preventing to send any other command

Is there a way to send the 0xAB command to release my flash chip from deep sleep?

Parents
  • Hello,

    I don't have the same flash chip that you are using, but have you tried to use a logic analyzer and capture a logic trace of when the NRF_QSPI_TASKS_ACTIVATE is called? Does it generate any data on the QSPI pins? Have you tried to increase the QSPI_DEF_WAIT_TIME_US from 10 to a higher number? Just for debugging purposes. 

    Also, is it possible to upload the application that you are using to reproduce this issue? 

    Best regards,

    Edvin

Reply
  • Hello,

    I don't have the same flash chip that you are using, but have you tried to use a logic analyzer and capture a logic trace of when the NRF_QSPI_TASKS_ACTIVATE is called? Does it generate any data on the QSPI pins? Have you tried to increase the QSPI_DEF_WAIT_TIME_US from 10 to a higher number? Just for debugging purposes. 

    Also, is it possible to upload the application that you are using to reproduce this issue? 

    Best regards,

    Edvin

Children
  • Thanks for looking into my issue.

    I'm using this code, a small variation of the code linked in the original question

    #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 CMD_DEEP_PWRDOWN    0xB9
    
    #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;
        NRF_QSPI->DPMDUR = (0x3 << 16) | 0x3;
        
        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;
    
        nrf_qspi_cinstr_conf_t cinstr_cfg = {
            .opcode    = CMD_DEEP_PWRDOWN,
            .length    = NRF_QSPI_CINSTR_LEN_1B,
            .io2_level = true, // High level during transmission
            .io3_level = true, // High level during transmission
            .wipwait   = true,
            .wren      = false};
    
        err_code = nrfx_qspi_cinstr_xfer(&cinstr_cfg, NULL, NULL);          // send deep power down instruction
        APP_ERROR_CHECK(err_code);
    
        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);      // NRF_ERROR_TIMEOUT error here
        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 code works for any SPI NOR flash chip that can be released from deep power down using the CS line. For most NOR flash chips, the datasheet says “After CS# goes high, there is a delay of tRDP before the device transitions from Deep Power-down mode back to Stand-by mode.”

    In the newer 512 Mbit chips (all of them, not just mine), the device doesn't exit deep power down only using the CS line, it explicitly requires a 0xAB instruction to release. Problem is, the QSPI peripheral won't let me send anything, can't even complete the init function.

    I haven't connected a logic analyzer yet, because it would require removing the chip and soldering wires. But from everything I have seen, when initializing the QSPI peripheral, it sends 0x05 to the chip to verify that a chip is connected. In my case, the chip ignores the 0x05 because the only instruction it recognizes is 0xAB. So QSPI times out (I changed QSPI_DEF_WAIT_TIME_US  but it makes no difference, given it's not a real timeout issue)

    I need a way to either change the QSPI peripheral initialization to send 0xAB instead of 0x05, or a way to "trick" the QSPI peripheral to initialize even if the flash chip doesn't reply, so that I can send 0xAB either as a custom instruction or a nrfx_qspi_cinstr_xfer()

    Let me try to convey why this is more than just a minor issue for a specific case: all new 256/512Mbit chips cannot exit deep power down only by using CS, as far as I can tell. They all need the 0xAB instruction (it's done to improve security and avoid exiting low power by  mistake). As such, none of those chips can be properly used by the nRF52840 QSPI when extreme low power is needed.

  • I see.

    Unfortunately, I am not able to reproduce this with what I have at hand, because the QSPI chip on the DK reacts on the CSN pin only. 

    Can you please try to replace the rest of the main function, from line 115 in your snippet, with this one, and let me know what the log says?

        err_code = nrfx_qspi_cinstr_xfer(&cinstr_cfg, NULL, NULL);          // send deep power down instruction
        APP_ERROR_CHECK(err_code);
        WAIT_FOR_PERIPH();                                                  // Check if this one makes a difference first.
    
        NRF_QSPI->IFCONFIG1 |= 0x01000000;
        nrf_delay_ms(100);
        NRF_LOG_INFO("after change: IFCONFIG1: %08x", NRF_QSPI->IFCONFIG1);
    
        nrf_drv_qspi_uninit();
        NRF_LOG_INFO("after uninit: IFCONFIG1: %08x", NRF_QSPI->IFCONFIG1);
        NRF_LOG_INFO("QSPI closed in DPM.");
        nrf_delay_ms(500);
        
        NRF_LOG_INFO("after delay: %08x", NRF_QSPI->IFCONFIG);
    
        NRF_QSPI->IFCONFIG1 &= ~(0x01000000);
        NRF_LOG_INFO("after change: IFCONFIG1: %08x", NRF_QSPI->IFCONFIG1);
    
        err_code = nrf_drv_qspi_init(&config, qspi_handler, NULL);      // NRF_ERROR_TIMEOUT error here
        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 (;;)
        {
        }
    }

    You can try only adding line 3 first, and then try to write to the IFCONFIG1 register. According to the documentation, this should be written to to enable DPM. Let me know whether it changes anything.

    Best regards,

    Edvin

  • Thanks again for the code. 

    I first tried adding  WAIT_FOR_PERIPH(); after the DPM instruction as you suggested, and it hangs forever. I believe that, at that point, the chip is already in DPM

    So I removed it and added the rest of your code (just a note that line 14 uses NRF_QSPI->IFCONFIG so I used NRF_QSPI->IFCONFIG1 instead, I'm sure it's just a typo)

    This is what I get

    [00:00:03.219,238] <info> app: after change: IFCONFIG1: F1040401
    [00:00:03.219,238] <info> app: after uninit: IFCONFIG1: F1040401
    [00:00:03.219,299] <info> app: QSPI closed in DPM.
    [00:00:03.726,013] <info> app: after delay: F1040401
    [00:00:03.726,013] <info> app: after change: IFCONFIG1: F0040401
    [00:00:03.728,820] <info> app: QSPI opened again.
    

    Alas, line 23 call to configure_memory() errs with the same NRF_ERROR_TIMEOUT on the first nrf_drv_qspi_cinstr_xfer(). Since there is no 0xAB sent to the chip, it's still in DPM. Not sure why this time the nrf_drv_qspi_init() call worked (unlike in my case), but the QSPI peripheral doesn't seem to be able to send anything

    I tried adding 

        nrf_qspi_cinstr_conf_t cinstr_cfg = {
            .opcode    = CMD_RELEASE_PWRDOWN,
            .length    = NRF_QSPI_CINSTR_LEN_1B,
            .io2_level = true, // High level during transmission
            .io3_level = true, // High level during transmission
            .wipwait   = true,
            .wren      = false};
    
        err_code = nrfx_qspi_cinstr_xfer(&cinstr_cfg, NULL, NULL);
        APP_ERROR_CHECK(err_code); 

    to actually send 0xAB, but it also errs on the nrf_drv_qspi_cinstr_xfer() (NRF_ERROR_TIMEOUT)

    Is there a way to send 0xAB as a custom QSPI instruction instead of using nrf_drv_qspi_cinstr_xfer()?

  • Hello,

    robca said:
    NRF_QSPI->IFCONFIG so I used NRF_QSPI->IFCONFIG1 instead, I'm sure it's just a typo)

    Indeed.

    robca said:
    Is there a way to send 0xAB as a custom QSPI instruction instead of using nrf_drv_qspi_cinstr_xfer()?

    It is not very clear to me. I did see that it is part of the instruction set, but I am not yet sure how this is used.

    Do you have the possiblilty to analyze the QSPI pins using a logic analyzer to see whats actually happens when we try to reinitialize the QSPI and bring the chip back to life?

    I was planning on doing so, but I don't have the QSPI flash chip that you are using. Also, it requires some modifications to the DK (which I can do), but if you have the possibility, it would perhaps give a better picture of what's going on, since it seems to work without issues on my side.

    Or even better, if we can both do it, we can compare the traces to see what it waits for. If not, I need to check if I can get hold of a breakout board with the QSPI chip that you are using. Are you aware of any breakout boards containing the flash chip that you are using?

    Best regards,

    Edvin

  • Once again, I appreciate everything you are doing. And, funny you should ask, but I came to the exact same conclusion: we were flying blind and it was unfair for me to ask you to help without a logic analyzer trace. So I bit the bullet, desoldered the chip and create a horrible rat's nest of bodge wire.

    My logic analyzer channels are: 0 - clock, 1 - DIO0, 2 - DIO1, 3 - DIO2, 4 - DIO3, 5 - CS

    Here's what happens when nrf_drv_qspi_init() runs. The QSPI peripheral actually sends 0x05 (Read Status Register), the EEPROM replies

    Then we have configure_memory. As you can see, every instruction is preceded by sending once more 0x05. It looks as if every instruction starts with 0x05 again, but I'm not sure I understand the 0x06 (Write Enable) before 0x66 (Reset Enable), 0x99 (Reset) and the change to the configuration register 0x01 0x40

    Using your code, the instructions in line 5-12 look like this, i.e. sends yet another 0x05, followed by 0xB9 (deep power down)

    Incidentally, absolutely the same as this code

        nrf_qspi_cinstr_conf_t cinstr_cfg = {
            .opcode    = CMD_DEEP_PWRDOWN,
            .length    = NRF_QSPI_CINSTR_LEN_1B,
            .io2_level = true, // High level during transmission
            .io3_level = true, // High level during transmission
            .wipwait   = true,
            .wren      = false};
    
        err_code = nrfx_qspi_cinstr_xfer(&cinstr_cfg, NULL, NULL);
        APP_ERROR_CHECK(err_code);

    as you can see here

    Good, so now we are in deep sleep.

    Then we get to lines 16-21 in  your code snippet, which is supposed to wake up the EEPROM. As you can see, it simply tries to toggle CS as per the "old" EEPROM convention to exit DPM using only CS  (I needed to use 2 screengrabs due to the different zoom levels, look at the timing on top to make sense of them)

    The device is not out of DPM, though.

    I'm seeing inconsistent results between the device I bodge-wired and another, and I need to do a bit more soldering, but wanted to start capturing some traces and share them.

    Also, if I read the spec sheet correctly, this device should work like mine, and exit DPM only when receiving 0xAB. But I haven't tested it https://www.adafruit.com/product/5634

    To be honest, dead-bug soldering eight 35AWG wires directly the bottom of the WSON-8 version of MX25U51245G is not too bad, so I'm not entirely sure you need a breakout board for the tests

    EDIT: please let me know what traces you need. I'm seeing a slightly different behavior between the board with a dead bug soldered device and the unmodified one. I'm tired now, though, and I'm not sure if I'm doing something else wrong.

    But if there were a way to send 0xAB instead of 0x05 during the qspi init call ,that would solve all problems. Otherwise it looks as if the init returns timeout because there's no reply from the EEPROM

Related