diff --git a/LICENSE b/LICENSE index 8794dbf..ee4c63b 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) 2013 Peter Andersson +Copyright (c) 2013 Peter Andersson (pelleplutt1976gmail.com) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in diff --git a/README b/README index 8c0ba22..fb7a746 100644 --- a/README +++ b/README @@ -1,11 +1,11 @@ SPIFFS (SPI Flash File System) V0.1 -Copyright (c) 2013 Peter Andersson +Copyright (c) 2013 Peter Andersson (pelleplutt1976gmail.com) For legal stuff, see LICENCE in this directory. Basically, you may do whatever -you want with the source. Use, modify, sell, print it out and smoke it - as -long as I won't be held responsible. +you want with the source. Use, modify, sell, print it out, roll it and smoke it + - as long as I won't be held responsible. Love to hear feedback though! @@ -28,278 +28,19 @@ What spiffs does: - Spiffs presents a posix-like api: open, close, read, write, seek, stat, etc - Multiple spiffs configurations can be run on same target - and even on same SPI flash device + - It implements static wear leveling - It has built in file system consistency checks What spiffs does not: - - Presently, spiffs does not support directories. It produces a flat + - Presently, spiffs does not support directories. It produces a flat structure. Creating a file with path "tmp/myfile.txt" will create a file - called "tmp/myfile.txt" instead of a myfile.txt under directory "tmp". + called "tmp/myfile.txt" instead of a "myfile.txt" under directory "tmp". - It is not a realtime stack. One write operation might take much longer than another. - Poor scalability. Spiffs is intended for small memory devices - the normal sizes for SPI flashes. - - -* 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) + - Presently, it does not detect or handle bad blocks. -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. 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 +For integration, see the docs/INTEGRATION file. +For use and design, see the docs/TECH_SPEC file. diff --git a/docs/INTEGRATION b/docs/INTEGRATION new file mode 100644 index 0000000..b3c60aa --- /dev/null +++ b/docs/INTEGRATION @@ -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 + + 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 <-- + diff --git a/docs/TECH_SPEC b/docs/TECH_SPEC new file mode 100644 index 0000000..18d6d2f --- /dev/null +++ b/docs/TECH_SPEC @@ -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 + diff --git a/makefile b/makefile index a902806..c10ab4f 100644 --- a/makefile +++ b/makefile @@ -17,7 +17,6 @@ builddir = build ############# CC = gcc $(COMPILEROPTIONS) -AS = gcc $(ASSEMBLEROPTIONS) LD = ld GDB = gdb OBJCOPY = objcopy @@ -38,7 +37,7 @@ FILES = main.c \ testsuites.c \ testrunner.c include files.mk -INCLUDE_DIRECTIVES = -I./${sourcedir} -I./${sourcedir}/default +INCLUDE_DIRECTIVES = -I./${sourcedir} -I./${sourcedir}/default -I./${sourcedir}/test COMPILEROPTIONS = $(INCLUDE_DIRECTIVES) ############ @@ -47,7 +46,7 @@ COMPILEROPTIONS = $(INCLUDE_DIRECTIVES) # ############ -vpath %.c ${sourcedir} ${sourcedir}/default +vpath %.c ${sourcedir} ${sourcedir}/default ${sourcedir}/test OBJFILES = $(FILES:%.c=${builddir}/%.o) @@ -57,7 +56,7 @@ ALLOBJFILES += $(OBJFILES) DEPENDENCIES = $(DEPFILES) -# link object files, create binary for flashing +# link object files, create binary $(BINARY): $(ALLOBJFILES) @echo "... linking" @${CC} $(LINKEROPTIONS) -o ${builddir}/$(BINARY) $(ALLOBJFILES) $(LIBS) @@ -90,8 +89,4 @@ clean: @echo ... removing build files in ${builddir} @rm -f ${builddir}/*.o @rm -f ${builddir}/*.d - @rm -f ${builddir}/*.out - @rm -f ${builddir}/*.hex @rm -f ${builddir}/*.elf - @rm -f ${builddir}/*.map - @rm -f ${builddir}/*_disasm.s diff --git a/src/main.c b/src/test/main.c similarity index 100% rename from src/main.c rename to src/test/main.c diff --git a/src/stypes.h b/src/test/stypes.h similarity index 100% rename from src/stypes.h rename to src/test/stypes.h diff --git a/src/test_check.c b/src/test/test_check.c similarity index 100% rename from src/test_check.c rename to src/test/test_check.c diff --git a/src/test_dev.c b/src/test/test_dev.c similarity index 100% rename from src/test_dev.c rename to src/test/test_dev.c diff --git a/src/test_hydrogen.c b/src/test/test_hydrogen.c similarity index 100% rename from src/test_hydrogen.c rename to src/test/test_hydrogen.c diff --git a/src/test_spiffs.c b/src/test/test_spiffs.c similarity index 100% rename from src/test_spiffs.c rename to src/test/test_spiffs.c diff --git a/src/test_spiffs.h b/src/test/test_spiffs.h similarity index 100% rename from src/test_spiffs.h rename to src/test/test_spiffs.h diff --git a/src/testrunner.c b/src/test/testrunner.c similarity index 100% rename from src/testrunner.c rename to src/test/testrunner.c diff --git a/src/testrunner.h b/src/test/testrunner.h similarity index 100% rename from src/testrunner.h rename to src/test/testrunner.h diff --git a/src/testsuites.c b/src/test/testsuites.c similarity index 100% rename from src/testsuites.c rename to src/test/testsuites.c