This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

Writing to and reading from flash on nRF5340

Hello,

I am trying to use part of the code from this example https://github.com/zephyrproject-rtos/zephyr/blob/master/samples/subsys/nvs

to write to and read from flash on nRF5340 SoC. However, I am getting the following linking error when building the project in Segger.

I am using Segger v5.34a, NCS v1.5.0-rc1 and Zephyr v2.4.99-ncs1-rc1.

I am including the following header files

#include <zephyr.h>
#include <power/reboot.h>
#include <device.h>
#include <string.h>
#include <drivers/flash.h>
#include <storage/flash_map.h>
#include <fs/nvs.h>

Can you please help ?

1> Linking ‘zephyr_prebuilt.elf’
1> C:\Zypher\v1.5.0-rc1\toolchain\opt/bin/arm-none-eabi-gcc zephyr/CMakeFiles/zephyr_prebuilt.dir/misc/empty_file.c.obj -Wl,-T zephyr/linker.cmd -Wl,-Map=C:/Sandbox/PID4-Firmware/app_core/build_nrf5340pdk_nrf5340_cpuapp_flash/zephyr/zephyr_prebuilt.map -Wl,--whole-archive app/libapp.a zephyr/libzephyr.a zephyr/arch/common/libarch__common.a zephyr/arch/arch/arm/core/aarch32/libarch__arm__core__aarch32.a zephyr/arch/arch/arm/core/aarch32/cortex_m/libarch__arm__core__aarch32__cortex_m.a zephyr/arch/arch/arm/core/aarch32/cortex_m/mpu/libarch__arm__core__aarch32__cortex_m__mpu.a zephyr/arch/arch/arm/core/aarch32/cortex_m/cmse/libarch__arm__core__aarch32__cortex_m__cmse.a zephyr/lib/libc/newlib/liblib__libc__newlib.a zephyr/lib/posix/liblib__posix.a zephyr/soc/arm/common/cortex_m/libsoc__arm__common__cortex_m.a zephyr/boards/boards/arm/nrf5340pdk_nrf5340_cpuapp/libboards__arm__nrf5340dk_nrf5340.a zephyr/drivers/adc/libdrivers__adc.a zephyr/drivers/gpio/libdrivers__gpio.a zephyr/drivers/ipm/libdrivers__ipm.a zephyr/drivers/spi/libdrivers__spi.a modules/nrf/lib/fatal_error/lib..__nrf__lib__fatal_error.a modules/nrf/drivers/hw_cc310/lib..__nrf__drivers__hw_cc310.a modules/hal_nordic/libmodules__hal_nordic.a -Wl,--no-whole-archive zephyr/kernel/libkernel.a zephyr/CMakeFiles/offsets.dir/./arch/arm/core/offsets/offsets.c.obj -Lc:/zypher/v1.5.0-rc1/toolchain/opt/bin/../lib/gcc/arm-none-eabi/9.2.1/thumb/v8-m.main/nofp -LC:/Sandbox/PID4-Firmware/app_core/build_nrf5340pdk_nrf5340_cpuapp_flash/zephyr -lgcc -Wl,--print-memory-usage zephyr/arch/common/libisr_tables.a C:/Zypher/v1.5.0-rc1/nrfxlib/crypto/nrf_cc312_platform/lib/cortex-m33/soft-float/no-interrupts/libnrf_cc312_platform_0.9.7.a -mcpu=cortex-m33 -mthumb -mabi=aapcs -Wl,--gc-sections -Wl,--build-id=none -Wl,--sort-common=descending -Wl,--sort-section=alignment -Wl,-u,_OffsetAbsSyms -Wl,-u,_ConfigAbsSyms -nostdlib -static -no-pie -Wl,-X -Wl,-N -Wl,--orphan-handling=warn -lm -Wl,-lc -LC:/Zypher/v1.5.0-rc1/toolchain/opt/arm-none-eabi/lib/thumb/v8-m.main/nofp -u_printf_float -Wl,-lgcc -lc -specs=nano.specs -o zephyr\zephyr_prebuilt.elf
1> Memory region Used Size Region Size %age Used
1> FLASH: 73512 B 1 MB 7.01%
1> SRAM: 28680 B 448 KB 6.25%
1> c:/zypher/v1.5.0-rc1/toolchain/opt/bin/../lib/gcc/arm-none-eabi/9.2.1/../../../../arm-none-eabi/bin/ld.exe: app/libapp.a(pid4_flash.c.obj): in function `flash_get_page_info_by_offs':
1> C:\Sandbox\PID4-Firmware\app_core\build_nrf5340pdk_nrf5340_cpuapp_flash/zephyr/include/generated/syscalls/flash.h:85: undefined reference to `z_impl_flash_get_page_info_by_offs'
1> c:/zypher/v1.5.0-rc1/toolchain/opt/bin/../lib/gcc/arm-none-eabi/9.2.1/../../../../arm-none-eabi/bin/ld.exe: app/libapp.a(pid4_flash.c.obj): in function `flash_init':
1> C:\Sandbox\PID4-Firmware\app_core\build_nrf5340pdk_nrf5340_cpuapp_flash/../src/flash/pid4_flash.c:45: undefined reference to `nvs_init'
1> collect2.exe: error: ld returned 1 exit status
1> IDT_LIST: 104 B 2 KB 5.08%
Build failed

Kind regards

Mohamed

Parents
  • Hello,

    I have just realised what the problem was. I needed to add this to my prj.conf file

    CONFIG_FLASH=y
    CONFIG_NVS=y

    I have also noticed the existence of other APIs flash_read() and flash_write(). 

    What is the difference between flash_read() and nvs_read() and the corresponding write APIs?

    Kind regards

    Mohamed

  • Hi!

    Great that you managed to fix the issue. The flash API is a more low level API used for interacting directly with the flash peripheral. The NVS API uses the flash API for easier organization and usage of storage. What you chose depends on your application, but NVS is commonly used for storing BLE bonding data for example.

    Best regards,
    Carl Richard

  • Hi!

    It's my pleasure. 

    Learner said:
    I think you meant nRF5340 because this is the SoC I am using.

    I did mean the nRF52840, as that was the SoC I was testing with. Evidently, it seems like the same values apply to the nRF5340 aswell.


    Most of the questions below are answered in the sample description and NVS documentation, so please read those carefully. Here is a short summary:

    Learner said:
    Q1/ Where are the historical values for reboot_counter stored?

    The historical values are stored in the same sector as the original data. Or rather, the historical values stay, while the new data is written to a new location in the sector.

    Learner said:
    Q2/ Why would we want to keep old values if we are overwriting with newer ones?

    Historical data can be valuable in some applications. For example, NVS is commonly used for storing BT bonding data and it can be beneficial to have access to earlier stored bond information.

    Learner said:
    Q4/ Isn't this inefficient way of using flash? Instead of using 4 bytes we end up using Nx4 bytes of flash?

    I'm not sure what you mean here? Each written piece of data is bundled with 8 bytes metadata. There is no other overhead except for the initial two pieces of metadata reserved for delete and sector close.

    Learner said:
    Q5/ Can history be disabled?

     No, it cannot.


    To me it sounds like you want more control over your flash, in which case I suggest either using the flash API directly. If you have further inquires about the NVS implementation I'm sure the Zephyr team will be able to provide you with more detailed answers in their channels.

    Best regards,
    Carl Richard

  • Thank you Carl. I really appreciate your help.

    Most of the questions below are answered in the sample description and NVS documentation, so please read those carefully.

    The only place where I have been getting information from is this link

     https://developer.nordicsemi.com/nRF_Connect_SDK/doc/latest/zephyr/reference/storage/nvs/nvs.html

    It does not go into enough details hence all the questions I have been asking you. Is there another place where I can find more info about NVS?

    Learner said:
    Q4/ Isn't this inefficient way of using flash? Instead of using 4 bytes we end up using Nx4 bytes of flash?

    I'm not sure what you mean here? Each written piece of data is bundled with 8 bytes metadata. There is no other overhead except for the initial two pieces of metadata reserved for delete and sector close.

    What I meant is this. If for example my application needs to store in a set of data stored in a structure of size 50 bytes every hour say. Then because of the concept of historical data after one week my application would have used up 7*24*50 = 8400 bytes. Without history data the application would have used only 50 bytes of flash .

    The historical values are stored in the same sector as the original data. Or rather, the historical values stay, while the new data is written to a new location in the sector.

    Is there a limit to how much historical data can be stored on flash other than the size of the flash itself?

    Does it keep growing in size for ever?

    To me it sounds like you want more control over your flash, in which case I suggest either using the flash API directly.

    You are right, I have come to the same conclusion. I will start looking at the flash APIs now. It is a shame because NVS had some advantages such CRC, prevention of excessive writes etc...

    Kind regards

    Mohamed

  • Hi again!

    Understood. The NVS source code as well as the NVS sample documentation is quite informative as well. Anyway, I read through the NVS documentation carefully again the history function makes more sense to me after that. While data is indeed stored in multiple instances this is to protect excessive overwriting of the same flash location, in addition this makes the history functionality possible. I quote the docs:

    Elements are appended to a sector until storage space in the sector is exhausted. Then a new sector in the flash area is prepared for use (erased). Before erasing the sector it is checked that identifier - data pairs exist in the sectors in use, if not the id-data pair is copied.

    From this it's clear that NVS is not wasteful when it comes to using flash, rather the other way around. The historical data will be overwritten over time, and the NVS subsystem will always ensure that the newest version id-data pair is kept. 

    With this in mind the API may fit your application anyway?

    Best regards,
    Carl Richard

  • Hi Carl,

    Elements are appended to a sector until storage space in the sector is exhausted. Then a new sector in the flash area is prepared for use (erased). Before erasing the sector it is checked that identifier - data pairs exist in the sectors in use, if not the id-data pair is copied.

    From this it's clear that NVS is not wasteful when it comes to using flash, rather the other way around. The historical data will be overwritten over time, and the NVS subsystem will always ensure that the newest version id-data pair is kept. 

    I have read the documentation again and even after reading it many times I am just about beginning to understand better how NVS works. It is not very easy to read and digest how it all works for someone new to NVS. It would have been much better to complement the worded description with an illustrated example showing for example what happens when the current sector is full and writing to a new sector starts. Also, what happens when all sectors allocated are full.

    I would appreciate a great deal if you could do that for me. HandshakeMask

    I am beginning to warm up to NVS much more than I did a day ago thanks to your explanation. I think I am going to stick with NVS and delay my move to flash API.

    Thank you.

    Kind regards

    Mohamed

  • Hi again, Mohamed!

    Thank you for your patience and good to hear that NVS is more understandable now. I'm glad I could help! This is a part of the Zephyr RTOS so I think it's better if you open an issue for your suggestions in their GitHub issue tracker, as they likely can provide better help than me on the specifics of the NVS API.

    Good luck on your project!

    Best regards,
    Carl Richard

Reply Children
  • Hi Carl,

    I've recently ported the code developed for the nRF5340 to the nRF52833 SoC and I am seeing this error when I call nvs_init(),

    <error> fs_nvs: NVS not initialized

    Any idea what could be causing this?

    Kind regards

    Mohamed

  • Hi!

    That error should not be possible to get from nvs_init(...). Have you made sure that nvs_init is called before any other NVS functions? A complete log might help me identify the issue. I also suggest debugging the code to see where it fails.

    Best regards,
    Carl Richard

  • Hi Carl,

    My NVS initialisation code looks like this and I am seeing the message in red below "Flash Init failed".

    I think the returned value rc = 0xffffffd3

    void flash_init( void )
    {
    uint32_t rc = 0;
    struct flash_pages_info info;

    /* define the nvs file system by settings with:
    * sector_size equal to the pagesize,
    * 3 sectors
    * starting at FLASH_AREA_OFFSET(storage)
    */
    fs.offset = FLASH_AREA_OFFSET( storage );
    flash_dev = device_get_binding( DT_CHOSEN_ZEPHYR_FLASH_CONTROLLER_LABEL );
    if ( flash_dev == NULL )
    {
    printk( "Flash device not bound!\n" );
    return;
    }
    else
    {
    rc = flash_get_page_info_by_offs( flash_dev, fs.offset, &info );
    if ( rc )
    {
    printk( "Unable to get page info" );
    }
    fs.sector_size = info.size;
    fs.sector_count = 3U;

    rc = nvs_init( &fs, DT_CHOSEN_ZEPHYR_FLASH_CONTROLLER_LABEL );
    if ( rc )
    {
    printk( "Flash Init failed\n" );
    }

    size_t page_count = flash_get_page_count(flash_dev);
    printk( "Flash contains %d pages of 4kB each\n", page_count );

    ...

    ...

    Kind regards

    Mohamed

  • <error> fs_nvs: NVS not initialized

    This message is probably returned in response to subsequent NVS function calls. Although I am calling the function flash_get_page_count() after nvs_init() and it is returning page_count = 128 which looks like a valid value.

  • Hi again!

    I think this might be caused by the flash controller already being bound to "flash_dev". NVS binds the flash controller as well and will likely fail if the device is already bound. Could you try to do as in the NVS sample, where they get the page info by referencing DT_CHOSEN_ZEPHYR_FLASH_CONTROLLER_LABEL directly? This ensures that it's released before nvs_init is called. See the code below.

    int rc = 0,
    struct flash_pages_info info;
    
    /* define the nvs file system by settings with:
     *	sector_size equal to the pagesize,
     *	3 sectors
     *	starting at FLASH_AREA_OFFSET(storage)
     */
    fs.offset = FLASH_AREA_OFFSET(storage);
    rc = flash_get_page_info_by_offs(
    	device_get_binding(DT_CHOSEN_ZEPHYR_FLASH_CONTROLLER_LABEL),
    	fs.offset, &info);
    if (rc) {
    	printk("Unable to get page info");
    }
    fs.sector_size = info.size;
    fs.sector_count = 3U;
    
    rc = nvs_init(&fs, DT_CHOSEN_ZEPHYR_FLASH_CONTROLLER_LABEL);
    if (rc) {
    	printk("Flash Init failed\n");
    }


    Best regards,
    Carl Richard

Related