littlefs-project / littlefs

A little fail-safe filesystem designed for microcontrollers
BSD 3-Clause "New" or "Revised" License
4.91k stars 772 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

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?

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.

Here's what I'm struggling now because I agree with what you say about what the datasheet says. However, my tests tell me that I can really write byte-by-byte to the cell array. My prog function looks like:

BOOL Flash_LFS_Prog( ... )
{
   if (Flash_EnableWrite() == FALSE)
        return FALSE;

   if (Flash_ProgramLoad(src, colAddr, size) == FALSE)
        return FALSE;

   if (Flash_ProgramExecute(rowAddr) == FALSE)
        return FALSE;

   Flash_DisableWrite();
   return TRUE;
}

By performing the Program execute, I think I'm writing it to the cell array. What am I missing? Thanks again for your support!

sslupsky commented 3 years ago

Yes, that is correct. Each time you execute "Program Execute", you write the on chip cache to the NAND cell array. My understanding of what the datasheet says is you can only do this 4 times before you have to erase the block. I haven't actually tried to see if I could write the page more than 4 times. If you go down this path I would be interested to learn how reliable it is.

There are some implications for the spare area as well. ECC is calculated on 512 byte sub blocks of the page. So, when you program the page, ECC is calculated for each 512 chunk.

pauloet commented 3 years ago

Yes, that is correct. Each time you execute "Program Execute", you write the on chip cache to the NAND cell array. My understanding of what the datasheet says is you can only do this 4 times before you have to erase the block. I haven't actually tried to see if I could write the page more than 4 times. If you go down this path I would be interested to learn how reliable it is.

There are some implications for the spare area as well. ECC is calculated on 512 byte sub blocks of the page. So, when you program the page, ECC is calculated for each 512 chunk.

Yes, with ECC enabled it's like that, but for now I'm disabling it to keep it simple. I'm not using the spare area also because I can detect if the block is bad or not while doing a block erase or a page program. Besides that, my Flash_LFS_Sync function is returning always 0 because I'm doing the program execute always on the Flash_LFS_Prog. Maybe it's better to switch the program execute to the Flash_LFS_Sync function right?

About the reliability you say, I suspect it's not the best approach (byte-by-byte) to achieve a good one.

alperenemiroglu 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

@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

Hi, I am currently experiencing the problem you describe. When You say you are sending it back to back, Firstly, HAL_StatusTypeDef status; QSPI_CommandTypeDef cmd; cmd.InstructionMode = QSPI_INSTRUCTION_1_LINE; cmd.AddressMode = QSPI_ADDRESS_NONE; cmd.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE; cmd.DataMode = QSPI_DATA_NONE; cmd.DummyCycles = dummyCycles; cmd.DdrMode = QSPI_DDR_MODE_DISABLE; cmd.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY; cmd.SIOOMode = QSPI_SIOO_INST_EVERY_CMD;

cmd.Instruction       = 0xD8; // erase
cmd.Address           = 0x00;
cmd.AddressSize     =0x00;
cmd.NbData            = 0;

Are you sending the address like this?

QSPI_CommandTypeDef cmd;
cmd.InstructionMode   = QSPI_INSTRUCTION_1_LINE;
cmd.AddressMode       = QSPI_ADDRESS_1_LINE;
cmd.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE;
cmd.DataMode          = QSPI_DATA_NONE;
cmd.DummyCycles       = 0;
cmd.DdrMode           = QSPI_DDR_MODE_DISABLE;
cmd.DdrHoldHalfCycle  = QSPI_DDR_HHC_ANALOG_DELAY;
cmd.SIOOMode          = QSPI_SIOO_INST_EVERY_CMD;

cmd.Instruction       = 0xD8; // erase
cmd.Address           = page_address;
cmd.AddressSize     = QSPI_ADDRESS_16_BITS;
cmd.NbData            = 0;

Did you try to do this? I understood that from what you said.

zbqxyz commented 2 years ago

@hchaudhary1 @geky How to deal with bad blocks ?Factory-generated bad blocks or erase/write? I have this nand flash: Page Size 2048 block_Count 1024

Use below config is OK?

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

hchaudhary1 commented 2 years ago

I believe LittleFS already has bad block management

rojer commented 2 years ago

yes, the idea is that failing writes will be reallocated to a different block

zbqxyz commented 2 years ago

@hchaudhary1 @rojer when failing writes return LFS_ERR_CORRUPT?but i test ,mark block 0 bad ,Error when open then read file,test code below, Step1: erase all block Step2: mark block0 bad Step3: in nand drv port : _block_read ,_block_erase _block_prog if block bad return LFS_ERR_CORRUPT

Run code : int err = lfs_mount(&cfg_NandFlash, &cfg_NandFlash); if (err) { LFS_LOG_WARN("first fs mount failed\n"); lfs_format(&cfg_NandFlash, &cfg_NandFlash); lfs_mount(&cfg_NandFlash, &cfg_NandFlash); }

if 1

uint32_t boot_count = 0;
FS_FILE *fp = fs_open("boot_count", LFS_O_RDWR | LFS_O_CREAT);
fs_read(&boot_count, sizeof(boot_count), fp);  
LFS_LOG_ERROR("boot_count is %d\n", boot_count);
boot_count += 1;
fs_seek(fp, 0, LFS_SEEK_SET);
fs_write(&boot_count, sizeof(boot_count), fp);
fs_seek(fp, 0, LFS_SEEK_SET);
boot_count = 0;
fs_read(&boot_count, sizeof(boot_count), fp);
LFS_LOG_ERROR("boot_count is %d\n", boot_count);
fs_close(fp);

endif

Err info: lfs.c line:5161 Expression Faild : lfs_mlist_open

zbqxyz commented 2 years ago

I tested without bad blocks and it works fine

zbqxyz commented 2 years ago

i debug in lfs_format ,when call lfs_dir_compact, lfs_bd_erase(block 0) return LFS_ERR_CORRUPT,goto relocate

run here return LFS_ERR_NOSPC; // can't relocate superblock, filesystem is now frozen if (lfs_pair_cmp(dir->pair, (const lfs_block_t[2]){0, 1}) == 0) { LFS_WARN("Superblock 0x%"PRIx32" has become unwritable", dir->pair[1]); return LFS_ERR_NOSPC; }

zbqxyz commented 2 years ago

@geky @hchaudhary1 @rojer so when bad block at 0 or 1 littlefs cannot handle this situation?

rojer commented 2 years ago

yeah, i guess when block 0 or 1, the superblocks, go bad, it's game over. fortunately, they don't get changed much, normally.

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

Can you share the MT29F wrapper ?

votuananhs commented 1 year ago

@zbqxyz and @hchaudhary1

Which nand (mt29f2g01abagdwb), it has 128 bytes per page. So I don't see and understand why you didn't include it in littlefs configuration. Without that it will calculate wrong physical block ?

Jacq666 commented 1 year ago

Hello,

I am using LittleFs on a spi nand flash Micron, MT29F and it works very well.

But now, I would like to improve write speed, if possible.

I understand (tell me if I am wrong), that after every write, there is a read spi transfert, and a compare to check.

Pleased find enclosed a timing:

But MT29F nand flash provide an 'uncorrected ECC error code' when it cannot correct read page.

So I think that we just have to test the flag, no need to do a spi read transfert to check write. So we could avoid many read spi transferts.

What do you think about this ?

Thank you, best regards

image

Jacq666 commented 1 year ago

Hello,

Here was my config:

// minimum read and prog size operation // if use ecc, only 4 'partial page programming' are allowed // so size must be > PAGE_DATA_SIZE/4 // if not, ecc error bit remains high ...

define LFS_SIZE_READ_PROG 1024

define NUM_PAGE_BLOCK 64 / Number of pages for block/

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 = LFS_SIZE_READ_PROG,
  .prog_size = LFS_SIZE_READ_PROG,
  .block_size = PAGE_DATA_SIZE * NUM_PAGE_BLOCK,
  .block_count = NUM_BLOCKS,
  .lookahead_size = LFS_SIZE_READ_PROG,

  // cache_size must be multiple of read_size
  .cache_size = LFS_SIZE_READ_PROG,

  // Set to -1 to disable block-level wear-leveling.
  .block_cycles = -1

};

Is that what you expected ?

Le sam. 15 oct. 2022 à 05:28, Matt Mercaldo @.***> a écrit :

Hello Jac666, What was your config for the MT29F NAND chip? Thanks!

— Reply to this email directly, view it on GitHub https://github.com/littlefs-project/littlefs/issues/11#issuecomment-1279646322, or unsubscribe https://github.com/notifications/unsubscribe-auth/AI6ZODFOFCQUXCIGL5TYC5LWDIQHBANCNFSM4EMT3LFQ . You are receiving this because you commented.Message ID: @.***>

mattmercaldo commented 1 year ago

Perfect! Thanks so much!

CalebDueck commented 1 year ago

@joicetm I am wondering what SPI driver you are using? I am using the same Winbond NAND memory and can't find the driver that provides the functions your wrapper uses. flash_w25n01gv_read_lfs or similar functions

Any help would be appreciated. Or if there are other drivers that would do the job that would be much appreciated.

CalebDueck commented 1 year ago

Update, if you contact Winbond they can provide you with an example driver that works on STM32 MCUs. I used this as the basis of my own driver.

p3zhy commented 1 year ago

Hello .I am using LittleFs on a spi nand flash in esp32s3. What suggestions do you have to increase the speed of reading and writing? In a situation where the number of files is small, I can read 3 files with size 100 byte in a second. By increasing the number of files to 3000, the speed decreases a lot and it takes 40 seconds to read 3 files .

gineelee commented 1 year ago

Hi, Does your user application directly access the NAND flash through Zephyr's FS subsystem (fs_xxx) or LittleFS interfaces (lfs_xxx)? I ask because I encountered a linking issue when attempting to use Zephyr's FS subsystem by adding partition configuration in the device tree.

However, everything works perfectly when I utilize the LittleFS interfaces to access the NAND flash.

[Partition configuration] &MT29FXXX { partitions { compatible = "fixed-partitions";

address-cells = <1>;

    #size-cells = <1>;
    littlefs_partition:  partition@0 {
      label = "littlefs_storage";
      reg = <0x00000000 0x001000000>;
};

}; };

[Application]

define STORAGE_PARTITION littlefs_partition

define STORAGE_PARTITION_ID FIXED_PARTITION_ID(STORAGE_PARTITION)

static struct fs_mount_t lfs_mnt = { .type = FS_LITTLEFS, .fs_data = &cfg, .storage_dev = (void*)STORAGE_PARTITION_ID, .mnt_point = "/littlefs", }; ... fs_mount(&lfs_mnt);

[Error messages] [4/12] Linking C executable zephyr/zephyr_pre0.elf FAILED: bin/ld.bfd: zephyr/libzephyr.a(flash_map_default.c.obj):(.rodata.default_flash_map+0x4c): undefined reference to __device_dts_ord_98

__device_dts_ord_98 corresponds to my spi flash driver

I constructed my SPI NAND flash driver as an out-of-tree driver, considering that it might be the cause of the problem. I'm curious to know if you encountered a similar issue when building your SPI NAND driver outside of the Zephyr codebase and adding partition configurations.

mintisan commented 1 year ago

wonderful discussion...

ParthJaiswal17 commented 12 months ago

Hi,

I am also working on porting littlefs driver, on top of MT29. I am able to port the fs.

Although while life testing, I am getting -84 error (i.e. LFS_ERR_CORRUPT) after I write around 210 files of 3K, and after that complete file system gets corrupted.

So can anyone help me to understand the cause of error.

My config struct is as follows:

const struct lfs_config cfg = {

.context = NULL,
.read = lfsread,
.prog = lfsprog,
.erase = lfserase,
.sync = lfssync,

ifdef LFS_THREADSAFE

.lock = sh_lfslock,
.unlock = sh_lfsunlock,

endif

.read_size = 4096,
.prog_size = 4096,
.block_size = 4096,
.block_count = 262144,
.block_cycles = 512,
.cache_size = 4096,
.lookahead_size = 4096,

};

mattmercaldo commented 12 months ago

I'll let the others speak up to correct me if i am wrong..

It looks like your configuration is not correct. Read size is pretty high - you may want to think about tuning it to something smaller.

My running config is read size - 16 prog size - 1024 cache size - 1024 lookahead - 16 block count - 2048

you have to be careful because the sizes are not bytes all the time. Remember, the chip can only do 4 write operations before it needs to get refreshed to all FF as per the documentation on the chip.

There are some good notes in this blog overall about these value and what they should be.

You will also want to deep dive the amazing documentation created for littlefs.

On 07/13/2023 7:18 AM EDT ParthJaiswal17 @.***> wrote:

Hi,

I am also working on porting littlefs driver, on top of MT29. I am able to port the fs.

Although while life testing, I am getting -84 error (i.e. LFS_ERR_CORRUPT) after I write around 210 files of 3K, and after that complete file system gets corrupted.

So can anyone help me to understand the cause of error.

My config struct is as follows:

const struct lfs_config cfg = {

.context = NULL, .read = lfsread, .prog = lfsprog, .erase = lfserase, .sync = lfssync,

ifdef LFS_THREADSAFE

.lock = sh_lfslock, .unlock = sh_lfsunlock,

endif

.read_size = 4096, .prog_size = 4096, .block_size = 4096, .block_count = 262144, .block_cycles = 512, .cache_size = 4096, .lookahead_size = 4096,

};

— Reply to this email directly, view it on GitHub https://github.com/littlefs-project/littlefs/issues/11#issuecomment-1634065169, or unsubscribe https://github.com/notifications/unsubscribe-auth/AQAXUR2QSIS3HN6Q5TRYFA3XP7KQTANCNFSM4EMT3LFQ. You are receiving this because you commented.Message ID: @.***>

ParthJaiswal17 commented 11 months ago

Hi mattmercaldo,

Thank you for the advice, although if decrease the prog size to 1024, I get ecc error during read, also if reduce read size I am getting error while reading file number 32, must be metadeta issue.

Although the update is I have resolved the Issue by Increasing the block size to actual block size of MT29 flash which is 256KB and now it is working well, but with the cost of low speed.

Thank you.

BadboyIntheLoop commented 11 months ago

NAND working well

Hello @hchaudhary1 Can you give me the config for littleFS, in my case, I'vs already completed the driver for MT29F2G01 flash (read/prog/erase working ok) but I'm stucking at some points. Anyway, if you do not mind, can you give me your contact, I wanna ask you some questions about your config for littlefs on that nand flash. Thank you

Motirn commented 6 months ago

Trying to get littleFS running on w25m02gw SPI NAND, So far it seem to be working okay, with ability to write multiple file, read back and confirm data..etc.

.read_size/.prog_size is 2048 bytes(page size), and .erase_size is 64 x page size(128KB)

@hchaudhary1 Any guess Why I am seeing frequent block erases.. on every 4K write? It appears like its starting on a new block on every write.

More details here - https://github.com/littlefs-project/littlefs/issues/905