2 Persistent storage
sys64738 edited this page 2021-08-26 19:00:58 +00:00

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 0x0010
  • mm: current mode: 1 byte
  • nm: number of modes: 1 byte, equal to the number of elements in the "mode data table" array
  • tbl 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.