LittleFS config creating un-used partition in flash_primary

I am using LittleFS for an external NAND flash by using the KCONFIG option CONFIG_FILE_SYSTEM_LITTLEFS=y and managing the driver implementation manually. This has worked well for me, but I have found that this config option seems to also create an un-used partition on the internal flash_primary shown below:

I assume, since the implementation of littleFS is on external flash, that this is not being used for anything.

I had plans to use some of that flash for an NVS implementation, but having the random partition in the middle of everything complicates it a bit.

Is that partition being used for anything internally? or is there a way to enable use of littleFS without allocating a partition in flash_primary?

Thanks,

- Brett

Parents
  • Hi Brett,

    We are also started to implement little fs on external NAND memory. We we able to comminicate basic spi functions. Can you share a guide to implementation with little fs? You mention you are managing the driver implementation manually and how it is connected to little fs.

    Best Regards,

    Furkan

  • Always willing to help.

    A lot of it hinges on you having a driver for your NAND flash that you have validated has working read, write, and erase commands, as well as bad block detection and mapping.

    I wont be able to help with that driver, but I can share a stripped down version of my littlefs wrapper c file here:

    #include <zephyr/kernel.h>
    #include <zephyr/sys/printk.h>
    #include <zephyr/fs/littlefs.h>
    #include <string.h>
    
    #include "drivers/nand_flash.h"
    
    // Private Functions
    int read_littlefs_wrapper(const struct lfs_config *c, lfs_block_t block, lfs_off_t off, void *buffer, lfs_size_t size){
    
        int err = 0;
    
        uint16_t page_numb = off/(nand_metrics.page_size - nand_metrics.spare_size);
        uint16_t column_numb = off%(nand_metrics.page_size - nand_metrics.spare_size);
    
        if (nand_flash_is_current_block_bad(block)) return LFS_ERR_CORRUPT;
    
        err = nand_read_data(block, page_numb, column_numb, buffer, size, true, true);
    
        if (err == NRFX_SUCCESS) err = 0;
        else err = LFS_ERR_IO;
        return err;
    }
    
    int write_littlefs_wrapper(const struct lfs_config *c, lfs_block_t block,
                lfs_off_t off, const void *buffer, lfs_size_t size){
    
        int err = 0;
        uint16_t page_numb = off/(nand_metrics.page_size - nand_metrics.spare_size);
        uint16_t column_numb = off%(nand_metrics.page_size - nand_metrics.spare_size);
    
        if (nand_flash_is_current_block_bad(block)) return LFS_ERR_CORRUPT;
    
        err = nand_write_data(block, page_numb, column_numb, (uint8_t*) buffer, size);
        //printk("LFS Write B:%d P:%d C:%d S:%d, err = %d\n", block, page_numb, column_numb, size, err);
    
        if (err == NRFX_SUCCESS) err = 0;
        else err = LFS_ERR_IO;
        return err;
    }
    
    int erase_littlefs_wrapper(const struct lfs_config *cfg, lfs_block_t block){
    
        if (nand_flash_is_current_block_bad(block)) return LFS_ERR_CORRUPT;
    
        nand_erase_block( block);
        return 0;
    }
    
    int sync_littlefs_wrapper(const struct lfs_config *cfg){
        //printk("LFS Sync\n");
        return 0;
    }
    
    struct lfs_config cfg = {
        // block device operations
        .read                   = read_littlefs_wrapper,
        .prog                   = write_littlefs_wrapper,
        .erase                  = erase_littlefs_wrapper,
        .sync                   = sync_littlefs_wrapper,
        .file_max               = LFS_FILE_MAX,
        .attr_max               = LFS_ATTR_MAX,
        .name_max               = LFS_NAME_MAX,
        .metadata_max           = 0,
    };
    
    bool fs_bad_blocks(void)
    {
        lfs_file_t bad_blocks_file;
        nand_bbt_t* bad_blocks;
        bool success = true;
    
        // check if bad blocks are already mapped
        if(nand_flash_get_bad_blocks(&bad_blocks))
        {
            // if so, then just load them into flash
            lfs_file_open(&lfs, &bad_blocks_file, "bad_blocks", LFS_O_WRONLY | LFS_O_CREAT);
            lfs_file_write(&lfs, &bad_blocks_file, bad_blocks, sizeof(nand_bbt_t));
            lfs_file_close(&lfs, &bad_blocks_file);
        }
        else
        {
            // otherwise check if a bad blocks file already exists
            if (lfs_file_open(&lfs, &bad_blocks_file, "bad_blocks", LFS_O_RDONLY) == LFS_ERR_OK)
            {
                // if so, load it into RAM
                lfs_file_read(&lfs, &bad_blocks_file, bad_blocks, sizeof(nand_bbt_t));
                lfs_file_close(&lfs, &bad_blocks_file);
                nand_flash_set_bad_blocks(bad_blocks);
            }
            else
            {
                // otherwise, manually scan SPI flash and save the file to flash
                nand_flash_bad_block_detect();
                if (lfs_file_open(&lfs, &bad_blocks_file, "bad_blocks", LFS_O_WRONLY | LFS_O_CREAT) == LFS_ERR_OK)
                {
                    lfs_file_write(&lfs, &bad_blocks_file, bad_blocks, sizeof(nand_bbt_t));
                    lfs_file_close(&lfs, &bad_blocks_file);
                }
                else success = false;
            }
        }
        return success;
    }
    
    void fs_test_boot_count(void)
    {
        // read current count
        uint32_t boot_count = 0;
        lfs_file_t boot_count_file;
    
        if (lfs_file_open(&lfs, &boot_count_file, "boot_count", LFS_O_RDWR | LFS_O_CREAT) == LFS_ERR_OK)
        {
    
            lfs_file_read(&lfs, &boot_count_file, &boot_count, sizeof(boot_count));
    
            // update boot count
            boot_count += 1;
            lfs_file_rewind(&lfs, &boot_count_file);
            lfs_file_write(&lfs, &boot_count_file, &boot_count, sizeof(boot_count));
    
            // remember the storage is not updated until the file is closed successfully
            lfs_file_close(&lfs, &boot_count_file);
        }
    
        // print the boot count
        printk("boot_count: %d\n", boot_count);
    }
    
    
    // Public Functions
    void fs_init(bool reformat)
    {
    
        // init the spi flash
        if (nand_flash_init() != NRFX_SUCCESS)
        {
            printk("Error: Unable to start the file system.\n");
            return;
        }
    
        // set up the config file metrics
        cfg.read_size              = 64;
        cfg.prog_size              = 64;
        cfg.block_size             = (nand_metrics.page_size - nand_metrics.spare_size)*nand_metrics.page_per_block;
        cfg.block_count            = nand_metrics.num_blocks;
        cfg.block_cycles           = 500;
        cfg.cache_size             = (nand_metrics.page_size - nand_metrics.spare_size);
        cfg.lookahead_size         = 256;
    
        // check if we need to reformat
        if (reformat) 
        {
            nand_flash_bad_block_detect();
            lfs_format(&lfs, (const struct lfs_config *) &cfg);
        }
        
        // mount the filesystem
        int err = lfs_mount(&lfs, (const struct lfs_config *) &cfg);
    
        // reformat if we can't mount the filesystem
        // this should only happen on the first boot
        if (err) {
            // check for bad blocks manually
            nand_flash_bad_block_detect();
    
            // then format the drive and re-mount
            lfs_format(&lfs, (const struct lfs_config *) &cfg);
            err = lfs_mount(&lfs, (const struct lfs_config *) &cfg);
            if (err) return;
    
        }
    
        // initialize the bad blocks tracker
        if (fs_bad_blocks() == false)
        {
            // then format the drive and re-mount
            lfs_format(&lfs, (const struct lfs_config *) &cfg);
            err = lfs_mount(&lfs, (const struct lfs_config *) &cfg);
            if (err) return;
        }
        
        // perform simple test with boot count
        fs_test_boot_count();
    }

    The main thing to focus on is the read, write and erase wrappers. I just have them checking for any bad blocks, converting the offset addressing of littlefs to pages and columns, and then handing it off to my NAND flash driver to perform the actual operation. Also make sure to return the proper LFS_ERR codes.

    In the example above, my NAND flash driver provides the block, and page size info required to configure littlefs. Formats and mounts the filesystem. Generates or loads a mapping of bad blocks, and updates a boot_count value within the filesystem.

  • Hi Brett,

    Thank you for your response and for sharing the guide. Your assistance is greatly appreciated!

    Best regards,
    Furkan

Reply Children
Related