I am not able to install littlefs on an external qspi flash

Hello

I'm new with zephyr environment and I want to use littlefs in order to partition an external flash over spi, and to be able to upload file over BLE by using a SMP server.

I know this topic comes up regularly. I downloaded the littlefs sample project for inspiration. Here's the error I'm getting when building :

main.c:111: undefined reference to `z_fsmp_DT_N_S_soc_S_qspi_40029000_S_mx25r6435f_0_S_partitions_S_partition_0'

I guess I misconfigured something but I can't figure out what.

My environment : 

- board : nrf52840dk

- SDK v2.9.0

- Toolchain : v2.9.1

Here is my app.overlay : 

 / {
     chosen {
         nordic,pm-ext-flash = &mx25r64;
     };

     fstab {
         compatible = "zephyr,fstab";
         lfs1: lfs1 {
             compatible = "zephyr,fstab,littlefs";
             mount-point = "/lfs1";
             partition = <&lfs1_partition>;
             /*automount;*/
             read-size = <16>;
             prog-size = <16>;
             cache-size = <64>;
             lookahead-size = <32>;
             block-cycles = <512>;
         };
     };
 };

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

        lfs1_partition: partition@0 {
            label = "lfs1";
            reg = <0x00000000 0x00010000>;
        };
    };
};

Here is my custom dts file  : 

/dts-v1/;
#include <nordic/nrf52840_qfaa.dtsi>
#include "ngtble-pinctrl.dtsi"

/ {
	model = "Nordic NGT BLE NRF52840";
	compatible = "teledyne,ngtble";

	chosen {
		/*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;
		zephyr,ieee802154 = &ieee802154;
	};

	reserved-memory {
		#address-cells = <1>;
		#size-cells = <1>;
		ranges;
	};
};

&gpiote {
	status = "okay";
};

&gpio0 {
	status = "okay";
};

&gpio1 {
	status = "okay";
};

&uart1 {
	compatible = "nordic,nrf-uarte";
	status = "okay";
	current-speed = <9600>;
	pinctrl-0 = <&uart1_default>;
	pinctrl-1 = <&uart1_sleep>;
	pinctrl-names = "default", "sleep";

	modbus0 {
		compatible = "zephyr,modbus-serial";
		status = "okay";
	};
};

&flash0 {

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

		boot_partition: partition@0 {
			label = "mcuboot";
			reg = <0x00000000 DT_SIZE_K(48)>;
		};

		slot0_partition: partition@c000 {
			label = "image-0";
			reg = <0x0000c000 DT_SIZE_K(472)>;
		};

		slot1_partition: partition@82000 {
			label = "image-1";
			reg = <0x00082000 DT_SIZE_K(472)>;
		};

		storage_partition: partition@f8000 {
			label = "storage";
			reg = <0x000f8000 DT_SIZE_K(32)>;
		};
	};
};

&spi0 {
	compatible = "nordic,nrf-spi";
	/* Cannot be used together with i2c0. */
	/* status = "okay"; */
	pinctrl-0 = <&spi0_default>;
	pinctrl-1 = <&spi0_sleep>;
	pinctrl-names = "default", "sleep";
};

&spi1 {
	compatible = "nordic,nrf-spi";
	status = "okay";
	pinctrl-0 = <&spi1_default>;
	pinctrl-1 = <&spi1_sleep>;
	pinctrl-names = "default", "sleep";
};

&spi2 {
	compatible = "nordic,nrf-spi";
	status = "disabled";
	pinctrl-0 = <&spi2_default>;
	pinctrl-1 = <&spi2_sleep>;
	pinctrl-names = "default", "sleep";
};

&qspi {
	status = "okay";
	pinctrl-0 = <&qspi_default>;
	pinctrl-1 = <&qspi_sleep>;
	pinctrl-names = "default", "sleep";
	mx25r64: mx25r6435f@0 {
		compatible = "nordic,qspi-nor";
		reg = <0>;
		/* MX25R64 supports only pp and pp4io */
		writeoc = "pp4io";
		/* MX25R64 supports all readoc options */
		readoc = "read4io";
		sck-frequency = <8000000>;
		jedec-id = [c2 28 17];
		sfdp-bfp = [
			e5 20 f1 ff  ff ff ff 03  44 eb 08 6b  08 3b 04 bb
			ee ff ff ff  ff ff 00 ff  ff ff 00 ff  0c 20 0f 52
			10 d8 00 ff  23 72 f5 00  82 ed 04 cc  44 83 68 44
			30 b0 30 b0  f7 c4 d5 5c  00 be 29 ff  f0 d0 ff ff
		];
		size = <67108864>;
		has-dpd;
		t-enter-dpd = <10000>;
		t-exit-dpd = <35000>;
        partitions {
            compatible = "fixed-partitions";
            #address-cells = <1>;
            #size-cells = <1>;
			lfs1_partition: partition@0 {
				label = "lfs1";
				reg = <0x00000000 0x00010000>;
			};
		};
	};
};

my pm_static.yml : 

app:
  address: 0xc200
  end_address: 0x85000
  region: flash_primary
  size: 0x78e00
mcuboot:
  address: 0x0
  end_address: 0xc000
  placement:
    align:
      end: 0x1000
    before:
    - mcuboot_primary
  region: flash_primary
  size: 0xc000
mcuboot_pad:
  address: 0xc000
  end_address: 0xc200
  placement:
    align:
      start: 0x1000
    before:
    - mcuboot_primary_app
  region: flash_primary
  size: 0x200
mcuboot_primary:
  address: 0xc000
  end_address: 0x85000
  orig_span: &id001
  - mcuboot_pad
  - app
  region: flash_primary
  sharers: 0x1
  size: 0x79000
  span: *id001
mcuboot_primary_app:
  address: 0xc200
  end_address: 0x85000
  orig_span: &id002
  - app
  region: flash_primary
  size: 0x78e00
  span: *id002
mcuboot_secondary:
  address: 0x85000
  end_address: 0xfe000
  placement:
    after:
    - mcuboot_primary
    align:
      start: 0x1000
  region: flash_primary
  share_size:
  - mcuboot_primary
  size: 0x79000
settings_storage:
  address: 0xfe000
  end_address: 0x100000
  placement:
    align:
      start: 0x1000
    before:
    - end
  region: flash_primary
  size: 0x2000
sram_primary:
  address: 0x20000000
  end_address: 0x20040000
  region: sram_primary
  size: 0x40000
littlefs_storage:
  address: 0x0
  device: mx25r6435f
  end_address: 0x800000
  region: external_flash
  size: 0x800000

my prj.conf file : 

#
# Copyright 2024 DPEdesign
#
# All rights reserved
#

CONFIG_NCS_SAMPLES_DEFAULTS=y
CONFIG_DK_LIBRARY=y
CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=2048

CONFIG_USE_SEGGER_RTT=y
CONFIG_RTT_CONSOLE=y
CONFIG_UART_CONSOLE=n

CONFIG_NEWLIB_LIBC=y
CONFIG_NEWLIB_LIBC_FLOAT_PRINTF=y

CONFIG_BT=y
CONFIG_BT_PERIPHERAL=y
CONFIG_BT_DEVICE_NAME="FREEDOM"
CONFIG_BT_MAX_CONN=2
CONFIG_BT_SMP=y
CONFIG_BT_MAX_PAIRED=8

CONFIG_BT_GATT_AUTO_SEC_REQ=y
CONFIG_BT_FIXED_PASSKEY=y

CONFIG_BT_CTLR_TX_PWR_DYNAMIC_CONTROL=y

# Enable bonding
CONFIG_BT_SETTINGS=y
CONFIG_FLASH=y
CONFIG_FLASH_PAGE_LAYOUT=y
CONFIG_FLASH_MAP=y
CONFIG_NVS=y
CONFIG_SETTINGS=y
CONFIG_SETTINGS_RUNTIME=y

#Enable MCUBOOT bootloader build in the application
CONFIG_BOOTLOADER_MCUBOOT=y
CONFIG_MCUBOOT_SIGNATURE_KEY_FILE="priv.pem"
#Include MCUMGR and the dependencies in the build
CONFIG_NCS_SAMPLE_MCUMGR_BT_OTA_DFU=y
#CONFIG_MCUMGR_TRANSPORT_BT_AUTHEN=y

CONFIG_BT_EXT_ADV=y
CONFIG_BT_EXT_ADV_MAX_ADV_SET=2
CONFIG_BT_DEVICE_NAME_DYNAMIC=y
CONFIG_BT_CTLR_ADV_DATA_LEN_MAX=100

CONFIG_MODBUS=y
CONFIG_MODBUS_ROLE_CLIENT=y

CONFIG_PARTITION_MANAGER_ENABLED=y

CONFIG_FILE_SYSTEM=y
CONFIG_FILE_SYSTEM_LITTLEFS=y

# Enable binary descriptors
#CONFIG_BINDESC=y

# Enable definition of binary descriptors
#CONFIG_BINDESC_DEFINE=y

# Enable default build time binary descriptors
#CONFIG_BINDESC_DEFINE_BUILD_TIME=y
#CONFIG_BINDESC_BUILD_DATE_TIME_STRING=y

#############################################

# Enable file system commands
CONFIG_MCUMGR_GRP_FS=y
CONFIG_MCUMGR_GRP_FS_DL_CHUNK_SIZE=200

# Enable the storage erase command.
CONFIG_MCUMGR_GRP_ZBASIC=y
CONFIG_MCUMGR_GRP_ZBASIC_STORAGE_ERASE=y
# Optionally force the file system to be recreated
#CONFIG_APP_WIPE_STORAGE=y
# fs_dirent structures are big.
CONFIG_MAIN_STACK_SIZE=4096
CONFIG_BT_RX_STACK_SIZE=4096
CONFIG_BT_HCI_TX_STACK_SIZE=4096
# Let __ASSERT do its job
CONFIG_DEBUG=y

CONFIG_INIT_STACKS=y
CONFIG_THREAD_STACK_INFO=y
CONFIG_STACK_SENTINEL=y
CONFIG_THREAD_MONITOR=y

CONFIG_PM_SINGLE_IMAGE=y  # ou CONFIG_PM_MULTI_IMAGE si tu utilises MCUboot
CONFIG_PM_OVERRIDE_EXTERNAL_DRIVER_CHECK=y

CONFIG_PM_PARTITION_REGION_LITTLEFS_EXTERNAL=y
#CONFIG_PM_PARTITION_REGION_SETTINGS_STORAGE_EXTERNAL=y
#CONFIG_PM_PARTITION_REGION_NVS_STORAGE_EXTERNAL=y

CONFIG_SPI=n
#CONFIG_SPI_NOR=y
#CONFIG_SPI_NOR_FLASH_LAYOUT_PAGE_SIZE=4096
CONFIG_NORDIC_QSPI_NOR=y
CONFIG_NORDIC_QSPI_NOR_FLASH_LAYOUT_PAGE_SIZE=4096

#############################################

and here is the main.c file : 

#include <stdio.h>

#include <zephyr/kernel.h>
#include <zephyr/device.h>
#include <zephyr/fs/fs.h>
#include <zephyr/fs/littlefs.h>
#include <zephyr/logging/log.h>
#include <zephyr/storage/flash_map.h>
#include "ble_mgt.h"

#define CONFIG_APP_LITTLEFS_STORAGE_FLASH

LOG_MODULE_REGISTER(main);

/* Matches LFS_NAME_MAX */
#define MAX_PATH_LEN 255
#define TEST_FILE_SIZE 547

static int lsdir(const char *path)
{
	int res;
	struct fs_dir_t dirp;
	static struct fs_dirent entry;

	fs_dir_t_init(&dirp);

	/* Verify fs_opendir() */
	res = fs_opendir(&dirp, path);
	if (res) {
		LOG_ERR("Error opening dir %s [%d]\n", path, res);
		return res;
	}

	LOG_PRINTK("\nListing dir %s ...\n", path);
	for (;;) {
		/* Verify fs_readdir() */
		res = fs_readdir(&dirp, &entry);

		/* entry.name[0] == 0 means end-of-dir */
		if (res || entry.name[0] == 0) {
			if (res < 0) {
				LOG_ERR("Error reading dir [%d]\n", res);
			}
			break;
		}

		if (entry.type == FS_DIR_ENTRY_DIR) {
			LOG_PRINTK("[DIR ] %s\n", entry.name);
		} else {
			LOG_PRINTK("[FILE] %s (size = %zu)\n",
				   entry.name, entry.size);
		}
	}

	/* Verify fs_closedir() */
	fs_closedir(&dirp);

	return res;
}


#ifdef CONFIG_APP_LITTLEFS_STORAGE_FLASH
static int littlefs_flash_erase(unsigned int id)
{
	const struct flash_area *pfa;
	int rc;

	rc = flash_area_open(id, &pfa);
	if (rc < 0) {
		LOG_ERR("FAIL: unable to find flash area %u: %d\n",
			id, rc);
		return rc;
	}

	LOG_PRINTK("Area %u at 0x%x on %s for %u bytes\n",
		   id, (unsigned int)pfa->fa_off, pfa->fa_dev->name,
		   (unsigned int)pfa->fa_size);

	/* Optional wipe flash contents */
	if (IS_ENABLED(CONFIG_APP_WIPE_STORAGE)) {
		rc = flash_area_flatten(pfa, 0, pfa->fa_size);
		LOG_ERR("Erasing flash area ... %d", rc);
	}

	flash_area_close(pfa);
	return rc;
}
#define PARTITION_NODE DT_NODELABEL(lfs1)

#if DT_NODE_EXISTS(PARTITION_NODE)
FS_FSTAB_DECLARE_ENTRY(PARTITION_NODE);
#else /* PARTITION_NODE */
FS_LITTLEFS_DECLARE_DEFAULT_CONFIG(storage);
static struct fs_mount_t lfs_storage_mnt = {
	.type = FS_LITTLEFS,
	.fs_data = &storage,
	.storage_dev = (void *)FIXED_PARTITION_ID(storage_partition),
	.mnt_point = "/lfs1",
};
#endif /* PARTITION_NODE */

	struct fs_mount_t *mountpoint =
#if DT_NODE_EXISTS(PARTITION_NODE)
		&FS_FSTAB_ENTRY(PARTITION_NODE)
#else
		&lfs_storage_mnt
#endif
		;

static int littlefs_mount(struct fs_mount_t *mp)
{
	int rc;

	rc = littlefs_flash_erase((uintptr_t)mp->storage_dev);
	if (rc < 0) {
		return rc;
	}

	/* Do not mount if auto-mount has been enabled */
#if !DT_NODE_EXISTS(PARTITION_NODE) ||						\
	!(FSTAB_ENTRY_DT_MOUNT_FLAGS(PARTITION_NODE) & FS_MOUNT_FLAG_AUTOMOUNT)
	rc = fs_mount(mp);
	if (rc < 0) {
		LOG_PRINTK("FAIL: mount id %" PRIuPTR " at %s: %d\n",
		       (uintptr_t)mp->storage_dev, mp->mnt_point, rc);
		return rc;
	}
	LOG_PRINTK("%s mount: %d\n", mp->mnt_point, rc);
#else
	LOG_PRINTK("%s automounted\n", mp->mnt_point);
#endif

	return 0;
}
#endif /* CONFIG_APP_LITTLEFS_STORAGE_FLASH */

void show_stack_usage(struct k_thread *thread, void *user_data) {
    size_t unused = 0;
    if (k_thread_stack_space_get(thread, &unused) == 0) {
        printk("Thread %s → stack libre : %zu octets\n", k_thread_name_get(thread), unused);
    }
}

int main(void)
{
	struct fs_statvfs sbuf;
	int rc;

	LOG_PRINTK("Sample program to r/w files on littlefs\n");

	ble_init();
	rc = littlefs_mount(mountpoint);
	if (rc < 0) {
		return 0;
	}

	rc = fs_statvfs(mountpoint->mnt_point, &sbuf);
	if (rc < 0) {
		LOG_PRINTK("FAIL: statvfs: %d\n", rc);
		goto out;
	}

	LOG_PRINTK("%s: bsize = %lu ; frsize = %lu ;"
		   " blocks = %lu ; bfree = %lu\n",
		   mountpoint->mnt_point,
		   sbuf.f_bsize, sbuf.f_frsize,
		   sbuf.f_blocks, sbuf.f_bfree);

	rc = lsdir(mountpoint->mnt_point);
	if (rc < 0) {
		LOG_PRINTK("FAIL: lsdir %s: %d\n", mountpoint->mnt_point, rc);
		goto out;
	}

out:
	//rc = fs_unmount(mountpoint);
	//LOG_PRINTK("%s unmount: %d\n", mountpoint->mnt_point, rc);
	//return 0;

	for (;;) {
		//dk_set_led(CON_STATUS_LED, (++blink_status) % 2);
		k_sleep(K_MSEC(1000));		
		//k_thread_foreach(show_stack_usage, NULL);
		//printk("\r\n");
	}
}

If you have any suggestion, feel free to answer.

Thank you in advance,

Best regards,

Aurele

Parents
  • Hello

    I managed to get it to work, but for it to work, I have to force a file upload with the "Device Manager" app, and when I reset the device, I see:

    Area 0 at 0x0 on mx25r6435f@0 for 8388608 bytes
    I: LittleFS version 2.9, disk version 2.1
    I: FS at mx25r6435f@0:0x0 is 2048 0x1000-byte blocks with 512 cycles
    I: sizes: rd 16 ; pr 16 ; ca 64 ; la 32
    /lfs1 mount: 0
    /lfs1: bsize = 16 ; frsize = 4096 ; blocks = 2048 ; bfree = 2044
    
    Listing dir /lfs1 ...
    [FILE] Lorem_10.txt (size = 10)
    [FILE] Lorem_1000.txt (size = 1000)
    [FILE] boot_count (size = 1)
    [FILE] pattern.bin (size = 547)

    Otherwise, as soon as I boot for the first time or after an erase, I see the following error:

    E: FAIL: unable to find flash area 0: -19

Reply
  • Hello

    I managed to get it to work, but for it to work, I have to force a file upload with the "Device Manager" app, and when I reset the device, I see:

    Area 0 at 0x0 on mx25r6435f@0 for 8388608 bytes
    I: LittleFS version 2.9, disk version 2.1
    I: FS at mx25r6435f@0:0x0 is 2048 0x1000-byte blocks with 512 cycles
    I: sizes: rd 16 ; pr 16 ; ca 64 ; la 32
    /lfs1 mount: 0
    /lfs1: bsize = 16 ; frsize = 4096 ; blocks = 2048 ; bfree = 2044
    
    Listing dir /lfs1 ...
    [FILE] Lorem_10.txt (size = 10)
    [FILE] Lorem_1000.txt (size = 1000)
    [FILE] boot_count (size = 1)
    [FILE] pattern.bin (size = 547)

    Otherwise, as soon as I boot for the first time or after an erase, I see the following error:

    E: FAIL: unable to find flash area 0: -19

Children
No Data
Related