Skip to content

Configure spiffs

Jim Tonti edited this page Dec 28, 2019 · 17 revisions

File system structure

Build time config

Runtime config

Configuration examples

FILE SYSTEM STRUCTURE

First of all, you need to know how much memory your SPI flash has, its page size (henceforth called physical page size), and its sector/block size(s) (henceforth called physical block size). This can be collected from the datasheet.

Having this, you first need to decide roughly how much of the SPI flash you want to give to spiffs.

Then, you need to decide the spiffs logical block size. This must be an integer multiple of the physical block size. Say your flash has 4kB, 16kB and 64kB physical block sizes. Then you can have logical block sizes of n * 4kB, or n * 16kB, or n * 64kB, where n is an integer. The most common choice is set the logical block size to same amount as some physical block size. The file system size must be an integer multiple of the logical block size.

Now, you need to define the spiffs logical page size. This relates to the logical block size as such that the logical block size must be an integer multiple of the logical page size. You probably want several pages (>16) per block in order to utilize the flash efficiently. A logical page is the smallest data holder element in spiffs.

Say you have decided upon a 64kB logical block size. You use 256 bytes as logical page size, thus yielding 256 logical pages per logical block. If you want to store one byte on flash in a spiffs file, at least one logical page is used. Also here the most common choice is to have same logical page size as the physical page size. Having a bigger logical page size will require you to do more coding in the HAL layer for writing, in order not to wrap around the physical page size.

For each block and page comes some metadata, so having very small pages or blocks will leave data/metadata ratio pretty low. Having very large pages will probably leave much of the flash unused. Also, having very many pages will force spiffs to read more, making it slower.

It all depends on what kind of files you will normally store and the performance you need.

To get started, the easiest is to set sizes of logical page and block to same sizes as the physical page and block. After getting it up and running, one can experiment if needed. Do mind, altering logical block/page sizes will change the file system structure, needing a reformat.

BUILD TIME CONFIG

All build time config is in the file spiffs_config.h. One should work this through and enable/disable/define what is needed. It comes with a default vanilla flavor definition.

File system generics

SPIFFS_OBJ_NAME_LEN

Maximum number of characters in a file name. Must absolutely not exceed the logical page size minus some bytes.

SPIFFS_SINGLETON

If you want to run multiple instances of spiffs on the same target, either on same flash or on different flashes, disable this.

If you will have only one spiffs instance on the target, enable this.

When enabled, some run-time configurations such as file system size, block sizes, page sizes becomes build time configurations instead. This will save RAM, spiffs will make less lookups against RAM, and the compiler can make the spiffs binary smaller.

SPIFFS_CFG_PHYS_SZ

If SPIFFS_SINGLETON is enabled, this defines the total number of bytes of the SPI flash allocated to spiffs.

SPIFFS_CFG_PHYS_ERASE_SZ

If SPIFFS_SINGLETON is enabled, this defines the physical block size.

SPIFFS_CFG_PHYS_ADDR

If SPIFFS_SINGLETON is enabled, this defines the physical address on the SPI flash where spiffs resides. If you do not use all SPI flash for spiffs, you can position spiffs on the SPI flash with this define. This address must be on a logical block size boundary.

SPIFFS_CFG_LOG_PAGE_SZ

If SPIFFS_SINGLETON is enabled, this defines the logical page size.

SPIFFS_CFG_LOG_BLOCK_SZ

If SPIFFS_SINGLETON is enabled, this defines the logical block size.

Types spiffs_block_ix, spiffs_page_ix, spiffs_obj_id, spiffs_span_ix

These types must be set according to the file system requirements. They depend on the total size of the filesystem, and the logical block and page sizes. See comments in spiffs_config.h for each type. These types are included in lookup tables and page headers, so it is suboptimal to have them too wide. On the other hand, setting them too narrow can cause severe bugs. To sum it up:

Failure to configure these types correctly may result in eternal loops due to overflows, and a bunch of other nastinesses.

SPIFFS_CACHE

Enables or disables the spiffs cache mechanism. When enabled, extra RAM must given to spiffs. This can however speed up execution quite a bit.

SPIFFS_CACHE_WR

Enables or disables the write cache for spiffs. It is much recommended to enable this. Otherwise, make sure you store blocks of data, instead of writing single bytes. The latter, without SPIFFS_CACHE_WR, will wear the flash, as all writes are written through directly.

It is only valid to enable this when SPIFFS_CACHE is enabled.

SPIFFS_PAGE_CHECK

Enable this if spiffs shall check page validity each page access. This ensures safety at the cost of runtime performance. Then again, having cache enabled will make this check cost virtually nothing as all page data is already loaded.

SPIFFS_COPY_BUFFER_STACK

Sometimes spiffs need to copy SPI flash data from one place to another, needing a temporary buffer for shuffling the data. This buffer will reside on stack. If you're sparse on stack RAM, you can set this to a lower value - meaning spiffs will read/write many times, worse performance. There's no meaning setting this bigger than the logical page size.

File system extras

SPIFFS_USE_MAGIC

Enable this if you want to be able to recognize that the underlying structure on the SPI flash is indeed a spiffs file system at mount. See a discussion of this here. Otherwise, you're a bold person and assumes everything is ok without even looking at the flash at mount point.

SPIFFS_USE_MAGIC_LENGTH

Only valid when SPIFFS_USE_MAGIC is enabled. If SPIFFS_USE_MAGIC_LENGTH is enabled, the magic will also be dependent on the length of the filesystem. For example, a filesystem configured and formatted for 4 megabytes will not be accepted for mounting with a configuration defining the filesystem as 2 megabytes.

SPIFFS_READ_ONLY

Enable this to compile a read only version of spiffs. This will reduce binary size of spiffs. All code comprising modificationof the file system will not be compiled. Some config will be ignored. HAL functions for erasing and writing to spi-flash may be null. Cache can be disabled for even further binary size reduction (and ram savings). Functions modifying the fs will return SPIFFS_ERR_RO_NOT_IMPL. If the file system cannot be mounted due to aborted erase operation and SPIFFS_USE_MAGIC is enabled, SPIFFS_ERR_RO_ABORTED_OPERATION will be returned. Might be useful for e.g. bootloaders and such.

SPIFFS_ALIGNED_OBJECT_INDEX_TABLES

Getting bus errors or weird problems where file names are truncated at beginning or files do not show up at all? This (probably) means your spiffs configuration clashes with the target memory bus. You need to waste a couple of bytes and enable this.

SPIFFS_HAL_CALLBACK_EXTRA

Enable this if you want the hardware abstraction layer functions to have the spiffs structure as an argument also. Useful when running multiple spiffs instances in order to recognize to what spiffs instance a hal function belongs.

SPIFFS_FILEHDL_OFFSET

Enable this if you want the spiffs filehandles to be offseted for a specific spiffs instance. Useful when running multiple spiffs instances in order to recognize to what spiffs instance a filehandle belongs.

SPIFFS_LOCK

If you running in a multithreaded environment, you can add a mutex lock in this define.

SPIFFS_UNLOCK

If you running in a multithreaded environment, you can add a mutex unlock in this define.

SPIFFS_TEMPORAL_FD_CACHE

Enable to optimize opening of files. The more file descriptors spiffs gets, the more effective the cache will be. Do note, this adds an extra 6 bytes to each file descriptor.

SPIFFS_TEMPORAL_CACHE_HIT_SCORE

If temporal cache is enabled, this value will determine hit score when a file is opened. The temporal cache has a score system to determine which file to "forget" when out of entries. Each time a file is opened, all cache entries loses one point. If a cache entry was matched and reused when opening, it will gain SPIFFS_TEMPORAL_CACHE_HIT_SCORE points. This leads to files being opened frequently are less prone to be forgotten, and will linger in the cache.

SPIFFS_IX_MAP

Enabling this flag will open up functions for mapping a files meta data index to a given memory array. This is useful to optimize reading of files. Also, the time for each read call will be much more deterministic which can be preferred during streaming. The read cache has a somewhat similar function so with cache enabled, the index mapping works best for large files. Large in this sense means files being larger than (logical page size)^2 / sizeof(spiffs_page_ix).

File system tweaks

SPIFFS_GC_MAX_RUNS

Defines maximum number of physical block erases during a garbage collection. This parameter controls how much space a gc is allowed to free, i.e. (SPIFFS_GC_MAX_RUNS x logical block size) bytes. From this perspective, it seems good to have it very high. But keep in mind that a block erase normally takes time. During a gc the file system is blocked. This parameter is also a cap for how long the file system may be unresponsive during a gc, being (SPIFFS_GC_MAX_RUNS x time for one logical block erase) seconds. Also, sometimes the gc cleans blocks being totally full to honor the wear-leveling - such an erase will not free any space. To sum it up, SPIFFS_GC_MAX_RUNS should be set according to how much space a gc is supposed to be able to clean up (probably the maximum known filesize) and to the maximum time the file system may be unresponsive.

SPIFFS_GC_HEUR_W_DELET

Garbage collection heuristics. Tamper with these to modify wear-leveling. Only for the bold.

SPIFFS_GC_HEUR_W_USED

Garbage collection heuristics. Tamper with these to modify wear-leveling. Only for the bold.

SPIFFS_GC_HEUR_W_ERASE_AGE

Garbage collection heuristics. Tamper with these to modify wear-leveling. Only for the bold.

Debug

SPIFFS_TEST_VISUALISATION

Enable this if you want to be able to call SPIFFS_vis which dumps the file system structure to some printf defined by you.

SPIFFS_BUFFER_HELP

Enable this to have some handy functions to call when needing to decide number of bytes for cache and filedescriptor buffers. Takes some code in account.

SPIFFS_DBG

Generic debug printf call.

SPIFFS_GC_DBG

Garbage collection debug printf call.

SPIFFS_CACHE_DBG

Cache debug printf call.

SPIFFS_CHECK_DBG

File system check debug printf call.

SPIFFS_CACHE_STATS

Normally disable this. For the test bench.

SPIFFS_GC_STATS

Normally disable this. For the test bench.

RUNTIME CONFIG

This is more or less covered in the integration section. As good as all runtime config is defined when mounting spiffs.

CONFIGURATION EXAMPLE

Following are some examples of how a configuration could work.

Small single FS

Having a 32Mbit winbond W25Q32FV spi flash, the datasheet gives us a physical page size of 256 bytes. We can erase in chunks of 4kB (sector erase), 32kB and 64kB (block erase).

First try

From this, we select a logical page size of 256 bytes, and a logical block size of 32kB. We want to use 512kB of the spi flash for spiffs, being positioned at the end of the spi flash - address 0x380000 (4MB - 512kB).

As we have only one FS in our application, we set

SPIFFS_SINGLETON = (1)

We set the FS size to 512kB:

SPIFFS_CFG_PHYS_SZ = (512*1024)

The logical block size we set to 32kB:

SPIFFS_CFG_PHYS_ERASE_SZ = (32*1024)

The offset of the FS was decided to be at spi flash address 0x380000:

SPIFFS_CFG_PHYS_ADDR = (0x380000)

The logical page size was 256 bytes:

SPIFFS_CFG_LOG_PAGE_SZ = (256)

And, the logical block size was 32kB:

SPIFFS_CFG_LOG_BLOCK_SZ = (32*1024)

Also, we want spiffs to be able to recognize if there is a valid spiffs structure so we set:

SPIFFS_USE_MAGIC = (1)

and

SPIFFS_USE_MAGIC_LENGTH = (1)

To make things run smoothly we also set:

SPIFFS_CACHE = (1) SPIFFS_CACHE_WR = (1) and SPIFFS_TEMPORAL_FD_CACHE = (1)

But, when mounting we get error code -10028 SPIFFS_ERR_MAGIC_NOT_POSSIBLE. This means there are no bits left where we can put the magic in order to recognize a spiffs structure - see here.

Second try

Obviously, one way solve the problem is simply to disable SPIFFS_USE_MAGIC.

But, in our example, we really want spiffs to recognize if there is a spiffs structure or not. We redefine:

SPIFFS_CFG_LOG_PAGE_SZ = (128)

Now everything works, and we're ready to go.

Other options

Another way to get around the SPIFFS_ERR_MAGIC_NOT_POSSIBLE, we keep the page size to 256 bytes and instead increase the logical block size:

SPIFFS_CFG_LOG_BLOCK_SZ = (64*1024)

With this setting, spiffs will call two erase operations of 32kB each time a logical block is erased. As the spi flash supports 64kB blocks also, it would probably be an optimization to also define:

SPIFFS_CFG_PHYS_ERASE_SZ = (64*1024)

Explanation

What is the difference then, you ask?

In the first case we have 128 bytes logical page size and 32kB sized logical block size. This gives us (32768 / 128) = 256 pages per block, and (512kB / 32kB) = 16 blocks. In all, we will have (256 * 16) = 4096 pages. Spiffs requires that two logical blocks always must be free, so the free area would have summed up to 2 * 32kB = 64kB. Meaning, ignoring other metadata, the FS can store 512kB - 64kB = 448kB.

Second case, using same reasoning, we will have (65536 / 256) = 256 pages per block with (512kB / 64kB) = 8 blocks, yielding (256 * 8) = 2048 pages. Required free area will be 2 * 64kB = 128kB, and the FS can store 384kB.

Practically, the first case will give you a slower file system (more pages to traverse), but you can cram more files in it (smaller pages gives more granularity) and more data (less free area). Second case will give you faster file system (less pages), but maximum number of files are halved (less pages), and effective data area is less (more free area).

The first case is better if you plan to have bigger amount of smaller files, with less requirements on speed.

The second case is better if you plan to store smaller amount of bigger files, with more requirements on speed.