blob: e0d94c62ae585be2322baf35513c5b06918b78a6 [file] [log] [blame]
.. _module-pw_persistent_ram:
=================
pw_persistent_ram
=================
The ``pw_persistent_ram`` module contains utilities and containers for using
persistent RAM. By persistent RAM we are referring to memory which is not
initialized across reboots by the hardware nor bootloader(s). This memory may
decay or bit rot between reboots including brownouts, ergo integrity checking is
highly recommended.
.. Note::
This is something that not all architectures and applications built on them
support and requires hardware in the loop testing to verify it works as
intended.
.. Warning::
Do not treat the current containers provided in this module as stable storage
primitives. We are still evaluating lighterweight checksums from a code size
point of view. In other words, future updates to this module may result in a
loss of persistent data across software updates.
------------------------
Persistent RAM Placement
------------------------
Persistent RAM is typically provided through specially carved out linker script
sections and/or memory ranges which are located in such a way that any
bootloaders and the application boot code do not clobber it.
1. If persistent linker sections are provided, we recommend using our section
placement macro. For example imagine the persistent section name is called
`.noinit`, then you could instantiate an object as such:
.. code-block:: cpp
#include "pw_persistent_ram/persistent.h"
#include "pw_preprocessor/compiler.h"
using pw::persistent_ram::Persistent;
PW_KEEP_IN_SECTION(".noinit") Persistent<bool> persistent_bool;
2. If persistent memory ranges are provided, we recommend using a struct to wrap
the different persisted objects. This then could be checked to fit in the
provided memory range size, for example by asserting against variables
provided through a linker script.
.. code-block:: cpp
#include "pw_assert/assert.h"
#include "pw_persistent_ram/persistent.h"
// Provided for example through a linker script.
extern "C" uint8_t __noinit_begin;
extern "C" uint8_t __noinit_end;
struct PersistentData {
Persistent<bool> persistent_bool;
};
PersistentData& persistent_data =
*reinterpret_cast<NoinitData*>(&__noinit_begin);
void CheckPersistentDataSize() {
PW_DCHECK_UINT_LE(sizeof(PersistentData),
__noinit_end - __noinit_begin,
"PersistentData overflowed the noinit memory range");
}
-----------------------------------
Persistent RAM Lifecycle Management
-----------------------------------
In order for persistent RAM containers to be as useful as possible, any
invalidation of persistent RAM and the containers therein should be executed
before the global static C++ constructors, but after the BSS and data sections
are initialized in RAM.
The preferred way to clear Persistent RAM is to simply zero entire persistent
RAM sections and/or memory regions. Pigweed's persistents containers have picked
integrity checks which work with zerod memory, meaning they do not hold a value
after zeroing. Alternatively containers can be individually cleared.
The boot sequence itself is tightly coupled to the number of persistent sections
and/or memory regions which exist in the final image, ergo this is something
which Pigweed cannot provide to the user directly. However, we do recommend
following some guidelines:
1. Do not instantiate regular types/objects in persistent RAM, ensure integrity
checking is always used! This is a major risk with this technique and can
lead to unexpected memory corruption.
2. Always instantiate persistent containers outside of the objects which depend
on them and use dependency injection. This permits unit testing and avoids
placement accidents of persistents and/or their users.
3. Always erase persistent RAM data after software updates unless the
persistent storage containers are explicitly stored at fixed address and
with a fixed layout. This prevents use of swapped objects or their members
where the same integrity checks are used.
4. Consider zeroing persistent RAM to recover from crashes which may be induced
by persistent RAM usage, for example by checking the reboot/crash reason.
5. Consider zeroing persistent RAM on cold boots to always start from a
consistent state if persistence is only desired across warm reboots. This can
create determinism from cold boots when using for example DRAM.
6. Consider an explicit persistent clear request which can be set before a warm
reboot as a signal to zero all persistent RAM on the next boot to emulate
persistent memory loss in a threadsafe manner.
---------------------------------
pw::persistent_ram::Persistent<T>
---------------------------------
The Persistent is a simple container for holding its templated value ``T`` with
CRC16 integrity checking. Note that a Persistent will be lost if a write/set
operation is interrupted or otherwise not completed, as it is not double
buffered.
The default constructor does nothing, meaning it will result in either invalid
state initially or a valid persisted value from a previous session.
The destructor does nothing, ergo it is okay if it is not executed during
shutdown.
Example
-------
A common use case of persistent data is to track boot counts, or effectively
how often the device has rebooted. This can be useful for monitoring how many
times the device rebooted and/or crashed. This can be easily accomplished using
the Persistent container.
.. code-block:: cpp
#include "pw_persistent_ram/persistent.h"
#include "pw_preprocessor/compiler.h"
using pw::persistent_ram::Persistent;
class BootCount {
public:
explicit BootCount(Persistent<uint16_t>& persistent_boot_count)
: persistent_(persistent_boot_count) {
if (!persistent_.has_value()) {
persistent_ = 0;
} else {
persistent_ = persistent_.value() + 1;
}
boot_count_ = persistent_.value();
}
uint16_t GetBootCount() { return boot_count_; }
private:
Persistent<uint16_t>& persistent_;
uint16_t boot_count_;
};
PW_KEEP_IN_SECTION(".noinit") Persistent<uint16_t> persistent_boot_count;
BootCount boot_count(persistent_boot_count);
int main() {
const uint16_t boot_count = boot_count.GetBootCount();
// ... rest of main
}
Compatibility
-------------
* C++17
Dependencies
------------
* ``pw_checksum``