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
Parents
  • Hi,

    Is there any change to the need for pm_static.yml when using toolchains 2.9.x or 3.x?

    No. Same as before: Don't need for development, but required for release.

    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?

    boot and slot0 must be on internal flash. slot1 is typically on external flash if you got external flash, but it can be on internal flash if you want to.

    Do I need to create a seperate partition when using the Zephyr settings infrastructure?

    A settings partition will be created automatically for you if use dynamic partitioning and enable settings. If you use static partitioning, then yes.

    Does SRAM needs to be configured as well?

    This will be done dynamically, so I recommend to not do that manually.

    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?

    This will be done dynamically if you use TF-M. Will you use TF-M in your project?

    I've watched this Nordic video on DFU/FOTA which was great and I hope you do more of these.

    Im glad you liked it!

    In it he recommends using dynamic partitioning for development and then freeze the partitioning.

    I still stand by this!

    n the case of the dk I am trying to adapt to my custom board that would look like:

    This is a bit complicated, but:
    If you use the Partition Manager, the partitions in DTS will be ignored. So you can keep them there, but use the one from the partition manager instead.

    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?

    The easiest would be to use dynamic partitioning, and then make sure that the DTS partitioning matches the partition report from VS Code after the build. Then build again. 
    Or if you use pm_static.yml, then make the DTS and pm_static.yml match. This is kinda not needed, since DTS partitions are ignored, but I think keeping them aligned will help for tidyness.

    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

    But hey, we do have a support team that are doing our best to help you learn this!

    Regards,
    Sigurd Hellesvik

  • Wow. I get the firmware social media sensation on my ticket. I'm truly honored. One of the best decisions I made was to learn on Nordic. Support is incredible and though Zepher is difficult, particularly for a newbie, it seems like the right way to do things. Honestly I can't think of a better support experience I've ever had. My introduction was to Don J and it's been consistently high ever since.

    Ok. Excellent response. Thank you. So I'm going to do everything dynamically and then use the partition report to inform the pm_static.yml when moving to production.

    This will be done dynamically if you use TF-M. Will you use TF-M in your project?

    Probably. I'm just trying to get things working now but I will need secure everything.

    But if I am using fstab it references a partition which I need to have in the dts/overlay. Let's use the fstab I saw in the example:

    	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>;
    		};
    	};

    It references littlefs_storage which means I need to include that in a partition reference someplace, right? So how do I use fstab and dynamic partitioning to get this code to work in main.c?

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

    I actually got it building without the fstab but littlefs_storage is on the primary flash. Is there anyway to size it and ensure it's on external flash?

    If I'm using dynamic partitioning I at least need to get the partions onto the right regions, correct? Also, I need a lot more space on my littlefs_storage partition than what was dynamically created.

  • 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
Reply Children
No Data
Related