Configuring external flash and internal flash for MCU Boot, Little FS, and for settings with a custom board

I am trying to set up a w25q128 using SPI with a nrf5340 on a custom board and had a few questions. I have used DevAcademy as a reference as I do eventually want to set up MCUBoot but right now I want to get the fiile system and settings up and running. 

  • Is there any change to the need for pm_static.yml when using toolchains 2.9.x or 3.x?
  • Is there a reason why one would use the boot, slot0, and slot1 partitions for mcuboot on external flash vs internal flash? Could I use the 1MB of internal flash for this?
  • Do I need to create a seperate partition when using the Zephyr settings infrastructure?
  • Are the instructions to use pm manager until things are set applicable when using littlefs? Doesn't that also cause me to "freeze" the partitions (see reference below)?
  • Does SRAM needs to be configured as well?
  • I have seen multiple entries for secure and non-secure, do I need to create paritions for each? When does one get used vs another?

I've watched this Nordic video on DFU/FOTA which was great and I hope you do more of these. The use of pm manager and memory report to show the memory:

In it he recommends using dynamic partitioning for development and then freeze the partitioning. I've also listened to Zephyr 101 - Device Settings Using nRF9160 Feather External Flash which is also very helpful but would like to drill deeper into some of the specific topics I'm interested in and would like more coverage of the basics as well. For example:

When I have this code:

//start filesystem config
#define PARTITION_NODE DT_NODELABEL(lfs)
FS_FSTAB_DECLARE_ENTRY(PARTITION_NODE);
struct fs_mount_t *mp = &FS_FSTAB_ENTRY(PARTITION_NODE);

It references a fstab entry which in another example I have looks like this:

	fstab {
		compatible = "zephyr,fstab";
		lfs: lfs {
			compatible = "zephyr,fstab,littlefs";
			mount-point = "/lfs";
			partition = <&littlefs_storage>;
			automount;
            read-size = <16>;
			prog-size = <16>;
			cache-size = <64>;
			lookahead-size = <32>;
			block-cycles = <512>;
		};
	};

I believe that also has to be references in the device tree as well. In the case of the dk I am trying to adapt to my custom board that would look like:

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

		littlefs_storage: partition@0 {
			label = "littlefs_storage";
			reg = <0x00000000 0x00200000>;
		};
		lvgl_raw_partition: partition@200000 {
			label = "lvgl_raw_partition";
			reg = <0x00200000 0x00200000>;
		};
        settings_partition: partition@400000 {
            label = "settings_partition";
            reg = <0x400000 0x100000 >;
        };
	};
};


I don't really need all those partitions, but the example I was using had them. I don't know if I need to keep settings in a different partition from the littlefs_storage or not. I guess this is the part I need to mirror in the pm_static.yml, is that correct? Do I only need to have a subset of the entries from the pm_static.yml in here? Do the values (such as reg) need to match those in the pm_static.yml?

I've had even very seasoned Zephry/Nordic people mention "It's not super easy to get it up and running, took me some fiddling around until I got it to work...", so it may be worth while to cover. I've seen

I have seen the comments in here which brings up the quetion of trust zone and sram.

I see the documentation for partition manager and 

Regarding posts I've read:

I've looked in git and seen:

  • sdk-nrf which seems to also include sram entries
  • Maybe I spoke too soon. I'm getting and error and traced it to:

    static inline struct flash_area const *get_flash_area_from_id(int idx)
    {
    	for (int i = 0; i < flash_map_entries; i++) {
    		if (flash_map[i].fa_id == idx) {
    			return &flash_map[i];
    		}
    	}
    
    	return NULL;
    }

    when idx=1, i=0 (first iteration of the loop), and flash_map_entries = 1. flashmap[0] looks like this;

    It errors on an evaluation when there appear to be values to compare.

    and the calling function the area does not appear to be null, although I see a "cannot access memory..." error in there.

    It is called from but fails and -2 is returned.

    	area = get_flash_area_from_id(id);
    	if (area == NULL) {
    		return -ENOENT;
    	}

    The calling function was taken from the sample:

    		/* Mount the filesystem */
    		rc = fs_mount(&lfs_storage_mnt);
    		if (rc < 0) {
    			LOG_ERR("Failed to mount filesystem: %d", rc);
    			return rc;
    		}

    at runtime the mountpoint is:

    type =1
    mnt_point =0x45afc "/"
    fs_data =0x200004e8 <littlefs_storage>
    storage_dev =0x1 <settings_init>
    mountp_len =0
    fs =0x0 <settings_init>
    flags =0 '\000'

    Probably something to do with my configuration. These are the relevant parts of my .dts.

    / {
    	chosen {
    		nordic,pm-ext-flash = &w25q128;
    	};
    };
    
    
    
    
    
    &spi3  {
        status = "okay";
        compatible = "nordic,nrf-spim";
        pinctrl-0 = <&spi3_default>;
        pinctrl-1 = <&spi3_sleep>;
        pinctrl-names = "default", "sleep";
        cs-gpios = <&gpio0 13 GPIO_ACTIVE_LOW>;
        w25q128: w25q128@0 {
    		compatible = "jedec,spi-nor";
    		reg = <0>;
    		//status = "okay";
    		spi-max-frequency = <8000000>;
    		//jedec-id = [ef 18 40]; // Could be 18 40 ef also. You will see in error printout if wrong ID is detected (ID missmatch)
    		jedec-id = [ef 40 18]; // Could be 18 40 ef also. You will see in error printout if wrong ID is detected (ID missmatch)
    		size = <DT_SIZE_M(16*8)>;
    		wp-gpios = <&gpio0 14 GPIO_ACTIVE_LOW>;
    		reset-gpios = <&gpio0 15 GPIO_ACTIVE_LOW>;
    		has-dpd;
            // CS High to Power-Down Mode (tDP) - 3 us
            // https://www.mouser.se/datasheet/2/949/w25q128jv_revf_03272018_plus-1489608.pdf
    		// 9.6 AC Electrical Characteristics(6)
    		t-enter-dpd = <3000>;
            // CS High to Standby Mode without Electronic Signature Read (tRES1) 3 us
            // https://www.mouser.se/datasheet/2/949/w25q128jv_revf_03272018_plus-1489608.pdf
    		// 9.6 AC Electrical Characteristics(6)
    		t-exit-dpd = <3000>;
    	};
    };

    This is what it looks like in main.c:

    	FS_LITTLEFS_DECLARE_DEFAULT_CONFIG(storage);
    	static struct fs_mount_t lfs_storage_mnt = {
    		.type = FS_LITTLEFS,
    		.fs_data = &storage,
    		.storage_dev = (void *)FIXED_PARTITION_ID(storage_partition),
    		.mnt_point = "/lfs",
    	};
    
    	struct fs_mount_t *mountpoint = &lfs_storage_mnt;

    I did see this: the first initialization sends an error: -19 which is what I am getting but I'm on 2.9.1.

  • Ah I thought we had it. Oh well.

    If it is not too much hassle, can you try to disable the partition manager with SB_CONFIG_PARTITION_MANAGER=n and then try the same?

    Im thinking that it would give us good info if we see the same issue without the partition manager or not.
    Who knows, this could even be  a workaround for you

  • No problem at all. So I need a sysbuild.conf and put that in it, correct?

    My memory report shows no partitions when I do that:

    When I did it without the SB_CONFIG_PARTITION_MANAGER=n I added this to the overlay to see if that was the problem:

    &w25q128 {
    	status = "okay";
      partitions {
    		compatible = "fixed-partitions";
    		#address-cells = <1>;
    		#size-cells = <1>;
    		// For Nordic Partition manager to work it has to have this name.
    		littlefs_storage: partition@0 {
    			label = "littlefs_storage";
    			reg = <0x00000000 0xc00000>;
    		};
        external_flash: partition@00c00000 {
    			label = "external_flash";
    			reg = <0x00c00000 0x400000>;
    		};
    	};
    };

    Which matched the memory report:

    Here is where the error happens. I am watching two variables: flash_map and flash_map_entries. You can see flash_map_entries=1. I think I should have >1 according to the memory report. The error happens on the evaluation, so that is also indicative of a problem.

    I added a test to see if the device was enabled. I don't know if this is a proper way to test it or not, but thought I would try:

    #if CONFIG_X_TEST_FLASH_DEVICE
    	static int test_flash_device() {
    
    		LOG_DBG("Starting W25Q128 connection test...");
    
    		const struct device *flash_dev;
    
    		// 1. Get device binding by label from DTS
    		// The label "W25Q128" should match the 'label' property in your DTS node for the W25Q128
    		flash_dev = DEVICE_DT_GET(DT_NODELABEL(w25q128));
    		if (!device_is_ready(flash_dev)) {
    			LOG_ERR("Flash device %s is not ready!", flash_dev->name);
    			return -ENODEV;
    		} else {
    			LOG_DBG("Flash device %s is ready", flash_dev->name);
    		}
    
    		return 0;
    
    	}
    
    #endif

    The device is ready. So that should work. It seems to have to do with the flash_map. But I have these in the dts/dtsi/overlay:

    
    
    
    // in overlay
    &w25q128 {
    	status = "okay";
      partitions {
    		compatible = "fixed-partitions";
    		#address-cells = <1>;
    		#size-cells = <1>;
    		// For Nordic Partition manager to work it has to have this name.
    		littlefs_storage: partition@0 {
    			label = "littlefs_storage";
    			reg = <0x00000000 0xc00000>;
    		};
        external_flash: partition@c00000 {
    			label = "external_flash";
    			reg = <0x00c00000 0x400000>;
    		};
    	};
    };
    
    
    // in DTS
    
    / {
    	chosen {
    		nordic,pm-ext-flash = &w25q128;
    	};
    };
    
    &spi3  {
        status = "okay";
        compatible = "nordic,nrf-spim";
        pinctrl-0 = <&spi3_default>;
        pinctrl-1 = <&spi3_sleep>;
        pinctrl-names = "default", "sleep";
        cs-gpios = <&gpio0 13 GPIO_ACTIVE_LOW>;
        w25q128: w25q128@0 {
    		compatible = "jedec,spi-nor";
    		reg = <0>;
    		//status = "okay";
    		spi-max-frequency = <8000000>;
    		//jedec-id = [ef 18 40]; // Could be 18 40 ef also. You will see in error printout if wrong ID is detected (ID missmatch)
    		jedec-id = [ef 40 18]; // Could be 18 40 ef also. You will see in error printout if wrong ID is detected (ID missmatch)
    		size = <DT_SIZE_M(16*8)>;
    		wp-gpios = <&gpio0 14 GPIO_ACTIVE_LOW>;
    		reset-gpios = <&gpio0 15 GPIO_ACTIVE_LOW>;
    		has-dpd;
            // CS High to Power-Down Mode (tDP) - 3 us
            // https://www.mouser.se/datasheet/2/949/w25q128jv_revf_03272018_plus-1489608.pdf
    		// 9.6 AC Electrical Characteristics(6)
    		t-enter-dpd = <3000>;
            // CS High to Standby Mode without Electronic Signature Read (tRES1) 3 us
            // https://www.mouser.se/datasheet/2/949/w25q128jv_revf_03272018_plus-1489608.pdf
    		// 9.6 AC Electrical Characteristics(6)
    		t-exit-dpd = <3000>;
    	};
    };
    
    // in dtsi
    &pinctrl {
        spi3_default: spi3_default {
    		group1 {
    			psels = <NRF_PSEL(SPIM_SCK, 0, 8)>,
    				<NRF_PSEL(SPIM_MOSI, 0, 10)>,
    				<NRF_PSEL(SPIM_MISO, 0, 9)>;
    		};
    	};
    
    	spi3_sleep: spi3_sleep {
    		group1 {
    			psels = <NRF_PSEL(SPIM_SCK, 0, 8)>,
    				<NRF_PSEL(SPIM_MOSI, 0, 10)>,
    				<NRF_PSEL(SPIM_MISO, 0, 9)>;
    			low-power-enable;
    		};
    	};
    };

    We see in the memory report I have the partition and the partitions.yml looks like this in the build directory:

    app:
      address: 0x0
      end_address: 0x100000
      region: flash_primary
      size: 0x100000
    external_flash:
      address: 0xc00000
      end_address: 0x1000000
      region: external_flash
      size: 0x400000
    littlefs_storage:
      address: 0x0
      device: DT_CHOSEN(nordic_pm_ext_flash)
      end_address: 0xc00000
      placement:
        before:
        - tfm_storage
        - end
      region: external_flash
      size: 0xc00000
    otp:
      address: 0xff8100
      end_address: 0xff83fc
      region: otp
      size: 0x2fc
    rpmsg_nrf53_sram:
      address: 0x20070000
      end_address: 0x20080000
      placement:
        before:
        - end
      region: sram_primary
      size: 0x10000
    sram_primary:
      address: 0x20000000
      end_address: 0x20070000
      region: sram_primary
      size: 0x70000

    The code in main.c which is responsible for the mount is:

    	FS_LITTLEFS_DECLARE_DEFAULT_CONFIG(storage);
    	static struct fs_mount_t lfs_storage_mnt = {
    		.type = FS_LITTLEFS,
    		.fs_data = &storage,
    		.storage_dev = (void *)FIXED_PARTITION_ID(storage_partition),
    		.mnt_point = "/lfs",
    	};

    It looks like those default to the littlefs_storage in flash_map_pm.h which I have:

    #define storage littlefs_storage
    #define storage_partition littlefs_storage

    and the function which is erroring is this which is taken from the sample and the id=1 when it fails:

    		rc = flash_area_open(id, &pfa);
    		if (rc < 0) {
    			LOG_ERR("FAIL: unable to find flash area %u: %d",id, rc);
    			return rc;
    		}

    I'm watching the flash_map, flash_map_entries and lfs_storage_mount which all look like this on during the error:

    lfs_storage_mnt = 
    {
        Node {
            <anonymous union> = {...}
            <anonymous union> #2 = {...}
            type=1
        }
        mount_point = 0x4610c "/" {
            *mnt_point = 47 '/'
        }
        fs_data = 0x200004e8 <littlefs_storage>
        storage_dev = 0x1 <settings_init>
        mountp_len = 0
        fs = 0x0 <settings_init> {
            open = 0x2000f010 <sys_work_q_stack>
            read = 0x6345 <z_arm_reset>
            write = 0x30819 <z_arm_nmi>
            lseek = 0x6331 <z_arm_usage_fault>
            tell = 0x6331 <z_arm_usage_fault>
            truncate = 0x6331 <z_arm_usage_fault>
            sync = 0x6331 <z_arm_usage_fault>
            close = 0x6331 <z_arm_usage_fault>
            opendir = 0x0 <settings_init>
            readir = 0x0 <settings_init>
            closedir = 0x0 <settings_init>
            mount = 0x6519 <z_arm_svc>
            unmount = 0x6331 <z_arm_usage_fault>
            unlink = 0x0 <settings_init>
            rename = 0x64d9 <z_arm_pendsv>
            mkdir = 0x6331 <z_arm_usage_fault>
            stat = 0x66c5 <_isr_wrapper>
            statvfs = 0x66c5 <_isr_wrapper>
        }
        flags = 0 '\000'
    }
    
    
    
    
    flash_map = 0x47918 <default_flash_map> {
        fa_id = 0 '\000'
        pad16 = 0
        fa_off = 0
        fa_size = 048576
        fa_dev = 0x4546c <__device_dts_ord_107> {
            name = 0x488c8 "f" {
                *name = 102 'f'
            }
            config = 0x0 <settings_init>
            api = 0x488ec <flash_nrf_api>
            state = 0x2000091e <__devstate_dts_ord_107> {
                init_res = 0 '\000'
                initialized = true
            }
            data = 0x0 <settings_init>
        }
    }

    I read the documentation on Partition Manager flash map:

    In the nRF Connect SDK, when the Partition Manager is used, flash area information is not taken from devicetree. Instead, it is taken from pm_config.h. In this configuration, flash_map_pm.h acts as the Partition Manager’s backend for the flash area interface.

    But that still doesn't explain why I only have one flash area and the external is not showing.

  • Fixed

    CONFIG_PM_OVERRIDE_EXTERNAL_DRIVER_CHECK
    =y # If the external flash device is not using the nordic,qspi-nor driver, you must enable
Related