Emulación Flash EEPROM

Dado que existen muchas diferencias en la forma en que se escriben los datos en EEPROM y Flash, ¿cómo se realiza exactamente la emulación de EEPROM con flash interno?

Además, la memoria flash debe borrarse en bloques grandes antes de que se puedan sobrescribir los datos. En ese caso, si queremos escribir solo una pequeña cantidad de datos en la EEPROM emulada sin perturbar el resto de contenidos, ¿cómo se consigue? ¿Cuáles son las técnicas de software para esto?

Respuestas (3)

Probablemente hay muchos algoritmos diferentes para lograr la emulación de EEPROM sin forzar muchas escrituras innecesarias. El concepto principal es como un libro de contabilidad (excepto que los valores no dependen de los valores anteriores). El conjunto de datos o una variable tiene muchos "registros" en el libro mayor. Cuando se escribe un nuevo valor, el valor antiguo debe invalidarse. Por ejemplo, puede invalidar el valor antiguo escribiendo algún valor mágico que signifique "ese registro no es válido". Luego, se puede crear un nuevo registro. Buscar un valor significa leer el libro mayor para encontrar el registro actual. Todos los registros invalidados se pueden ignorar. Las páginas flash se pueden borrar cuando están llenas (todos los valores deben ser inválidos o migrar a una nueva página flash antes de borrar).

Aquí hay un ejemplo de algoritmo simple y concreto:

Digamos que tengo un conjunto de datos de 6 bytes. Dedicaré 2 páginas flash a la EEPROM emulada para mi conjunto de datos de 6 bytes. Haga las siguientes suposiciones:

  • Supongamos que mis páginas flash son significativamente más grandes que 6 bytes.
  • Suponga que el flash después de borrarse tiene un valor de 0xFF.
  • Suponga que puedo reescribir flash repetidamente sin borrar, pero solo cambiar unos a ceros (1-> 0).
  • Supongamos que mi página flash está llena cuando no caben más registros completos.
  • No estoy prestando atención a endianness (obtendrá la imagen).

Encabezado de página flash

Comenzaré mi página flash con algún tipo de encabezado para indicar que estoy almacenando datos activamente allí (o no). Permítanme usar 4 bytes al comienzo de la página flash que indican una de las siguientes condiciones:

  1. 0x5555FFFF: esta página está actualmente en uso (el valor actual es uno de los registros).
  2. 0x55555555: esta página está completamente llena (no se pueden encontrar valores actuales en esta página). (En este punto, la página debe borrarse antes de volver a necesitarla).
  3. 0xFFFFFFFF: Esta página está completamente borrada.
  4. Cualquier otra cosa: Error. (No debería suceder.)

Encabezado de registro

Para cada registro, también comenzaré con un encabezado. Permítanme usar 2 bytes, indicando uno de los siguientes:

  1. 0x55FF: Este es el registro actual.
  2. 0x5555: este registro está invalidado.
  3. 0xFFFF: este espacio de registro no se ha llenado desde el último borrado.
  4. Cualquier otra cosa: Error. Ninguna ranura de encabezado de registro debe tener algo más.

Las páginas flash comienzan a borrarse

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

Inicialización del sistema

Después de detectar que los datos de la EEPROM no se han inicializado, comenzaré inicializando mi conjunto de datos a todos 0:

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

Observe que los bytes 2 y 3 no se escribieron y siguen siendo 0xFFFF. Cuando esta página flash se llene y comience una nueva página flash, escribiré 0x5555 sobre los bytes 2-3.

El encabezado del registro esencialmente funciona de la misma manera. El byte 1 del encabezado del registro #0 sigue siendo 0xFF, lo que indica que es el registro actual (porque el byte 0 es 0x55).

Escribe el valor 0xDEADBEEFCAFE

En este orden:

  1. Escriba los datos del nuevo registro.
  2. Escriba 0x55 en el byte 0 del nuevo registro (ranura de registro n.º 1) para indicar que es el registro actual.
  3. Finalmente, escriba 0x55 en el byte 1 del registro anterior (ranura de registro #0) para invalidarlo.

El orden es importante para la recuperación de restablecimientos falsos (esto es similar a un diario en un sistema de archivos).

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

Escribe el valor 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
...

La página actual está llena

Así es como se ven las páginas cuando la página actual (Página #0) está completamente llena (el valor actual es 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
...

Observe que la página flash #0 aún no está invalidada (el registro actual es el último registro en la página flash #0).

Escribe el valor 0x80009000ABCD

El siguiente valor debe ir a la página flash #1. Después de escribir el nuevo registro en la página flash #1 y crear el encabezado de página para indicar que la página flash #1 ahora contiene el valor actual, invalidamos la página flash #0 (escribimos 0x55 en los bytes 2-3) y el último registro en la página flash #0 (escribe 0x55 en el byte 1 del encabezado del último registro).

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

Ahora, la página Flash #0 es completamente inválida y la nueva página es la página Flash #1.

La página Flash n.° 0 se recolecta como basura (borrada)

Finalmente, la página flash #0 se puede borrar cuando el sistema lo considere conveniente.

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

Búsqueda de registros, complejidades del mundo real

En el mundo real, el conjunto de datos sería más grande y más complejo que un solo fragmento de 6 bytes. Hay muchas complejidades que pasé por alto. Este es un ejemplo realmente simple, pero funcionaría. Ilustra cómo se puede emular la EEPROM en flash sin un borrado excesivo mediante la creación de un libro mayor de registros de valores.

Para buscar los valores actuales del conjunto de datos, encuentre qué página flash es actual (una y solo una página flash debe tener el encabezado 0x5555FFFF). Luego, lea los registros en esa página secuencialmente hasta encontrar el registro actual (uno y solo un registro debe tener el encabezado de registro 0x55FF). Los errores podrían invalidar la configuración del encabezado, lo que podría generar múltiples páginas y/o registros que parecen estar actualizados. Una aplicación real necesitaría manejar tales errores.

De esta nota de aplicación :

En general, existen dos enfoques comunes cuando se usa el programa flash para emular EEPROM. El primer enfoque es mantener una copia de los datos de la EEPROM en un búfer de RAM y escribir periódicamente todo el contenido del búfer en la memoria flash del programa. Este enfoque es relativamente simple de implementar, permite leer los datos del búfer de RAM en cualquier momento y permite el control del número de ciclos de programa/borrado. El inconveniente obvio de este enfoque es la cantidad de RAM que debe dedicarse al búfer EEPROM emulado. Además, existe el riesgo de perder datos si se reinicia después de actualizar un búfer de RAM, pero antes de que los datos se programen en flash.

Un segundo enfoque utiliza múltiples sectores de programa flash para almacenar datos no volátiles utilizando un sistema de archivos flash. Dependiendo de la implementación, este método generalmente requerirá menos RAM que el primer enfoque. La principal desventaja de este enfoque es el tamaño y la complejidad del firmware requerido para implementar un sistema de archivos flash sólido, de modo que minimice el riesgo de perder datos cuando se produce un reinicio inesperado o una pérdida de energía.

Quiero compartir otro nodo de aplicación de ST :

Principio

La emulación de EEPROM se realiza de varias formas, teniendo en cuenta las limitaciones de la memoria Flash y los requisitos del producto. El enfoque que se detalla a continuación requiere al menos dos páginas de memoria Flash de un tamaño idéntico asignadas a datos no volátiles: una que se borre inicialmente y la otra que esté lista para tomar el control cuando la página anterior deba ser recolectada como basura. Un campo de encabezado que ocupa la primera mitad de la palabra (16 bits) de cada página indica el estado de la página. Cada una de estas páginas se denomina Página0 y Página1 en el resto de este documento. Cada página tiene tres estados posibles:

● BORRADO: la página está vacía.

● RECEIVE_DATA: la página está recibiendo datos de la otra página completa.

● _PÁGINA VÁLIDA: la página contiene datos válidos y este estado no cambia hasta que todos los datos válidos se transfieren por completo a la página borrada.

Un diagrama bastante detallado sobre los estados de la página (haga clic para ampliar):

ingrese la descripción de la imagen aquí

Caso de uso: ejemplo de aplicación

El siguiente ejemplo muestra la gestión por software de tres variables EEPROM (Var1, Var2 y Var3) con las siguientes direcciones virtuales:

Var1 dirección virtual 5555h

Var2 dirección virtual 6666h

Var3 dirección virtual 7777h

ingrese la descripción de la imagen aquí