Here’s a practical, “from blank board to production” blueprint for data storage & management on embedded systems. Skim the bold headings first; dive where you need details.
1) Start with a data brief (5 questions)
Write this down before you pick any tech:
-
What do you store? (configs, logs, sensor data, ML params, firmware)
-
How much total and per write? (bytes…MB)
-
How often do you write? (Hz / per day)
-
How long must it survive power-offs & heat? (retention, temp)
-
How safe must it be? (integrity, security, regulatory)
2) Pick the memory technology
Need | Good fits | Notes |
---|---|---|
Small configs (≤16 KB), rare writes | MCU internal Flash (EEPROM emulation), EEPROM | Use wear-leveled driver; don’t rewrite same page. |
Frequent tiny writes | FRAM / MRAM | “No-erase” and very high endurance; simple drivers. |
Code XIP + small data | QSPI/OSPI NOR Flash | Fast reads; erase in 4–64 KB sectors; 50–100k P/E typical. |
Large data/logs (MB-GB) | NAND, eMMC, SD | Needs FTL/wear leveling; expect bad blocks (raw NAND). |
High bandwidth + OS | eMMC/UFS | Built-in controller handles FTL/ECC; simpler than raw NAND. |
3) Decide the storage topology
-
Raw flash (NOR/NAND) → FTL/wear-level → FS or KV store
-
Managed media (eMMC/SD) → FS directly (FAT/ext/F2FS)
-
NVRAM (FRAM/MRAM) → raw records / KV (no FTL needed)
Use a partition map (even on MCUs):
-
bootloader
|fw_A
|fw_B
(OTA) |factory_cal
|kv_config
|logs
|user_data
4) Filesystem vs Key-Value vs Raw
Key-Value (KV) store (recommended for configs)
-
Pros: tiny footprint, atomic, easy wear leveling.
-
Examples: littlefs (has directories + KV style), ESP-IDF NVS, Zephyr settings, STM32 EEPROM emulation app notes.
Filesystems
-
littlefs (NOR): copy-on-write, power-fail safe, great for ≤16 MB.
-
SPIFFS (NOR): lightweight; no directories; aging project.
-
FAT/exFAT (SD/eMMC): ubiquitous & PC-friendly; add journaling layer for power-fail safety.
-
UBIFS/YAFFS2 (raw NAND, Linux): handle bad blocks & wear leveling.
-
F2FS/ext4 (eMMC/UFS, Linux): log-structured/ext with journaling.
Raw append-only logs / ring buffers
-
Best for telemetry and black-box logs; you manage indexing, CRC, rollover.
5) Wear leveling & endurance
-
Never update-in-place on flash sectors; use copy-on-write or log-structured writes.
-
Rotate erase blocks (dynamic leveling). Keep hot data (counters) in FRAM if possible.
-
Budget writes against endurance (e.g., 50–100k P/E NOR; eMMC/NAND varies by grade).
Tip: move high-churn data (e.g., uptime counters) to FRAM or to a journal page that rotates.
6) Data integrity: make corruption boring
-
Per-record CRC/CRC32 (or AEAD tag if encrypted).
-
Atomic commit pattern: write header → data → CRC → flip commit bit/sequence.
-
Prefer journaling / copy-on-write FS (littlefs, UBIFS, ext4 journaled).
-
Add ECC where available (BCH/LDPC in NAND, eMMC controller built-in).
7) Power-fail safety
-
Keep writes small and bounded; avoid multi-page “transactions”.
-
Use double buffering or A/B records with monotonically increasing version/counter.
-
Hardware assists: brown-out reset (BOR), PMIC POK, optional supercap to flush last block.
-
Test with yank-the-plug campaigns at random write times.
8) Concurrency & RTOS integration
-
Use a single storage task with a message queue (producer/consumer); it serializes access.
-
Gate the block device with a mutex; keep critical sections short.
-
Use DMA for SD/eMMC; align buffers to cache lines; flush/invalidate caches on MMU systems.
9) Performance & footprint
-
Batch tiny writes into a write buffer (e.g., 512–2048 bytes).
-
Choose page/sector-aligned structures; avoid read-modify-erase loops.
-
Keep FS cache small but nonzero; e.g., littlefs block cache = 1–4 blocks.
10) Security (when data matters)
-
Secure boot for firmware partitions; rollback protection (monotonic counter).
-
Encryption at rest for sensitive data: AES-GCM/XTS, keys in HUK/PUF/TPM/TrustZone.
-
Per-record nonce + auth tag; consider key rotation and sealed storage APIs (TEE).
11) OTA & versioned data
-
Use A/B firmware slots + small boot control KV with a trial-boot flag.
-
Store schema version with each dataset; include migrations on upgrade.
-
Keep factory calibration in a protected, rarely-erased area.
12) Data formats that work on MCUs
-
TLV (Type-Length-Value): tiny and robust.
-
CBOR (small, self-describing) or protobuf-nano/flatbuffers for bigger MCUs.
-
Always pack a magic, version, length, CRC, timestamp.
Minimal, robust patterns
A) Power-safe KV record (copy-on-write on NOR/FRAM)
Recovery: scan from the end; last record with valid CRC and commit mark wins.
B) Ring logger (fixed-size records)
C) LittleFS on external QSPI NOR (bare-metal sketch)
-
Partition NOR into
lfs
area (multiple erase blocks). -
Mount with small caches and wear-level config; store configs in a single JSON/TLV file; logs in
/log/
rolling files.
Platform hints (common stacks)
-
STM32 (bare-metal/FreeRTOS): FATFS + wear-level for SD/eMMC; ST app notes for EEPROM emulation in Flash; littlefs via CubeMX middleware or open-source ports.
-
ESP32: NVS (KV store) on SPI flash; FATFS with wear-leveling driver; OTA with A/B provided by ESP-IDF.
-
Zephyr RTOS:
settings
(KV on NVS),littlefs
/fcb
(flash circular buffer),mcuboot
for secure A/B OTA. -
Linux SBC (e.g., i.MX, Zynq): ext4/F2FS on eMMC; UBIFS on raw NAND; use
journald
or your own ring logger for telemetry.
Validation & test checklist
-
Endurance plan: compute estimated daily writes vs device P/E and confirm margin.
-
Power-pull tests: hundreds of random pulls during active writes; zero-brick criteria.
-
Fault injection: flip bits in staged images, corrupt CRCs, simulate bad blocks.
-
Aging/thermal: soak tests at temperature corners; verify retention & boot times.
-
Security tests: verify rollback protection, key sealing, and tamper responses.
-
Field telemetry: include counters (erase cycles, mount failures, recovery count).
Quick decision guide
-
Just configs, rare writes: NVS/KV on internal flash (wear-leveled) → done.
-
Configs + small logs, power-safe: littlefs on QSPI NOR + ring logger.
-
Lots of data or high write rate: eMMC/SD with ext4/F2FS (Linux) or FAT + journaling layer (RTOS) + periodic checkpoints.
-
High-churn tiny writes: add FRAM/MRAM for hot variables; keep flash for bulk.