Browse Source

restructured files

pull/7/head
Peter Andersson 11 years ago
parent
commit
840e5aab12
  1. 2
      LICENSE
  2. 277
      README
  3. 232
      docs/INTEGRATION
  4. 30
      docs/TECH_SPEC
  5. 11
      makefile
  6. 0
      src/test/main.c
  7. 0
      src/test/stypes.h
  8. 0
      src/test/test_check.c
  9. 0
      src/test/test_dev.c
  10. 0
      src/test/test_hydrogen.c
  11. 0
      src/test/test_spiffs.c
  12. 0
      src/test/test_spiffs.h
  13. 0
      src/test/testrunner.c
  14. 0
      src/test/testrunner.h
  15. 0
      src/test/testsuites.c

2
LICENSE

@ -1,6 +1,6 @@
The MIT License (MIT) The MIT License (MIT)
Copyright (c) 2013 Peter Andersson Copyright (c) 2013 Peter Andersson (pelleplutt1976<at>gmail.com)
Permission is hereby granted, free of charge, to any person obtaining a copy of 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 this software and associated documentation files (the "Software"), to deal in

277
README

@ -1,11 +1,11 @@
SPIFFS (SPI Flash File System) SPIFFS (SPI Flash File System)
V0.1 V0.1
Copyright (c) 2013 Peter Andersson Copyright (c) 2013 Peter Andersson (pelleplutt1976<at>gmail.com)
For legal stuff, see LICENCE in this directory. Basically, you may do whatever 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 you want with the source. Use, modify, sell, print it out, roll it and smoke it
long as I won't be held responsible. - as long as I won't be held responsible.
Love to hear feedback though! 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 - 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 - Multiple spiffs configurations can be run on same target - and even on same
SPI flash device SPI flash device
- It implements static wear leveling
- It has built in file system consistency checks - It has built in file system consistency checks
What spiffs does not: 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 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 - It is not a realtime stack. One write operation might take much longer than
another. another.
- Poor scalability. Spiffs is intended for small memory devices - the normal - Poor scalability. Spiffs is intended for small memory devices - the normal
sizes for SPI flashes. sizes for SPI flashes.
- Presently, it does not detect or handle bad blocks.
* 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 For integration, see the docs/INTEGRATION file.
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 <--
* 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 use and design, see the docs/TECH_SPEC file.

232
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 <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 <--

30
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

11
makefile

@ -17,7 +17,6 @@ builddir = build
############# #############
CC = gcc $(COMPILEROPTIONS) CC = gcc $(COMPILEROPTIONS)
AS = gcc $(ASSEMBLEROPTIONS)
LD = ld LD = ld
GDB = gdb GDB = gdb
OBJCOPY = objcopy OBJCOPY = objcopy
@ -38,7 +37,7 @@ FILES = main.c \
testsuites.c \ testsuites.c \
testrunner.c testrunner.c
include files.mk include files.mk
INCLUDE_DIRECTIVES = -I./${sourcedir} -I./${sourcedir}/default INCLUDE_DIRECTIVES = -I./${sourcedir} -I./${sourcedir}/default -I./${sourcedir}/test
COMPILEROPTIONS = $(INCLUDE_DIRECTIVES) 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) OBJFILES = $(FILES:%.c=${builddir}/%.o)
@ -57,7 +56,7 @@ ALLOBJFILES += $(OBJFILES)
DEPENDENCIES = $(DEPFILES) DEPENDENCIES = $(DEPFILES)
# link object files, create binary for flashing # link object files, create binary
$(BINARY): $(ALLOBJFILES) $(BINARY): $(ALLOBJFILES)
@echo "... linking" @echo "... linking"
@${CC} $(LINKEROPTIONS) -o ${builddir}/$(BINARY) $(ALLOBJFILES) $(LIBS) @${CC} $(LINKEROPTIONS) -o ${builddir}/$(BINARY) $(ALLOBJFILES) $(LIBS)
@ -90,8 +89,4 @@ clean:
@echo ... removing build files in ${builddir} @echo ... removing build files in ${builddir}
@rm -f ${builddir}/*.o @rm -f ${builddir}/*.o
@rm -f ${builddir}/*.d @rm -f ${builddir}/*.d
@rm -f ${builddir}/*.out
@rm -f ${builddir}/*.hex
@rm -f ${builddir}/*.elf @rm -f ${builddir}/*.elf
@rm -f ${builddir}/*.map
@rm -f ${builddir}/*_disasm.s

0
src/main.c → src/test/main.c

0
src/stypes.h → src/test/stypes.h

0
src/test_check.c → src/test/test_check.c

0
src/test_dev.c → src/test/test_dev.c

0
src/test_hydrogen.c → src/test/test_hydrogen.c

0
src/test_spiffs.c → src/test/test_spiffs.c

0
src/test_spiffs.h → src/test/test_spiffs.h

0
src/testrunner.c → src/test/testrunner.c

0
src/testrunner.h → src/test/testrunner.h

0
src/testsuites.c → src/test/testsuites.c

Loading…
Cancel
Save