Over the past years, I have make use of FatFS to manage files on SPI flash. This is not a good solution, because of FatFS’s lack of wear leveling ability. In the GIF below we can see how fast FatFS get failed without wear leveling.

FatFS vs LittleFS

In addition, unexpected power failure is common in embedded system. In this case, FatFS is not reliable too. Most SPI Flashs have a 4kB minimal eraseable sector, so FatFS need a 4kB SRAM buffer, some entry level STM32 MCUs have only 4kB SRAM in total, we can’t use FatFS on those MCUs.

There are two file system SPIFFS and LittleFS which were designed for SPI flash(or other small NOR flash).

SPIFFS is a file system intended for SPI NOR flash devices on embedded targets. You can get more information by click it’s README and WIKI.

LittleFS is a little fail-safe file system designed for embedded systems. You can get more information by click it’s README and DESIGN and SPEC.

I choosed LittleFS because it seems more compact with only 4 files and 100kB source code.

LittleFS: 4 files and 100kB
  68K  lfs.c
  15K  lfs.h
 1.4K  lfs_util.c
 5.5K  lfs_util.h
SPIFFS: 8 files and 296kB
 9.8K  spiffs_cache.c
  45K  spiffs_check.c
  14K  spiffs_config.h
  26K  spiffs_gc.c
  28K  spiffs.h
  41K  spiffs_hydrogen.c
  87K  spiffs_nucleus.c
  25K  spiffs_nucleus.h

Here are the main porting codes, including 4 SPI Flash access API, 4 buffers and an initialization function.

int block_device_read(const struct lfs_config *c, lfs_block_t block,
	lfs_off_t off, void *buffer, lfs_size_t size)
{
	W25X_Read((uint8_t*)buffer, (block * c->block_size + off), size);
	return 0;
}

int block_device_prog(const struct lfs_config *c, lfs_block_t block,
	lfs_off_t off, const void *buffer, lfs_size_t size)
{
	W25X_Write_NoCheck((uint8_t*)buffer, (block * c->block_size + off), size);
	
	return 0;
}

int block_device_erase(const struct lfs_config *c, lfs_block_t block)
{
	W25X_Erase_Sector(block * c->block_size);
	
	return 0;
}

int block_device_sync(const struct lfs_config *c)
{
	return 0;
}

lfs_t lfs;
lfs_file_t file;
struct lfs_config cfg;

uint8_t lfs_read_buf[256];
uint8_t lfs_prog_buf[256];
uint8_t lfs_lookahead_buf[16];	// 128/8=16
uint8_t lfs_file_buf[256];

void LFS_Config(void)
{
	// block device operations
	cfg.read  = block_device_read;
	cfg.prog  = block_device_prog;
	cfg.erase = block_device_erase;
	cfg.sync  = block_device_sync;

	// block device configuration
	cfg.read_size = 256;
	cfg.prog_size = 256;
	cfg.block_size = 4096;
	cfg.block_count = 4096;
	cfg.lookahead = 256;
	
	cfg.read_buffer = lfs_read_buf;
	cfg.prog_buffer = lfs_prog_buf;
	cfg.lookahead_buffer = lfs_lookahead_buf;
	cfg.file_buffer = lfs_file_buf;
}

The MCU is STM32F103C8T6 and the SPI Flash is W25Q128FV. Based on LittleFS, I implemented some commands such as mount unmount format ls rm mkdir mv cat, and some more utilities.

help
 reboot -> reboot [delay ms] Restart system.
 help -> help Info.
 version -> display SW version and SN.
 flash -> flash [read|write|erase] [addr] [data] Operate SPI Flash.
 lfs -> fs [unmount|mount|format|info] Operate File System.
 ls -> list DIR or FILE.
 rm -> remove FILE or DIR.
 mkdir -> mkdir DIR.
 mv -> mv FILE or DIR.
 cat -> cat File.
 file -> file [open|close|read|write] Operate files.

Let’s mount LittleFS, create a directory ‘abc’, remove directory ‘test’, and rename directory ‘abc’ to ‘def’.

lfs mount
mount=0
ls
DIR       0B     .
DIR       0B     ..
FIL     351B     log.csv
DIR       0B     test
mkdir abc
mkdir=0
ls
DIR       0B     .
DIR       0B     ..
FIL     351B     log.csv
DIR       0B     test
DIR       0B     abc
rmdir test
remove=0
mv abc def
rename=0
ls
DIR       0B     .
DIR       0B     ..
FIL     351B     log.csv
DIR       0B     def

With the built-in command flash, we can read the SPI Flash raw bytes. Two super blocks locate on block 0 and 1.

Super block 0 looks like this:

flash read 0
0x00000000 02 00 00 00 34 00 00 00 03 00 00 00 02 00 00 00 ....4...........
0x00000010 2E 14 00 08 03 00 00 00 02 00 00 00 00 10 00 00 ................
0x00000020 00 10 00 00 01 00 01 00 6C 69 74 74 6C 65 66 73 ........littlefs
0x00000030 63 BD 62 5C 00 00 00 00 00 00 00 00 00 00 00 00 c.b\............
0x00000040 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0x00000050 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0x00000060 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0x00000070 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0x00000080 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0x00000090 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0x000000A0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0x000000B0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0x000000C0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0x000000D0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0x000000E0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0x000000F0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................

Super block 1:

flash read 1000
0x00001000 01 00 00 00 34 00 00 00 03 00 00 00 02 00 00 00 ....4...........
0x00001010 2E 14 00 08 03 00 00 00 02 00 00 00 00 10 00 00 ................
0x00001020 00 10 00 00 01 00 01 00 6C 69 74 74 6C 65 66 73 ........littlefs
0x00001030 07 88 B0 1A 00 00 00 00 00 00 00 00 00 00 00 00 ................
0x00001040 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0x00001050 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0x00001060 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0x00001070 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0x00001080 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0x00001090 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0x000010A0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0x000010B0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0x000010C0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0x000010D0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0x000010E0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0x000010F0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................

So far, LittleFS works well on my board. In the future, maybe I will try SPIFFS or do some benchmark on LittleFS.