mirror of https://github.com/pellepl/spiffs
Peter Andersson
11 years ago
15 changed files with 275 additions and 277 deletions
@ -0,0 +1,232 @@ |
|||
* 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 |
|||
|
|||
Other stuff may be needed, threaded systems might need mutexes and so on. |
|||
|
|||
** Logical structure |
|||
|
|||
First and foremost, 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 following seems arcane, read the "DESIGN" chapter first. |
|||
|
|||
- Decide the logical size of blocks. This must be a multiple of the biggest |
|||
physical SPI flash block size. To go safe, use the physical block size - |
|||
which in many cases is 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 <spiffs.h> |
|||
|
|||
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 <-- |
|||
|
@ -0,0 +1,30 @@ |
|||
* 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. Datasheets sometimes name blocks as |
|||
sectors and vice versa. |
|||
|
|||
Common memory capacaties for SPI flashes are 512kB up to 8 MB 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 |
|||
|
Loading…
Reference in new issue