Emulated EEPROM
Loading...
Searching...
No Matches
Emulated EEPROM Library - Non-Volatile Storage with Wear Leveling for ModusToolbox

Overview

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.

Features

  • EEPROM-Like Non-Volatile Storage - access flash memory through a familiar EEPROM-style API
  • Easy-to-use Read and Write API
  • Optional Wear Leveling - distributes writes evenly to extend flash endurance (factor 1-10)
  • Optional Redundant Data Storage - maintains a backup copy with checksum validation for data recovery
  • Supports any block-storage-compatible memory, including internal NVM and external flash (e.g. via QSPI)
  • Resilient to power failures — automatically detects and erases NVM sectors left in a partially-erased state at startup on supported eCT-flash devices (XMC7xxx, XMC5xxx); requires an up-to-date HAL (see HAL Requirements)

When to Use

Use the Emulated EEPROM middleware when your application needs to:

  • Store small amounts of non-volatile configuration or calibration data in flash memory without a dedicated EEPROM device.
  • Increase flash write endurance beyond the raw hardware specification by enabling wear leveling.
  • Protect against data corruption caused by unexpected power loss or bit errors, using the redundant copy feature with checksum verification.
  • Retain a familiar EEPROM-style API (Read, Write, Erase) while targeting MCUs that expose only raw NVM or block-storage interfaces.
  • Support external flash storage (e.g. QSPI) through the block-storage abstraction layer.

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.

How to Use

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.

#include "cy_em_eeprom.h"

Step 4 - Define the Em_EEPROM configuration macros.

/* The size of data to store in EEPROM */
#define DATA_SIZE (CY_FLASH_SIZEOF_ROW)
/* The Simple Mode is turned off */
#define SIMPLE_MODE (0u)
/* Increases the flash endurance twice */
#define WEAR_LEVELING (2u)
/* The Redundant Copy is turned off */
#define REDUNDANT_COPY (0u)
/* The Blocking Write is turned on */
#define BLOCKING_WRITE (1u)
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_ALIGN(CY_EM_EEPROM_FLASH_SIZEOF_ROW)
const uint8_t emEepromStorage[CY_EM_EEPROM_GET_PHYSICAL_SIZE(DATA_SIZE, SIMPLE_MODE,
WEAR_LEVELING, REDUNDANT_COPY)] = {0u};

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.

The Em_EEPROM context data structure.
Definition cy_em_eeprom.h:284

Step 7 - Populate the Em_EEPROM configuration structure.

{
.eepromSize = DATA_SIZE,
.simpleMode = SIMPLE_MODE,
.wearLevelingFactor = WEAR_LEVELING,
.redundantCopy = REDUNDANT_COPY,
.blockingWrite = BLOCKING_WRITE,
.userNvmStartAddr = (uint32_t)&(emEepromStorage[0u]),
};
Em_EEPROM new configuration structure.
Definition cy_em_eeprom.h:206

Step 8 - Initialize the middleware once at startup.

For devices using the HAL NVM block-storage backend:

mtb_block_storage_t my_bsd;
cy_rslt_t result;
if (CY_RSLT_SUCCESS == mtb_block_storage_nvm_create(&my_bsd))
{
result = Cy_Em_EEPROM_Init_BD(&eepromConfig, &eepromContext, &my_bsd);
if (CY_RSLT_SUCCESS != result)
{
printf("Em_EEPROM init failed: 0x%08lx\r\n", (unsigned long)result);
}
}
cy_en_em_eeprom_status_t Cy_Em_EEPROM_Init_BD(const cy_stc_eeprom_config2_t *config, cy_stc_eeprom_context_t *context, mtb_block_storage_t *block_device)
Initializes the Emulated EEPROM library by filling the context structure.
Definition cy_em_eeprom.c:165

PSOC™ 4, PMG1 (CAT2) and CCGxF_CFP family of devices (CAT2 PDL block-storage backend):

mtb_block_storage_t my_bsd;
cy_rslt_t result;
if (CY_RSLT_SUCCESS == mtb_block_storage_cat2_create(&my_bsd))
{
result = Cy_Em_EEPROM_Init_BD(&eepromConfig, &eepromContext, &my_bsd);
if (CY_RSLT_SUCCESS != result)
{
printf("Em_EEPROM init failed: 0x%08lx\r\n", (unsigned long)result);
}
}

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.

HAL Requirements

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:

  1. Update the HAL (recommended). This is the only option that keeps partially-erased sector recovery available.
  2. Stay on the previous Em_EEPROM version. If the HAL cannot be updated, pin the middleware to the last release that did not require these functions. Recovery from interrupted erases is then unavailable.

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.

uint8_t readData = 0xAAu;
cy_rslt_t result;
result = Cy_Em_EEPROM_Write(0u, &readData, 1u, &eepromContext);
if (CY_RSLT_SUCCESS != result)
{
printf("Em_EEPROM write failed: 0x%08lx\r\n", (unsigned long)result);
}
result = Cy_Em_EEPROM_Read(0u, &readData, 1u, &eepromContext);
if (CY_RSLT_SUCCESS != result)
{
printf("Em_EEPROM read failed: 0x%08lx\r\n", (unsigned long)result);
}
result = Cy_Em_EEPROM_Erase(&eepromContext);
if (CY_RSLT_SUCCESS != result)
{
printf("Em_EEPROM erase failed: 0x%08lx\r\n", (unsigned long)result);
}
cy_en_em_eeprom_status_t Cy_Em_EEPROM_Read(uint32_t addr, void *eepromData, uint32_t size, cy_stc_eeprom_context_t *context)
Reads data from a specified location.
Definition cy_em_eeprom.c:253
cy_en_em_eeprom_status_t Cy_Em_EEPROM_Write(uint32_t addr, const void *eepromData, uint32_t size, cy_stc_eeprom_context_t *context)
Writes data to a specified location.
Definition cy_em_eeprom.c:537
cy_en_em_eeprom_status_t Cy_Em_EEPROM_Erase(cy_stc_eeprom_context_t *context)
This function erases the entire content of Em_EEPROM.
Definition cy_em_eeprom.c:767

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:

Em_EEPROM <init|write|read|erase> failed: 0x<status code>

Flash Sector Mapping

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.

What Flash Mapping Is

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.

Where and How Flash Mapping Is Changed

The mechanism for changing the flash sector mapping varies by device family:

  • PSOC Control: Two separate mechanisms affect flash sector layout on these devices and it is important not to confuse them:
    • Boot sector configuration (SFLASH): A persistent entry in the supervisory flash (SFLASH) determines which physical sector the CPU fetches its reset vector from on power-up. This is a programming-time setting written via a dedicated SFLASH programming sequence. It is subject to Lifecycle Stage (LCS) restrictions and does not change at runtime. How to configure this is described in the Secured provisioning and re-provisioning chapter of the PSC3 Technical Reference Manual.
    • Runtime bank switching: The device also supports switching the active flash bank at runtime through a dedicated register, independently of the SFLASH boot configuration. This is what allows OTA-style updates where a new image can be written to the inactive bank and then activated without a re-programming cycle. After a bank switch, the logical address of the previously inactive bank changes and userNvmStartAddr must be updated accordingly before the next Em_EEPROM operation. How to perform bank switching is described in the Bank modes and address mapping chapter of the PSC3 Technical Reference Manual.
  • PSOC 6: The auxiliary flash region is independent of the main flash sector and is not affected by sector mapping. For main flash placement the application linker script controls which physical sector code and data occupy.
  • Traveo II (T2G): Dedicated BANK_MODE and BANK_MAPPING registers control single-bank vs. dual-bank mode and the active bank assignment. See the Bank Switch section below for a detailed step-by-step example.
  • XMC7xxx: Similar dual-bank capability exists; refer to the device TRM for the applicable registers and programming sequence.

Always consult the device TRM for the exact register names, programming sequences, and any lifecycle constraints that apply.

Why This Matters for Em_EEPROM

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.

Bank Switch

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):

  • Memory space: 0x10000000 – 0x107F0000
  • Supports Single Bank and Dual Bank mode (Bank 0 / Bank 1)
  • Bank switch is configured using BANK_MODE and BANK_MAPPING registers:
    • BANK_MODE — specify bank mode (Single bank or Dual bank)
    • BANK_MAPPING — specify memory mapping of flash (Mapping A or Mapping B)

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.

  1. 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.

    #define LOGICAL_TO_PHYSICAL(addr) (MAPPING_A_ACTIVE_REG ? addr : INACTIVE_SECTOR_BASE + addr)
    // INACTIVE_SECTOR_BASE will be 0x02000000 in this case
    {
    .eepromSize = <EEPROM Size>,
    .simpleMode = 0u,
    .wearLevelingFactor = <Wear Level Factor>,
    .redundantCopy = <Redundant Copy>,
    .blockingWrite = <Use Blocking Write>,
    .userNvmStartAddr = (uint32_t)LOGICAL_TO_PHYSICAL(&emEepromStorage[0u]),
    };
  2. Reinitialize the Em_EEPROM middleware using this updated config:

    cy_rslt_t result = Cy_Em_EEPROM_Init_BD(&eepromConfig, &eepromContext, &my_bsd);
    if (CY_RSLT_SUCCESS != result)
    {
    printf("Em_EEPROM init failed: 0x%08lx\r\n", (unsigned long)result);
    }
  3. The Em_EEPROM middleware is now ready to use. Call the Write or Read functions to access the original Em_EEPROM data as usual.

XMC7xxx, XMC5xxx, and T2G-B-H Storage Restrictions

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.


Backward Compatibility

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.

Operating Modes

Wear Leveling

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.

Redundant Copy

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.

Simple Mode

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.

Non-Blocking Operation (Interrupts Enabled, API Still Blocks Until Completion)

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:

Cy_Flash_Init(false); /* refresh_enable = false: disable flash refresh */

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:

  1. Sector 1 is not available for Em_EEPROM storage. This is a fixed hardware restriction in blocking mode.
  2. Em_EEPROM storage and the executing code must be in different flash sectors (Read-While-Write restriction). This includes any ISR that may run during a write or erase.

Where exactly to place Em_EEPROM depends on how many write cycles your application requires:

  • Up to 100,000 writes: Place Em_EEPROM in any sector other than Sector 0 in non-blocking mode. The most common setup is application code in Sector 0 and Em_EEPROM in another sector (e.g., Sector 1), which satisfies the RWW rule above without any special configuration. For larger applications that span multiple sectors, ensure via the linker script that no executing code shares the sector used for Em_EEPROM.
  • More than 100,000 writes: The hardware's flash_refresh mechanism, which is required at this write frequency, only works when Em_EEPROM is in physical Sector 0. Since Em_EEPROM now occupies Sector 0, the application code must execute from a different sector. This means the boot sector must be reconfigured and the linker script updated accordingly. How to reconfigure the boot sector and use the Flash-related sections of the PSC3 Technical Reference Manual.

Storage Variable Location and Size

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:

  • Simple mode on: storageSize = eepromSize (rounded up to CY_EM_EEPROM_FLASH_SIZEOF_ROW)
  • Simple mode off: storageSize = eepromSize × 2 × wearLevelingFactor × (1 + redundantCopy) (where eepromSize is rounded up to CY_EM_EEPROM_FLASH_SIZEOF_ROW / 2)

Use the CY_EM_EEPROM_GET_PHYSICAL_SIZE() macro to compute the required storage size.

Em_EEPROM in the Application Flash

CY_ALIGN(CY_EM_EEPROM_FLASH_SIZEOF_ROW)
const uint8_t emEepromStorage[STORAGE_SIZE] = {0u};

For PSOC 6 devices with the ARM compiler, the linker script must align ER_FLASH_CODE to 512 bytes (the default is 16):

ER_FLASH_CODE
AlignExpr(FLASH_START_VMA+ImageLength(ER_FLASH_VECTORS)+ImageLength(ER_FLASH_ROOT), 512) OVERLAY

Em_EEPROM in the Auxiliary Flash

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:

CY_SECTION(".cy_em_eeprom")
CY_ALIGN(CY_EM_EEPROM_FLASH_SIZEOF_ROW)
const uint8_t emEepromStorage[STORAGE_SIZE] = {0u};

Em_EEPROM at a Fixed Address

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.

GCC Compiler

Add after etext = . ; in the .ld linker script:

EM_EEPROM_START_ADDRESS = <EEPROM Storage Address>;
.my_emulated_eeprom EM_EEPROM_START_ADDRESS :
{
KEEP(*(.my_emulated_eeprom))
} > flash

Then declare the storage:

CY_SECTION(".my_emulated_eeprom")
CY_ALIGN(CY_EM_EEPROM_FLASH_SIZEOF_ROW)
const uint8_t emEepromStorage[STORAGE_SIZE];

ARM Compiler

Add before the LR_EM_EEPROM region in the .sct scatter file:

#define EM_EEPROM_START_ADDRESS <EEPROM Storage Address>
EM_EEPROM (EM_EEPROM_START_ADDRESS)
{
.my_emulated_eeprom+0
{
*(.my_emulated_eeprom)
}
}

Then declare the storage (same as GCC example above).

IAR Compiler

Add after .cy_app_signature in the .icf file:

define symbol EM_EEPROM_START_ADDRESS = <EEPROM Storage Address>
".my_emulated_eeprom" : place at address (EM_EEPROM_START_ADDRESS)
{ section .my_emulated_eeprom };

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.


Limitations and Restrictions

  • The Em_EEPROM storage location must be initialized with zeros and aligned to the flash row size (CY_EM_EEPROM_FLASH_SIZEOF_ROW), otherwise behavior may be unexpected.
  • The Em_EEPROM storage size depends on the configuration. Use the CY_EM_EEPROM_GET_PHYSICAL_SIZE macro to compute the required size.
  • Do not modify the cy_stc_eeprom_context_t context structure after initialization; this may cause unexpected behavior.
  • The internal memory address map, flash organization, and row sizes are device-family-specific. Refer to the specific device datasheet.
  • The Read-While-Write (RWW) feature available in PSOC 6 MCU allows writing to flash while executing from flash. There are restrictions on using RWW for EEPROM emulation, and multiple constraints for blocking and non-blocking flash operations relating to interrupts, power modes, and IPC usage. Refer to the "Flash (Flash System Routine)" section of the
  • For PSOC 6, the compiler assigns both cores the full auxiliary flash range (0x14000000–0x14008000) for Em_EEPROM by default. If another driver also uses auxiliary flash (e.g. BLE for bonding list storage), a linker error will occur. See Manage Flash Space for Both Cores of PSOC 6 – KBA224173 for resolution.
  • Writing multiple rows in a single Cy_Em_EEPROM_Write() call and experiencing a reset mid-operation (e.g. power failure) may cause partial data loss: the first written row will contain new data while remaining rows retain old data. Em_EEPROM cannot detect this condition because the row checksum of the first row is valid.
  • On first boot after a power failure that interrupted an erase on a supported eCT-flash device (XMC7xxx, XMC5xxx), Cy_Em_EEPROM_Init_BD() erases all affected sectors before returning. Any data in those sectors is lost — this is the expected recovery behavior. It is not possible to distinguish a partially-erased sector from an intentionally erased one.
  • Corrupt-sector recovery is not available on PSC3 or Traveo II devices, including T2G-B-H.
  • Corrupt-sector recovery requires a HAL providing mtb_hal_nvm_is_blank() and mtb_hal_nvm_is_sector_corrupt(). With an older HAL the feature is unavailable on eCT-flash devices; see HAL Requirements.

MISRA-C 2012 Compliance

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.

More Information

  • Emulated EEPROM Middleware Reference Guide
  • Block Storage Library
  • Infineon GitHub
  • ModusToolbox Software Environment

Release Notes and Changelog

  • RELEASE.md - Detailed release notes for all versions

License

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.

  • LICENSE - Infineon End User License Agreement (EULA)

Copyright

(c) 2019-2026, Infineon Technologies AG, or an affiliate of Infineon Technologies AG. All rights reserved.