Getting External Flash working with LittleFS on a nRF5340 DK using qspi

I am using a nrf5340 DK and trying to get littlefs working with the external flash using qspi. I started with the blinky sample so I could try to understand each step, but I'm having a lot of problems. 

In the main.c I added:

#include <stdio.h>
#include <zephyr/kernel.h>
#include <zephyr/drivers/gpio.h>
#include <zephyr/fs/fs.h>
#include <zephyr/fs/littlefs.h>
#include <zephyr/logging/log.h>
#include <zephyr/storage/flash_map.h>

LOG_MODULE_REGISTER(main, LOG_LEVEL_DBG);

#define MAX_PATH_LEN 255

#define PARTITION_NODE DT_NODELABEL(lfs1_part)
FS_FSTAB_DECLARE_ENTRY(PARTITION_NODE);
struct fs_mount_t *mp = &FS_FSTAB_ENTRY(PARTITION_NODE);

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

	rc = fs_mount(mp);
	if (rc < 0) {
		LOG_DBG("FAIL: mount id %" PRIuPTR " at %s: %d", (uintptr_t)mp->storage_dev, mp->mnt_point, rc);
		return rc;
	}
	LOG_DBG("%s mount: %d\n", mp->mnt_point, rc);

	rc = fs_statvfs(mp->mnt_point, &sbuf);
	if (rc < 0) {
		LOG_DBG("FAIL: statvfs: %d", rc);
	}

	return 0;
}
I have a nrf5340dk_nrf5340_cpuapp.overlay which I know is configured wrong and is not qspi:

/delete-node/ &storage_partition;

/ {
	fstab {
		compatible = "zephyr,fstab";
		lfs1: lfs1 {
			compatible = "zephyr,fstab,littlefs";
			mount-point = "/lfs1";
			partition = <&lfs1_part>;
			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_part: partition@0 {
			label = "storage";
			reg = <0x00000000 0x00010000>;
		};
	};
};
And a prj.conf:
CONFIG_GPIO=y
CONFIG_MAIN_STACK_SIZE=2048

# logging
CONFIG_LOG=y
CONFIG_LOG_MODE_IMMEDIATE=y
CONFIG_DEBUG=y

# flash
CONFIG_FLASH=y
CONFIG_FLASH_MAP=y
CONFIG_FLASH_PAGE_LAYOUT=y

# file system
CONFIG_FILE_SYSTEM=y
CONFIG_FILE_SYSTEM_LITTLEFS=y

# Need this when storage is on flash
CONFIG_MPU_ALLOW_FLASH_WRITE=y

# Need this when storage is on MX25R64
CONFIG_NORDIC_QSPI_NOR=y
CONFIG_NORDIC_QSPI_NOR_FLASH_LAYOUT_PAGE_SIZE=4096
I get the following error:
main.c:20: undefined reference to `z_fsmp_DT_N_S_soc_S_peripheral_50000000_S_qspi_2b000_S_mx25r6435f_0_S_partitions_S_partition_0'
collect2: error: ld returned 1 exit status
ninja: build stopped: subcommand failed.
Somebody else told me I also need a "Nrf partition manager yml file" and I'm not sure exactly what that is.
Parents
  • Hello Leo,

    What NCS version are you using?

    Regards,

    Elfving

  • -- Zephyr version: 3.4.99 (/opt/nordic/ncs/v2.5.0/zephyr)

    I decided to create a new project based on the littlefs sample to see if that helped and am getting it mounted, I think, but am getting an error:


    E: file open error (-12)
    E: FAIL: open /lfs/boot_count: -12

    Copilot said: "The error code -12 in Zephyr’s LittleFS file system API indicates that the file open operation failed due to a read-only file system or insufficient permissions 1."

    The full readout from flashing seems to indicate the filesystem is being mounted:

    I: littlefs partition at /lfs
    I: LittleFS version 2.5, disk version 2.0
    I: FS at mx25r6435f@0:0x0 is 512 0x1000-byte blocks with 512 cycle
    I: sizes: rd 1024 ; pr 512 ; ca 4096 ; la 4096
    I: Automount /lfs succeeded
    *** Booting nRF Connect SDK v2.5.0 ***
    D: Sample program to r/w files on littlefs
    D: Area 0 at 0x0 on mx25r6435f@0 for 2097152 bytes
    /lfs automounted
    D: /lfs: bsize = 512 ; frsize = 4096 ; blocks = 512 ; bfree = 510
    D: 
    Listing dir /lfs ...
    E: file open error (-12)
    E: FAIL: open /lfs/boot_count: -12
    I: /lfs unmounted
    D: /lfs unmount: 0


    I have a nrf5340dk_nrf5340_cpuapp.overlay file with:

    / {
    	fstab {
    		compatible = "zephyr,fstab";
    		lfs: lfs {
    			compatible = "zephyr,fstab,littlefs";
    			mount-point = "/lfs";
    			partition = <&littlefs_storage>;
    			automount;
    			read-size = <1024>;
    			prog-size = <512>;
    			cache-size = <4096>;
    			lookahead-size = <4096>;
    			block-cycles = <512>;
    		};
    	};
    };
    
    /delete-node/ &storage_partition;
    
    &mx25r64 {
    	partitions {
    		compatible = "fixed-partitions";
    		#address-cells = <1>;
    		#size-cells = <1>;
    
    		littlefs_storage: partition@0 {
    			label = "littlefs_storage";
    			reg = <0x00000000 0x00200000>;
    		};
    		lvgl_raw_partition: partition@200000 {
    			label = "lvgl_raw_partition";
    			reg = <0x00200000 0x00200000>;
    		};
            storage_partition: partition@400000 {
                label = "storage_partition";
                reg = <0x400000 0x100000>;
            };
    	};
    };
    
    / {
        chosen {
            nordic,pm-ext-flash = &mx25r64;
        };
    };



    and the prj.conf file:

    CONFIG_MAIN_STACK_SIZE=4096
    CONFIG_DEBUG=y
    
    CONFIG_LOG=y
    CONFIG_LOG_MODE_MINIMAL=y
    
    CONFIG_FLASH=y
    CONFIG_FLASH_MAP=y
    CONFIG_FLASH_PAGE_LAYOUT=y
    
    CONFIG_FILE_SYSTEM=y
    CONFIG_FILE_SYSTEM_LITTLEFS=y
    
    CONFIG_SPI=n
    CONFIG_NORDIC_QSPI_NOR=y

    I changed a line in the sample to match my overlay:

    #define PARTITION_NODE DT_NODELABEL(lfs)

    I am getting an error when trying to open the file to test:

    I: littlefs partition at /lfs
    I: LittleFS version 2.5, disk version 2.0
    I: FS at mx25r6435f@0:0x0 is 512 0x1000-byte blocks with 512 cycle
    I: sizes: rd 1024 ; pr 512 ; ca 4096 ; la 4096
    I: Automount /lfs succeeded
    *** Booting nRF Connect SDK v2.5.0 ***
    D: Sample program to r/w files on littlefs
    D: Area 0 at 0x0 on mx25r6435f@0 for 2097152 bytes
    /lfs automounted
    D: /lfs: bsize = 512 ; frsize = 4096 ; blocks = 512 ; bfree = 510
    D: 
    Listing dir /lfs ...
    E: file open error (-12)
    E: FAIL: open /lfs/boot_count: -12
    I: /lfs unmounted
    D: /lfs unmount: 0



    The main.c is from the sample:
    #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>
    
    LOG_MODULE_REGISTER(main, LOG_LEVEL_DBG);
    
    /* Matches LFS_NAME_MAX */
    #define MAX_PATH_LEN 255
    #define TEST_FILE_SIZE 547
    
    static uint8_t file_test_pattern[TEST_FILE_SIZE];
    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_DBG("\nListing dir %s ...", 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_DBG("[DIR ] %s", entry.name);
    		} else {
    			LOG_DBG("[FILE] %s (size = %zu)",
    				   entry.name, entry.size);
    		}
    	}
    
    	/* Verify fs_closedir() */
    	fs_closedir(&dirp);
    
    	return res;
    }
    
    static int littlefs_increase_infile_value(char *fname)
    {
    	uint8_t boot_count = 0;
    	struct fs_file_t file;
    	int rc, ret;
    
    	fs_file_t_init(&file);
    	rc = fs_open(&file, fname, FS_O_CREATE | FS_O_RDWR);
    	if (rc < 0) {
    		LOG_ERR("FAIL: open %s: %d", fname, rc);
    		return rc;
    	}
    
    	rc = fs_read(&file, &boot_count, sizeof(boot_count));
    	if (rc < 0) {
    		LOG_ERR("FAIL: read %s: [rd:%d]", fname, rc);
    		goto out;
    	}
    	LOG_DBG("%s read count:%u (bytes: %d)", fname, boot_count, rc);
    
    	rc = fs_seek(&file, 0, FS_SEEK_SET);
    	if (rc < 0) {
    		LOG_ERR("FAIL: seek %s: %d", fname, rc);
    		goto out;
    	}
    
    	boot_count += 1;
    	rc = fs_write(&file, &boot_count, sizeof(boot_count));
    	if (rc < 0) {
    		LOG_ERR("FAIL: write %s: %d", fname, rc);
    		goto out;
    	}
    
    	LOG_DBG("%s write new boot count %u: [wr:%d]", fname,
    		   boot_count, rc);
    
     out:
    	ret = fs_close(&file);
    	if (ret < 0) {
    		LOG_ERR("FAIL: close %s: %d", fname, ret);
    		return ret;
    	}
    
    	return (rc < 0 ? rc : 0);
    }
    
    static void incr_pattern(uint8_t *p, uint16_t size, uint8_t inc)
    {
    	uint8_t fill = 0x55;
    
    	if (p[0] % 2 == 0) {
    		fill = 0xAA;
    	}
    
    	for (int i = 0; i < (size - 1); i++) {
    		if (i % 8 == 0) {
    			p[i] += inc;
    		} else {
    			p[i] = fill;
    		}
    	}
    
    	p[size - 1] += inc;
    }
    
    static void init_pattern(uint8_t *p, uint16_t size)
    {
    	uint8_t v = 0x1;
    
    	memset(p, 0x55, size);
    
    	for (int i = 0; i < size; i += 8) {
    		p[i] = v++;
    	}
    
    	p[size - 1] = 0xAA;
    }
    
    static void print_pattern(uint8_t *p, uint16_t size)
    {
    	int i, j = size / 16, k;
    
    	for (k = 0, i = 0; k < j; i += 16, k++) {
    		LOG_DBG("%02x %02x %02x %02x %02x %02x %02x %02x ",
    			   p[i], p[i+1], p[i+2], p[i+3],
    			   p[i+4], p[i+5], p[i+6], p[i+7]);
    		LOG_DBG("%02x %02x %02x %02x %02x %02x %02x %02x\n",
    			   p[i+8], p[i+9], p[i+10], p[i+11],
    			   p[i+12], p[i+13], p[i+14], p[i+15]);
    
    		/* Mark 512B (sector) chunks of the test file */
    		if ((k + 1) % 32 == 0) {
    			LOG_DBG("------------------------------------");
    		}
    	}
    
    	for (; i < size; i++) {
    		LOG_PRINTK("%02x ", p[i]);
    	}
    
    	LOG_DBG("---------------------------");
    }
    
    static int littlefs_binary_file_adj(char *fname)
    {
    	struct fs_dirent dirent;
    	struct fs_file_t file;
    	int rc, ret;
    
    	/*
    	 * Uncomment below line to force re-creation of the test pattern
    	 * file on the littlefs FS.
    	 */
    	/* fs_unlink(fname); */
    	fs_file_t_init(&file);
    
    	rc = fs_open(&file, fname, FS_O_CREATE | FS_O_RDWR);
    	if (rc < 0) {
    		LOG_ERR("FAIL: open %s: %d", fname, rc);
    		return rc;
    	}
    
    	rc = fs_stat(fname, &dirent);
    	if (rc < 0) {
    		LOG_ERR("FAIL: stat %s: %d", fname, rc);
    		goto out;
    	}
    
    	/* Check if the file exists - if not just write the pattern */
    	if (rc == 0 && dirent.type == FS_DIR_ENTRY_FILE && dirent.size == 0) {
    		LOG_INF("Test file: %s not found, create one!",
    			fname);
    		init_pattern(file_test_pattern, sizeof(file_test_pattern));
    	} else {
    		rc = fs_read(&file, file_test_pattern,
    			     sizeof(file_test_pattern));
    		if (rc < 0) {
    			LOG_ERR("FAIL: read %s: [rd:%d]",
    				fname, rc);
    			goto out;
    		}
    		incr_pattern(file_test_pattern, sizeof(file_test_pattern), 0x1);
    	}
    
    	LOG_DBG("------ FILE: %s ------", fname);
    	print_pattern(file_test_pattern, sizeof(file_test_pattern));
    
    	rc = fs_seek(&file, 0, FS_SEEK_SET);
    	if (rc < 0) {
    		LOG_ERR("FAIL: seek %s: %d", fname, rc);
    		goto out;
    	}
    
    	rc = fs_write(&file, file_test_pattern, sizeof(file_test_pattern));
    	if (rc < 0) {
    		LOG_ERR("FAIL: write %s: %d", fname, rc);
    	}
    
     out:
    	ret = fs_close(&file);
    	if (ret < 0) {
    		LOG_ERR("FAIL: close %s: %d", fname, ret);
    		return ret;
    	}
    
    	return (rc < 0 ? rc : 0);
    }
    
    #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_DBG("Area %u at 0x%x on %s for %u bytes",
    		   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_erase(pfa, 0, pfa->fa_size);
    		LOG_ERR("Erasing flash area ... %d", rc);
    	}
    
    	flash_area_close(pfa);
    	return rc;
    }
    //#define PARTITION_NODE DT_NODELABEL(lfs1)
    
    #define PARTITION_NODE DT_NODELABEL(lfs)
    
    #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(littlefs_storage),
    	.mnt_point = "/lfs",
    };
    #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_DBG("FAIL: mount id %" PRIuPTR " at %s: %d",
    		       (uintptr_t)mp->storage_dev, mp->mnt_point, rc);
    		return rc;
    	}
    	LOG_DBG("%s mount: %d", mp->mnt_point, rc);
    #else
    	LOG_PRINTK("%s automounted\n", mp->mnt_point);
    #endif
    
    	return 0;
    }
    #endif /* CONFIG_APP_LITTLEFS_STORAGE_FLASH */
    
    #ifdef CONFIG_APP_LITTLEFS_STORAGE_BLK_SDMMC
    
    #if defined(CONFIG_DISK_DRIVER_SDMMC)
    #define DISK_NAME CONFIG_SDMMC_VOLUME_NAME
    #elif IS_ENABLED(CONFIG_DISK_DRIVER_MMC)
    #define DISK_NAME CONFIG_MMC_VOLUME_NAME
    #else
    #error "No disk device defined, is your board supported?"
    #endif
    
    struct fs_littlefs lfsfs;
    static struct fs_mount_t __mp = {
    	.type = FS_LITTLEFS,
    	.fs_data = &lfsfs,
    	.flags = FS_MOUNT_FLAG_USE_DISK_ACCESS,
    };
    struct fs_mount_t *mountpoint = &__mp;
    
    static int littlefs_mount(struct fs_mount_t *mp)
    {
    	static const char *disk_mount_pt = "/"DISK_NAME":";
    	static const char *disk_pdrv = DISK_NAME;
    
    	mp->storage_dev = (void *)disk_pdrv;
    	mp->mnt_point = disk_mount_pt;
    
    	return fs_mount(mp);
    }
    #endif /* CONFIG_APP_LITTLEFS_STORAGE_BLK_SDMMC */
    
    int main(void)
    {
    	char fname1[MAX_PATH_LEN];
    	char fname2[MAX_PATH_LEN];
    	struct fs_statvfs sbuf;
    	int rc;
    
    	LOG_DBG("Sample program to r/w files on littlefs");
    
    	rc = littlefs_mount(mountpoint);
    	if (rc < 0) {
    		return 0;
    	}
    
    	snprintf(fname1, sizeof(fname1), "%s/boot_count", mountpoint->mnt_point);
    	snprintf(fname2, sizeof(fname2), "%s/pattern.bin", mountpoint->mnt_point);
    
    	rc = fs_statvfs(mountpoint->mnt_point, &sbuf);
    	if (rc < 0) {
    		LOG_DBG("FAIL: statvfs: %d", rc);
    		goto out;
    	}
    
    	LOG_DBG("%s: bsize = %lu ; frsize = %lu ;"
    		   " blocks = %lu ; bfree = %lu",
    		   mountpoint->mnt_point,
    		   sbuf.f_bsize, sbuf.f_frsize,
    		   sbuf.f_blocks, sbuf.f_bfree);
    
    	rc = lsdir(mountpoint->mnt_point);
    	if (rc < 0) {
    		LOG_DBG("FAIL: lsdir %s: %d", mountpoint->mnt_point, rc);
    		goto out;
    	}
    
    	rc = littlefs_increase_infile_value(fname1);
    	if (rc) {
    		goto out;
    	}
    
    	rc = littlefs_binary_file_adj(fname2);
    	if (rc) {
    		goto out;
    	}
    
    out:
    	rc = fs_unmount(mountpoint);
    	LOG_DBG("%s unmount: %d", mountpoint->mnt_point, rc);
    	return 0;
    }
    



    When trying to do the same in my main project, it seems to automount ok:

    [00:00:00.402,191] <inf> littlefs: littlefs partition at /lfs
    [00:00:00.408,355] <inf> littlefs: LittleFS version 2.5, disk version 2.0
    [00:00:00.415,710] <inf> littlefs: FS at flash-controller@39000:0xfa000 is 6 0x1000-byte blocks with 512 cycle
    [00:00:00.426,086] <inf> littlefs: sizes: rd 1024 ; pr 512 ; ca 4096 ; la 4096
    [00:00:00.435,577] <inf> littlefs: Automount /lfs succeeded



    but when I try to get statistics on it:
        rc = fs_statvfs(mp->mnt_point, &sbuf);
        if (rc < 0) {
            LOG_ERR("FAIL: statvfs: %d", rc);
            return -1;
        }

    The numbers are:

    [00:00:00.484,558] <dbg> testfs: test_init: /lfs: bsize = 512 ; frsize = 4096 ;blocks = 6 ; bfree = 3

    Not like the sample:

    D: /lfs: bsize = 512 ; frsize = 4096 ; blocks = 512 ; bfree = 510

    I guess there are two questions in here but both amount to configuration of qspi for littlefs on a nrf5340 DK.

  • Ok. First problem is solved by either the following in prj.conf:

    CONFIG_FS_LITTLEFS_FC_HEAP_SIZE=8192
    or by changing the values in the overlay:
    		lfs: lfs {
    			compatible = "zephyr,fstab,littlefs";
    			mount-point = "/lfs";
    			partition = <&littlefs_storage>;
    			automount;
                read-size = <16>;
    			prog-size = <16>;
    			cache-size = <64>;
    			lookahead-size = <32>;
    			block-cycles = <512>;
    		};

    The second problem is caused by this value in the nrf5340_cpuapp_peripherals.dtsi:
    flash_controller: flash-controller@39000 {
    	compatible = "nordic,nrf53-flash-controller";
    	reg = <0x39000 0x1000>;
    	partial-erase;
    
    	#address-cells = <1>;
    	#size-cells = <1>;
    
    
    	flash0: flash@0 {
    		compatible = "soc-nv-flash";
    		erase-block-size = <4096>;
    		write-block-size = <4>;
    	};
    };



    which results in the fs using the flash on the SOC as seen here:
    littlefs: FS at flash-controller@39000:0xfa000 is 6 0x1000-byte blocks with 512 cycle

    but I have no idea why the sample is not picking this up as well
Reply
  • Ok. First problem is solved by either the following in prj.conf:

    CONFIG_FS_LITTLEFS_FC_HEAP_SIZE=8192
    or by changing the values in the overlay:
    		lfs: lfs {
    			compatible = "zephyr,fstab,littlefs";
    			mount-point = "/lfs";
    			partition = <&littlefs_storage>;
    			automount;
                read-size = <16>;
    			prog-size = <16>;
    			cache-size = <64>;
    			lookahead-size = <32>;
    			block-cycles = <512>;
    		};

    The second problem is caused by this value in the nrf5340_cpuapp_peripherals.dtsi:
    flash_controller: flash-controller@39000 {
    	compatible = "nordic,nrf53-flash-controller";
    	reg = <0x39000 0x1000>;
    	partial-erase;
    
    	#address-cells = <1>;
    	#size-cells = <1>;
    
    
    	flash0: flash@0 {
    		compatible = "soc-nv-flash";
    		erase-block-size = <4096>;
    		write-block-size = <4>;
    	};
    };



    which results in the fs using the flash on the SOC as seen here:
    littlefs: FS at flash-controller@39000:0xfa000 is 6 0x1000-byte blocks with 512 cycle

    but I have no idea why the sample is not picking this up as well
Children
Related