Is it possible to mount a LittleFS partition in RAM only, in order to extend the product's lifespan with respect to flash erase/write limitations?

Hello

I'm currently mounting a LittleFS partition in the internal flash of my Nordic MCU. However, since data is saved regularly to this partition, I’m concerned that the flash’s erase/write cycle limit could be reached in approximately 1.5 years, after which data integrity issues might occur.

To extend the product's lifespan, I’m considering moving this partition to RAM only.

Is this possible?

Environment : 

- nrf52840
- nrf connect SDK : 2.9.0

Best regards,
Aurélien

  • I think I had a small breakthrough working on your case. Now I'm getting this output

    *** Booting nRF Connect SDK v3.0.1-9eb5615da66b ***
    *** Using Zephyr OS v4.0.99-77f865b8f8d0 ***
    I: LittleFS RAM sample started
    I: LittleFS version 2.9, disk version 2.1
    I: FS at RAM: is 128 0x200-byte blocks with 512 cycle
    I: partition sizes: rd 512 ; pr 512 ; ca 512 ; la 2048
    E: WEST_TOPDIR/modules/fs/littlefs/lfs.c:1374: Corrupted dir pair at {0x0, 0x1}
    W: can't mount (LFS -84); formatting
    I: /RAM: mounted
    I: Mounted /RAM: successfully

    I will look more at this tomorrow, but in the mean time you can have a look at the code.

    main.c

    
    #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/disk_access.h>
    
    LOG_MODULE_REGISTER(main);
    
    struct fs_file_t file;
    
    uint8_t data[] = "any text";
    
    uint8_t data2[255];
    #define DISK_NAME "RAM"
    
    // LittleFS data structure
    static struct fs_littlefs lfsfs;
    
    // Mount configuration
    static struct fs_mount_t __mp = {
        .type = FS_LITTLEFS,
        .mnt_point = "/" DISK_NAME ":",
        .fs_data = &lfsfs,
        .storage_dev = (void *)DISK_NAME,
        .flags = FS_MOUNT_FLAG_USE_DISK_ACCESS,
    };
    
    // Pointer to the mount struct
    struct fs_mount_t *mountpoint = &__mp;
    
    // Mounting function
    static int littlefs_mount(struct fs_mount_t *mp)
    {
    	int rc = fs_mount(mp);
    	if (rc < 0) {
    		LOG_ERR("Failed to mount %s (err %d)", mp->mnt_point, rc);
    	} else {
    		LOG_INF("Mounted %s successfully", mp->mnt_point);
    	}
    
    	return rc;
    }
    
    int write_stuff(struct fs_file_t *file, void *data, int size)
    {
    	int ret;
    	ret = fs_seek(file, 0, FS_SEEK_SET);
    	if (ret<0) {
    		LOG_ERR("seek fail!");
    	}
    	ret = fs_write(file, data, size);
    	if (ret<0) {
    		LOG_ERR("write fail!");
    	}
    	LOG_INF("write %d bytes", ret);
    	fs_sync(file);
    	return ret;
    }
    
    int read_stuff(struct fs_file_t *file, void *data, int size)
    {
    	int ret, c = 0;
    	ret = fs_seek(file, 0, FS_SEEK_SET);
    	if (ret<0) {
    		LOG_ERR("seek fail!");
    	}
    	do {
    		ret = fs_read(file, data, size);
    		LOG_INF("read; %s", (char*) data);
    		c+=ret;
    	} while (ret > 0);
    	if (ret<0) {
    		LOG_ERR("read fail!");
    		return ret;
    	}
    
    	LOG_INF("read %d bytes", c);
    	return c;
    }
    
    int main(void)
    {
    	LOG_INF("LittleFS RAM sample started");
    	int ret;
    	ret = littlefs_mount(mountpoint);
    	if (ret<0) {
    		LOG_ERR("mount fail!");
    	}	
    	k_sleep(K_MSEC(100));
    	fs_file_t_init(&file);
    
    	ret = fs_open(&file, "/RAM:/test.txt", FS_O_CREATE | FS_O_RDWR);
    	if (ret<0) {
    		LOG_ERR("open fail!");
    	}
    	write_stuff(&file, data, sizeof(data));
    	read_stuff(&file, data2, sizeof(data2));
    }

    prj.conf

    #
    # Copyright (c) 2019 Peter Bigot Consulting, LLC
    #
    # SPDX-License-Identifier: Apache-2.0
    #
    
    # Optionally force the file system to be recreated
    #CONFIG_APP_WIPE_STORAGE=y
    
    # fs_dirent structures are big.
    CONFIG_MAIN_STACK_SIZE=4096
    
    # Let __ASSERT do its job
    CONFIG_DEBUG=y
    CONFIG_NO_OPTIMIZATIONS=y
    
    CONFIG_LOG=y
    CONFIG_LOG_MODE_MINIMAL=y
    
    CONFIG_FLASH=y
    CONFIG_FLASH_MAP=y
    
    CONFIG_FILE_SYSTEM=y
    CONFIG_FILE_SYSTEM_LITTLEFS=y
    
    CONFIG_FS_LITTLEFS_BLK_DEV=y
    CONFIG_FS_LITTLEFS_FMP_DEV=n
    CONFIG_FS_LITTLEFS_CACHE_SIZE=1024
    CONFIG_FS_LITTLEFS_LOOKAHEAD_SIZE=2048
    
    CONFIG_DISK_ACCESS=y
    CONFIG_DISK_DRIVERS=y
    CONFIG_DISK_DRIVER_RAM=y
    

    overlay

    /*
     * Copyright (c) 2023 Nordic Semiconductor ASA
     *
     * SPDX-License-Identifier: Apache-2.0
     */
    
    / {
        lfs1_partition: sram0@20000000 {
            compatible = "zephyr,memory-region";
            reg = <0x20000000 0x2000>;
            zephyr,memory-region = "lfs1_partition"; //region
            status = "okay";
        };
    
        fstab {
            compatible = "zephyr,fstab";
            lfs1: lfs1 {
                compatible = "zephyr,fstab,littlefs";
                mount-point = "/RAM:";
                partition = <&lfs1_partition>;
                read-size = <16>;
                prog-size = <16>;
                cache-size = <64>;
                lookahead-size = <512>;
                block-cycles = <512>;
            };
        };
    
        ramdisk0: ramdisk  {
            compatible = "zephyr,ram-disk";
            status = "okay";
            disk-name = "RAM";
            sector-size = <512>;
            sector-count = <128>;
        };
    };

    I don't have time to confirm that this is indeed being mounted in SRAM, but I will check it tomorrow hopefully.

  • Hello Hakon

    Sorry for the late reply. I was on vacation last week.
    I'll test your code today and let you know.

    Thank you very much!

  • Hello Hakon

    I've made a few tests and I have several questions:

      • First, why is the RAM disk size around 64 kB while the partition is only 8 kB?

      • Even with your code, I can see that the linker places some variables/functions between 0x20000000 and 0x20002000. Why is that? Shouldn't the disk or partition configuration prevent the linker from placing anything there?

      • Finally, I can mount the partition, but I can't open or create a file on it — it returns -12 (ENOMEM).

    Mount LITTLEFS on internal ram ...
    I: LittleFS version 2.9, disk version 2.1
    I: FS at RAM: is 128 0x200-byte blocks with 512 cycle
    I: sizes: rd 512 ; pr 512 ; ca 512 ; la 2048
    E: WEST_TOPDIR/modules/fs/littlefs/lfs.c:1374: Corrupted dir pair at {0x0, 0x1}
    W: can't mount (LFS -84); formatting
    I: /RAM: mounted
    /RAM: mount: 0
    /RAM:: bsize = 512 ; frsize = 512 ; blocks = 128 ; bfree = 126
    
    Listing dir /RAM: ...
    /RAM:: bsize = 512 ; frsize = 512 ; blocks = 128 ; bfree = 126
    /RAM:: bsize = 512 ; frsize = 512 ; blocks = 128 ; bfree = 126
    
    Listing dir /RAM: ...
    E: file open error (-12)
    E: FAIL: open /RAM:/boot_count: -12

  • I don't know why the linker places stuff in that region, but I will see if I have time to look at that tomorrow.

    Aurele said:
    Finally, I can mount the partition, but I can't open or create a file on it — it returns -12 (ENOMEM).

    The sample I posted also writes to a file, without error. Are you testing this on a DK? Can you share some of the code that fails for you?

  • Hello Hakon

    I finally got it working by setting:

    CONFIG_FS_LITTLEFS_FC_HEAP_SIZE=8192

    I'm still wondering why some variables are being linked in the first 80 kB of RAM (0x20000000 – 0x20014000)?

    From build/app/zephyr/zephyr.map : 

Related