NRF53 QSPI XIP mode: unable to uninitialized qspi

We are using QSPI XIP mode to access data in external flash for our project. We are trying put QSPI peripheral in standby mode.

However, we can't uninitialized QSPI peripheral by calling nrfx_qspi_uninit() even though XIP mode is disabled and no data is being accessed . The following are the code that we use to test. And, it's based on the example project in SDK (i.e. zephyr\samples\application_development\code_relocation_nocopy) with some minor modification.

ext_mem_init.c

const struct device *dev;

int qspi_ext_mem_init(void) {
	ARG_UNUSED(dev);

	static const nrfx_qspi_config_t qspi_config = {
		.prot_if = {
			.readoc    = NRF_QSPI_READOC_READ4IO,
			.writeoc   = NRF_QSPI_WRITEOC_PP4IO,
			.addrmode  = NRF_QSPI_ADDRMODE_24BIT,
		},
		.phy_if = {
			/* Frequency = PCLK192M / 2 * (sck_freq + 1) -> 32 MHz.
			 * 33 MHz is the maximum for WRSR, even in the High
			 * Performance mode.
			 */
			.sck_freq  = 2,
			.sck_delay = 0x05,
			.spi_mode  = NRF_QSPI_MODE_0,
		},
		.skip_gpio_cfg = true,
		.skip_psel_cfg = true,
	};
	static const nrf_qspi_phy_conf_t qspi_phy_48mhz = {
		/* After sending WRSR, use 48 MHz (96 MHz cannot be used,
		 * as 80 MHz is the maximum for the MX25R6435F chip).
		 */
		.sck_freq  = 1,
		.sck_delay = 0x05,
		.spi_mode  = NRF_QSPI_MODE_0,
	};
	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,
	};
	static const uint8_t flash_chip_cfg[] = {
		/* QE (Quad Enable) bit = 1 */
		BIT(6),
		0x00,
		/* L/H Switch bit = 1 -> High Performance mode */
		BIT(1),
	};
	nrfx_err_t err;
	int ret;

	PINCTRL_DT_DEFINE(QSPI_NODE);

	ret = pinctrl_apply_state(PINCTRL_DT_DEV_CONFIG_GET(QSPI_NODE),
				  PINCTRL_STATE_DEFAULT);
	if (ret < 0) {
		return ret;
	}

	err = nrfx_qspi_init(&qspi_config, NULL, NULL);
	if (err != NRFX_SUCCESS) {
		return -EIO;
	}

	nrf_clock_hfclk192m_div_set(NRF_CLOCK, NRF_CLOCK_HFCLK_DIV_1);

	/* Send reset enable */
	nrfx_qspi_cinstr_xfer(&cinstr_cfg, NULL, NULL);

	/* Send reset command */
	cinstr_cfg.opcode = QSPI_STD_CMD_RST;
	nrfx_qspi_cinstr_xfer(&cinstr_cfg, NULL, NULL);

	/* Switch to Quad I/O and High Performance mode */
	cinstr_cfg.opcode = QSPI_STD_CMD_WRSR;
	cinstr_cfg.wren   = true;
	cinstr_cfg.length = NRF_QSPI_CINSTR_LEN_4B;
	nrfx_qspi_cinstr_xfer(&cinstr_cfg, &flash_chip_cfg, NULL);

	nrf_qspi_ifconfig1_set(NRF_QSPI, &qspi_phy_48mhz);

	/* Enable XiP */
	nrf_qspi_xip_set(NRF_QSPI, true);

	/* Send deep sleep mode command */
	cinstr_cfg.opcode = QSPI_STD_CMD_DP;
	nrfx_qspi_cinstr_xfer(&cinstr_cfg, NULL, NULL);
	return 0;
}

int qspi_ext_mem_uninit(void)
{
	nrf_qspi_xip_set(NRF_QSPI, false);
	nrfx_qspi_uninit();
	printk("qspi_ext_mem_uninit successfully\n");
	return 0;
}
//SYS_INIT(qspi_ext_mem_init, POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_OBJECTS);

main.c 

/*
 * Copyright (c) 2018 Intel Corporation.
 * Copyright (c) 2022 Carlo Caione <[email protected]>
 *
 * SPDX-License-Identifier: Apache-2.0
 */

#include <zephyr/kernel.h>
#include <zephyr/sys/printk.h>

/*
 * This function will allow execute from sram region.  This is needed only for
 * this sample because by default all soc will disable the execute from SRAM.
 * An application that requires that the code be executed from SRAM will have
 * to configure the region appropriately in arm_mpu_regions.c.
 */
#ifdef CONFIG_ARM_MPU
#include <zephyr/arch/arm/aarch32/cortex_m/cmsis.h>
void disable_mpu_rasr_xn(void)
{
	uint32_t index;

	/*
	 * Kept the max index as 8(irrespective of soc) because the sram would
	 * most likely be set at index 2.
	 */
	for (index = 0U; index < 8; index++) {
		MPU->RNR = index;
#if defined(CONFIG_ARMV8_M_BASELINE) || defined(CONFIG_ARMV8_M_MAINLINE)
		if (MPU->RBAR & MPU_RBAR_XN_Msk) {
			MPU->RBAR ^= MPU_RBAR_XN_Msk;
		}
#else
		if (MPU->RASR & MPU_RASR_XN_Msk) {
			MPU->RASR ^= MPU_RASR_XN_Msk;
		}
#endif /* CONFIG_ARMV8_M_BASELINE || CONFIG_ARMV8_M_MAINLINE */
	}
}
#endif /* CONFIG_ARM_MPU */

extern void function_in_ext_flash(void);
extern void function_in_sram(void);

void main(void)
{
#ifdef CONFIG_ARM_MPU
	disable_mpu_rasr_xn();
#endif	/* CONFIG_ARM_MPU */

	qspi_ext_mem_init();

	printk("Address of %s function %p\n", __func__, &main);

	function_in_ext_flash();
	function_in_sram();

	printk("Hello World! %s\n", CONFIG_BOARD);
	qspi_ext_mem_uninit();
}

After flashing the program into NRF5304DK, it's unable to run this instruction: printk("qspi_ext_mem_uninit successfully\n");

May we know how we can uninitialized QSPI peripheral with XIP mode successfully in order to save power consumption?

  • Hi, 

    I modify the code as
    main.c

    /*
     * Copyright (c) 2018 Intel Corporation.
     * Copyright (c) 2022 Carlo Caione <[email protected]>
     *
     * SPDX-License-Identifier: Apache-2.0
     */
    
    #include <zephyr/kernel.h>
    #include <zephyr/sys/printk.h>
    
    /*
     * This function will allow execute from sram region.  This is needed only for
     * this sample because by default all soc will disable the execute from SRAM.
     * An application that requires that the code be executed from SRAM will have
     * to configure the region appropriately in arm_mpu_regions.c.
     */
    #ifdef CONFIG_ARM_MPU
    #include <zephyr/arch/arm/aarch32/cortex_m/cmsis.h>
    void disable_mpu_rasr_xn(void)
    {
    	uint32_t index;
    
    	/*
    	 * Kept the max index as 8(irrespective of soc) because the sram would
    	 * most likely be set at index 2.
    	 */
    	for (index = 0U; index < 8; index++) {
    		MPU->RNR = index;
    #if defined(CONFIG_ARMV8_M_BASELINE) || defined(CONFIG_ARMV8_M_MAINLINE)
    		if (MPU->RBAR & MPU_RBAR_XN_Msk) {
    			MPU->RBAR ^= MPU_RBAR_XN_Msk;
    		}
    #else
    		if (MPU->RASR & MPU_RASR_XN_Msk) {
    			MPU->RASR ^= MPU_RASR_XN_Msk;
    		}
    #endif /* CONFIG_ARMV8_M_BASELINE || CONFIG_ARMV8_M_MAINLINE */
    	}
    }
    #endif /* CONFIG_ARM_MPU */
    
    extern void function_in_ext_flash(void);
    extern void function_in_sram(void);
    
    void main(void)
    {
    #ifdef CONFIG_ARM_MPU
    	disable_mpu_rasr_xn();
    #endif	/* CONFIG_ARM_MPU */
    
        //qspi_ext_mem_init();
    
    	printk("Address of %s function %p\n", __func__, &main);
    
    	function_in_ext_flash();
    	function_in_sram();
    
    	printk("Hello World! %s\n", CONFIG_BOARD);
    	qspi_ext_mem_uninit();
    }
    

    ext_mem_init.c

    /*
     * Copyright (c) 2022 Nordic Semiconductor ASA.
     *
     * SPDX-License-Identifier: Apache-2.0
     */
    
    #include <errno.h>
    #include <zephyr/init.h>
    #include <zephyr/drivers/pinctrl.h>
    #include <nrfx_qspi.h>
    #include <hal/nrf_clock.h>
    
    #define QSPI_STD_CMD_WRSR  0x01
    #define QSPI_STD_CMD_RSTEN 0x66
    #define QSPI_STD_CMD_RST   0x99
    
    #define QSPI_NODE DT_NODELABEL(qspi)
    
    static int qspi_ext_mem_init(const struct device *dev)
    {
    	ARG_UNUSED(dev);
    
    	static const nrfx_qspi_config_t qspi_config = {
    		.prot_if = {
    			.readoc    = NRF_QSPI_READOC_READ4IO,
    			.writeoc   = NRF_QSPI_WRITEOC_PP4IO,
    			.addrmode  = NRF_QSPI_ADDRMODE_24BIT,
    		},
    		.phy_if = {
    			/* Frequency = PCLK192M / 2 * (sck_freq + 1) -> 32 MHz.
    			 * 33 MHz is the maximum for WRSR, even in the High
    			 * Performance mode.
    			 */
    			.sck_freq  = 2,
    			.sck_delay = 0x05,
    			.spi_mode  = NRF_QSPI_MODE_0,
    		},
    		.skip_gpio_cfg = true,
    		.skip_psel_cfg = true,
    	};
    	static const nrf_qspi_phy_conf_t qspi_phy_48mhz = {
    		/* After sending WRSR, use 48 MHz (96 MHz cannot be used,
    		 * as 80 MHz is the maximum for the MX25R6435F chip).
    		 */
    		.sck_freq  = 1,
    		.sck_delay = 0x05,
    		.spi_mode  = NRF_QSPI_MODE_0,
    	};
    	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,
    	};
    	static const uint8_t flash_chip_cfg[] = {
    		/* QE (Quad Enable) bit = 1 */
    		BIT(6),
    		0x00,
    		/* L/H Switch bit = 1 -> High Performance mode */
    		BIT(1),
    	};
    	nrfx_err_t err;
    	int ret;
    
    	PINCTRL_DT_DEFINE(QSPI_NODE);
    
    	ret = pinctrl_apply_state(PINCTRL_DT_DEV_CONFIG_GET(QSPI_NODE),
    				  PINCTRL_STATE_DEFAULT);
    	if (ret < 0) {
    		return ret;
    	}
    
    	err = nrfx_qspi_init(&qspi_config, NULL, NULL);
    	if (err != NRFX_SUCCESS) {
    		return -EIO;
    	}
    
    	nrf_clock_hfclk192m_div_set(NRF_CLOCK, NRF_CLOCK_HFCLK_DIV_1);
    
    	/* Send reset enable */
    	nrfx_qspi_cinstr_xfer(&cinstr_cfg, NULL, NULL);
    
    	/* Send reset command */
    	cinstr_cfg.opcode = QSPI_STD_CMD_RST;
    	nrfx_qspi_cinstr_xfer(&cinstr_cfg, NULL, NULL);
    
    	/* Switch to Quad I/O and High Performance mode */
    	cinstr_cfg.opcode = QSPI_STD_CMD_WRSR;
    	cinstr_cfg.wren   = true;
    	cinstr_cfg.length = NRF_QSPI_CINSTR_LEN_4B;
    	nrfx_qspi_cinstr_xfer(&cinstr_cfg, &flash_chip_cfg, NULL);
    
    	nrf_qspi_ifconfig1_set(NRF_QSPI, &qspi_phy_48mhz);
    
    	/* Enable XiP */
    	nrf_qspi_xip_set(NRF_QSPI, true);
    
    	return 0;
    }
    
    int qspi_ext_mem_uninit(void)
    {
    	nrf_qspi_xip_set(NRF_QSPI, false);
    	nrfx_qspi_uninit();
    	printk("qspi_ext_mem_uninit successfully\n");
    	return 0;
    }
    
    SYS_INIT(qspi_ext_mem_init, POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_OBJECTS);
    

    Get the log as

    *** Booting Zephyr OS build v3.1.99-ncs1-1  ***
    Address of main function 0x485
    Address of function_in_ext_flash 0x42d
    Address of var_ext_sram_data 0x20000000 (10)
    Address of function_in_sram 0x4c1
    Address of var_sram_data 0x20000004 (10)
    Hello World! nrf5340dk_nrf5340_cpuapp
    qspi_ext_mem_uninit successfully

    Regards,
    Amanda H.

  • Hi Amanda,

    I've tried your modified code. My log is as followed

    :

    *** Booting Zephyr OS build v3.1.99-ncs1  ***
    Address of main function 0x4f9
    Address of function_in_ext_flash 0x10000001
    Address of var_ext_sram_data 0x200000a0 (10)
    Address of function_in_sram 0x20000001
    Address of var_sram_data 0x200000a4 (10)
    Hello World! nrf5340dk_nrf5340_cpuapp

    it's still unable to run this instruction: printk("qspi_ext_mem_uninit successfully\n"); And, I noticed that, the output log of "Address of function_in_ext_flash" is different.

     Address of function_in_ext_flash 0x10000001 (mine)

    Address of function_in_ext_flash 0x42d (yours)

  • Hi, 

    Thanks to point out the difference in the log. I am able to reproduce the issue now. 

    This behavior is caused by anomaly 43 which is not yet handled in the nrfx_qspi driver - the mentioned nrf_qspi_cinstr_long_transfer_is_ongoing function is accessing the NRF_QSPI->CINSTRCONF register, which is at offset 0x634, so it triggers the anomaly.
    Currently, the workaround described in the errata pointed out above needs to be applied in the application, so

    nrf_qspi_task_trigger(NRF_QSPI, NRF_QSPI_TASK_ACTIVATE);

    needs to be called before nrfx_qspi_uninit().

    -Amanda H.

Related