The Emulated EEPROM middleware emulates an EEPROM device in the device's non-volatile memory. The middleware operates on top of the block-storage library that abstracts the underlying memory architecture by specifying how to perform read, write, and erase operations, and provides auxiliary functions to retrieve erase and program sizes and to verify that an address is within the memory range.
Use the Emulated EEPROM to store non-volatile data on a target device, with optional wear leveling and the ability to restore corrupted data from a redundant copy.
Use the Emulated EEPROM middleware when your application needs to:
Do not use this middleware when the application requires large-scale file storage or a filesystem — consider a dedicated flash file system instead, such as emFile or littlefs, both available as ModusToolbox middleware.
The steps below describe the most common setup: Em_EEPROM data placed in the application flash with wear leveling enabled. For other storage locations (auxiliary flash, fixed address, Work Flash on XMC7xxx/XMC5xxx/T2G-B-H), refer to the API Reference Guide or device-specific documentation.
Step 1 - Open or create an application. Open an existing ModusToolbox application or create a new one into which you want to add Em_EEPROM functionality.
Step 2 - Add the Em_EEPROM middleware to the project. In the ModusToolbox IDE, select the application in Project Explorer, then open Project > ModusToolbox Library Manager, check Emulated EEPROM, and click OK. Alternatively, add emeeprom as a dependency in your deps/ folder.
Step 3 - Include the Em_EEPROM header.
Step 4 - Define the Em_EEPROM configuration macros.
| Macro | Values | Effect |
|---|---|---|
| DATA_SIZE | any (bytes) | User-visible EEPROM size; here one flash row (minimum granularity) |
| SIMPLE_MODE | 0 / 1 | 0 stores checksums, headers, write counters; 1 writes raw data with no overhead |
| WEAR_LEVELING | 1–10 | Multiplies flash endurance by distributing writes; 1 = no wear leveling |
| REDUNDANT_COPY | 0 / 1 | 1 maintains a backup copy with checksum validation for automatic data recovery |
| BLOCKING_WRITE | 0 / 1 | 1 busy-waits with interrupts disabled; 0 polls for completion with interrupts enabled |
Note: When BLOCKING_WRITE is 0, interrupts must remain enabled for the duration of Cy_Em_EEPROM_Write() and Cy_Em_EEPROM_Erase() — disabling them will stall the operation indefinitely.
Refer to the cy_stc_eeprom_config2_t structure for details of all configuration options.
Step 5 - Declare the Em_EEPROM storage array in NVM.
CY_EM_EEPROM_GET_PHYSICAL_SIZE is a library-provided macro that computes the required array size in bytes from the configuration parameters (not a user-defined value); CY_EM_EEPROM_FLASH_SIZEOF_ROW is a device-specific constant defined by the PDL representing the flash row size in bytes on the target device.
The array must be zero-initialized and aligned to CY_EM_EEPROM_FLASH_SIZEOF_ROW.
Step 6 - Allocate the Em_EEPROM context structure.
Step 7 - Populate the Em_EEPROM configuration structure.
Step 8 - Initialize the middleware once at startup.
For devices using the HAL NVM block-storage backend:
PSOC™ 4, PMG1 (CAT2) and CCGxF_CFP family of devices (CAT2 PDL block-storage backend):
The init function stores the configuration and current state of EEPROM storage in the context. It is used and updated by all subsequent API calls.
Note: Cy_Em_EEPROM_Init_BD() performs an NVM sector scan on every startup on supported eCT-flash devices (XMC7xxx, XMC5xxx). On first boot after a power failure during an erase, this scan erases any partially-erased sectors before returning; typical boot time is unaffected when no corruption is found. On all other devices the scan is compiled out and no recovery is performed.
Note: To disable partially-erased sector recovery, define CY_EM_EEPROM_DISABLE_PARTIALLY_ERASED_SECTOR_RECOVERY in the application build settings before compiling the Em_EEPROM middleware. Disabling recovery can leave sectors with ECC errors after an interrupted erase.
Note: On the affected eCT-flash devices this feature depends on HAL APIs that are not present in older HAL releases. See HAL Requirements below before upgrading to this version of the middleware.
On eCT-flash devices (XMC7xxx, XMC5xxx) the Em_EEPROM middleware calls two HAL NVM APIs that were introduced in a recent HAL release:
| API | Used by |
|---|---|
| mtb_hal_nvm_is_blank() | Blank-region detection in the read, write, and checksum paths |
| mtb_hal_nvm_is_sector_corrupt() | Partially-erased sector recovery in Cy_Em_EEPROM_Init_BD() |
If the project uses a HAL that does not provide these functions, partially-erased sector recovery is not available. Depending on the Em_EEPROM release, the build either fails with undeclared or unresolved symbols, or the feature is compiled out and the compiler reports a warning. There is no run-time fallback in either case.
Choose one of the following:
Defining CY_EM_EEPROM_DISABLE_PARTIALLY_ERASED_SECTOR_RECOVERY is not a workaround for an outdated HAL: the middleware also calls mtb_hal_nvm_is_blank() outside the recovery path, so the build is unaffected by that macro.
Note: The example code uses printf() for diagnostic output, which requires the retarget-io middleware. Either start from a ModusToolbox project that already includes retarget-io (for example, the Hello World code example), or add it to your project via Project > ModusToolbox Library Manager. If you prefer not to use retarget-io, replace the printf() calls with another error handler appropriate for your application — for example CY_ASSERT(0) to halt in debug builds, or a dedicated error-reporting routine.
Step 9 - Read, write, and erase data.
Step 10 - Build, program, and verify. Build the application and program it onto the target device using the ModusToolbox IDE (Build Application followed by Run or Debug) or from the command line (make program). Open a serial terminal connected to the kit's KitProg UART (typical settings: 115200 baud, 8-N-1) to view the diagnostic output produced by printf().
If initialization and all three operations succeed, the terminal will remain silent — none of the printf() calls in the example above are executed because every API call returns CY_RSLT_SUCCESS. Any failure prints a single line of the form:
Flash sector mapping determines which physical flash sector the CPU boots from and how sectors appear in the logical address space; this matters for Em_EEPROM because the userNvmStartAddr field must always reflect the current logical address of the storage after any mapping change.
Many Infineon MCUs divide their internal flash into multiple physical sectors (or banks). The mechanism that controls which physical sector the CPU boots from and how sectors appear in the logical address space varies across device families: some devices use a dedicated hardware register that can be changed at runtime (bank switching), while others store this configuration persistently in a special area of flash that is only updated during programming.
Flash sector mapping is the hardware-level assignment of physical flash sectors to the addresses the CPU sees at runtime. The mapping configuration determines which physical sector is the active (boot) sector — the one from which the CPU fetches the reset vector and begins execution — and, on devices that support dual-sector or dual-bank modes, which sector occupies the inactive slot in the address space.
Because the active sector always appears at the same logical base address regardless of which physical sector is mapped there, the same logical address can refer to different physical locations depending on the current mapping configuration.
The mechanism for changing the flash sector mapping varies by device family:
Always consult the device TRM for the exact register names, programming sequences, and any lifecycle constraints that apply.
Flash sector mapping affects Em_EEPROM usage in three ways:
| Concern | How mapping is relevant |
|---|---|
| Read-While-Write (RWW) compliance | NVM write operations cannot target the same physical sector from which the CPU is currently executing code. Placing Em_EEPROM storage in a sector that is separate from the active code execution sector satisfies this requirement. |
| Stable storage address | After a mapping change a sector's logical address changes even though its physical location does not. The userNvmStartAddr field in cy_stc_eeprom_config2_t must always reflect the current logical address of the Em_EEPROM storage. If the mapping changes (for example after a bank switch or OTA update), reinitialize the middleware with the updated address before performing any read or write operation. |
| High-endurance operation | On devices where high-endurance Em_EEPROM requires storage in a specific physical sector (for example physical Sector 0 on PSC3 family devices), the mapping configuration determines whether that physical sector is currently accessible at the expected logical address. Plan boot sector assignment and Em_EEPROM placement together to avoid conflicts. |
Certain devices allow the on-board flash to be used in single bank or in dual bank modes. In single bank mode, the entire memory is mapped to a contiguous address range, where the logical address directly maps to the physical address. In dual bank mode, the memory is divided into two separate memory regions with one being the active bank (where the application currently runs), and the other being the inactive bank (can be used to store data or for storing a copy of the application). Bank switching allows the user to switch the active bank with the inactive bank, allowing for features such as seamless updates to the application to be uploaded at run-time. In such a use-case, the logical address of the active bank remains the same but the actual physical address that this address points to changes depending on how the mapping is configured. If there are limitations or user preferences to have only one copy of the Em_EEPROM storage in one specific physical location, then the user application must take precautions when populating cy_stc_eeprom_config2_t so that the address passed in for userNvmStartAddr is the correct one.
For an in-depth reference on using the bank switch feature, refer to the product datasheet or its Technical Reference Manual. Below is an example using the Traveo II family: AN220242 - Flash accessing procedure for TRAVEO T2G family
Flash IP details (Traveo II, Code Region, Large Sectors):
BANK_MODE vs. BANK_MAPPING
!BANK_MODE vs BANK_MAPPING
In Dual Bank mode, the user can run an application in one bank and download a newer image in another bank. BANK_MAPPING can then be configured to launch the newer image in that bank with minimal downtime.
Use Case
Let's say that in Dual Bank mode (Mapping A), the user saved the Em_EEPROM data in bank 0. The user would like to keep the Em_EEPROM data in bank 0 even after launching the newer application in bank 1. In this case, the user needs to make sure to set up the Em_EEPROM middleware correctly in order to access the original Em_EEPROM data that was stored in bank 0.
!Bank Switch Use Case
For how to enable the Em_EEPROM middleware to write/read Em_EEPROM data, refer to the How to Use section above.
After the user configures BANK_MAPPING from Mapping A to Mapping B, the Em_EEPROM start address must be updated in the config struct (.userNvmStartAddr) with all other struct member values (e.g. .eepromSize, .simpleMode, ...) kept the same as before.
Reinitialize the Em_EEPROM middleware using this updated config:
XMC7xxx, XMC5xxx, and T2G-B-H based devices all use eCT Flash, which has separate Code Flash and Work Flash regions. Em_EEPROM data must be placed in Work Flash (a dedicated flash region separate from the main code flash) on all three families. Code Flash sectors on these devices are 32 KB — too large for run-time EEPROM emulation — so Work Flash must be used instead. Work Flash provides sectors with two sizes: Large (2 kbytes) and Small (128 bytes). Specify the start address in the configuration structure to select which Work Flash region will be used.
Note: Non-blocking write mode (blockingWrite = 0) is not supported on XMC5xxx devices; the blocking write is used regardless of the blockingWrite setting.
Prior to v2.30, the only available init function was Cy_Em_EEPROM_Init() which accepted cy_stc_eeprom_config_t (with userFlashStartAddr) and auto-selected the block storage backend internally. This function is kept for backward compatibility — existing projects that use it will continue to work. New projects should use cy_stc_eeprom_config2_t and Cy_Em_EEPROM_Init_BD() instead, which support any block-storage-compatible memory backend.
Wear leveling distributes write cycles across multiple physical rows to extend flash endurance. The higher the wearLevelingFactor value (range 1–10), the more flash is consumed, but more erase/write cycles are supported. A factor of 1 means no wear leveling. Multiply the factor by the datasheet write endurance spec to determine the maximum supported write cycles.
When redundantCopy is enabled (1), a checksum is stored with each row of data and a redundant copy of the entire Em_EEPROM is maintained at a separate location. On each read the checksum is validated; if it fails and the redundant copy's checksum is good, the redundant copy is automatically restored.
When simpleMode is enabled (1), no service data (checksums, headers, write counters) is stored. Data is written directly to the specified address. The storage size equals eepromSize rounded up to a full row (CY_EM_EEPROM_FLASH_SIZEOF_ROW). Wear leveling and redundant copy are ignored in this mode.
Note: Blank (never-written) regions return the erase value reported by the underlying storage.
When blockingWrite is set to 0, the Em_EEPROM middleware uses the non-blocking write and erase operations provided by the underlying block-storage middleware.
In both modes the API call blocks until the NVM operation completes. The difference is how the wait is implemented: in blocking mode (blockingWrite = 1) the CPU busy-waits with interrupts disabled, so no interrupt service routines can execute during the operation. In non-blocking mode (blockingWrite = 0) the block-storage middleware polls for completion while keeping interrupts enabled, so other interrupt service routines can execute during the write or erase. Therefore, interrupts must be enabled when calling Cy_Em_EEPROM_Write() or Cy_Em_EEPROM_Erase() in non-blocking mode; disabling interrupts before those calls will cause the operation to stall indefinitely.
Non-blocking NVM operations are only supported on a limited set of devices: PSOC Control PSC3/PSC3M8, PSOC6, XMC7xxx, and T2G-B-H. On all other devices the blocking write is used regardless of the blockingWrite setting.
Note: When using non-blocking operation, the Em_EEPROM storage must reside in a different flash array from the one from which the CPU is currently executing code. Writing to a flash array while executing from the same array is not supported in non-blocking mode. It is the user's responsibility to ensure the Em_EEPROM storage is placed in the appropriate flash array (for example, in the auxiliary flash, a separate Flash Sector) so that code execution and NVM writes target different arrays or different memory.
Note: PSOC Control — Non-Blocking Mode Initialization (up to 100,000 write cycles)
For the standard case (up to 100,000 write cycles), Cy_Flash_Init(false) must be called after Cy_Em_EEPROM_Init_BD() or Cy_Em_EEPROM_Init() and before the first Cy_Em_EEPROM_Write() or Cy_Em_EEPROM_Erase() call, otherwise those operations will fail:
The argument is refresh_enable; pass false to disable the flash refresh feature.
For more than 100,000 write cycles the flash_refresh mechanism must be enabled, which requires a different Cy_Flash_Init() configuration and additional setup. Refer to the Cy_Flash_Init() API description in the PSC3 PDL documentation for the required call sequence.
Note: PSOC Control — Storage Placement Guidance
Two hardware rules always apply on PSC3 and PSC3M8 devices:
Where exactly to place Em_EEPROM depends on how many write cycles your application requires:
The user is responsible for allocating space in NVM for the Em_EEPROM storage. The Em_EEPROM middleware operates on top of the block-storage abstraction layer and can work with any memory type that the block-storage middleware supports (internal NVM, Work Flash, auxiliary flash, external serial flash, etc.), subject to the constraints of the underlying block-storage middleware.
The storage location must be aligned to CY_EM_EEPROM_FLASH_SIZEOF_ROW.
Storage size equations:
Use the CY_EM_EEPROM_GET_PHYSICAL_SIZE() macro to compute the required storage size.
For PSOC 6 devices with the ARM compiler, the linker script must align ER_FLASH_CODE to 512 bytes (the default is 16):
Applicable to PSOC 6 devices, which include a dedicated auxiliary flash array (0x14000000–0x14008000). Writes to rows in the main flash affect the endurance of other rows in the same sector. Using auxiliary flash is recommended for frequently-updated data.
The .cy_em_eeprom linker section is pre-defined in the default PSOC 6 BSP linker scripts — no linker script changes are needed. Declare the storage array with:
To place the Em_EEPROM storage at a fixed address, ensure the chosen address is aligned to the flash row size and does not overlap the application code.
Note: On newer devices such as PSOC Edge and PSOC Control C3M8, use the Memory tab in the Infineon Device Configurator to assign a fixed address to the storage array — no manual linker script editing is needed. Refer to the Infineon Device Configurator User Guide for details. For all other devices, modify the linker script as described below.
Add after etext = . ; in the .ld linker script:
Then declare the storage:
Add before the LR_EM_EEPROM region in the .sct scatter file:
Then declare the storage (same as GCC example above).
Add after .cy_app_signature in the .icf file:
Add section .my_emulated_eeprom, inside the existing keep { ... } block.
Then declare the storage (same as GCC example above).
After declaring the storage array at a fixed address, pass its address as userNvmStartAddr in the configuration structure.
There are no high or medium severity compliance issues for this asset. Listed below are the deviations for minor issues.
The Cy_Em_EEPROM library's specific deviations:
| MISRA Rule | Rule Class (Required/Advisory) | Rule Description | Description of Deviation(s) |
|---|---|---|---|
| 5.9 | A | Static identifiers should be unique. | Following naming convention for static functions. |
| 11.5 | A | Typecast of void pointer should be avoided. | The cast is used intentionally for performance reasons. |
This software is governed by the Infineon End User License Agreement (EULA). You may use this Software only as permitted under that agreement. Redistribution, modification, or use outside of those terms requires the express written permission of Infineon Technologies AG.
(c) 2019-2026, Infineon Technologies AG, or an affiliate of Infineon Technologies AG. All rights reserved.