NOR Flash driver is not working with external SPI Flash

Hi,

I'm integrating an external SPI flash W25Q32 with the nRF52840 using the NOR FLASH driver, but it's not functioning properly as the MCU log indicates an MPU error.

I have taken the spi_flash sample and am trying to integrate it into my project, but unfortunately, it's not functioning. I suspect there might be a partition error.

The Log displays the next error:

*** Booting nRF Connect SDK v3.5.99-ncs1 ***
Bluetooth initialized
Mesh initialized
Inicializando NUS-SPP service
Recovered identity for NUS: 1
Advertising successfully started
[00:00:00.001,098] <err> spi_nor: Device id 00 00 00 does not match config 15 40 16
[00:00:00.005,035] <inf> MAIN_THREAD: Initializing...

[00:00:00.019,165] <inf> fs_nvs: 8 Sectors of 4096 bytes
[00:00:00.019,165] <inf> fs_nvs: alloc wra: 7, fb8
[00:00:00.019,195] <inf> fs_nvs: data wra: 7, 58
[00:00:00.019,348] <inf> bt_sdc_hci_driver: SoftDevice Controller build revision: 
                                            36 f0 e5 0e 87 68 48 fb  02 fd 9f 82 cc 32 e5 7b |6....hH. .....2.{
                                            91 b1 5c ed                                      |..\.             
[00:00:00.022,827] <inf> bt_hci_core: HW Platform: Nordic Semiconductor (0x0002)
[00:00:00.022,857] <inf> bt_hci_core: HW Variant: nRF52x (0x0002)
[00:00:00.022,888] <inf> bt_hci_core: Firmware: Standard Bluetooth controller (0x00) Version 54.58864 Build 1214809870
[00:00:00.023,315] <inf> bt_hci_core: No ID address. App must call settings_load()
[00:00:00.025,573] <wrn> bt_mesh_access: Unused space in relation list: 1
[00:00:00.318,817] <inf> bt_hci_core: Identity[0]: F6:24:AF:24:B9:8A (random)
[00:00:00.318,908] <inf> bt_hci_core: Identity[1]: F0:21:2E:86:30:2B (random)
[00:00:00.318,939] <inf> bt_hci_core: HCI: version 5.4 (0x0d) revision 0x118f, manufacturer 0x0059
[00:00:00.318,969] <inf> bt_hci_core: LMP: version 5.4 (0x0d) subver 0x118f
[00:00:00.340,881] <inf> MAIN_THREAD: 
                                      spi@40004000 SPI flash testing

[00:00:00.340,881] <inf> MAIN_THREAD: ==========================

[00:00:00.340,911] <inf> MAIN_THREAD: 
                                      Perform test on single sector
[00:00:00.340,911] <inf> MAIN_THREAD: 
                                      Test 1: Flash erase

[00:00:00.340,911] <err> os: ***** MPU FAULT *****
[00:00:00.340,942] <err> os:   Instruction Access Violation
[00:00:00.340,942] <err> os: r0/a1:  0x00051824  r1/a2:  0x00000000  r2/a3:  0x00001000
[00:00:00.340,972] <err> os: r3/a4:  0x4002f000 r12/ip:  0x20008b00 r14/lr:  0x0001d759
[00:00:00.340,972] <err> os:  xpsr:  0x60000000
[00:00:00.341,003] <err> os: Faulting instruction address (r15/pc): 0x4002f000
[00:00:00.341,033] <err> os: >>> ZEPHYR FATAL ERROR 20: Unknown error on CPU 0
[00:00:00.341,064] <err> os: Current thread: 0x20007020 (main)
[00:00:00.554,229] <err> os: Halting system
 

And also had take a look in the logic lines and this is what I see:

This occurs just before the MCU halting. As you can see the only lines that are working is the CLK and CS.


This is my overlay file:

/ {
	aliases {
		spi-flash0 = &spi1_cs0_flash;
	};
};


&spi1 {
	cs-gpios = <&gpio0 29 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>;
	
	status = "okay";
	spi1_cs0_flash: W25Q32@0 {
		compatible = "jedec,spi-nor";
		status = "okay";
		reg = <0x0>;
		spi-hold-cs;
		has-dpd;
		// wp-gpios = <&gpio0 28 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>;
		// hold-gpios = <&gpio0 4 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>;
        spi-max-frequency = <DT_FREQ_M(1)>;
		// label = "W25Q32";
        jedec-id = [15 40 16];
        size = <4194304>;
	};
};

&spi1_default {
	group1 {
		psels = <NRF_PSEL(SPIM_SCK, 0, 31)>,
				<NRF_PSEL(SPIM_MISO, 1, 8)>,
				<NRF_PSEL(SPIM_MOSI, 0, 30)>;
	};
};

&spi1_cs0_flash {
	partitions {
		compatible = "fixed-partitions";
		#address-cells = <1>;
		#size-cells = <1>;

		ext_storage: partition@0 {
			label = "external_storage";
			reg = <0x00000000 0x00400000>;
		};
	};
};

And this is my prj.conf file

#
# Copyright (c) 2020 Nordic Semiconductor ASA
#
# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
#
CONFIG_NCS_SAMPLES_DEFAULTS=y

# Deffered logging helps improve LPN power consumption
# when friendship is established.
CONFIG_LOG_MODE_DEFERRED=y


# General configuration
CONFIG_MAIN_STACK_SIZE=4096
CONFIG_HEAP_MEM_POOL_SIZE=2048
CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=4096
CONFIG_FLASH=y
CONFIG_FLASH_MAP=y
CONFIG_NVS=y
CONFIG_NVS_LOOKUP_CACHE=y
CONFIG_SETTINGS=y
CONFIG_SETTINGS_NVS_NAME_CACHE=y
CONFIG_HWINFO=y
CONFIG_DK_LIBRARY=y
CONFIG_PM_SINGLE_IMAGE=y
CONFIG_PM_PARTITION_SIZE_SETTINGS_STORAGE=0x8000
CONFIG_SOC_FLASH_NRF_PARTIAL_ERASE=y

# Temperature sensor
CONFIG_SENSOR=y
CONFIG_TEMP_NRF5=y

# Bluetooth configuration
CONFIG_BT=y
CONFIG_BT_COMPANY_ID=0x0059
CONFIG_BT_DEVICE_NAME="OmegaMesh"
CONFIG_BT_L2CAP_TX_BUF_COUNT=8
CONFIG_BT_OBSERVER=y
CONFIG_BT_PERIPHERAL=y
CONFIG_BT_SETTINGS=y
CONFIG_BT_EXT_ADV=y
# 5 sets are used by Bluetooth Mesh, 1 is needed for the sample advs.
CONFIG_BT_EXT_ADV_MAX_ADV_SET=6
CONFIG_BT_MAX_CONN=3
CONFIG_BT_ID_MAX=2

# Disable unused Bluetooth features
# CONFIG_BT_CTLR_DUP_FILTER_LEN=0
CONFIG_BT_CTLR_LE_ENC=n
CONFIG_BT_PHY_UPDATE=n
CONFIG_BT_CTLR_CHAN_SEL_2=n
CONFIG_BT_CTLR_MIN_USED_CHAN=n
CONFIG_BT_CTLR_PRIVACY=n

# Bluetooth mesh configuration
CONFIG_BT_MESH=y
CONFIG_BT_MESH_RELAY=y
CONFIG_BT_MESH_FRIEND=y
CONFIG_BT_MESH_TX_SEG_MAX=32
CONFIG_BT_MESH_RX_SEG_MAX=32
CONFIG_BT_MESH_PB_GATT=y
CONFIG_BT_MESH_GATT_PROXY=y
CONFIG_BT_MESH_PROXY_USE_DEVICE_NAME=y
CONFIG_BT_MESH_DK_PROV=y
CONFIG_BT_MESH_NLC_PERF_CONF=y
CONFIG_BT_MESH_MODEL_EXTENSION_LIST_SIZE=6

# CONFIG_BT_MESH_PROP_MAXCOUNT

CONFIG_BT_MESH_CDB=y
CONFIG_BT_MESH_CDB_NODE_COUNT=16
CONFIG_BT_MESH_CDB_SUBNET_COUNT=3
CONFIG_BT_MESH_CDB_APP_KEY_COUNT=3

# Enable the NUS service
CONFIG_BT_NUS=y
CONFIG_BT_MESH_BATTERY_SRV=y
CONFIG_BT_MESH_BATTERY_CLI=y
# Enable bonding
CONFIG_FLASH_PAGE_LAYOUT=y


CONFIG_BT_MESH_SUBNET_COUNT=2
CONFIG_BT_MESH_APP_KEY_COUNT=3
CONFIG_BT_MESH_CRPL=32
CONFIG_BT_MESH_MSG_CACHE_SIZE=64
# CONFIG_BT_MESH_SHELL=y

# Bluetooth mesh models
CONFIG_BT_MESH_SENSOR_SRV=y
CONFIG_BT_MESH_SENSOR_SRV_SENSORS_MAX=5
CONFIG_BT_MESH_SENSOR_CLI=y
CONFIG_BT_MESH_SENSOR_USE_LEGACY_SENSOR_VALUE=n
CONFIG_BT_MESH_PROP_SRV=y

# Debugging
CONFIG_BT_MESH_LOG_LEVEL_DBG=y
# CONFIG_BT_MESH_PROV_DEVICE_LOG_LEVEL_INF=y

# Config logger
CONFIG_LOG=y

# CONFIG_USE_SEGGER_RTT=y
CONFIG_LOG_BACKEND_RTT=n
CONFIG_LOG_BACKEND_UART=y
CONFIG_LOG_PRINTK=n

CONFIG_ASSERT=y

#External flash
CONFIG_SPI=y
CONFIG_SPI_NOR=y
CONFIG_SPI_NOR_FLASH_LAYOUT_PAGE_SIZE=4096


#debugging
CONFIG_RESET_ON_FATAL_ERROR=n
CONFIG_THREAD_NAME=y

With the following testing code

#define SPI_FLASH_TEST_REGION_OFFSET 0x0000
#define SPI_FLASH_SECTOR_SIZE        4096

static void single_sector_test(const struct device *flash_dev)
{
	const uint8_t expected[] = { 0x55, 0xaa, 0x66, 0x99 };
	const size_t len = sizeof(expected);
	uint8_t buf[sizeof(expected)];
	int rc;
	

	LOG_INF("\nPerform test on single sector");
	/* Write protection needs to be disabled before each write or
	 * erase, since the flash component turns on write protection
	 * automatically after completion of write and erase
	 * operations.
	 */
	LOG_INF("\nTest 1: Flash erase\n");

	/* Full flash erase if SPI_FLASH_TEST_REGION_OFFSET = 0 and
	 * SPI_FLASH_SECTOR_SIZE = flash size
	 */
	rc = flash_erase(flash_dev, SPI_FLASH_TEST_REGION_OFFSET,
			 SPI_FLASH_SECTOR_SIZE);
	if (rc != 0) {
		LOG_ERR("Flash erase failed! %d\n", rc);
	} else {
		LOG_INF("Flash erase succeeded!\n");
	}

	LOG_INF("\nTest 2: Flash write\n");

	LOG_INF("Attempting to write %zu bytes\n", len);
	rc = flash_write(flash_dev, SPI_FLASH_TEST_REGION_OFFSET, expected, len);
	if (rc != 0) {
		LOG_ERR("Flash write failed! %d\n", rc);
		return;
	}

	memset(buf, 0, len);
	rc = flash_read(flash_dev, SPI_FLASH_TEST_REGION_OFFSET, buf, len);
	if (rc != 0) {
		LOG_ERR("Flash read failed! %d\n", rc);
		return;
	}

	if (memcmp(expected, buf, len) == 0) {
		LOG_INF("Data read matches data written. Good!!\n");
	} else {
		const uint8_t *wp = expected;
		const uint8_t *rp = buf;
		const uint8_t *rpe = rp + len;

		LOG_ERR("Data read does not match data written!!\n");
		while (rp < rpe) {
			LOG_INF("%08x wrote %02x read %02x %s\n",
			       (uint32_t)(SPI_FLASH_TEST_REGION_OFFSET + (rp - buf)),
			       *wp, *rp, (*rp == *wp) ? "match" : "MISMATCH");
			++rp;
			++wp;
		}
	}
}

int main(void)
{
	const struct device *flash_dev = DEVICE_DT_GET(DT_NODELABEL(spi1));

	LOG_INF("Initializing...\n");

	if (!device_is_ready(flash_dev)) {
		LOG_ERR("%s: device not ready.\n", flash_dev->name);
		// return 0;
	}

	LOG_INF("\n%s SPI flash testing\n", flash_dev->name);
	LOG_INF("==========================\n");

	single_sector_test(flash_dev);
	
	// app code
}

What could be happening? 

Thanks for advance!

  • This probably isn't the main issue since it isn't reading the JEDEC ID correctly, but one thing I noticed is that your size should be 33,554,432 or 0x2000000 (16384 pages of 256 bytes each: 16384 * 256 * 8 = 33554432).

    Also, I believe the JEDEC ID should be [ef 40 16]. The datasheet is misleading there. This matches what the .dts says for the same w25q32jv on the circuitdojo feather dts in `ncs\v2.4.1\zephyr\boards\arm\circuitdojo_feather_nrf9160\circuitdojo_feather_nrf9160_common.dts`.

    &spi3 {
    	compatible = "nordic,nrf-spim";
    	status = "okay";
    	cs-gpios = < &gpio0 7 GPIO_ACTIVE_LOW >;
    	pinctrl-0 = <&spi3_default>;
    	pinctrl-1 = <&spi3_sleep>;
    	pinctrl-names = "default", "sleep";
    	w25q32jv: w25q32jv@0 {
    		compatible = "jedec,spi-nor";
    		reg = < 0 >;
    		spi-max-frequency = < 40000000 >;
    		wp-gpios = < &gpio0 8 GPIO_ACTIVE_LOW >;
    		hold-gpios = < &gpio0 10 GPIO_ACTIVE_LOW >;
    		size = < 0x2000000 >;
    		has-dpd;
    		t-enter-dpd = < 3000 >;
    		t-exit-dpd = < 30000 >;
    		jedec-id = [ ef 40 16  ];
    	};
    };

    When you were scoping the signals, did you see any traffic on MOSI where it was asking for the JEDEC ID? It's strange that it's reading 00 00 00.

  • Hello! Thanks for supporting me.

    I verified the JEDEC ID externally and I found that, effectively, is a different one. So I changed the JEDEC ID property in the DTS for marching them. Also I change the size property that you mentioned, I was wrong.

    Adicionally, I checked the Flash Spi configuration and I deactivate the spi-hold-cs prop, doing this, the flash transactions MISO & MOSI lines starting to work correctly. 

    But now that I corrected these error/bugs, the MPU error persists. In the logic lines I can see that the MCU perform the next operations before crashing:

    1. Sends a Wake up command

    2. Sends a Read Command 

    3. Sends a JEDEC ID command

    3.1 The Flash responds with it's ID

    3.2 The JEDEC ID marches with the one in the DTS

    4. The MCU crashes with MPU error while trying to perform the flash_erase command.

    I believe that by activating the KConfig CONFIG_FLASH_MAP & CONFIG_FLASH_PAGE_LAYOUT, is the cause of this error due to a misconfiguration or lack of configuration in the Flash layout with this new external Flash-SPI.

  • Hi

    Can you confirm that this is the same exact error/fault that you see in the initial ticket?

    [00:00:00.340,911] <err> os: ***** MPU FAULT *****
    [00:00:00.340,942] <err> os:   Instruction Access Violation
    [00:00:00.340,942] <err> os: r0/a1:  0x00051824  r1/a2:  0x00000000  r2/a3:  0x00001000
    [00:00:00.340,972] <err> os: r3/a4:  0x4002f000 r12/ip:  0x20008b00 r14/lr:  0x0001d759
    [00:00:00.340,972] <err> os:  xpsr:  0x60000000
    [00:00:00.341,003] <err> os: Faulting instruction address (r15/pc): 0x4002f000
    [00:00:00.341,033] <err> os: >>> ZEPHYR FATAL ERROR 20: Unknown error on CPU 0
    [00:00:00.341,064] <err> os: Current thread: 0x20007020 (main)
    [00:00:00.554,229] <err> os: Halting system

    Try finding out where the program counter points, as it seems to point somewhere in the main stack here at least. If you set the CONFIG_THREAD_NAME the fault will usually list the thread name instead of "unknown" that should help you with finding out what is reporting this error.

    You can check out this Lesson on debugging/troubleshooting in DevAcademy if you want to get started on debugging.

    Best regards,

    Simon

Related