Every once and a while, one needs to persistently store settings, information or other data on an embedded device. This poses risks. There are a few (simplified) categories of embedded data storage solutions to look at.
More ‘advanced’ and relatively larger embedded systems…
…use file systems. Many times, we have heard it is a question of partitioning, but somehow we do not think that is all. Let us see: The (second level) boot loader is on one partition, the root file system(s) on other(s), and the data on yet another partition. But there is more… Does the file system run on ‘naked’ inherently unreliable NAND flash and does it have to organize its own (hardware/software)-assisted error correction and wear levelling, or do we put that in the responsible hands of the proprietary File system Translation Layer of the memory vendor? Then, are partitions really separated so that they cannot influence each other erroneously, or is it just a logical organization – which is enforced by a hopefully bug-less firmware or driver? Maybe, we should use NOR flash? But that comes at a cost.
Without pretending to know all the answers beforehand (since it depends on the specific situation and target development), here is some advice:
- do not put root file system (RFS) or executable software on the same partition as the data (unless it is read only).
- if – under normal (i.e. not upgrade) scenarios – one is not supposed to write to the partition, it should be read-only and this should be enforced: only allow writing at the moment data should be changed!
- make sure all driver and file system layers are thoroughly tested.
- do not forget power-cut tests.
- continuous logging (and thus writing) is strongly discouraged.
Simple and low cost embedded systems…
…maybe use eeprom, external or internal flash.
Two sub-categories here.
Software is located on one physical device, and the data on another physical device
e.g. software on internal flash and data on external eeprom connected via spi or i2c. In that case, of course, one keeps internal flash read (and execute) only, and the external storage is made writable when required. This is the safer organization; only the data is (or should be) at risk, but that can be mitigated by logic.
Software and data are on the same physical device
e.g. software on internal flash and data on the same internal flash but at predefined sector positions; there is partitioning on the basis of sector boundaries, since – usually with flash memory – one needs to erase a complete sector before writing pages into it.
Why do people choose sub-option two? Cost, of course: One less component to pay for. We are no different and thus have to organize and implement logic to enforce (reasonably) safe data updates.
A word about device specific information.
Sometimes, one wants to store device specific information such as a serial number. It is recommend to store this on a type of write-once/read-many-times or OTP (one time programmable) persistent memory. If cost prevents the usage of such a specific memory, we have to use what is available. For example, in the case of the availability of flash memory, a particular fixed sector could be used for this essential information. The idea is that the info is set during production, and never altered during the product life cycle.
The “archive control” concept
Conceptually, we have the “Archive” object that steers logic for storing and retrieving data to and from a flash position that coincides with a flash sector (the smallest erase granularity). In order to safely ‘commit’ data changes to flash:
- one needs two versions (and thus two Archives).
- one needs to know whether an Archive is valid (and completely written).
- one needs to know which one is the last and thus most recent version.
The stm32 peripheral library implements the necessary functions and provides the demo examples to read and write internal flash.
A settings object then contains functions and algorithm to choose which Archive is valid and the most recent one. Pseudo code to do just that:
Reading valid data
Open Archive1 for reading, open only succeeds if content is valid Open Archive2 for reading, open only succeeds if content is valid if Archive1 is valid AND Archive2 is valid if Archive1_versionnr > Archive2_versionnr: pick Archive1 else: pick Archive2 else if Archive1 is valid: pick Archive1 else if Archive2 is valid: pick Archive2 else: pick standard factory default
Storing valid data
Pick the next version_number version_number = version_number + 1 if version_number = 0 (overflow): version_number = 1 Pick the Archive_to_write_to if last valid and most recent archive = Archive1: Archive_to_write_to = Archive2 else if last valid and most recent archive = Archive2: Archive_to_write_to = Archive1 else: Archive_to_write_to = Archive1 if Archive write operation succeeded: if version_number = 1: erase other Archive.
When is an archive considered to be valid?
A check-sum or (even better a) CRC algorithm can be used to determine the validity of the Archive. When – during read – the check succeeds, one can assume that the previous data write intention has indeed succeeded. But, the functions or algorithms that use the data still need to determine and evaluate whether the data makes sense… !
Speak Your Mind