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

    There is a DevZone case on this topic in the past. It is about safely using NVS to store data safely without overwriting other kernel storage in general.
    Setting and using NVS and BT Settings with Static Partition Manager.

    They discuss the solutions, and an example doing exactly what you are trying to do is discussed and provided at the end.

    Could you please take a look and see if it helps?

    If you still have difficulties then, I will be happy to investigate with you.

    Regards,

    Hieu

  • 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 Hieu

    I read through those posts, and tried to modify my code based on my understanding of what they were saying, but I obviously don’t know what I am doing Frowning2

    Point 1

    I was NOT using the CONFIG_PARTITION_MANAGER_ENABLED setting in my prj.conf file, but now I am:

    CONFIG_PARTITION_MANAGER_ENABLED=y


    Point 2


    I have removed my overlay file, since my understanding is that the .yaml file will be used instead

    Point 3

    I defined a new yaml file: pm_static.yaml
    I didn’t know how to get this file included in the project, so I placed this in the boards directory, and modified the makelist file to use this file instead:

    # set(PM_STATIC_YML_FILE
    # ${CMAKE_CURRENT_SOURCE_DIR}/boards/pm_static_${BOARD}.yml
    # )
    set(PM_STATIC_YML_FILE
    ${CMAKE_CURRENT_SOURCE_DIR}/boards/pm_static.yml
    )

    Here is the contents of the yaml file:

    user_storage:
    address: 0xfa000
    size: 0x2000
    end_address:0xfc000
    placement:
    before:
    - settings_storage
    region: flash_primary
    settings_storage:
    address: 0xfc000
    size: 0x4000
    end_address: 0x100000
    placement:
    before:
    - end
    region: flash_primary

    Point 4

    I don’t understand how/why they are using the CONFIG_SETTINGS_NVS_SECTOR_COUNT setting, in their prj.conf file, but I went ahead and included it:

    CONFIG_SETTINGS_NVS_SECTOR_COUNT=6

    I used the value 6, because user_storage uses 2 sectors, and settings_storage uses 4 sectors

    Point 5

    My code now tries to refer to this storage section:

    // #define STORAGE_NODE_LABEL storage
    #define STORAGE_NODE_LABEL user_storage
    
    


    Result


    I still get an error:


    'PM_PM_PM_user_storage_ID_LABEL_OFFSET' undeclared (first use in this function)

  • Hi Garrett,

    garrettb said:
    I read through those posts, and tried to modify my code based on my understanding of what they were saying, but I obviously don’t know what I am doing Frowning2

    I know that feeling. It took me some bits of time to fully understand all that information too.

    I can only answer some of your points quickly today, because I will only work for a bit more today and then go on vacation from tomorrow. However, let's go over what I can, and if that turns out insufficient, someone else will surely follow up with you in future replies. 

    P.s: My apologies for the repeated edits. I keep realizing mistakes I made writing this answer too quickly. The time pressure got the better of me Disappointed

    garrettb said:

    Point 1

    I was NOT using the CONFIG_PARTITION_MANAGER_ENABLED setting in my prj.conf file, but now I am:

    I did not mean that you should use Partition Manager, but that it could have been set behind the scene. Some Kconfig can set other Kconfig. Take for example, CONFIG_LOG will set CONFIG_PRINTK.

    To check if a configuration is set or not, you can look in the <build folder>/zephyr/.config file.

    I am ~90% sure that the Partition Manager is already enabled before you set it in prj.conf, so I will move forward with that assumption.

    garrettb said:

    Point 2


    I have removed my overlay file, since my understanding is that the .yaml file will be used instead

    With the Partition Manager enabled, this is the correct approach.

    garrettb said:

    Point 3

    I defined a new yaml file: pm_static.yaml
    I didn’t know how to get this file included in the project, so I placed this in the boards directory, and modified the makelist file to use this file instead:

    Please don't modify the CMake file like that. You can refer to the Partition Manager documentation to see how to use pm_static.yml file.

    I unfortunately don't have time to review your pm_static.yml for now. Please retry after understanding my answer today, and if you still have issues, you can reply here and another engineer will answer you in my stead.

    garrettb said:

    Point 4

    I don’t understand how/why they are using the CONFIG_SETTINGS_NVS_SECTOR_COUNT setting, in their prj.conf file, but I went ahead and included it:

    The config is discussed in the DevZone question that I linked:  RE: Problem to read back flash with NVS when concurrent use with Bluetooth.

    However, I understand that it is quite long. In short, Zephyr RTOS stores some persistent data using the Zephyr Settings subsystem. This subsystem by default use NVS as a backend, and store data in the same partition named storage. It stores data within the first n sectors, where n is CONFIG_SETTINGS_NVS_SECTOR_COUNT.

    Therefore, if you want to share the same storage partition, you don't need to change CONFIG_SETTINGS_NVS_SECTOR_COUNT, but you need to offset your data location by that many sectors.

    It seems you don't want that, from point 5, so you don't have to worry about it.

    garrettb said:

    Point 5

    My code now tries to refer to this storage section:

    It is probably the pm_static.yml file still not working properly. First, please try to set it up like I answered above. If the issue persists, please reply, and someone will help you.

    Finally, it is the summer vacation season here, so we are understaffed. Please excuse some delays in responses in the coming weeks.

  • Hi Hieu

    Thanks for the quick response – and have a great vacationBlush

    For anyone else who is going to pick up this thread, I made some more changes and feel I am getting a bit closer, but….
    So here’s my update for now:

    I removed the following two settings from the prj.conf, and confirmed that CONFIG_PARTITION_MANAGER_ENABLED is still set, via some other configuration flag as you suggested:

    #CONFIG_PARTITION_MANAGER_ENABLED=y
    #CONFIG_SETTINGS_NVS_SECTOR_COUNT=6

    I moved the pm_static.yml to the source directory, and modified the contents, which are now:

    storage:
      address: 0xfa000
      size: 0x4000
      end_address: 0xfe000
      placement:
      before:
      - user_storage
      region: flash_primary
    user_storage:
      address: 0xfe000
      size: 0x2000
      end_address: 0x100000
      placement:
      before:
      - end
      region: flash_primary


    I reverted the makefile back to its original:

    set(PM_STATIC_YML_FILE
      ${CMAKE_CURRENT_SOURCE_DIR}/boards/pm_static_${BOARD}.yml
      )

    The build system detects the .yml file and uses it.

    So now, I go to use the partition(s) mentioned in the .yml fiule.

    In my NVS code, I have the following init function:

    int my_file_init() {
    	int rc = 0, cnt = 0, cnt_his = 0;
    	char buf[16];
    	uint8_t key[8], longarray[128];
    	struct flash_pages_info info;
    
    	is_initialised = false;
    
    	reboot_counter = 0U;
    
    	/* define the nvs file system by settings with:
    	 *	sector_size equal to the pagesize,
    	 *	2 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)) {
    		return -1;
    	}
    
    	fs.offset = FLASH_AREA_OFFSET(STORAGE_NODE_LABEL);
    
    	rc = flash_get_page_info_by_offs(fs.flash_device, fs.offset, &info);
    
    	// info.size = 0x1000
    	// fs.offset = 0xFE000
    	// info.index = 254
    	// info.offset = 0xFE000
    
    	if (rc) {
    		return -2;
    	}
    	fs.sector_size = info.size;
    	fs.sector_count = 2U;
    
    	rc = nvs_mount(&fs);
    
    	if (rc) {
    		return -3;
    	}
    
    	:
    	:

    If I define STORAGE_NODE_LABEL as ‘storage’, the code runs / works.
    However, I assume I might be using storage space that might be required by Bluetooth, or another system,

    define STORAGE_NODE_LABEL storage
    //#define STORAGE_NODE_LABEL user_storage

    So, then I defineSTORAGE_NODE_LABEL as ‘user_storage’, the code runs

    //define STORAGE_NODE_LABEL storage
    #define STORAGE_NODE_LABEL user_storage

    But it crashes here (does not return from the call):

    	rc = nvs_mount(&fs);

    Here are the verified values before the call to nvs_mount()

    	// fs.offset = 0xFE000
    	// info.size = 0x1000
    	// info.index = 254
    	// info.offset = 0xFE000

    So, even though the .yml file is “accepted” and used, as soon as I use the “user_storage” region, the code does not work.

    By the way, the reason I am using addresses: 0xFA000 (size 0x6000) and 0xFE000 (size 0x2000), is because of the addresses used in nrf5340_cpuapp_comon.dts

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

  • Hi Garret,

    I will try to follow up here while Hieu is out of the office. I'm not sure why the program fails to return from your nvs_mount() call. But are you still on SDK v2.1.0?  prepared a new test example based on the peripheral_lbs sample and the code snippets you posted here, and it would be great if you could try running this on your end to see if it works for you as well.

    I didn't make much changes to the project. Here is a 'diff' of the changes I made to main.c:

    diff --git a/src/main.c b/src/main.c
    index 7483c69..163a979 100644
    --- a/src/main.c
    +++ b/src/main.c
    @@ -181,6 +181,77 @@ static int init_button(void)
     	return err;
     }
     
    +#include <zephyr/drivers/flash.h>
    +#include <zephyr/storage/flash_map.h>
    +#include <zephyr/fs/nvs.h>
    +
    +
    +static struct nvs_fs fs;
    +
    +#define RBT_CNT_ID 3
    +
    +#define STORAGE_NODE_LABEL	user_storage
    +
    +int my_file_init() 
    +{
    +	int rc;
    +	struct flash_pages_info info;
    +	uint32_t reboot_counter = 0U;
    +
    +	/* define the nvs file system by settings with:
    +	 *	sector_size equal to the pagesize,
    +	 *	2 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)) {
    +		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) {
    +		return -2;
    +	}
    +	fs.sector_size = info.size;
    +	/* Allocate the whole partition to nvs */
    +	fs.sector_count = FLASH_AREA_SIZE(STORAGE_NODE_LABEL) / info.size ;
    +
    +	rc = nvs_mount(&fs);
    +
    +	if (rc) {
    +		return -3;
    +	}
    +
    +	printk("*** %s partition mounted *** \n", STRINGIFY(STORAGE_NODE_LABEL));
    +	printk("Start address: 0x%x\n", (uint32_t) info.start_offset);
    +	printk("Sectors: %d\n", fs.sector_count);
    +
    +	/* 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 */
    +		reboot_counter++;
    +		(void)nvs_write(&fs, RBT_CNT_ID, &reboot_counter,
    +			  sizeof(reboot_counter));
    +		printk("Id: %d, Reboot_counter: %d\n",
    +			RBT_CNT_ID, reboot_counter);
    +
    +	} else   {/* item was not found, add it */
    +		printk("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));
    +	}
    +
    +	return 0;
    +}
    +
    +
     void main(void)
     {
     	int blink_status = 0;
    @@ -241,6 +312,8 @@ void main(void)
     
     	printk("Advertising successfully started\n");
     
    +	my_file_init();
    +
     	for (;;) {
     		dk_set_led(RUN_STATUS_LED, (++blink_status) % 2);
     		k_sleep(K_MSEC(RUN_LED_BLINK_INTERVAL));

    I also added this 'pm_static.yml' file to allocate the 'user_storage' partition:

    user_storage:
      address: 0xfe000
      size: 0x2000
      end_address: 0x100000
      placement:
      before:
      - end
      region: flash_primary

    Generated Memory report

    Note: the DTS partitions are ignored when the partition manager is enabled, which is true for any multi-image builds.

    peripheral_lbs_nvs_user_storage.zip

  • Hi Vidar

    You inadvertently solved my problem.
    In my existing project, I modified the pm_static.yml from:

    storage:
      address: 0xfa000
      size: 0x4000
      end_address: 0xfe000
      placement:
      before:
      - storage
      region: flash_primary
    user_storage:
      address: 0xfe000
      size: 0x2000
      end_address: 0x100000
      placement:
      before:
      - end
      region: flash_primary
    

    to this:

    user_storage:
      address: 0xfe000
      size: 0x2000
      end_address: 0x100000
      placement:
      before:
      - end
      region: flash_primary
    
    

    And everything worked :)

    So the issue was that I must have been messing up the existing ‘storage’ allocation/definition, and once I removed it from the .yml file, it must be happy with what ever default settings it has from elsewhere.

Reply
  • Hi Vidar

    You inadvertently solved my problem.
    In my existing project, I modified the pm_static.yml from:

    storage:
      address: 0xfa000
      size: 0x4000
      end_address: 0xfe000
      placement:
      before:
      - storage
      region: flash_primary
    user_storage:
      address: 0xfe000
      size: 0x2000
      end_address: 0x100000
      placement:
      before:
      - end
      region: flash_primary
    

    to this:

    user_storage:
      address: 0xfe000
      size: 0x2000
      end_address: 0x100000
      placement:
      before:
      - end
      region: flash_primary
    
    

    And everything worked :)

    So the issue was that I must have been messing up the existing ‘storage’ allocation/definition, and once I removed it from the .yml file, it must be happy with what ever default settings it has from elsewhere.

Children
No Data
Related