4
\$\begingroup\$

Given that there are many differences in the way data is written to EEPROM and Flash, how exactly is EEPROM emulation done with internal flash?

Also, flash needs to be erased in large blocks before data can be written over. In that case, if we want to write only a small amount of data in emulated EEPROM without disturbing the other contents, how is it achieved? What are the software techniques for this?

\$\endgroup\$
6
\$\begingroup\$

There are probably many different algorithms to accomplish EEPROM emulation without forcing a lot of unnecessary writes. The main concept is like a ledger (except values don't depend on previous values). The data set or a variable has many "records" in the ledger. When a new value is written, the old value must be invalidated. For example, you can invalidate the old value by writing some magic value which means "that record is invalid". Then, a new record can be created. Looking up a value means reading through the ledger to find the current record. All invalidated records can be ignored. Flash pages can be erased when full (the values must all be invalid or be migrated to a new flash page before erasing).

Here's a simple, concrete algorithm example:

Let's say I have a data set of 6 bytes. I will devote 2 flash pages to emulated EEPROM for my 6 byte data set. Make the following assumptions:

  • Assume my flash pages are significantly larger than 6 bytes.
  • Assume that flash after being erased is value 0xFF.
  • Assume that I can re-write flash repeatedly without erasing, but only change ones to zeros (1->0).
  • Assume that my flash page is full when I can't fit any more full records.
  • I am not paying attention to endianness (you'll get the picture).

Flash page header

I'll start my flash page with some sort of header to indicate that I am actively storing data there (or not). Let me use 4 bytes at the start of the flash page which indicate one of the following conditions:

  1. 0x5555FFFF: This page is currently in use (the current value is one of the records).
  2. 0x55555555: This page is completely full (no current values can be found in this page). (At this point, the page should be erased before it is needed again).
  3. 0xFFFFFFFF: This page is completely erased.
  4. Anything else: Error. (Should not happen.)

Record header

For each record, I will start with a header also. Let me use 2 bytes, indicating one of the following:

  1. 0x55FF: This is the current record.
  2. 0x5555: This record is invalidated.
  3. 0xFFFF: This record slot has not been filled since the last erase.
  4. Anything else: Error. No record header slot should have anything else.

Flash pages start erased

Flash Page #0
FFFFFFFF // Page Header (unused: 0xFFFFFFFF header.)
FFFFFFFF // Record slot #0 (unused: 0xFFFF header.)
FFFFFFFF
FFFFFFFF // Record slot #1 (unused: 0xFFFF header.)
FFFFFFFF
...

Flash Page #1
FFFFFFFF // Page Header (unused: 0xFFFFFFFF header.)
FFFFFFFF // Record slot #0 (unused: 0xFFFF header.)
FFFFFFFF
FFFFFFFF // Record slot #1 (unused: 0xFFFF header.)
FFFFFFFF
...

System initialization

After detecting that the EEPROM data has not been initialized, I will begin by initializing my data set to all 0s:

Flash Page #0
5555FFFF // Page Header (current value here: 0x5555FFFF header.)
55FF0000 // Record slot #0 (current value: 0x55FF header.)
00000000
FFFFFFFF // Record slot #1 (unused: 0xFFFF header.)
FFFFFFFF
...

Flash Page #1
FFFFFFFF // Page Header (unused: 0xFFFFFFFF header.)
FFFFFFFF // Record slot #0 (unused: 0xFFFF header.)
FFFFFFFF
FFFFFFFF // Record slot #1 (unused: 0xFFFF header.)
FFFFFFFF
...

Notice that bytes 2-3 were not written and remain 0xFFFF. When this flash page fills up and I start a new flash page, I will write 0x5555 over bytes 2-3.

The record header essentially works the same. Byte 1 of the record #0 header remains 0xFF indicating that it is the current record (because byte 0 is 0x55).

Write the value 0xDEADBEEFCAFE

In this order:

  1. Write the new record's data.
  2. Write 0x55 to byte 0 of the new record (record slot #1) to indicate that it is the current record.
  3. Finally, write 0x55 to byte 1 of the old record (record slot #0) to invalidate it.

Order is important for recovery from spurious resets (this is similar to a journal in a filesystem).

Flash Page #0
5555FFFF // Page Header (current value here: 0x5555FFFF header.)
55550000 // Record slot #0 (now invalid: 0x5555 header.)
00000000
55FFDEAD // Record slot #1 (current value: 0x55FF header.)
BEEFCAFE
...

Flash Page #1
FFFFFFFF // Page Header (unused: 0xFFFFFFFF header.)
FFFFFFFF // Record slot #0 (unused: 0xFFFF header.)
FFFFFFFF
FFFFFFFF // Record slot #1 (unused: 0xFFFF header.)
FFFFFFFF
...

Write the value 0x12345678ABCD

Flash Page #0
5555FFFF // Page Header (current value here: 0x5555FFFF header.)
55550000 // Record slot #0 (now invalid: 0x5555 header.)
00000000
5555DEAD // Record slot #1 (now invalid: 0x5555 header.)
BEEFCAFE
55FF1234 // Record slot #2 (current value: 0x55FF header.)
5678ABCD
...

Flash Page #1
FFFFFFFF // Page Header (unused: 0xFFFFFFFF header.)
FFFFFFFF // Record slot #0 (unused: 0xFFFF header.)
FFFFFFFF
FFFFFFFF // Record slot #1 (unused: 0xFFFF header.)
FFFFFFFF
...

The current page is full

This is what the pages look like when the current page (Page #0) is completely full (the current value is 0xAAAA5555BBBB):

Flash Page #0
5555FFFF // Page Header (current value here: 0x5555FFFF header.)
55550000 // Record slot #0 (now invalid: 0x5555 header.)
00000000
5555DEAD // Record slot #1 (now invalid: 0x5555 header.)
BEEFCAFE
55551234 // Record slot #2 (now invalid: 0x5555 header.)
5678ABCD
...
55FFAAAA // Final record slot (current value: 0x55FF header.)
5555BBBB

Flash Page #1
FFFFFFFF // Page Header (unused: 0xFFFFFFFF header.)
FFFFFFFF // Record slot #0 (unused: 0xFFFF header.)
FFFFFFFF
FFFFFFFF // Record slot #1 (unused: 0xFFFF header.)
FFFFFFFF
...

Notice that flash page #0 is not yet invalidated (the current record is the last record in flash page #0).

Write the value 0x80009000ABCD

The next value must go into flash page #1. After writing the new record in flash page #1 and creating the page header to indicate that flash page #1 now contains the current value, we invalidate flash page #0 (write 0x55 to bytes 2-3) and the last record in flash page #0 (write 0x55 to the last record's header byte 1).

Flash Page #0
55555555 // Page Header (page is all now invalid: 0x55555555 header.)
55550000 // Record slot #0 (now invalid: 0x5555 header.)
00000000
5555DEAD // Record slot #1 (now invalid: 0x5555 header.)
BEEFCAFE
55551234 // Record slot #2 (now invalid: 0x5555 header.)
5678ABCD
...
5555AAAA // Final record slot (now invalid: 0x5555 header.)
5555BBBB

Flash Page #1
5555FFFF // Page Header (current value here: 0x5555FFFF header.)
55FF8000 // Record slot #0 (current value: 0x55FF header.)
9000ABCD
FFFFFFFF // Record slot #1 (unused: 0xFFFF header.)
FFFFFFFF
...

Now, flash page #0 is completely invalid, and the new page is Flash page #1.

Flash page #0 is garbage collected (erased)

Finally, flash page #0 can be erased when the system finds it convenient.

Flash Page #0
FFFFFFFF // Page Header (unused: 0xFFFFFFFF header.)
FFFFFFFF // Record slot #0 (unused: 0xFFFF header.)
FFFFFFFF
FFFFFFFF // Record slot #1 (unused: 0xFFFF header.)
FFFFFFFF
...

Flash Page #1
5555FFFF // Page Header (current value here: 0x5555FFFF header.)
55FF8000 // Record slot #0 (current value: 0x55FF header.)
9000ABCD
FFFFFFFF // Record slot #1 (unused: 0xFFFF header.)
FFFFFFFF
...

Record lookup, real-world complexities

In the real world, the data set would be larger and more complex than a single 6-byte chunk. There are many complexities I glossed over. This is a really simple example, but it would work. It illustrates how EEPROM can be emulated in flash without excessive erasing by creating a ledger of value records.

To look up the current data set values, find which flash page is current (one and only one flash page should have the header 0x5555FFFF). Then, read through the records in that page sequentially until finding the current record (one and only one record should have record header 0x55FF). Errors could invalidate the header configuration, potentially leading to multiple pages and/or records which appear to be current. A real application would need to handle such errors.

\$\endgroup\$
1
\$\begingroup\$

From this application note:

In general, there are two common approaches when using program flash to emulate EEPROM. The first approach is to keep a copy of the EEPROM data in a RAM buffer and periodically write the entire contents of the buffer to the program flash. This approach is relatively simple to implement, permits the data to be read from the RAM buffer at any time, and allows control of the number of program/erase cycles. The obvious drawback to this approach is the amount of RAM that must be dedicated to the emulated EEPROM buffer. In addition, there is a risk of losing data if a reset occurs after updating a RAM buffer, but before the data is programmed into flash.

A second approach uses multiple sectors of program flash to store nonvolatile data using a flash file system. Depending on the implementation, this method will usually require less RAM than the first approach. The major disadvantage to this approach is the size and complexity of the firmware required to implement a robust flash file system, such that it minimizes the risk of losing data when an unexpected reset or loss of power occurs.

\$\endgroup\$
1
\$\begingroup\$

I want to share another application node from ST:

Principle

EEPROM emulation is performed in various ways, taking into consideration the Flash memory limitations and product requirements. The approach detailed below requires at least two Flash memory pages of an identical size allocated to non-volatile data: one that is initially erased, the other that is ready to take over when the former page needs to be garbage-collected. A header field that occupies the first half word (16-bit) of each page indicates the page status. Each of these pages is called Page0 and Page1 in the rest of this document Each page has three possible states:

● ERASED: the page is empty.

● RECEIVE_DATA: the page is receiving data from the other full page.

● VALID _PAGE: the page contains valid data and this state does not change until all valid data is completely transferred to the erased page.

A rather detailed diagram about the page statuses (click to enlarge):

enter image description here

Case of use: application example

The following example shows the software management of three EEPROM variables (Var1, Var2 and Var3) with the following virtual addresses:

Var1 virtual address 5555h

Var2 virtual address 6666h

Var3 virtual address 7777h

enter image description here

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.