13. EEPROM Emulation Component
13.1. Overview
The EEPROM emulation component (eeprom_emulation) implements power-loss protected data storage similar to EEPROM on NOR Flash. The component ensures data safety and consistency under any power-loss scenario through a dual-area design and state machine mechanism.
13.2. Component Architecture
+--------------------------------------------------+
| Application Layer |
| - Data operations via e2p_write/read/delete |
+--------------------------------------------------+
|
| API Calls
|
+--------------------------------------------------+
| EEPROM Emulation Core Layer |
| - eeprom_emulation.h / eeprom_emulation.c |
| - State management, index table, CRC verification |
+--------------------------------------------------+
|
| Function Pointers
|
+------------------------+-------------------------+
| | |
+-----------------------+ | +----------------------+
| NOR Flash Port | | | Other Flash Port |
| (port/hpm_nor_flash) | | | (to be extended) |
+-----------------------+ | +----------------------+
| | |
+--------------------------------------------------+
| Flash Driver Layer |
| - ROM API / XPI Driver |
+--------------------------------------------------+
|
+--------------------------------------------------+
| Hardware Layer |
| XPI External Flash |
+--------------------------------------------------+
13.3. Key Features
Power-loss Protection
Dual half-area design ensures safe recovery from any power-loss scenario
State machine implemented via 1→0 bit transitions (no erase needed)
Automatic detection and recovery of incomplete flush operations
Data Integrity
Each entry includes CRC32 checksum
Automatic verification on read
Corrupted data returns error code
Space Management
Automatic defragmentation: triggers when space is insufficient
Delete marking: e2p_delete() marks entries as invalid
Obsolete data reclamation: old data for same block_id is cleaned during flush
Portability
Abstracts Flash drivers via function pointers
Supports different Flash media (NOR Flash, SPI Flash, etc.)
Port layer independent, easy to extend
Flexible Configuration
Configurable erase block size (erase_size)
Configurable sector count (sector_cnt)
Configurable version number (version changes trigger automatic erase)
13.4. Flash Area Layout
EEPROM emulation uses a continuous NOR Flash region, divided into two half-areas (A and B):
+--------------------------------------------------+
| Half Area A |
| +------------------------------------------+ |
| | Data Region (growing ↑) | |
| | - User data | |
| | - Data blocks: block_id + data | |
| +------------------------------------------+ |
| | Info Region (growing ↓) | |
| | - Info descriptor array | |
| | - Each Info: 16 bytes | |
| | - Tail: Header (12 bytes) | |
| +------------------------------------------+ |
+--------------------------------------------------+
| Half Area B |
| (Same layout as above) |
+--------------------------------------------------+
Data Region: Stores actual user data, grows from low to high address within each half-area
Info Region: Stores data block descriptors, grows from high to low address within each half-area
Area Header: 12 bytes at tail of each half-area, contains: version, magic, state
Info Descriptor Structure:
typedef struct {
uint32_t block_id; /* Block ID (user-defined) */
uint32_t data_addr; /* Data address in Data Region */
uint16_t length; /* Data length */
uint16_t valid_state; /* Validity marker */
uint32_t crc; /* CRC32 checksum */
} e2p_block_t;
13.5. State Machine and Power-Loss Protection
The component uses a 4-bit state machine, implemented via NOR Flash 1→0 bit transitions (no erase needed):
State Value |
State Name |
Description |
|---|---|---|
0 |
valid |
Normal operating state |
8 |
finish |
Data copy complete, ready to erase old area |
12 |
write |
Writing data to new area |
14 |
start |
Defragmentation started |
15 |
invalid |
Uninitialized/erased state |
State Transition Flow:
invalid(15) → start(14) → write(12) → finish(8) → valid(0)
Power-loss Recovery Strategy:
Power-loss Timing |
Area A State |
Area B State |
Recovery Action |
|---|---|---|---|
First boot |
15 |
15 |
Select Area A, mark as valid |
After flush, old erased |
0 (valid) |
8 (finish) |
Erase Area A, switch to B, mark B as valid |
During flush write |
0 (valid) |
12 (write) |
Erase B, re-flush |
Before flush start |
0 (valid) |
14 (start) |
Erase B, re-flush |
13.6. Primary APIs
13.6.1. Initialization and Configuration
hpm_stat_t e2p_config(e2p_t *e2p);
Function: Initialize EEPROM emulation, verify flash headers, recover power-loss state, rebuild index table
Parameters: - e2p: Pointer to context structure, must have flash operation function pointers pre-configured
Returns: E2P_STATUS_OK on success, error code on failure
13.6.2. Write Data
hpm_stat_t e2p_write(uint32_t block_id, uint16_t length, const uint8_t *data);
Function: Write data, automatically updates old data for same block_id
Parameters: - block_id: User-defined identifier (generated via e2p_generate_id()) - length: Data length in bytes - data: Pointer to data buffer
Returns: E2P_STATUS_OK on success, error code on failure
13.6.3. Read Data
hpm_stat_t e2p_read(uint32_t block_id, uint16_t length, uint8_t *data);
Function: Read data, automatically verifies CRC32
Parameters: - block_id: User-defined identifier - length: Buffer size (can be less than stored length) - data: Pointer to output buffer
Returns: E2P_STATUS_OK on success, E2P_ERROR_NOT_FOUND if not found, E2P_ERROR_CRC if verification fails
13.6.4. Delete Data
hpm_stat_t e2p_delete(uint32_t block_id);
Function: Delete data for specified block_id (marks as invalid)
Parameters: - block_id: User-defined identifier
Returns: E2P_STATUS_OK on success, E2P_ERROR_NOT_FOUND if not found
13.6.5. Defragment Data
hpm_stat_t e2p_flush(uint8_t flag);
Function: Defragment data, reclaim space
Parameters: - flag: E2P_FLUSH_TRY (flush only when space insufficient) or E2P_FLUSH_FORCE (force flush)
Returns: E2P_STATUS_OK on success, error code on failure
13.6.6. Generate ID
uint32_t e2p_generate_id(const char *name);
Function: Generate 32-bit block_id from string
Parameters: - name: String pointer (first 4 characters are packed)
Returns: 32-bit block_id
13.6.7. Clear Data
void e2p_clear(void);
Function: Erase entire storage area (both half-areas)
13.6.8. Show Status Info
void e2p_show_info(void);
Function: Print current EEPROM emulation status (active area, remaining size, etc.)
Parameters: None
Returns: None
13.7. Configuration Parameters
Core configuration structure:
typedef struct {
uint32_t start_addr;
uint32_t sector_cnt;
uint32_t erase_size;
uint32_t version;
hpm_stat_t (*flash_read)(uint8_t *buf, uint32_t addr, uint32_t size);
hpm_stat_t (*flash_write)(const uint8_t *buf, uint32_t addr, uint32_t size);
hpm_stat_t (*flash_erase)(uint32_t start_addr, uint32_t size);
} e2p_config_t;
Field Name |
Description |
|---|---|
start_addr |
Flash start address |
sector_cnt |
Sector count (must be even) |
erase_size |
Erase block size in bytes |
version |
Version number (triggers automatic erase when changed) |
flash_read/write/erase |
Flash operation function pointers |
User configuration (user_config.h):
Configuration |
Description |
|---|---|
E2P_DEBUG_LEVEL |
Debug level (0-4) |
E2P_MAX_VAR_CNT |
Maximum variable count (default 100) |
E2P_FLUSH_BUF_SIZE |
Defragmentation buffer size (default 512) |
13.8. Quick Start
Include the header and NOR Flash port:
#include "eeprom_emulation.h" #include "hpm_nor_flash.h"
Configure NOR Flash and EEPROM emulation context (see Usage Example below).
Call nor_flash_init() then e2p_config().
Use e2p_generate_id(“NAME”) for block IDs, then e2p_write() / e2p_read().
13.9. Usage Example
Basic usage example:
#include "eeprom_emulation.h"
#include "hpm_nor_flash.h"
static nor_flash_config_t g_nor_cfg;
static e2p_t g_e2p_ctx;
/* Flash operation wrappers */
static hpm_stat_t my_read(uint8_t *buf, uint32_t addr, uint32_t size) {
return nor_flash_read(&g_nor_cfg, buf, addr, size);
}
static hpm_stat_t my_write(const uint8_t *buf, uint32_t addr, uint32_t size) {
return nor_flash_write(&g_nor_cfg, buf, addr, size);
}
static hpm_stat_t my_erase(uint32_t start_addr, uint32_t size) {
return nor_flash_erase(&g_nor_cfg, start_addr, size);
}
int main(void) {
/* Configure NOR Flash port */
g_nor_cfg.xpi_base = BOARD_APP_XPI_NOR_XPI_BASE;
g_nor_cfg.base_addr = BOARD_FLASH_BASE_ADDRESS;
g_nor_cfg.opt_header = BOARD_APP_XPI_NOR_CFG_OPT_HDR;
g_nor_cfg.opt0 = BOARD_APP_XPI_NOR_CFG_OPT_OPT0;
g_nor_cfg.opt1 = BOARD_APP_XPI_NOR_CFG_OPT_OPT1;
/* Configure EEPROM emulation */
g_e2p_ctx.config.start_addr = 0x80080000;
g_e2p_ctx.config.erase_size = 4096;
g_e2p_ctx.config.sector_cnt = 128;
g_e2p_ctx.config.version = 0x4553;
g_e2p_ctx.config.flash_read = my_read;
g_e2p_ctx.config.flash_write = my_write;
g_e2p_ctx.config.flash_erase = my_erase;
/* Initialize */
nor_flash_init(&g_nor_cfg);
if (e2p_config(&g_e2p_ctx) != E2P_STATUS_OK) {
printf("Init failed\n");
return -1;
}
/* Write data */
uint32_t var_id = e2p_generate_id("TEST");
const char *data = "hello,world";
e2p_write(var_id, strlen(data), (uint8_t *)data);
/* Read data */
uint8_t buf[32];
if (e2p_read(var_id, sizeof(buf), buf) == E2P_STATUS_OK) {
printf("Read: %s\n", buf);
}
return 0;
}
13.10. Extending Flash Port
To support other Flash media (e.g., SPI Flash, internal Data Flash), add driver files under port/:
/* port/my_flash.h */
typedef struct {
/* Port-specific configuration */
} my_flash_config_t;
hpm_stat_t my_flash_init(my_flash_config_t *cfg);
hpm_stat_t my_flash_read(my_flash_config_t *cfg,
uint8_t *buf, uint32_t addr, uint32_t size);
hpm_stat_t my_flash_write(my_flash_config_t *cfg,
const uint8_t *buf, uint32_t addr, uint32_t size);
hpm_stat_t my_flash_erase(my_flash_config_t *cfg,
uint32_t start_addr, uint32_t size);
Usage is the same as NOR Flash port: register the function pointers in e2p_config_t.
13.11. Error Codes
Error Code |
Value |
Description |
|---|---|---|
E2P_STATUS_OK |
0 |
Success |
E2P_ERROR |
1 |
Generic error |
E2P_ERROR_NO_MEM |
2 |
Insufficient space |
E2P_ERROR_INIT_ERR |
3 |
Initialization error |
E2P_ERROR_BAD_ID |
4 |
Invalid block_id |
E2P_ERROR_BAD_ADDR |
5 |
Invalid address |
E2P_ERROR_OVERFLOW |
6 |
Index table overflow |
E2P_ERROR_CRC |
7 |
CRC verification failed |
E2P_ERROR_NOT_FOUND |
8 |
Not found |
13.12. Best Practices
Block size: Keep single variable length ≤ E2P_FLUSH_BUF_SIZE (default 512 bytes); split large data into multiple variables.
Flush: Avoid calling e2p_flush(E2P_FLUSH_FORCE) in time-critical paths; flush can be slow due to erase.
Critical section: Keep code inside E2P_CRITICAL_ENTER / E2P_CRITICAL_EXIT short; avoid long operations.
Version: Changing version in config triggers full erase and re-init; use when layout or format changes.
13.13. Troubleshooting
Init failed: Check start_addr, erase_size, sector_cnt (must be even), and that flash read/write/erase pointers are set.
E2P_ERROR_NO_MEM: Run e2p_flush(E2P_FLUSH_FORCE) to reclaim space, or reduce variable count/size.
E2P_ERROR_CRC: Stored data may be corrupted; re-write the variable or check flash hardware.
E2P_ERROR_NOT_FOUND: Ensure the block was written and block_id matches (e.g. from e2p_generate_id()).
13.14. Notes
sector_cnt must be even
Component requires two half-areas for power-loss protection.
Single write size limit
Single variable length should not exceed E2P_FLUSH_BUF_SIZE (default 512 bytes). Large data should be split into multiple variables.
Version number changes
Component automatically erases and reinitializes when version field changes.
Critical section protection
E2P_CRITICAL_ENTER / E2P_CRITICAL_EXIT macros should be as short as possible. Avoid time-consuming operations within critical sections.
Flash erase time
Erase operations typically take long time. Avoid calling flush in time-critical code paths.