Discussion: littlefs on SPI NAND
Hi I would like to use LittleFS directly on a NAND chip, such as Micron MT29F.
The minimum erasable unit on the MT29F is 128KiB The smallest writable unit is 2KiB (random write) The smallest writable unit is 1 byte (sequential write)
Thanks
Forgot to ask my question... will LittleFS work with such a memory? Thanks
Hi @hchaudhary1, at the moment you may run into problems.
LittleFS has only been used on NOR flash, eMMC, SD cards; devices with <4KB erase units, so I'm not sure how it will perform on NAND flash.
Concerns:
-
You must provide ECC
LittleFS will hand write errors, which is the main effect of flash wear. But with NAND memory you also have to worry about read errors. NAND memory is so dense that the stored electrons start misbehaving pretty easily.
Scanning the MT29F's datasheet, it looks like that part has built in ECC you can use.
-
On disk, each file uses 1 erase unit at minimum.
For 128KiB erase units, this may waste a large amount of space.
But NAND devices are also very large, so if you have few files this may not be a problem? I'm not sure. It probably depends a lot on how you use the filesystem.
I have plans to improve this issue by storing multiple files in a block, but I don't know when I'll be able to implement the idea. So unfortunately at the moment 1 file per erase unit is the best LittleFS can do.
-
You may need 2KiB (writable unit) of RAM for the program cache?
I'm not sure if you can take advantage of the 1 byte sequential writes.
For each block, LittleFS does write the data out sequentially. But, it may partially write to multiple erase units multiple times (for example, partially write erase unit A, partially write erase unit B, finish writing erase unit A). Can the NAND chip handle that sort of pattern?
At the moment, I think LittleFS might need a flash translation layer (FTL) to be efficient. An FTL would provide a smaller erase unit and prevent all of the issues, though you may still want to use LittleFS for power resilience.
At the very least, LittleFS is probably better than putting FAT directly on top of raw NAND.
If you do try to put LittleFS on a NAND chip, let me know how it goes! I'd be interested to know what sort of problems you run into.
geky, thanks for elaborating on this
My hope is to avoid using a fully featured FTL layer such as dhara (https://github.com/dlbeer/dhara), since fail-safety, wear-leveling, and bad-block-management are already built into littlefs...
-
ECC correction upon physical read is handled by the chip itself. Up to 8-bits can be corrupted in the physical page, but when the read happens, the data is corrected and fixed before transferring over SPI.
-
The smallest capacity NAND (1Gbit) contains 1024 erasable units (each unit being 128KiB). This means I can have over 1000 files. This is more than enough for me. Capacity is also not an issue; I just need fast write speeds, which is why I am not using NOR.
-
This is something I will have to test; especially in a multi-threaded environment where I will have multiple files open simultaneously.
I guess my main concern is what will happen to stale data that over several years develops more than 8-bits of errors. I supposed only static wear leveling can reduce this issue...
Thanks. I will provide some feedback
Ah interesting, that is a nice side effect of static wear leveling.
Though I've heard that errors from stale data are less concerning on SLC NAND parts. Looking at the MT29F's datasheet, it only mentions concerns about write errors:
Over time, some memory locations may fail to program or erase properly.
The same datasheet also lists a data retention of 10 years. I suspect that is in the case when the chip has no power, and may be the same for stale data. If you need longer data retention, you could probably increase the size of the ECC, though that may require software ECC.
Although it may be a good idea to ask someone who's a better expert on NAND memory.
implementation was a success!
Ah! That is exciting! Thanks for the update.
Hi,
just to understand, does littleFS support NAND flash without any change?
regards Haithem.
Yes, it supports NAND without modifications. You have to write your own read, write, erase functions ofcoarse
EDIT: Tested with Micron MT29F 1GiB model, that has hardware ECC.
is there any example,how to use littlefs on spi nand?
What is the status of LittleFS with NAND flash support? Any improvement?
there isn't anyhting special about nand and lfs, same as NOR pretty much. blocks are larger, but that's it.
NAND working well
I have connected the littlefs to NAND flash too. 1gb SPI nand W25N01. It works too. FTL didn't connect yet, maybe I will connect it later.
Hi,
its good to see @hchaudhary1, @timandr made it work with NAND ,
i am also trying to add lfs to NAND based flash (winbond w25n01gv) with nrf52 but it gives mount error.
lfs error:495: Corrupted dir pair at 0 1 lfs error:2228: Invalid superblock at 0 1 lfs mount error lfs error:494: Corrupted dir pair at 0 1 lfs error:2222: Invalid superblock at 0 1
i have added it without making any changes to the lfs, i have tested the read/write/erase functions and all are working fine.
this is how my implementation looks like,
const struct lfs_config cfg = { // block device operations .read = read_littlefs_wrapper, .prog = write_littlefs_wrapper, .erase = erase_littlefs_wrapper, .sync = sync_littlefs_wrapper,
// block device configuration
.read_size = 24,
.prog_size = 24,
.block_size = 1024*64,
.block_count = 1024,
.lookahead = 2048,
};
these wrapper functions will call the spi based flash drivers.
void init_littlefs(){
int err = lfs_mount(&lfs, &cfg);
if (err) {
printf("lfs mount error\n");
lfs_format(&lfs, &cfg);
err = lfs_mount(&lfs, &cfg);
if(err) printf("failed to mount--------->\n");
}
// read current count
uint32_t boot_count = 0;
lfs_file_open(&lfs, &file, "boot_count", LFS_O_RDWR | LFS_O_CREAT);
lfs_file_read(&lfs, &file, &boot_count, sizeof(boot_count));
boot_count += 1;
lfs_file_rewind(&lfs, &file);
lfs_file_write(&lfs, &file, &boot_count, sizeof(boot_count));
lfs_file_read(&lfs, &file, &boot_count, sizeof(boot_count));
printf("boot count%ld\n", boot_count);
fs_file_close(&lfs, &file);
lfs_unmount(&lfs);
print the boot count
printf("boot_count: %d\n", boot_count);
}
I have also read somewhere that the SPI operations needs to be synchronous/blocking in order for lfs to work. what does it mean?
as i have looked at the code lfc.c, at line 462 condition fails,
if ((0x7fffffff & test.size) < sizeof(test)+4 ||
(0x7fffffff & test.size) > lfs->cfg->block_size) {
continue;
}
the read for srtuct test is all 0xff
i can see only one write operation before it fails, which i have tried to read back and it gives some data (1,0,0,0,20,0,0,0).
i think i am missing something but couldn't figure out whats wrong , i haven't also looked at the implementation of lfs in detail.
Post your read/write/erase/sync functions... also you lfs_config struct settings...
const struct lfs_config cfg = {
// block device operations
.read = read_littlefs_wrapper,
.prog = write_littlefs_wrapper,
.erase = erase_littlefs_wrapper,
.sync = sync_littlefs_wrapper,
// block device configuration
.read_size = 16,
.prog_size = 16,
.block_size = 2048*64,
.block_count = 1024,
.lookahead = 2048,
};
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 = (block * FLASH_BLOCK_SIZE_INPAGES) + (off/FLASH_PAGE_SIZE_INBYTES);
uint16_t column_numb = off%FLASH_PAGE_SIZE_INBYTES;
err = flash_w25n01gv_read_lfs(page_numb, column_numb, buffer, size);
return 0;
//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 = (block * FLASH_BLOCK_SIZE_INPAGES) + (off/FLASH_PAGE_SIZE_INBYTES);
uint16_t column_numb = off%FLASH_PAGE_SIZE_INBYTES;
err = flash_w25n01gv_write_lfs(page_numb, column_numb, (uint8_t *) buffer, size);
//return err;
return 0;
}
int erase_littlefs_wrapper(const struct lfs_config *cfg, lfs_block_t block){
flash_w25n01gv_erase_lfs(block);
return 0;
}
int sync_littlefs_wrapper(const struct lfs_config *cfg){
return 0;
}
flash details,
page size 2048 block size 64 pages total blocks 1024
minimum write = 1 byte minimum erase = 1 block
i can't give my code, since it is closed source. you need to test your read & write wrappers independently of littleFS first. you are not handling all the corner cases, such as, what if the requested read size is larger than the page size, etc...
Write in a test pattern (at a random offeset) with a large size, and read it back. Do this at multiple random starting blocks. once you can make that portion work, then everything should fall in place
@hchaudhary1 thanks for the update, there was an issue with my spi driver while reading big blocks of code(limited dma size).
now its working like a charm :)
Thanks for all your information sharing, plan to test this myself next week.
Thank you for the great work!!! It works for Windbond W25M02GWZEIT (SLC Nand SPI Flash, 2 Gbit)
@hchaudhary1 thanks for the update, there was an issue with my spi driver while reading big blocks of code(limited dama size).
now its working like a charm :)
Hi hcChaudary/Joice,
-
I am using Winbond QSPI Nand flash with W25N01GV 1 G-bit(128 MB) memory with only block erase(128 KB). Is there any size limitation for using higher size/Block-size in LittleFs?.
-
For my flash memory (with a page read/program and block erase interface) , does it requires a flash translation layer?. In my first look and up on looking the configuration structures I felt like LittleFs is handling these translation.
-
If it not requires the translation layer, Can I use it straight in my program which runs in FreeRTOS*?. With minimal mapping, i.e. only configuring "lfs_config".
Regards, Visakh SV
@joicetm . Hi I am working on the same Winbond flash. Could you have some time to talk
Hi visakh,
1, Not sure, the minimum value should be the chip block size 1024*64 2, I have not used FTL, it will work without an FTL. 3, Only the read, write , erase functions you have to create.
FYI, LFS still have some performance issues on NAND flash as the number of files increases.
@joicetm Hi Thanks Joice,
Yes. I have given the actual block size (128 KiB - 2048*64). I have created a separate post where I have added my code for test. Can you please check over there. This I have pasted here as well, can you please review it.
https://github.com/ARMmbed/littlefs/issues/361
I have ported littlefs to my application and written wrapper functions for it to attach to the config structure (lfs_config). This wrapper function is tested individually by writing a test application that writes a 100byte buffer to all the 1024 blocks (each block 128 KB) start addresses(first column) and read back and compared. All is Success.
But when I started to test with the example like in the littlefs readme.md mount error and format error has returned and finally hangs at fileopen(the next instruction). I can paste the test code here and config here, if someone have a clue please respond..... I expects a solution from @geky @geky // configuration of the filesystem is provided by this struct const struct lfs_config cfg = { // block device operations .read = QSPIFlashRead, .prog = QSPIflashProgram, .erase = QSPIflashErase, .sync = QSPIflashSync,
// block device configuration .read_size = 16, .prog_size = 16, .block_size = 0x20000, //128KB .block_count = 0x400, //1024 blocks .lookahead_size = 0x800, // less idea , but given size of a page .cache_size = 16, .block_cycles = 500, };
------------------------------Test code -------------------------
uint32_t FlashTest(void) { // mount the filesystem
uint32_t err = lfs_mount(&lfs, &cfg);
// reformat if we can't mount the filesystem // this should only happen on the first boot if (err) { err= lfs_format(&lfs, &cfg); { CPrintf("ERROR: Format \n\r"); } lfs_mount(&lfs, &cfg); { CPrintf("ERROR: Mount \n\r"); } } // read current count uint32_t boot_count = 0; err= lfs_file_open(&lfs, &file, "boot_count", LFS_O_RDWR | LFS_O_CREAT); if(err) { CPrintf("ERROR: File Open \n\r"); } err= lfs_file_read(&lfs, &file, &boot_count, sizeof(boot_count)); if(err) { CPrintf("ERROR: File Read \n\r"); }
// update boot count boot_count += 1; err= lfs_file_rewind(&lfs, &file); if(err) { CPrintf("ERROR: File Rewind \n\r"); }
err= lfs_file_write(&lfs, &file, &boot_count, sizeof(boot_count));
if(err) { CPrintf("ERROR: File Write \n\r"); }
// remember the storage is not updated until the file is closed successfully err= lfs_file_close(&lfs, &file);
if(err) { CPrintf("ERROR: File Close \n\r"); }
// release any resources we were using lfs_unmount(&lfs);
// print the boot count CPrintf("boot_count: %d\n", boot_count); }
@joicetm .
Hi @joicetm . PF the log from my console, If you can figure out the issue,
lfs_trace:3603: lfs_mount(0x2001b484, 0x8022470 {.context=0, .read=0x8003d65, .p rog=0x8003d91, .erase=0x8003dbd, .sync=0x8003de3, .read_size=16, .prog_size=16, .block_size=131072, .block_count=1024, .block_cycles=500, .cache_size=16, .looka head_size=2048, .read_buffer=0, .prog_buffer=0, .lookahead_buffer=0, .name_max=0 , .file_max=0, .attr_max=0}) lfs_error:977: Corrupted dir pair at 0 1 lfs_trace:3727: lfs_unmount(0x2001b484) lfs_trace:3729: lfs_unmount -> 0 lfs_trace:3722: lfs_mount -> -84 lfs_trace:3522: lfs_format(0x2001b484, 0x8022470 {.context=0, .read=0x8003d65, . prog=0x8003d91, .erase=0x8003dbd, .sync=0x8003de3, .read_size=16, .prog_size=16, .block_size=131072, .block_count=1024, .block_cycles=500, .cache_size=16, .look ahead_size=2048, .read_buffer=0, .prog_buffer=0, .lookahead_buffer=0, .name_max= 0, .file_max=0, .attr_max=0})
Make sure that your read/write/erase functions are working fine. Also make some test cases to test all possible corner cases. it should work.
@joicetm . Hi It was worked for me. Sorry that i not mentioned here. I have already tested the corner cases with the wrappers. But there was OS related allocations for the sizes needed also to the read/write what I am passing is the Block+offset address, but i was not integrating the total block size to it. This is working for me now , reading and writing a buffer and wear leveling feature.
@joicetm . Have you handle it with large files. I found issues in large files, when writing all went success, but closing and opening a large file ends up in trouble (~350K).
@joicetm , May I now what interface you have used for W25N01 with uC. I am using QSPI and observed some error(of course without file system )
Hi I have interfaced Winbond(W25N01GV) with STM32L4 qspi . I am using STM HAL api's. The problem is erase/write to a page(2KB) address(say 0x0) repeats to the page after 512 KB(Say 0x80000) and so on.
The erase command(D8h) available for erasing 128KiB block. But what I experienced like it is erasing 2KiB. I don't know if i read it wrong. I use quad IO, ECC and buffer read mode.
I will pin-point my issues once again below,
-
A data written to Page 0(column 0) appears in all the 512 KiB sections next .(i.e. data written to 0x0 appears, in 0x80000, 0x100000 and so on).
-
Erase command (D8h) is for erasing entire 128KiB block from the page address, but in my case, if I erase 0x0 and read back only 2 KiB is erased.
Hi @visakh-png
i have used SPI drivers for interfacing.
I didn't face any erase issues, i was able to create multiple files and read/write data. But I have also ran into some performance issues like higher file size(>128 k) and speed.
Are you giving enough delay after erase command? have a look at the logic trace for a better understanding, it might help.
i will try to recreate the issue on my set up if get some time.