From 9c3d810cd7214674474a82bb2507b96b4bcc182c Mon Sep 17 00:00:00 2001 From: Peter Andersson Date: Sat, 27 Jul 2013 00:19:27 +0200 Subject: [PATCH] added readme, added some comments --- README | 282 ++++++++++++++++++++++++++++++++++++ src/default/spiffs_config.h | 7 +- src/spiffs.h | 1 + 3 files changed, 289 insertions(+), 1 deletion(-) create mode 100644 README diff --git a/README b/README new file mode 100644 index 0000000..5f198e3 --- /dev/null +++ b/README @@ -0,0 +1,282 @@ +SPIFFS (SPI Flash File System) +V1.0 + +Copyright (c) 2013 Peter Andersson + +For legal stuff, see LICENCE in this directory. + + +* INTRODUCTION + +Spiffs is a file system intended for SPI flash devices on embedded targets. + +Spiffs is designed with following characteristics in mind: + - Small (embedded) target, sparse RAM + - Only big areas of data (blocks) can be erased + - An erase will reset all bits in block to ones + - Writing pulls one to zeroes + - Zeroes can only be pulled to ones by erase + - Wear leveling + +Spiffs is inspired by YAFFS structurally, but is a clean write from scratch. + + +* INTEGRATING SPIFFS + +In order to integrate spiffs to your embedded target, you will basically need: + - A SPI flash device which your processor can communicate with + - An implementation for reading, writing and erasing the flash + - Memory (flash or ram) for the code + - Memory (ram) for the stack + +Threaded systems might need mutexes and so on. + +** Logical structure + +One must decide how to divide up the SPI flash for spiffs. Having the datasheet +for the actual SPI flash in hand will help. Spiffs can be defined to use all or +only parts of the SPI flash. + +If this seems arcane, read the "DESIGN" chapter first. + + - Decide the logical size of blocks. This must be a multiple of the physical + SPI flash block size. To go safe, use the physical block size - which in + almost all cases are 65536 bytes. + - Decide the logical size of pages. This must be a 2nd logarithm part of the + logical block size. To go safe, use 256 bytes to start with. + - Decide how much of the SPI flash memory to be used for spiffs. This must be + on logical block boundary. If unsafe, use 1 megabyte to start with. + - Decide where on the SPI flash memory the spiffs area should start. This must + be on physical block/sector boundary. If unsafe, use address 0. + +** SPI flash API + +The target must provide three functions to spiffs: + + - s32_t (*spiffs_read)(u32_t addr, u32_t size, u8_t *dst) + - s32_t (*spiffs_write)(u32_t addr, u32_t size, u8_t *src) + - s32_t (*spiffs_erase)(u32_t addr, u32_t size) + +These functions define the only communication between the SPI flash and the +spiffs stack. + +On success these must return 0 (or SPIFFS_OK). Anything else will be considered +an error. + +The size for read and write requests will never exceed the logical page size, +but it may be less. + +The address and size on erase requests will always be on physical block size +boundaries. + +** Mount specification + +In spiffs.h, there is a SPIFFS_mount function defined, used to mount spiffs on +the SPI flash. + +s32_t SPIFFS_mount( + spiffs *fs, + spiffs_config *config, + u8_t *work, + u8_t *fd_space, + u32_t fd_space_size, + void *cache, + u32_t cache_size, + spiffs_check_callback check_cb_f) + + - fs Points to a spiffs struct. This may be totally uninitialized. + - config Points to a spiffs_config struct. This struct must be + initialized when mounting. See below. + - work A RAM memory buffer being double the size of the logical page + size. This buffer is used excessively by the spiffs stack. If + logical page size is 256, this buffer must be 512 bytes. + - fd_space A RAM memory buffer used for file descriptors. + - fd_space_size The size of the file descriptor buffer. A file descriptor + normally is around 32 bytes depending on the build config - + the bigger the buffer, the more file descriptors are + available. + - cache A RAM memory buffer used for cache. Ignored if cache is + disabled in build config. + - cache_size The size of the cache buffer. Ignored if cache is disabled in + build config. One cache page will be slightly larger than the + logical page size. The more ram, the more cache pages, the + quicker the system. + - check_cb_f Callback function for monitoring spiffs consistency checks and + mending operations. May be null. + +The config struct must be initialized prior to mounting. One must always +define the SPI flash access functions: + + spiffs_config.hal_read_f - pointing to the function reading the SPI flash + spiffs_config.hal_write_f - pointing to the function writing the SPI flash + spiffs_config.hal_erase_f - pointing to the function erasing the SPI flash + + + + spiffs_config.phys_size - the physical number of bytes accounted for + spiffs on the SPI flash + spiffs_config.phys_addr - the physical starting address on the SPI flash + +TODO + +** Build config + +makefile: The files needed to be compiled to your target resides in files.mk to +be included in your makefile, either by cut and paste or by inclusion. + +Types: spiffs uses the types u8_t, s8_t, u16_t, s16_t, u32_t, s32_t; these must +be typedeffed. + +spiffs_config.h: you also need to define a spiffs_config.h header. Example of +this is found in the default/ directory. + + +* QUICK AND DIRTY INTEGRATION EXAMPLE + +So, assume you're running a Cortex-M3 board with a 2 MB SPI flash on it. The +SPI flash has 64kB blocks. Your project is built using gnumake, and now you +want to try things out. + +First, you simply copy the files named in files.mk to your own source folder. Then +you point out these files in your make script for compilation. + +Also copy the spiffs_config.h over from the default/ folder. + +Build fails, nagging about inclusions and u32_t and whatnot. Open the +spiffs_config.h and delete the bad inclusions. Also, add following typedefs: + + typedef signed int s32_t; + typedef unsigned int u32_t; + typedef signed short s16_t; + typedef unsigned short u16_t; + typedef signed char s8_t; + typedef unsigned char u8_t; + +Now it should builds. Over to the mounting business. Assume you already +implemented the read, write and erase functions to your SPI flash: + + void my_spi_read(int addr, int size, char *buf) + void my_spi_write(int addr, int size, char *buf) + void my_spi_erase(int addr, int size) + +In your main.c or similar, include the spiffs.h and do that spiffs struct: + + #include + + static spiffs fs; + +Also, toss up some of the needed buffers: + + #define LOG_PAGE_SIZE 256 + + static u8_t spiffs_work_buf[LOG_PAGE_SIZE*2]; + static u8_t spiffs_fds[32*4]; + static u8_t spiffs_cache[(LOG_PAGE_SIZE+32)*4]; + +Now, write the my_spiffs_mount function: + + void my_spiffs_mount() { + spiffs_config cfg; + cfg.phys_size = 2*1024*1024; // use all spi flash + cfg.phys_addr = 0; // start spiffs at start of spi flash + cfg.phys_erase_block = 65536; // well, this is what the datasheet says + cfg.log_block_size = 65536; // seems sensible + cfg.log_block_size = LOG_PAGE_SIZE; // seems sensible + + cfg.hal_read_f = my_spi_read; + cfg.hal_write_f = my_spi_write; + cfg.hal_erase_f = my_spi_erase; + + int res = SPIFFS_mount(&fs, + &cfg, + spiffs_work_buf, + spiffs_fds, + sizeof(spiffs_fds), + spiffs_cache, + sizeof(spiffs_cache), + 0); + printf("mount res: %i\n", res); + } + +Now, build warns about the my_spi_read, write and erase functions. Wrong +signatures, so go wrap them: + + static s32_t my_spiffs_read(u32_t addr, u32_t size, u8_t *dst) { + my_spi_read(addr, size, dst); + return SPIFFS_OK; + } + + static s32_t my_spiffs_write(u32_t addr, u32_t size, u8_t *src) { + my_spi_write(addr, size, dst); + return SPIFFS_OK; + } + + static s32_t my_spiffs_erase(u32_t addr, u32_t size) { + my_spi_erase(addr, size); + return SPIFFS_OK; + } + +Redirect the config in my_spiffs_mount to the wrappers instead: + + cfg.hal_read_f = my_spiffs_read; + cfg.hal_write_f = my_spiffs_write; + cfg.hal_erase_f = my_spiffs_erase; + +Ok, now you should be able to build and run. However, you get this output: + + mount res: -1 + +but you wanted + + mount res: 0 + +This is probably due to you having being experimenting with your SPI flash, so +it contains garbage from spiffs's point of view. Do a mass erase and run again. + +If all is ok now, you're good to go. Try creating a file and read it back: + + static void test_spiffs() { + char buf[12]; + + spiffs_file fd = SPIFFS_open(&fs, "my_file", 0, SPIFFS_CREAT | SPIFFS_TRUNC | SPIFFS_RDWR); + if (SPIFFS_write(&fs, fd, "Hello world", 12) < 0) printf("errno %i\n", SPIFFS_errno(&fs)); + SPIFFS_close(&fs, fd); + + fd = SPIFFS_open(&fs, "my_file", 0, SPIFFS_RDWR); + if (SPIFFS_read(&fs, fd, buf, 12) < 0) printf("errno %i\n", SPIFFS_errno(&fs)); + SPIFFS_close(&fs, fd); + + printf("--> %s <--\n", buf); + } + +And, crossing fingers hard, you'll get the output: + + --> Hello world <-- + + +* USING SPIFFS + +TODO + +* DESIGN + +** SPI flash devices + +Below is a small description of how SPI flashes work internally. + +SPI flash devices are physically divided in blocks. On some SPI flash devices, +blocks are further divided into sectors. Common memory capacaties for SPI +flashes are 1,2,4 or 8 megabyte of data, with blocks of 64K. Sectors normally +are 4K, if supported. The entire memory is linear and can be written in random +access, but 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 erasing. + +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 area given by the sector or block to ones. Writing to a SPI flash +pulls ones to zeroes. Writing 0xFF to an address is simply a no-op. This way +of "nand-writing" is used considerably in spiffs. + +** TODO diff --git a/src/default/spiffs_config.h b/src/default/spiffs_config.h index c259fc2..44a2ea6 100644 --- a/src/default/spiffs_config.h +++ b/src/default/spiffs_config.h @@ -8,7 +8,8 @@ #ifndef SPIFFS_CONFIG_H_ #define SPIFFS_CONFIG_H_ - +// following includes are for the linux test build of spiffs +// this may/should/must be removed/altered/replaced in your target #include "stypes.h" #include #include @@ -17,9 +18,13 @@ // compile time switches +// set generic spiffs debug output #define SPIFFS_DBG(...) +// set spiffs debug output for garbage collecting #define SPIFFS_GC_DBG(...) +// set spiffs debug output for caching #define SPIFFS_CACHE_DBG(...) +// set spiffs debug output for system consistency checks #define SPIFFS_CHECK_DBG(...) // define maximum number of gc runs to perform to reach desired free pages diff --git a/src/spiffs.h b/src/spiffs.h index 53cdda6..84b26f4 100644 --- a/src/spiffs.h +++ b/src/spiffs.h @@ -224,6 +224,7 @@ typedef struct { * @param fd_space_size memory size of file descriptors * @param cache memory for cache, may be null * @param cache_size memory size of cache + * @param check_cb_f callback function for reporting during consistency checks */ s32_t SPIFFS_mount(spiffs *fs, spiffs_config *config, u8_t *work, u8_t *fd_space, u32_t fd_space_size,