littlefs-project / littlefs

A little fail-safe filesystem designed for microcontrollers
BSD 3-Clause "New" or "Revised" License
4.9k stars 770 forks source link

Discussion: littlefs on SPI NAND #11

Open hchaudhary1 opened 6 years ago

hchaudhary1 commented 6 years ago

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

hchaudhary1 commented 6 years ago

Forgot to ask my question... will LittleFS work with such a memory? Thanks

geky commented 6 years ago

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:

  1. 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.

  2. 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.

  3. 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.

hchaudhary1 commented 6 years ago

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...

  1. 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.

  2. 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.

  3. 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

geky commented 6 years ago

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.

hchaudhary1 commented 6 years ago

implementation was a success!

geky commented 6 years ago

Ah! That is exciting! Thanks for the update.

rahmanih commented 6 years ago

Hi,

just to understand, does littleFS support NAND flash without any change?

regards Haithem.

hchaudhary1 commented 6 years ago

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.

tomyqg commented 6 years ago

is there any example,how to use littlefs on spi nand?

hassanmj commented 5 years ago

What is the status of LittleFS with NAND flash support? Any improvement?

rojer commented 5 years ago

there isn't anyhting special about nand and lfs, same as NOR pretty much. blocks are larger, but that's it.

hchaudhary1 commented 5 years ago

NAND working well

timandr commented 5 years ago

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.

joicetm commented 5 years ago

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.

hchaudhary1 commented 5 years ago

Post your read/write/erase/sync functions... also you lfs_config struct settings...

joicetm commented 5 years ago
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

hchaudhary1 commented 5 years ago

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

joicetm commented 5 years ago

@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 :)

kazola commented 5 years ago

Thanks for all your information sharing, plan to test this myself next week.

kuoyaoming93 commented 4 years ago

Thank you for the great work!!! It works for Windbond W25M02GWZEIT (SLC Nand SPI Flash, 2 Gbit)

visakh-png commented 4 years ago

@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,

  1. 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?.

  2. 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.

  3. 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

visakh-png commented 4 years ago

@joicetm . Hi I am working on the same Winbond flash. Could you have some time to talk

joicetm commented 4 years ago

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.

visakh-png commented 4 years ago

@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); }

visakh-png commented 4 years ago

@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})

joicetm commented 4 years ago

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.

visakh-png commented 4 years ago

@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.

visakh-png commented 4 years ago

@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).

visakh-png commented 3 years ago

@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,

  1. 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).

  2. 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.

joicetm commented 3 years ago

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.

visakh-png commented 3 years ago

Hi @joicetm , Yes I am waiting for the BUSY flag(0x01) in status register(0xC0) to get cleared after doing erase(D8).

I am not using file system now and instead of using some circular buffers, to manage with addresses, size and time.

I doubts the addressing goes wrong in qspi.

geky commented 3 years ago

This has been a valuable source of discussion for an area that's still an open question for littlefs, so I'm going to pin this issue as a sort of "discussion" issue.

visakh-png commented 3 years ago

@geky @joicetm . I have solved the issue with the help of another person who has faced same issue with QSPI NAND. Please note that this will never come in the case of SPI, as this is a problem bw STM32 QSPI and the W25N01GV NAND.

The microcontroller sends the QSPI command(HAL_QSPI_Command) fields in the order:

  1. Instruction
  2. Address
  3. Alternate bytes
  4. Dummy Clocks
  5. Data

But, according to the Winbond data sheet, some commands (those in which the page address is used) need the address after the dummy clock. So this cannot be achieved with sending a QSPI command. So, for these commands now I am sending HAL_QSPI_Command with no address and then sending the address as if it were the data, with "HAL_QSPI_Transmit" function.

FYI. I am not using littlefs now, Instead managing a static queue that holds address and size of data in NAND. Once I get some time I will check with LittleFS. I hope performance will be better as compared to SPI since Quad SPI is much faster.

Regards, Visakh

Igor-Misic commented 3 years ago

Did anyone of you who are using W25N01G experienced a problem when flashing multiple times to the same page (2K page) that data get corrupted? I have written a driver that is capable of writing and reading byte by byte by using different column addresses. According to the datasheet Data Buffer should put a 0xFF value to unused locations and unused data should not be written to flash. I am a bit skeptical about this, maybe this flash, in general, has some issue or it is just my sample that has this problem.

The problem appears after around 200 flashes at the same pages (but at different column addresses), the data readout is corrupted for some locations.

Edit: I found an answer to my question. Each page is limited to a max of 4 writing before needs to be erased. Here is the answer from Winbond support: https://igor-misic.blogspot.com/2020/12/winbond-nand-flash-w25n01gv-problem.html

So in addition to that, the write buffer size should not be lower than 512 bytes.

mjs513 commented 3 years ago

Just as a FYI as well as a question. We did manage to get the W25N01 and W25N02 working with LittleFS with both SPI and QSPI on a Teensy 4.1. QSPI does have much better performance than when operating with SPI. In our tests seems to working without issue so far but we are still in the testing process.

The question I have is related to whether its worth it or advisable to implement ECC with these chips. Both chips implement it slightly differently. And if it is any advice on getting it working would be appreciated. Been going through the datasheets and its a bit confusing.

sslupsky commented 3 years ago

To add to the discussion, I am using LittleFS with a Toshiba serial NAND device. My initial use case for testing was to write my console log messages to the NAND. I do this by periodically appending data to a log file. So, there are not many files on the volume but the log file can get quite large depending on the rate of log messages. What I observed was that the time it took for LittleFS to open / append /close the file and the time it took to start up after a reboot appeared to increase significantly over time as the log file grew in size. It seems like an exponentially growing amount of time.

jimarcher commented 3 years ago

Edit: I found an answer to my question. Each page is limited to a max of 4 writing before needs to be erased. Here is the answer from Winbond support: https://igor-misic.blogspot.com/2020/12/winbond-nand-flash-w25n01gv-problem.html

So in addition to that, the write buffer size should not be lower than 512 bytes.

@Igor-Misic I'm wondering how you addressed this number of writes limitation in your code. Did you track the number of writes in a wrapper function and issue an erase as needed? Something else? Thank you...

Igor-Misic commented 3 years ago

@jimarcher I have done it through the configuration of littlefs. My driver for W25N01G still can write any size to any place but littlefs is limited with a minimum size of 512. Since each page size is 2048, it is limited to write 4 times to each page.

    .read_size = 512,
    .prog_size = 512,
    .block_size = 2048 * 64,
    .block_count = 1024,
    .cache_size = 512,
    .lookahead_size = 512,
    .block_cycles = 500,
jimarcher commented 3 years ago

@jimarcher I have done it through the configuration of littlefs. My driver for W25N01G still can write any size to any place but littlefs is limited with a minimum size of 512. Since each page size is 2048, it is limited to write 4 times to each page.

    .read_size = 512,
    .prog_size = 512,
    .block_size = 2048 * 64,
    .block_count = 1024,
    .cache_size = 512,
    .lookahead_size = 512,
    .block_cycles = 500,

Ah, brilliant, thank you!

jimarcher commented 3 years ago

I realize this may not be possible, but would anyone be willing to share their read, write, erase functions for the Winbond W25N chips? I'm trying to make Little FS work with Mongoose OS (based upon FreeRTOS) and not having much luck. I have very little experience talking to these kinds of devices, and with a starting point I would do much better. Sadly, Mongoose has a driver for the W25N but it's broken, so I'm trying to debug that as well. Thanks...

pauloet commented 3 years ago

Hi all! Great work here! Thanks to all contributors! I've just put the last version of littlefs working (at least with the read boot count example given) with the SPI Nand Flash TH58CVG3S0HRAIJ (8Gb). I'm using a PIC32 with low RAM to interface with the flash, and I define the LFS_NO_MALLOC in order to use RAM buffers.

Configs doubts

I want to set the configurations to achieve the maximum performance. My questions are:

const struct lfs_config Flash_cfg = { // block device operations .read = Flash_LFS_Read, .prog = Flash_LFS_Prog, .erase = Flash_LFS_Erase, .sync = Flash_LFS_Sync,

// block device configuration
.read_size = 16,   // isn't better to be 1?
.prog_size = 16,   // isn't better to be 1?
.block_size = 4096 * 64,
.block_count = 4096,
.cache_size = 4096,
.read_buffer = Flash_readBuffer,
.prog_buffer = Flash_progBuffer,
.lookahead_size = 4096,
.lookahead_buffer = Flash_lookahead,
.block_cycles = 500,

};



### User function doubts
This is related with the "corner cases" (as @hchaudhary1 and @joicetm mentioned) that the functions must handle. As an example, here are the logs when I perform a format operation:

> [LFS] lfs_format(0xa0006500, 0x9d027ac8 {.context=0x0, .read=0x9d00ee90, .prog=0x9d00f044, .erase=0x9d00ed80, .sync=0x9d00de00, .read_size=16, .prog_size=16, .block_size=262144, .block_count=4096, .block_cycles=500, .cache_size=4096, .lookahead_size=4096, .read_buffer=0xa0005500, .prog_buffer=0xa0001fc8, .lookahead_buffer=0xa000342c, .name_max=0, .file_max=0, .attr_max=0})
> [LFS] Read Block: 1 Offset: 0 Size: 16
> [FLASH] Read page. Block: 1. Page: 0. Col: 0. Size: 16
> [FLASH] Block 0 erased. Raddr: 0
> [LFS] Read Block: 0 Offset: 64 Size: 16
> [FLASH] Read page. Block: 0. Page: 0. Col: 64. Size: 16
> [LFS] Prog Block: 0 Offset: 0 Size: 64
> [FLASH] Write page. Block: 0. Page: 0. Col: 0. Size: 64
> [LFS] Read Block: 0 Offset: 0 Size: 64
> [FLASH] Read page. Block: 0. Page: 0. Col: 0. Size: 64
> [FLASH] Block 1 erased. Raddr: 64
> [LFS] Read Block: 1 Offset: 64 Size: 16
> [FLASH] Read page. Block: 1. Page: 0. Col: 64. Size: 16
> [LFS] Prog Block: 1 Offset: 0 Size: 64
> [FLASH] Write page. Block: 1. Page: 0. Col: 0. Size: 64
> [LFS] Read Block: 1 Offset: 0 Size: 64
> [FLASH] Read page. Block: 1. Page: 0. Col: 0. Size: 64
> [LFS] Read Block: 0 Offset: 0 Size: 16
> [FLASH] Read page. Block: 0. Page: 0. Col: 0. Size: 16
> [LFS] Read Block: 1 Offset: 0 Size: 16
> [FLASH] Read page. Block: 1. Page: 0. Col: 0. Size: 16
> **[LFS] Read Block: 1 Offset: 16 Size: 4096**
> **[FLASH] Read page. Block: 1. Page: 0. Col: 16. Size: 4096**
> [LFS] lfs_format -> 0

- **In the last LFS operation, it tries to read 4096 (which is a page size) starting on column 16, which will end already in the spare area of the same page because it has 256 bytes for spare but belonging to the same page - ECC disabled**. In this "corner case", what should the function do?
    - Never use spare area and read 4080 bytes starting in column 16 of the page 0 and read more 16 bytes from page 1?
    - Or we should use the spare area? In my case, I'm using it.
 - **Another corner case can happen if LFS requests to read, for example,  4096 bytes starting on column 2048 of the last page of a block. This kind of situation can happen or not? If so, we must read the remaining 2048 bytes from the first page of the next block or return an error?**
sslupsky commented 3 years ago

I do not think the look ahead buffer needs to be that large. You can probably reduce that considerably.

The Kioxia flash devices support partial page writes so you can use a smaller page size (1024 instead of 4096) and reduce your buffer sizes as well.

I have noticed a peculiar thing about the Kioxia nand. When reading a page from nand, the OIP time will occasionally exceed the max page read time specification in the data sheet. I am wondering if this is common for other nand devices?

sslupsky commented 3 years ago

Regarding your question about the the spare bytes, all the main memory is aligned to the 4096 page size. The spare bytes only exist in the page buffer, not the main space. So, you do not have to do much unless you plan to do use the spare area for bad block management and/or custom ecc.

pauloet commented 3 years ago

Regarding your question about the the spare bytes, all the main memory is aligned to the 4096 page size. The spare bytes only exist in the page buffer, not the main space. So, you do not have to do much unless you plan to do use the spare area for bad block management and/or custom ecc.

Thank you @sslupsky for your fast response! So, in the corner case I must read 4080 bytes starting in column 16 of the page 0 and read more 16 bytes from page 1? And about the block corner case, what do you think?

pauloet commented 3 years ago

I do not think the look ahead buffer needs to be that large. You can probably reduce that considerably.

The Kioxia flash devices support partial page writes so you can use a smaller page size (1024 instead of 4096) and reduce your buffer sizes as well.

I have noticed a peculiar thing about the Kioxia nand. When reading a page from nand, the OIP time will occasionally exceed the max page read time specification in the data sheet. I am wondering if this is common for other nand devices?

Yes, it supports the page partial program, but I've tested to write byte-by-byte and it also works. So maybe doing as discussed at #277 can achieve a best performance right?

pauloet commented 3 years ago

Also, since the lfs_config struct has a cache_size, I suggest to add a buffer pointer in that structure for the cache buffer. This can make it simpler for those who don't use MALLOC.

void *cache_buffer;

This way, everything stays in the global configuration.

sslupsky commented 3 years ago

@pauloet Yes, that would be the case. The same applies to the "block" case as well. If you look closely at the datasheet and the illustration of the memory layout, the use of the "row address" ensures you always have alignment of the row. You can use the program load random data command to get to the spare bytes and ECC faster if you need to.

sslupsky commented 3 years ago

I do not think the look ahead buffer needs to be that large. You can probably reduce that considerably. The Kioxia flash devices support partial page writes so you can use a smaller page size (1024 instead of 4096) and reduce your buffer sizes as well. I have noticed a peculiar thing about the Kioxia nand. When reading a page from nand, the OIP time will occasionally exceed the max page read time specification in the data sheet. I am wondering if this is common for other nand devices?

Yes, it supports the page partial program, but I've tested to write byte-by-byte and it also works. So maybe doing as discussed at #277 can achieve a best performance right?

I think you might be confusing the write to the on-chip cache with the page write. There is a limit to the number of writes you can do to the cell array. This is specified in the datasheet as " Number of programs per page" which for the Kioxia chip is 4 (other vendors may only allow 1. For instance, the Alliance chips). You can certainly write byte-by-byte to the cache. But when it comes time to write the cache to the nand cell array, you are limited to 4 times. There are some other limitations related to how the ECC is calculated that impact this as well. The simple solution is to use 1K blocks.

sslupsky commented 3 years ago

Also, since the lfs_config struct has a cache_size, I suggest to add a buffer pointer in that structure for the cache buffer. This can make it simpler for those who don't use MALLOC.

void *cache_buffer;

This way, everything stays in the global configuration.

Are you suggesting a pointer to the on-chip cache? That is not the same as the LFS cache buffer and because it is not memory mapped you would have to implement a management layer. If you are using a QSPI memory interface to interface with the NAND, maybe it would be feasible to to do something like that. I have been wondering if we could disable the LFS cache somehow and use the on-chip cache but I am not certain you can do that and there may be performance issues. @geky would have to wade in on that.

pauloet commented 3 years ago

Also, since the lfs_config struct has a cache_size, I suggest to add a buffer pointer in that structure for the cache buffer. This can make it simpler for those who don't use MALLOC. void *cache_buffer; This way, everything stays in the global configuration.

Are you suggesting a pointer to the on-chip cache? That is not the same as the LFS cache buffer and because it is not memory mapped you would have to implement a management layer. If you are using a QSPI memory interface to interface with the NAND, maybe it would be feasible to to do something like that. I have been wondering if we could disable the LFS cache somehow and use the on-chip cache but I am not certain you can do that and there may be performance issues. @geky would have to wade in on that.

I was suggesting it just for the LFS cache buffer, but your idea seems to be useful indeed.