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

  • Thank you Carl,

    I am looking forward to your answers.

    In addition I see that earlier versions of Zephyr imposed a max size on the elements in NVS, but that's not present anymore.

    Which max size are you referring to?

    The "+1" when writing is reserved for the metadata I guess, but I'll double check this.

    I think the +1 is for the extra NULL  ('\0') string terminator which is not included in the length returned by strlen().

    Kind regards

    Mohamed

  • Hi again!

    Did some research. The ate_wra is the "Allocation table entry write address", meaning that it's the location where the information about allocated data is stored. These are stored at the end of the sector, and you will see the offset decrease with new data being written. The fact that the offset in this case is "FF0" is given by the maximum size of a single write in NVS, which is sector_size - 3*ate_size. Based on the following comment from nvs.c:

    /* The maximum data size is sector size - 3 ate
     * where: 1 ate for data, 1 ate for sector close
     * and 1 ate to always allow a delete.
     */


    For the nRF52840 for example the ate_size is 8 and with a sector size of 4096 this gives us a ate_wra of 4096-16=4080=0x0FF0, with two ate's reserved for sector close and delete. I've visualized this in the attached image:


    I think this answers both the question of metadata size and flash layout.

    Best regards,
    Carl Richard

  • Thank you Carl.

    For the nRF52840 for example the ate_size is 8

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

    I am confused about the read history concept that NVS uses. 

    The line of code in blue below is calling nvs_write() with the same id RBT_CNT_ID so, I expect we are writing to the same location over and over and therefore, the value of reboot_counter should overwritten after each write.

    Q1/ Where are the historical values for reboot_counter stored?

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

    Q3/ Doesn't the history use up more flash than is necessary?

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

    Q5/ Can history be disabled?

    while (1)
    {
       k_msleep(SLEEP_TIME);
       if (reboot_counter < MAX_REBOOT)
       {
          ... Skipped over the code printing the history information...

          printk("...%d", cnt);
          cnt--;
          if (cnt == 0)
          {
             printk("\n");
             reboot_counter++;
             (void)nvs_write(
                &fs, RBT_CNT_ID, &reboot_counter,
                sizeof(reboot_counter));
             if (reboot_counter == MAX_REBOOT)
             {
                printk("Doing last reboot...\n");
             }
             sys_reboot(0);
          }
       }
       else
       {
          printk("Reboot counter reached max value.\n");
          printk("Reset to 0 and exit test.\n");
          reboot_counter = 0U;
          (void)nvs_write(&fs, RBT_CNT_ID, &reboot_counter,
          sizeof(reboot_counter));
          break;
       }
    }

    Kind regards

    Mohamed

  • 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

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

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

Related