| * USING SPIFFS |
| |
| TODO |
| |
| * SPIFFS DESIGN |
| |
| Spiffs is inspired by YAFFS. However, YAFFS is designed for NAND flashes, and |
| for bigger targets with much more ram. Nevertheless, many wise thoughts have |
| been borrowed from YAFFS when writing spiffs. Kudos! |
| |
| The main complication writing spiffs was that it cannot be assumed the target |
| has a heap. Spiffs must go along only with the work ram buffer given to it. |
| This forces extra implementation on many areas of spiffs. |
| |
| ** SPI flash devices using NOR technology |
| |
| Below is a small description of how SPI flashes work internally. This is to |
| give an understanding of the design choices made in spiffs. |
| |
| SPI flash devices are physically divided in blocks. On some SPI flash devices, |
| blocks are further divided into sectors. Datasheets sometimes name blocks as |
| sectors and vice versa. |
| |
| Common memory capacaties for SPI flashes are 512kB up to 8MB of data, where |
| blocks may be 64kB. Sectors can be e.g. 4kB, if supported. Many SPI flashes |
| have uniform block sizes, whereas others have non-uniform - the latter meaning |
| that e.g. the first 16 blocks are 4kB big, and the rest are 64kB. |
| |
| The entire memory is linear and can be read and written in random access. |
| Erasing can only be done block- or sectorwise; or by mass erase. |
| |
| SPI flashes can normally be erased from 100.000 up to 1.000.000 cycles before |
| they fail. |
| |
| A clean SPI flash from factory have all bits in entire memory set to one. A |
| mass erase will reset the device to this state. Block or sector erasing will |
| put the all bits in the area given by the sector or block to ones. Writing to a |
| NOR flash pulls ones to zeroes. Writing 0xFF to an address is simply a no-op. |
| |
| Writing 0b10101010 to a flash address holding 0b00001111 will yield 0b00001010. |
| |
| This way of "write by nand" is used considerably in spiffs. |
| |
| Common characteristics of NOR flashes are quick reads, but slow writes. |
| |
| And finally, unlike NAND flashes, NOR flashes seem to not need any error |
| correction. They always write correctly I gather. |
| |
| ** Spiffs logical structure |
| |
| Some terminology before proceeding. Physical blocks/sectors means sizes stated |
| in the datasheet. Logical blocks and pages is something the integrator choose. |
| |
| ** Blocks and pages |
| |
| Spiffs is allocated to a part or all of the memory of the SPI flash device. |
| This area is divided into logical blocks, which in turn are divided into |
| logical pages. The boundary of a logical block must coincide with one or more |
| physical blocks. The sizes for logical blocks and logical pages always remain |
| the same, they are uniform. |
| |
| Example: non-uniform flash mapped to spiffs with 128kB logical blocks |
| |
| PHYSICAL FLASH BLOCKS SPIFFS LOGICAL BLOCKS: 128kB |
| |
| +-----------------------+ - - - +-----------------------+ |
| | Block 1 : 16kB | | Block 1 : 128kB | |
| +-----------------------+ | | |
| | Block 2 : 16kB | | | |
| +-----------------------+ | | |
| | Block 3 : 16kB | | | |
| +-----------------------+ | | |
| | Block 4 : 16kB | | | |
| +-----------------------+ | | |
| | Block 5 : 64kB | | | |
| +-----------------------+ - - - +-----------------------+ |
| | Block 6 : 64kB | | Block 2 : 128kB | |
| +-----------------------+ | | |
| | Block 7 : 64kB | | | |
| +-----------------------+ - - - +-----------------------+ |
| | Block 8 : 64kB | | Block 3 : 128kB | |
| +-----------------------+ | | |
| | Block 9 : 64kB | | | |
| +-----------------------+ - - - +-----------------------+ |
| | ... | | ... | |
| |
| A logical block is divided further into a number of logical pages. A page |
| defines the smallest data holding element known to spiffs. Hence, if a file |
| is created being one byte big, it will occupy one page for index and one page |
| for data - it will occupy 2 x size of a logical page on flash. |
| So it seems it is good to select a small page size. |
| |
| Each page has a metadata header being normally 5 to 9 bytes. This said, a very |
| small page size will make metadata occupy a lot of the memory on the flash. A |
| page size of 64 bytes will waste 8-14% on metadata, while 256 bytes 2-4%. |
| So it seems it is good to select a big page size. |
| |
| Also, spiffs uses a ram buffer being two times the page size. This ram buffer |
| is used for loading and manipulating pages, but it is also used for algorithms |
| to find free file ids, scanning the file system, etc. Having too small a page |
| size means less work buffer for spiffs, ending up in more reads operations and |
| eventually gives a slower file system. |
| |
| Choosing the page size for the system involves many factors: |
| - How big is the logical block size |
| - What is the normal size of most files |
| - How much ram can be spent |
| - How much data (vs metadata) must be crammed into the file system |
| - How fast must spiffs be |
| - Other things impossible to find out |
| |
| So, choosing the Optimal Page Size (tm) seems tricky, to say the least. Don't |
| fret - there is no optimal page size. This varies from how the target will use |
| spiffs. Use the golden rule: |
| |
| ~~~ Logical Page Size = Logical Block Size / 256 ~~~ |
| |
| This is a good starting point. The final page size can then be derived through |
| heuristical experimenting for us non-analytical minds. |
| |
| ** Objects, indices and look-ups |
| |
| A file, or an object as called in spiffs, is identified by an object id. |
| Another YAFFS rip-off. This object id is a part of the page header. So, all |
| pages know to which object/file they belong - not counting the free pages. |
| |
| An object is made up of two types of pages: object index pages and data pages. |
| Data pages contain the data written by user. Index pages contain metadata about |
| the object, more specifically what data pages are part of the object. |
| |
| The page header also includes something called a span index. Let's say a file |
| is written covering three data pages. The first data page will then have span |
| index 0, the second span index 1, and the last data page will have span index |
| 2. Simple as that. |
| |
| Finally, each page header contain flags, telling if the page is used, |
| deleted, finalized, holds index or data, and more. |
| |
| Object indices also have span indices, where an object index with span index 0 |
| is referred to as the object index header. This page does not only contain |
| references to data pages, but also extra info such as object name, object size |
| in bytes, flags for file or directory, etc. |
| |
| If one were to create a file covering three data pages, named e.g. |
| "spandex-joke.txt", given object id 12, it could look like this: |
| |
| PAGE 0 <things to be unveiled soon> |
| |
| PAGE 1 page header: [obj_id:12 span_ix:0 flags:USED|DATA] |
| <first data page of joke> |
| |
| PAGE 2 page header: [obj_id:12 span_ix:1 flags:USED|DATA] |
| <second data page of joke> |
| |
| PAGE 3 page header: [obj_id:545 span_ix:13 flags:USED|DATA] |
| <some data belonging to object 545, probably not very amusing> |
| |
| PAGE 4 page header: [obj_id:12 span_ix:2 flags:USED|DATA] |
| <third data page of joke> |
| |
| PAGE 5 page header: [obj_id:12 span_ix:0 flags:USED|INDEX] |
| obj ix header: [name:spandex-joke.txt size:600 bytes flags:FILE] |
| obj ix: [1 2 4] |
| |
| Looking in detail at page 5, the object index header page, the object index |
| array refers to each data page in order, as mentioned before. The index of the |
| object index array correlates with the data page span index. |
| |
| entry ix: 0 1 2 |
| obj ix: [1 2 4] |
| | | | |
| PAGE 1, DATA, SPAN_IX 0 --------/ | | |
| PAGE 2, DATA, SPAN_IX 1 --------/ | |
| PAGE 4, DATA, SPAN_IX 2 --------/ |
| |
| Things to be unveiled in page 0 - well.. Spiffs is designed for systems low on |
| ram. We cannot keep a dynamic list on the whereabouts of each object index |
| header so we can find a file fast. There might not even be a heap! But, we do |
| not want to scan all page headers on the flash to find the object index header. |
| |
| The first page(s) of each block contains the so called object look-up. These |
| are not normal pages, they do not have a header. Instead, they are arrays |
| pointing out what object-id the rest of all pages in the block belongs to. |
| |
| By this look-up, only the first page(s) in each block must to scanned to find |
| the actual page which contains the object index header of the desired object. |
| |
| The object lookup is redundant metadata. The assumption is that it presents |
| less overhead reading a full page of data to memory from each block and search |
| that, instead of reading a small amount of data from each page (i.e. the page |
| header) in all blocks. Each read operation from SPI flash normally contains |
| extra data as the read command itself and the flash address. Also, depending on |
| the underlying implementation, other criterions may need to be passed for each |
| read transaction, like mutexes and such. |
| |
| The veiled example unveiled would look like this, with some extra pages: |
| |
| PAGE 0 [ 12 12 545 12 12 34 34 4 0 0 0 0 ...] |
| PAGE 1 page header: [obj_id:12 span_ix:0 flags:USED|DATA] ... |
| PAGE 2 page header: [obj_id:12 span_ix:1 flags:USED|DATA] ... |
| PAGE 3 page header: [obj_id:545 span_ix:13 flags:USED|DATA] ... |
| PAGE 4 page header: [obj_id:12 span_ix:2 flags:USED|DATA] ... |
| PAGE 5 page header: [obj_id:12 span_ix:0 flags:USED|INDEX] ... |
| PAGE 6 page header: [obj_id:34 span_ix:0 flags:USED|DATA] ... |
| PAGE 7 page header: [obj_id:34 span_ix:1 flags:USED|DATA] ... |
| PAGE 8 page header: [obj_id:4 span_ix:1 flags:USED|INDEX] ... |
| PAGE 9 page header: [obj_id:23 span_ix:0 flags:DELETED|INDEX] ... |
| PAGE 10 page header: [obj_id:23 span_ix:0 flags:DELETED|DATA] ... |
| PAGE 11 page header: [obj_id:23 span_ix:1 flags:DELETED|DATA] ... |
| PAGE 12 page header: [obj_id:23 span_ix:2 flags:DELETED|DATA] ... |
| ... |
| |
| Ok, so why are page 9 to 12 marked as 0 when they belong to object id 23? These |
| pages are deleted, so this is marked both in page header flags and in the look |
| up. This is an example where spiffs uses NOR flashes "nand-way" of writing. |
| |
| As a matter of fact, there are two object id's which are special: |
| |
| obj id 0 (all bits zeroes) - indicates a deleted page in object look up |
| obj id 0xff.. (all bits ones) - indicates a free page in object look up |
| |
| Actually, the object id's have another quirk: if the most significant bit is |
| set, this indicates an object index page. If the most significant bit is zero, |
| this indicates a data page. So to be fully correct, page 0 in above example |
| would look like this: |
| |
| PAGE 0 [ 12 12 545 12 *12 34 34 *4 0 0 0 0 ...] |
| |
| where the asterisk means the msb of the object id is set. |
| |
| This is another way to speed up the searches when looking for object indices. |
| By looking on the object id's msb in the object lookup, it is also possible |
| to find out whether the page is an object index page or a data page. |