Table of Contents
Persistent storage data is saved to the on-chip/on-board flash, if available. On the Pico, this is the (usually 2 MiB) XIP/QSPI flash.
Storage structure
A header resides in the last 256 bytes of flash, with the following structure:
3 7 b f
--+----------------------------------------------------+
0 | f0 9f 8f b3 ef b8 8f e2 80 8d e2 9a a7 ef b8 8f |
10 | fwver mm nm <reserved (0xff) > <tbl djb2> |
20 | <mode data table ...> |
30 | |
.. | |
e0 | <reserved (0xff) ...> |
--+----------------------------------------------------+
Reserved space is filled with 0xff-bytes.
The first 16 bytes are filled with a fixed 'magic' value. Then the following values appear:
fwver
: firmware/storage structure version: 2 bytes, currently 0x0010mm
: current mode: 1 bytenm
: number of modes: 1 byte, equal to the number of elements in the "mode data table" arraytbl djb2
: DJB2 hash of the entire mode data table (including unused ones, cf.nm
)
After this data, the "mode data table" follows, consisting of 16 entries of 12 bytes each, specifying where the data of each mode resides in flash. It has the following structure:
struct mode_data {
uint16_t version;
uint16_t datasize;
uint28_t flashoffset;
uint4_t modeid;
uint32_t data_djb2;
};
The flashoffset
is relative to the beginning of flash. version
is the version of the mode. modeid
is encoded by extending flashoffset
to 32 bits, and encodign modeid
in the most-significant nybble. data_djb2
is the DJB2 hash of the on-flash data.
Mode data blocks are allocated from the start of the last flash page (which is implementation defined), filling it up, and then continuing to the block before. First, smaller mode data blocks are allocated, ending with the larger ones.
Storage API
Each mode has a storage
field, which is a structure describing the following items (one constant and three callback functions, used by the storage subsystem):
- "storage class": maximum size of the mode's data block
- "get size" function: gets the current size of the data as it is now
- "get data": copies the mode data into a block
- "is dirty": return true to indicate the data has been changed and should be saved on the next 'flush'
Additionally, modes can use the storage_mode_get_info(int mode) and
storage_mode_read(int mode, void* dest, size_t offset, size_t maxlen)functions to query and read their saved data.
storage_mode_get_info` returns a struct containing the size of the saved data, as well as the version of the mode that saved it.