Emergency data storage
Overview
The emergency data storage (EMDS) library provides persistent storage functionality designed to prevent the wear and tear of the flash memory. Its intended use is for storing of data undergoing frequent changes during runtime.
Implementation
The CONFIG_EMDS
Kconfig option enables the emergency data storage.
The application must initialize the pre-allocated flash area by using the emds_init()
function.
The CONFIG_EMDS_SECTOR_COUNT
option defines how many sectors should be used to store data.
The allocated storage space must be larger than the combined data stored by the application.
Allocating a larger flash area will demand more resources, but also increase the life expectancy of the flash.
The chosen size should reflect the amount of data stored, the available flash resources, and how the application calls the emds_store()
function.
In general, it should not be necessary to allocate a large flash area, since only a limited set of data should be stored to ensure swift completion of writing the flash on shutdown.
The memory location that is going to be stored must be added on initialization.
All memory areas must be provided through entries containing an ID, data pointer, and data length, using the emds_entry_add()
function and the EMDS_STATIC_ENTRY_DEFINE
macro.
Entries to be stored when the emergency data storage is triggered need their own unique IDs that are not changed after a reboot.
When all entries are added, the emds_load()
function restores the entries into the memory areas from the flash.
After restoring the previous data, the application must run the emds_prepare()
function to prepare the flash area for receiving new entries.
If the remaining empty flash area is smaller than the required data size, the flash area will be automatically erased to increase the available flash area.
The storage is done in deterministic time, so it is possible to know how long it takes to store all registered entries. However, this is chip-dependent, so it is important to measure the time. The Nordic Semiconductor Infocenter contains chip information and datasheet, and timing values can be found under the “Electical specification” for the Non-volatile memory controller. The following Kconfig options can be configured:
When configuring this values consider the time for erase when doing garbage collection in NVS.
If partial erase is not enabled or supported, the time of a complete sector erase has to be included in the CONFIG_EMDS_FLASH_TIME_BASE_OVERHEAD_US
.
When partial erase is enabled and supported by the hardware, include the time it takes for the scheduler to trigger, which is depending on the time defined in CONFIG_SOC_FLASH_NRF_PARTIAL_ERASE_MS
.
When changing the CONFIG_EMDS_FLASH_TIME_BASE_OVERHEAD_US
option, it is important that the worst time is considered.
The application must call the emds_store()
function to store all entries.
This can only be done once, before the emds_prepare()
function must be called again.
When invoked, the emds_store()
function stores all the registered entries.
Invocation of this call should be performed when the application detects loss of power, or when a reboot is triggered.
Note
Before calling the emds_store()
function, the application should try shutting down the application-specific features that consume a lot of power.
Shutting down these features may prolong the time the CPU is alive, and improve the storage time.
For example, if Bluetooth is used, disabling Bluetooth before shutdown will save power, and stopping the MPSL scheduler will shorten the total time required to complete the store operation.
The emds_is_ready()
function can be called to check if EMDS is prepared to store the data.
Once the data storage has completed, a callback is called if provided in emds_init()
.
This callback notifies the application that the data storage has completed, and can be used to reboot the CPU or execute another function that is needed.
After completion of emds_store()
, the emds_is_ready()
function call will return error, since it can no longer guarantee that the data will fit into the flash area.
The above described process is summarized in a message sequence diagram.
Requirements
To prevent frequent writes to flash memory, the EMDS library can write data to flash only when the device is shutting down. EMDS restores the application data to RAM at reboot.
EMDS can store data within a guaranteed time, based on the amount of data being stored. EMDS can be used to store data in memory in situations of critical power shortage, for example before the device battery is depleted. It is important that the hardware has the appropriate functionality to sustain power long enough for the storage to be completed before the power source is fully discharged.
Configuration
To initialize the emergency data storage, complete the following steps:
Enable the
CONFIG_EMDS
Kconfig option.Include the
emds/emds.h
file in yourmain.c
file.Create the callback function
emds_store_cb_t()
that can execute functions after storage has completed. This is optional.Call the
emds_init()
function.Add RAM areas that shall be loaded/stored through
emds_entry_add()
calls.Call
emds_load()
.Call
emds_prepare()
.Create interrupt or other functionality that will call
emds_store()
.
Limitations
The power-fail comparator for the nRF528xx cannot be used with EMDS, as it will prevent the NVMC from performing write operations to flash.
Dependencies
The emergency data storage is dependent on these Kconfig options:
API documentation
include/emds/emds.h
subsys/emds/emds.c
- group emds
Emergency Data Storage API.
Defines
-
EMDS_STATIC_ENTRY_DEFINE(_name, _id, _data, _len)
Define a static entry for emergency data storage items.
This creates a variable name prepended by emds.
- Parameters
_name – The entry name.
_id – Unique ID for the entry. This value and not an overlap with any other value.
_data – Data pointer to be stored at emergency data store.
_len – Length of data to be stored at emergency data store.
Typedefs
-
typedef void (*emds_store_cb_t)(void)
Callback for application commands when storing has been executed.
This can be used to perform other actions or execute busy-waiting until the power supply is discharged. It will be run by emds_store, and called from the same context the emds_store is called from. Interrupts are unlocked before triggering this callback. Therefore, this callback may not be reached before available backup power runs out. If the application needs interrupts to be locked during this callback, application can do so by calling irq_lock before calling emds_store.
Functions
-
int emds_init(emds_store_cb_t cb)
Initialize the emergency data storage.
Initializes the emergency data storage. This needs to be called before adding entries to the emds_dynamic_entry and loading data.
- Parameters
cb – Callback to notify application that the store operation has been executed or NULL if no notification is needed.
- Return values
0 – Success
-ERRNO – errno code if error
-
int emds_entry_add(struct emds_dynamic_entry *entry)
Add entry to be saved/restored when emergency data storage is called.
Adds the entry to the dynamic entry list. When the emds_store function is called, takes the given data pointer and stores the data to the emergency data storage.
Note
EMDS does not make a local copy of the dynamic entry structure.
- Parameters
entry – Entry to add to list and load data into.
- Return values
0 – Success
-ERRNO – errno code if error
-
int emds_store(void)
Start the emergency data storage process.
Triggers the process of storing all data registered to be stored. All data registered either through emds_entry_add function or the EMDS_STATIC_ENTRY_DEFINE macro is stored. It locks all interrupts until the write is finished. Once the data storage is completed, the data should not be changed, and the device should be halted. The device must not be allowed to reboot when operating on a backup supply, since reboot will trigger data load from the EMDS storage and clear the storage area. The reboot should only be allowed when the main power supply is available.
This is a time-critical operation and will be processed as fast as possible. To achieve better time predictability, this function must be called from an interrupt context with the highest priority.
When writing to flash, it bypasses the flash driver. Therefore, when running with MPSL, make sure to uninitialize the MPSL before this function is called. Otherwise, an assertion may be triggered by the exit of the function.
- Return values
0 – Success
-ERRNO – errno code if error
-
int emds_load(void)
Load all static data from the emergency data storage.
This function needs to be called after the static entries are added, as they are used to select the data to be loaded. The function also needs to be called before the emds_prepare function which will delete all the previously stored data.
- Return values
0 – Success
-ERRNO – errno code if error
-
int emds_clear(void)
Clear flash area from the emergency data storage.
This function clears the flash area for all previously stored data.
- Return values
0 – Success
-ERRNO – errno code if error
-
int emds_prepare(void)
Prepare flash area for the next emergency data storage.
This function prepares the flash area for the next emergency data storage. It deletes the current entries if there is enough space for the next emergency data storage, and clears the flash storage if there is not enough space for the next storage. This has to be done after all the dynamic entries are added. After this has been called emergency data storage should be ready to store.
- Return values
0 – Success
-ERRNO – errno code if error
-
uint32_t emds_store_time_get(void)
Estimate the time needed to store the registered data.
Estimate how much time it takes to store all dynamic and static data registered in the entries. This value is dependent on the chip used, and should be checked against the chip datasheet.
- Returns
Time needed to store all data (in microseconds).
-
uint32_t emds_store_size_get(void)
Calculate the size needed to store the registered data.
Calculates the size it takes to store all dynamic and static data registered in the entries.
- Returns
Byte size that is needed to store all data.
-
bool emds_is_ready(void)
Check if the store operation can be run.
When the data store operation has completed, the store operation is no longer ready and this function will then return false.
- Returns
True if the store operation can be started, otherwise false.
-
struct emds_entry
- #include <emds.h>
Information about entries used to store data in the emergency data storage.
-
struct emds_dynamic_entry
- #include <emds.h>
Entries with a node element, used for dynamic entries. These are registered using a call to emds_entry_add.
-
EMDS_STATIC_ENTRY_DEFINE(_name, _id, _data, _len)