Zephyr NVS on an nRF5340_DK

Hi

I am trying to get the Zephyr NVS working on an nRF5340_DK board.

I am using the notes here:


And my starting code is based on the example it references:

In nrf5340_cpuapp_common.dts, the flash partitions are defined as follows:

&flash0 {

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

		boot_partition: partition@0 {
			label = "mcuboot";
			reg = <0x00000000 0x00010000>;
		};
		slot0_partition: partition@10000 {
			label = "image-0";
		};
		slot0_ns_partition: partition@50000 {
			label = "image-0-nonsecure";
		};
		slot1_partition: partition@80000 {
			label = "image-1";
		};
		slot1_ns_partition: partition@c0000 {
			label = "image-1-nonsecure";
		};
		scratch_partition: partition@f0000 {
			label = "image-scratch";
			reg = <0x000f0000 0xa000>;
		};
		storage_partition: partition@fa000 {
			label = "storage";
			reg = <0x000fa000 0x00006000>;
		};
	};
};

The nRF5340, the Flash page size is 4096 bytes (0x1000). So I decided to define two sectors (I think what should then happen is: when the first sector is full, NVS will then use the second sector. When the second sector is full, it will erase the first sector and use it. I am not sure if we can use just one sector.)

So to reserve some space (two sectors, total size 0x2000) in the storage_partition, I added the following to the app.overlay file:

&flash0 {
	partitions {
		storage_partition: partition@fa000 {
			label = "storage";
			reg = <0x000fa000 0x00004000>;
		};
		user_partition: partition@fe000 {
			label = "user_storage";
			reg = <0x000fe000 0x00002000>;
		};
	};
};

Now here’s my code:

uint32_t nvs_var_1;
#define NVS_VAR_1_ID 1


int nvs_data_init() {

	struct flash_pages_info info;
	const struct device *flash_dev;
	int rc = 0;
	ssize_t num_bytes;

	// See if can use the Flash
	
	flash_dev = DEVICE_DT_GET(FLASH_NODE);

	if (!device_is_ready(flash_dev))
	{
		printk("Flash device %s is not ready\n", flash_dev->name);
		return (-1);
	}

	fs.offset = FLASH_AREA_OFFSET(storage);

	printk("fs offset %d\n", fs.offset);

	rc = flash_get_page_info_by_offs(flash_dev, fs.offset, &info);
	if (rc)
	{
		printk("Unable to get page info\n");
		return (-2);
	}

	fs.sector_size = info.size;
	fs.sector_count = 2U;

	printk("size: %d, total: %d\n", info.size, info.size * 2);

	rc = nvs_init(&fs, flash_dev->name);
	if (rc)
	{
		printk("Flash Init failed\n");
		return (-3);
	}

	// See if can read the data from the Flash

	if (fs.ready) {
		printk("ready to read\n");
	}

	rc = nvs_read(&fs, NVS_VAR_1_ID, &nvs_var_1, sizeof(nvs_var_1));
	if (rc > 0)
	{ // item was found, show it
		printk("Id: %d, nvs_var_1: %d\n",  NVS_VAR_1_ID, nvs_var_1);
	}
	else
	{ // item was not found, add it
		printk("imme_data_init: nvs_var_1 not found (rc = %d), adding it at id %d\n", rc, NVS_VAR_1_ID);
		nvs_var_1 = 99;
		if (fs.ready) {
			printk("ready to write\n");
		}		
		num_bytes = nvs_write(&fs, NVS_VAR_1_ID, &nvs_var_1, sizeof(nvs_var_1));
		printk("imme_data_init: num bytes written: %d\n", num_bytes);
	}

	return (0);

}

PROBLEM:

When I run this code, the nvs_read() function returns -2 (ENOENT: No such file or directory).
The nvs_write() function always return 4, indicating that 4 bytes have been written.

If I initiate a soft system reset or press the reset button n the board, I get the same results – i.e. each tie the code runs, the nvs_read() function returns -2

So it looks like the code is not defining / accessing the user_partition correctly

Regards

Garrett

Parents Reply Children
  • Hi Hieu

    Thank you for your response.
    Perhaps I am missing something - I read through that post and downloaded the minimal sample that they were discussing.

    I can see the contents of the pm_static.yml file:

    custom_nvs_storage:
      address: 0xfc000
      end_address: 0x100000
      region: flash_primary
      size: 0x4000

    Which indicates that there is a custom partition (custom_nvs_storage) of size 0x4000.
    And the code in main.c uses this partition, e.g.

    #define NVS_PARTITION		custom_nvs_storage
    #define NVS_PARTITION_DEVICE	FIXED_PARTITION_DEVICE(NVS_PARTITION)
    #define NVS_PARTITION_OFFSET	FIXED_PARTITION_OFFSET(NVS_PARTITION)

    I have two questions:

    Q1:
    I couldn't find anything, but did they define this "custom_nvs_storage" partition in an overlay file - similar to the way I tried to define my "user_partition"?
    Q2:
    Did they manually edit the pm_static.yml file (and so I should do the same)?

    Thanks

    Garrett

  • Hi Garett,

    Ah,

    garrettb said:
    Q1:
    I couldn't find anything, but did they define this "custom_nvs_storage" partition in an overlay file - similar to the way I tried to define my "user_partition"?

    They didn't.

    My apologies. I missed the difference in your setup vs. theirs. Let me explain, and it will answer the rest the "why" for Q1 and all of Q2.

    In that DevZone question, they are using the MCUboot Bootloader in order to enable DFU.

    In NCS, using MCUboot (or any other kind of child image) automatically enable the Partition Manager. The Partition Manager will supersede the partitioning done in DTS, including the overlay.

    We can proceed without further details; but if you are interested, I wrote about the details of how Partition Manager supersedes the DTS partitions in this DevZone answer.

    pm_static.yml provides a static (developer configured) configuration of the Partition Manager.

    For the next steps:

    • If you don't plan to use the Partition Manager, you can just use your overlay partitioning like you are doing. You can still refer to the code part from that sample.
    • If you foresee yourself needing DFU, you can go ahead and look into the Partition Manager solution.

    Regards,

    Hieu

  • Hi Hieu

    Thanks again for your response.

    A lot of it is very difficult for me to understand, but I will worry about that at a later stage, when I am implementing DFU.

    For now, I just want to deal with the basic system.

    I have a solution which works, using the default DTS settings for flash0. It is based on Zephyr’s NVBS sample project (nRF Connect SDK v2.1.0).
    I will detail the code and settings below.

    I am using the storage_partition area that was already defined in the nrf5340_cpuapp_common.dts file.

    I refer to this partition at the start of my code:

    #define STORAGE_NODE_LABEL storage
    

    My code initialises the Flash are successfully, and increments the reboot counter on each reboot.

    I don’t know what this storage_partition is defined for (I think Nordic’s BLE Peripheral UART example was my application starting point).
    So, I am guessing I shouldn’t really use this partition, as it is.
    This is why I try to define a new flash partition using a small space from this storage_partition.

    I try to define this new userstorage partition in my overlay file, and refer to it in my code:

    #define STORAGE_NODE_LABEL userstorage
    

    But while I rebuild, I get the following error:

    C:\Users\me\ncs\v2.1.0\nrf\include\flash_map_pm.h:39:11: error: 'PM_PM_PM_userstorage_ID_LABEL_OFFSET' undeclared (first use in this function)
    

    So my three questions for now:
    One – why is the storage_partition already defined in the nrf5340_cpuapp_common.dts file
    Two – should I define my own user storage partition, to store my own application variables
    Three – If so, what am I missing / doing wrong, when trying to define the new userstorage partition

    Here is my code:

    // ................................................................................
    
    static struct nvs_fs fs;
    
    #define STORAGE_NODE_LABEL storage
    //#define STORAGE_NODE_LABEL userstorage	<-- can't get this working
    
    /* 1000 msec = 1 sec */
    #define SLEEP_TIME      100
    /* maximum reboot counts, make high enough to trigger sector change (buffer */
    /* rotation). */
    #define MAX_REBOOT 400
    
    #define ADDRESS_ID 1
    #define KEY_ID 2
    #define RBT_CNT_ID 3
    #define STRING_ID 4
    #define LONG_ID 5
    
    // ................................................................................
    
    uint32_t reboot_counter;
    uint32_t reboot_counter_his;
    
    // ................................................................................
    
    int nvs_file_init() {
    	int rc = 0, cnt = 0, cnt_his = 0;
    	char buf[16];
    	uint8_t key[8], longarray[128];
    	struct flash_pages_info info;
    
    	printk("nsv_init - start\n");
    
    	reboot_counter = 0U;
    
    	/* define the nvs file system by settings with:
    	 *	sector_size equal to the pagesize,
    	 *	3 sectors
    	 *	starting at FLASH_AREA_OFFSET(STORAGE_NODE_LABEL)
    	 */
    	fs.flash_device = FLASH_AREA_DEVICE(STORAGE_NODE_LABEL);
    	if (!device_is_ready(fs.flash_device)) {
    		printk("nsv_init - Flash device %s is not ready\n", fs.flash_device->name);
    		return -1;
    	}
    	fs.offset = FLASH_AREA_OFFSET(STORAGE_NODE_LABEL);
    	rc = flash_get_page_info_by_offs(fs.flash_device, fs.offset, &info);
    	if (rc) {
    		printk("nsv_init - Unable to get page info\n");
    		return -2;
    	}
    	fs.sector_size = info.size;
    	fs.sector_count = 3U;
    
    	rc = nvs_mount(&fs);
    	if (rc) {
    		printk("nsv_init - Flash Init failed\n");
    		return -3;
    	}
    
    	printk("nsv_init - ADDRESS_ID\n");
    
    	/* ADDRESS_ID is used to store an address, lets see if we can
    	 * read it from flash, since we don't know the size read the
    	 * maximum possible
    	 */
    	rc = nvs_read(&fs, ADDRESS_ID, &buf, sizeof(buf));
    	if (rc > 0) { /* item was found, show it */
    		printk("nsv_init - Id: %d, Address: %s\n", ADDRESS_ID, buf);
    	} else   {/* item was not found, add it */
    		strcpy(buf, "192.168.1.1");
    		printk("nsv_init - No address found, adding %s at id %d\n", buf,
    		       ADDRESS_ID);
    		(void)nvs_write(&fs, ADDRESS_ID, &buf, strlen(buf)+1);
    	}
    
    	printk("nsv_init - KEY_ID\n");
    
    	/* KEY_ID is used to store a key, lets see if we can read it from flash
    	 */
    	rc = nvs_read(&fs, KEY_ID, &key, sizeof(key));
    	if (rc > 0) { /* item was found, show it */
    		printk("nsv_init - Id: %d, Key: ", KEY_ID);
    		for (int n = 0; n < 8; n++) {
    			printk("%x ", key[n]);
    		}
    		printk("\n");
    	} else   {/* item was not found, add it */
    		printk("nsv_init - No key found, adding it at id %d\n", KEY_ID);
    		key[0] = 0xFF;
    		key[1] = 0xFE;
    		key[2] = 0xFD;
    		key[3] = 0xFC;
    		key[4] = 0xFB;
    		key[5] = 0xFA;
    		key[6] = 0xF9;
    		key[7] = 0xF8;
    		(void)nvs_write(&fs, KEY_ID, &key, sizeof(key));
    	}
    
    	printk("nsv_init - RBT_CNT_ID\n");
    
    	/* RBT_CNT_ID is used to store the reboot counter, lets see
    	 * if we can read it from flash
    	 */
    	rc = nvs_read(&fs, RBT_CNT_ID, &reboot_counter, sizeof(reboot_counter));
    	if (rc > 0) { /* item was found, show it */
    		printk("nsv_init - Id: %d, Reboot_counter: %d\n",
    			RBT_CNT_ID, reboot_counter);
    	} else   {/* item was not found, add it */
    		printk("nsv_init - No Reboot counter found, adding it at id %d\n",
    		       RBT_CNT_ID);
    		(void)nvs_write(&fs, RBT_CNT_ID, &reboot_counter,
    			  sizeof(reboot_counter));
    	}
    
    	printk("nsv_init - STRING_ID\n");
    
    	/* STRING_ID is used to store data that will be deleted,lets see
    	 * if we can read it from flash, since we don't know the size read the
    	 * maximum possible
    	 */
    	rc = nvs_read(&fs, STRING_ID, &buf, sizeof(buf));
    	if (rc > 0) {
    		/* item was found, show it */
    		printk("nsv_init - Id: %d, Data: %s\n",
    			STRING_ID, buf);
    		/* remove the item if reboot_counter = 10 */
    		if (reboot_counter == 10U) {
    			(void)nvs_delete(&fs, STRING_ID);
    		}
    	} else   {
    		/* entry was not found, add it if reboot_counter = 0*/
    		if (reboot_counter == 0U) {
    			printk("nsv_init - Id: %d not found, adding it\n",
    			STRING_ID);
    			strcpy(buf, "DATA");
    			(void)nvs_write(&fs, STRING_ID, &buf, strlen(buf) + 1);
    		}
    	}
    
    	printk("nsv_init - LONG_ID\n");
    
    	/* LONG_ID is used to store a larger dataset ,lets see if we can read
    	 * it from flash
    	 */
    	rc = nvs_read(&fs, LONG_ID, &longarray, sizeof(longarray));
    	if (rc > 0) {
    		/* item was found, show it */
    		printk("nsv_init - Id: %d, Longarray: ", LONG_ID);
    		for (int n = 0; n < sizeof(longarray); n++) {
    			printk("%x ", longarray[n]);
    		}
    		printk("\n");
    	} else   {
    		/* entry was not found, add it if reboot_counter = 0*/
    		if (reboot_counter == 0U) {
    			printk("nsv_init - Longarray not found, adding it as id %d\n",
    			       LONG_ID);
    			for (int n = 0; n < sizeof(longarray); n++) {
    				longarray[n] = n;
    			}
    			(void)nvs_write(
    				&fs, LONG_ID, &longarray, sizeof(longarray));
    		}
    	}
    
    
    	printk("nsv_init - end\n");
    
    	return (0);
    
    }
    
    
    void nvs_file_test() {
    	ssize_t num_bytes;
    	int rc;
    	int cnt_his;
    
    	printk("nvs_test - start: reboot_counter = %d\n", reboot_counter);
    
    	// Increment reboot counter, that was read after boot up
    	reboot_counter++;
    	// Save the updated counter to flash
    	num_bytes = nvs_write(&fs, RBT_CNT_ID, &reboot_counter, sizeof(reboot_counter));
    	printk("nvs_test - nvs_write: reboot_counter = %d, num_bytes or err = %d\n", reboot_counter, num_bytes);
    
    	// Add a delay
    	k_sleep(K_MSEC(100));
    	printk("nvs_test - wake up 3\n");
    
    	// Calculate space avaiable
    	num_bytes = nvs_calc_free_space(&fs);
    	printk("nvs_test - nvs_calc_free_space- before: num_bytes or err = %d\n", num_bytes);
    
    	printk("nvs_test - end\n");
    }

    From the DTS file:

    &flash0 {
    
    	partitions {
    		compatible = "fixed-partitions";
    		#address-cells = <1>;
    		#size-cells = <1>;
    
    		boot_partition: partition@0 {
    			label = "mcuboot";
    			reg = <0x00000000 0x00010000>;
    		};
    		slot0_partition: partition@10000 {
    			label = "image-0";
    		};
    		slot0_ns_partition: partition@50000 {
    			label = "image-0-nonsecure";
    		};
    		slot1_partition: partition@80000 {
    			label = "image-1";
    		};
    		slot1_ns_partition: partition@c0000 {
    			label = "image-1-nonsecure";
    		};
    		scratch_partition: partition@f0000 {
    			label = "image-scratch";
    			reg = <0x000f0000 0xa000>;
    		};
    		storage_partition: partition@fa000 {
    			label = "storage";
    			reg = <0x000fa000 0x00006000>;
    		};
    	};
    };

    From my overlay file:
    (If I use this, and refer to userstorage at the top of my file, I get the build error mentioned at the start of this message)

    &flash0 {
    	partitions {
    		compatible = "fixed-partitions";
    		#address-cells = <1>;
    		#size-cells = <1>;
    		storage_partition: partition@fa000 {
    			label = "storage";
    			reg = <0x000fa000 0x00004000>;
    		};
    		user_partition: partition@fe000 {
    			label = "userstorage";
    			reg = <0x000fe000 0x00002000>;
    		};
    	};
    };

  • Hi Garrett,

    Please be informed that I am still around and will continue to help you. However, I am on sick leave at the moment, and my response are therefore delayed. I hope you could wait.

    Meanwhile, I can give these quick answers to your questions:

    garrettb said:
    One – why is the storage_partition already defined in the nrf5340_cpuapp_common.dts file

    The storage_partition is the default partition for several Zephyr features, such as LittleFS and NVS. They are predefined for most if not all Nordic boards. For example, it is also defined for the nrf52840dk board: https://github.com/nrfconnect/sdk-zephyr/blob/v3.3.99-ncs1/boards/arm/nrf52840dk_nrf52840/nrf52840dk_nrf52840.dts#L278-L285.

    garrettb said:
    Two – should I define my own user storage partition, to store my own application variables

    This is one approach to it.

    Another approach is to store your data in the same partition, but at other sectors.

    garrettb said:
    Three – If so, what am I missing / doing wrong, when trying to define the new userstorage partition

    I cannot look into this yet. Let me do that when I return to office.

  • Hi Garrett,

    Thank you for your patience.

    Regarding question Three:

    For the user storage method:

    Can you check if CONFIG_PARTITION_MANAGER_ENABLED is enabled in your project? I think it most likely is, because you have a build error mentioned a symbol that only appears when Partition Manager is in use: "PM_PM_PM_userstorage_ID_LABEL_OFFSET." (PM stands for Partition Manager).

    As you still have the Partition Manager enabled, you have to use pm_static.yml to define additional partitions. Changes in .dts do not have any effects.

    I think that you already see how to use pm_static.yml to define partitions from your earlier reply.

    For the storage partition sharing method:

    garrettb said:

    My code initialises the Flash are successfully, and increments the reboot counter on each reboot.

    I don’t know what this storage_partition is defined for (I think Nordic’s BLE Peripheral UART example was my application starting point).
    So, I am guessing I shouldn’t really use this partition, as it is.

    Your guess is correct. The storage partition is used by the Zephyr RTOS by default to store its Zephyr Settings. You can use this partition to store your data from at offset out of the Settings range. I still recommend your own storage partition over this. However, if you are interest, Vidar explained this in detail in this DevZone question. Please try to go over the entire discussion.

     Problem to read back flash with NVS when concurrent use with Bluetooth 

    Vidar also provides a sample code that does this. His sample code is based on a project that uses the Partition Manager. However, you can refer to the code where he initializes NVS with offset calculated from CONFIG_SETTINGS_NVS_SECTOR_COUNT.

     NVS not compatible with MCUBOOT 

    I understand that it can be a lot to take in. Please let me know if something is still confusing.

Related