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

  • Thank you Carl.

    I tried your suggestion but the problem is still there.

    I tracked it down in the debugger and it seems the problem is occurring when nvs_startup() is called from nvs_init() it returns rc = -45 (0xffffffd3).

    rc = nvs_startup(fs);
    if (rc) {
    return rc;
    }

    Digging even deeper in the API call tree, in nvs_startup(), I could see that the line of code  closed_sectors++  gets executed three times so, closed_sectors = 3 although the SES debugger did not allow me to set a watch on this variable. I also set two breakpoints at the two lines highlighted in yellow. The first breakpoint was reached but not the second one. This confirms that closed_sectors == fs->sector_count hence the jump to end:. So, it looks like all 3 sectors are closed and therefore there is no sector available NVS can write to.

    Why is this problem seen with nRF52833 (proprietary board) but not on nRF2833DK or on nRF5340 (another proprietary board)?

    static int nvs_startup(struct nvs_fs *fs)
    {

    ...

    /* step through the sectors to find a open sector following
    * a closed sector, this is where NVS can to write.
    */
    for (i = 0; i < fs->sector_count; i++) {
    addr = (i << ADDR_SECT_SHIFT) +
    (uint16_t)(fs->sector_size - ate_size);
    rc = nvs_flash_cmp_const(fs, addr, erase_value,
    sizeof(struct nvs_ate));
    if (rc) {
    /* closed sector */
    closed_sectors++;
    nvs_sector_advance(fs, &addr);
    rc = nvs_flash_cmp_const(fs, addr, erase_value,
    sizeof(struct nvs_ate));
    if (!rc) {
    /* open sector */
    break;
    }
    }
    }
    /* all sectors are closed, this is not a nvs fs */
    if (closed_sectors == fs->sector_count) {      // This breakpoint is reached
    rc = -EDEADLK;
    goto end;
    }

    if (i == fs->sector_count) {      // This breakpoint is NOT reached 
    /* none of the sectors where closed, in most cases we can set
    * the address to the first sector, except when there are only
    * two sectors. Then we can only set it to the first sector if
    * the last sector contains no ate's. So we check this first
    */

    ...

    }

    ...

    end:
    k_mutex_unlock(&fs->nvs_lock);
    return rc;   // rc = -45

    }//end of nvs_startup()

    Also, in C:\Zypher\v1.5.0-rc1\zephyr\include\devicetree\zephyr.h I see where the macro DT_CHOSEN_ZEPHYR_FLASH_CONTROLLER_LABEL is defined but it is greyed out meaning it is disabled.

    #if DT_NODE_HAS_PROP(DT_CHOSEN(zephyr_flash_controller), label)
    #define DT_CHOSEN_ZEPHYR_FLASH_CONTROLLER_LABEL \
    DT_LABEL(DT_CHOSEN(zephyr_flash_controller))
    #endif

    Thank you for your help.

    Kind regards

    Mohamed

  • Hi again!

    You're getting the EDEADLK error, caused by all sectors being closed and unavailable to NVS. Are you using a custom board definition for the proprietary nRF52833 board?

    Best regards,
    Carl Richard

Reply Children
  • Hi Carl,

    Are you using a custom board definition for the proprietary nRF52833 board?

    As far as I can tell I am not, I am using the generic nRF52833DK board definition.

    This is an extract of ...\build_nrf52833dk_nrf52833\zephyr\zephyr.dts,

    /dts-v1/;

    / {
    #address-cells = < 0x1 >;
    #size-cells = < 0x1 >;
    model = "Nordic nRF52833 DK NRF52833";
    compatible = "nordic,nrf52833-dk-nrf52833";
    chosen {
    zephyr,entropy = &rng;
    zephyr,flash-controller = &flash_controller;
    zephyr,console = &uart0;
    zephyr,shell-uart = &uart0;
    zephyr,uart-mcumgr = &uart0;
    zephyr,bt-mon-uart = &uart0;
    zephyr,bt-c2h-uart = &uart0;
    zephyr,sram = &sram0;
    zephyr,flash = &flash0;
    zephyr,code-partition = &slot0_partition;
    };
    aliases {

    ...

    ....

    My overlay file is called nrf52833dk_nrf52833.overlay.

    I am attaching the device tree file zephyr.dts and yaml file sample.yaml. 

    Note, This project was started by a colleague and I am adding the NVS flash functionality to it.

    5684.zephyr.dts

    sample.yaml

    Kind regards

    Mohamed

  • Hi again!

    What happens before you call flash_init? If possible, please share your main function or main.c. 

    In addition, could you try to build without the overlay, so that we can rule out any potential issues there? 

    Best regards,
    Carl Richard

  • Hi Carl,

    I cannot build without the overlay file. I don't think it has anything to do with it. I am attaching the overlay file just in case you may spot something.nrf52833dk_nrf52833.overlay

    What happens before you call flash_init?

    I call nvs_init() from within my function flash_init() that gets called from the main() function. Note, the failure occurs in nvs_startup() which is called from nvs_init().

    static struct nvs_fs fs;

    void main( void )

    {

       initialise();

       ...

    }

    void initialise( void )
    {
       // Setup a2d channels
       adc_device_setup();
       k_sleep( K_MSEC(1000) );

       // Setup SPI devices
       radio_spi_setup();
       

       //spi_init();
       k_sleep( K_MSEC(1000) );

       // Configure LR1110
       radio_gfsk_modem_config();
       k_sleep( K_MSEC(1000) );

       // Setup and Initialise timers
       timer_init();

       // Start timers
       timer_start();

      // Initialise Flash
       flash_init(); 

    }

    void flash_init( void )
    {
    int32_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 );

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

    // Initialise static data flash area
    flash_init_static();

    // Initialise dynamic data flash area
    flash_init_dynamic();

    #if NDEBUG
    /* Finally, lock the flash */
    //TBD!!! flash_SetLockByte(0x80U);
    #endif


    }//flash_init()

  • Hi again!

    I agree that nothing in the overlay file should affect the flash, so that's probably not the culprit. Just wanted to make sure, but if it's not possible that's okay!

    Can you confirm that the exact same code (except for the overlay) works with the nRF52833DK and the custom nRF5340 board? Could you test the standard zephyr/samples/subsys/nvs sample on the board and see if the behavior is similar?

    Best regards,
    Carl Richard

  • Can you confirm that the exact same code (except for the overlay) works with the nRF52833DK and the custom nRF5340 board?

    The exact same code including the same overlay works fine on the nRF52833DK.

    Also the same code but with a different overlay file works fine on the nRF5340PDK and nRF5340 proprietary board.

Related