Pregunta:
¿Tendrá un chip de memoria flash SPI los mismos problemas con operaciones de escritura no atómicas que la EEPROM interna de una dsPIC?
Stephen Collings
2014-03-06 22:32:00 UTC
view on stackexchange narkive permalink

Hace un tiempo tuve algunos problemas intermitentes con la EEPROM interna de un dsPIC. De vez en cuando, algún valor en la EEPROM se encuentra en cero al encenderlo. Seguí el problema hasta cuando el chip perdió energía después del paso de borrado del ciclo de escritura, pero antes de que se completara la escritura. Se trataba de la sincronización del apagado en relación con la ejecución del firmware, que era (en funcionamiento normal) aleatorio. Resolví esto agregando una sección de búfer a mi EEPROM, para asegurarme de que se pudiera completar un ciclo de escritura incompleto al restablecer la energía. Tuve que convertir las escrituras EEPROM en una operación atómica.

Ahora estoy usando una dsPIC diferente sin EEPROM interna, y estoy tratando de usar un chip de memoria flash externo para almacenar datos persistentes. Me pregunto si debería tener preocupaciones similares. ¿Debería preocuparme de que mi chip flash externo se apague a mitad de escritura y pierda datos, y escriba una solución para esto en mi firmware como lo hice para la EEPROM interna? ¿O el chip mismo garantiza operaciones de escritura atómicas?

Para obtener más detalles, mi técnica de almacenamiento en búfer define un área de memoria persistente que consta de tres campos: dirección para escribir, datos para escribir y un indicador READY . Una "escritura" consta de cuatro pasos: escribir en el búfer, configurar el indicador READY, escribir desde el búfer, borrar el indicador READY. Al encender, verifica el indicador LISTO. Si está configurado, ejecute lo que esté en el búfer. Esto funcionó bien en EEPROM, pero no estoy seguro de si funcionará bien en flash.

La memoria flash es en realidad un poco peor que EEPROM, porque dependiendo de lo que use (flash desnudo o flash con controlador, el suyo tiene un controlador interno), debe borrar y escribir bloques más grandes que en EEPROM (que generalmente está organizado por palabra). Por lo general, esto lleva mucho más tiempo y le da la posibilidad de escrituras corruptas. Como puede leer en la hoja de datos, el borrado de un sector puede tardar hasta un segundo (!), Mientras que las escrituras nuevas tardan 15 ms (en su chip). Entonces, diría que use lo que se ha recomendado antes: haga una verificación de apagón antes de escribir en flash.
FRAM evita este problema, a expensas del costo / disponibilidad.
Desafortunadamente, el costo y la disponibilidad son mis principales preocupaciones.
Cuatro respuestas:
#1
+5
Dave Tweed
2014-03-06 23:01:43 UTC
view on stackexchange narkive permalink

Nunca he oído hablar de un chip de memoria flash (o procesador con flash interno) que tenga suficiente almacenamiento de energía internamente para completar un ciclo de escritura (o borrado) si se debe quitar la alimentación externa. En otras palabras, si no tiene control sobre cuándo se apaga su sistema, siempre necesita crear un protocolo que pueda detectar y manejar cualquier operación de actualización flash individual que pueda haber sido interrumpida.

Una forma de evitar esto es proporcionar el almacenamiento de energía necesario (por ejemplo, un condensador electrolítico) en su placa, de modo que pueda detectar una falla de energía externa, pero aún así completar cualquier operación de escritura / borrado que ya haya iniciado.

EDITAR: Su concepto de búfer de escritura podría usarse con el flash externo, pero debe modificarse para tener en cuenta la granularidad de borrado mayor. Según la hoja de datos, el tamaño mínimo de borrado es un "sector" (4K bytes).

Deberá reservar tres sectores para su búfer de escritura. Uno de ellos sostendrá su bandera READY (llame a esto el wector WB_R). El segundo contendrá la dirección de sector del sector que se está actualizando (llamémosle sector WB_A). El tercero contendrá los datos actualizados para ese sector (llámelo sector WB_D).

Para actualizar cualquier byte en particular (o un grupo de bytes en un solo sector), siga los siguientes pasos. Suponemos que WB_R ya está borrado.

  1. Borre WB_A.
  2. Ubique el sector flash que contiene el byte que desea cambiar (llámelo sector DEST).
  3. Escriba la dirección de sector de DEST en WB_A.
  4. Borre WB_D.
  5. Copie el contenido de DEST en WB_D, pero cuando llegue al byte (s) que está cambiando, escriba los nuevos valores en WB_D en lugar de los valores anteriores.
  6. Establezca la marca READY en WB_R. Tenga en cuenta que esto significa que lo cambia a su estado no borrado. Dado que el estado borrado es 0xFF, esto significa que escribe 0x00.
  7. Erase DEST (obteniendo la dirección del sector de WB_A).
  8. Copie el contenido de WB_D a DEST.
  9. Borre WB_R.

Al encender, verifique la marca READY y si está configurada (cualquier cosa que no sea 0xFF - puede haber sido solo parcialmente escrito o borrado parcialmente), vaya directamente al paso 7.

Tenga en cuenta que con este algoritmo, cada uno de los sectores del búfer de escritura se escribe y se borra al menos una vez por cada operación de escritura que realice hacer. Esto podría convertirse en un problema si realiza muchas escrituras (más de 100.000) durante la vida útil del producto. Si ese es el caso, necesitará un algoritmo de nivelación de desgaste más sofisticado.

Mi técnica de escritura en búfer también debería funcionar, ¿verdad?
No sé, tendrías que describirlo con más detalle. Parece que puede estar confiando en el hecho de que su tablero tiene suficiente almacenamiento de energía para completar ciertos tipos de ciclos de escritura.
Agregué una descripción de mi técnica de almacenamiento en búfer a la pregunta. No creo que el almacenamiento de energía tenga ningún efecto en lo que estoy haciendo, pero podría estar equivocado.
Bien, dado que las escrituras de un solo byte son idempotentes (es decir, no importa si se hacen más de una vez), parece que su búfer de escritura puede funcionar. El único modo de falla que veo es si falla la energía durante la escritura de la dirección o los datos en el búfer de escritura o al configurar el indicador READY, esa escritura en particular nunca sucederá, pero al menos la dirección de destino no se borrará. Esta técnica también podría funcionar con flash externo, pero tendrá que tener en cuenta la granularidad de borrado más grande, ya que depende de que el búfer de escritura, el indicador READY y la ubicación de destino se puedan borrar individualmente.
No estoy 100% claro sobre la granularidad de borrado. ¿Estoy entendiendo correctamente que el flash borra inherentemente bloques grandes y reescribe todas las partes que no han cambiado? Si es así, mi idea de búfer definitivamente no funcionará ...
No, con la mayoría de los dispositivos flash externos (hay excepciones), el borrado y la escritura deben realizarse explícitamente como dos pasos separados, y usted es responsable del almacenamiento temporal de los datos que no cambian en una operación de tipo lectura-modificación-escritura. Por lo general, la escritura se puede hacer un byte a la vez, pero el borrado funciona en una "página" más grande o en un bloque de bytes a la vez. Vea mi edición arriba.
Obviamente, he estado usando la memoria incorrecta. EEPROM es claramente una mejor opción para mi aplicación. Debería haber comenzado con esta pregunta: http: //electronics.stackexchange.com/questions/65503/why-would-one-still-use-normal-eeprom-instead-of-flash ¡Aprecia la ayuda!
@StephenCollings: O un nvSRAM, que se almacena automáticamente usando la carga de un límite dedicado cuando el suministro primario cae.
#2
+3
Jon Watte
2014-03-07 00:00:30 UTC
view on stackexchange narkive permalink

La escritura almacenada en búfer no es suficiente. Debe tomar una hoja del sistema de archivos y los chicos de la base de datos aquí: necesita una estructura de datos en flash que pueda recuperarse a un estado "bueno" cuando un bloque está dañado.

Una forma típica de hacer esto Es para ping-pong entre dos cuadras. Haga que los últimos dos o cuatro bytes de los bloques sean el "número de serie" del bloque y el resto de sus datos en el resto del bloque. Cuando escriba un nuevo bloque, incremente el número de serie del bloque anterior en uno, omitiendo el valor de borrado "0" (que puede ser 0xff, dependiendo del tipo de flash) y escriba el nuevo bloque con ese número de serie.

Al encender, lea ambos bloques y vea cuál tiene el número de serie posterior (teniendo en cuenta el ajuste de 0xffff-> 0 e ignorando los valores de borrado omitidos). Utilice ese bloque. Es posible que también desee agregar un CRC de sus datos para verificar que no estén dañados en el medio (aunque si coloca el número de serie al final, eso "no debería" ser un problema).

Si tiene datos complejos, puede ampliarlos de la forma en que una base de datos o un sistema de archivos actualizarán un árbol en el disco, o incluso implementarán el registro de escritura.

He usado ese enfoque en el pasado, pero desde entonces me he encontrado con dispositivos flash con bloques que se comportaron de manera muy malvada después (presumiblemente) de haber sido borrados parcialmente.Desde entonces, comencé a usar al menos tres bloques para que en cualquier momento uno siempre pueda identificar el último bloque que se borró, usando solo la información de los otros dos bloques].Dos bloques no son suficientes porque un bloque que se está borrando puede adquirir arbitrariamente el patrón de bits necesario para decir que el otro bloque es el último borrado.Usar tres bloques evita ese problema ...
... ya que si dos bloques se "acusan" entre sí, entonces uno de ellos debe ser el último que se borre, y el bloque restante sabrá cuál era.
#3
+2
jonk
2014-03-06 23:28:43 UTC
view on stackexchange narkive permalink

Esta es un área en la que debes sentarte y elaborar estrategias cuidadosamente. Algunos detalles de la hoja de datos son:

  1. Borrado de sector 4k: \ $ 400ms \ $ peor caso
  2. Borrado de bloque de 32k: \ $ 800ms \ $ peor caso
  3. Borrado de bloque de 64k: \ $ 1000ms \ $ peor caso
  4. escritura de página: \ $ 3ms \ $ peor caso
  5. primer byte escribe: \ $ 40 \ mu s \ $ peor caso
  6. siguiente byte escriba: \ $ 12 \ mu s \ $ peor caso

Probablemente sea una buena idea, si tiene control sobre este detalle, organizar local potencia (a través de la carga almacenada de un condensador) que se mantendrá dentro de un "margen de caída" que usted determine mientras se realizan las escrituras críticas. Esto no tiene por qué ser un segundo completo, si utiliza sabiamente el tiempo de escritura del primer byte (no olvide incluir tiempos adicionales de comunicación / configuración con eso). Puede actualizar solo uno o dos bytes en una página especial que significa que se está iniciando un borrado de bloque o sector, por ejemplo. Esto le permite determinar, si experimenta un apagón o un reinicio, dónde estuvo por última vez para poder finalizar el proceso. Es posible que también necesite más de una "página especial". ¡Pero en cualquier caso, debe considerar todos los casos a fondo!

#4
+1
supercat
2014-03-07 23:44:02 UTC
view on stackexchange narkive permalink

Si se pierde energía mientras un chip flash está borrando el bloque, el software robusto debe asumir que el contenido del bloque puede cambiar arbitrariamente en cualquier momento a menos que o hasta que el bloque se vuelva a borrar y una el ciclo de borrado se completa. Incluso si el bloque todavía parece contener datos antiguos, no hay garantía de que continuará haciéndolo durante un período de tiempo. Incluso si el bloque parece borrado, no hay garantía de que los bits programados no "aparezcan" espontáneamente. He visto algunos procesadores con flash interno que incluían la capacidad de verificar si los bits estaban "realmente" en blanco o estaban programados "a fondo", pero nunca había visto tal funcionalidad expuesta por un dispositivo flash externo.

Si uno desea poder almacenar datos periódicamente en la memoria flash y asegurarse de que, en caso de falla de energía, cada actualización tendrá éxito por completo o no tendrá éxito en absoluto, debe tener al menos tres bloques de memoria flash y definir un protocolo tal que siempre se está borrando un bloque, se puede determinar eso basándose únicamente en el contenido de los otros dos bloques. Hay una variedad de protocolos para implementar esto; Sugeriré uno simple aquí, asumiendo que la cantidad de información a almacenar es un bloque completo menos una unidad programable de tamaño mínimo, y hay tres bloques disponibles, que llamaré X, Y y Z.

Cada bloque tendrá un bit de "control" dentro de él que está reservado para rastrear el estado de validez / borrado; Llamaré a esos bits x, y y z. Durante el funcionamiento, el sistema mantendrá la invariante de que el bloque que contiene los datos correctos tendrá su bit de control en blanco; el bloque "precedente" (X está precedido por Z) tendrá su bit de control programado. Los bits de control para el bloque restante (el que "sigue" al que tiene datos válidos) serán irrelevantes. Si todos los bits de control están en blanco, nunca se ha escrito correctamente; si todos los bits de control están programados, algo se ha dañado seriamente.

Para escribir nuevos datos, borre el bloque que sigue al que contiene los datos correctos, luego almacene los nuevos datos en ese bloque. Finalmente, como último paso, programe el bit de control del que solía ser el bloque actual. Hasta que se programe ese bit de control, nada se preocupará por el contenido del bloque que se acaba de programar. Una vez programado ese bit, nada se preocupará por el contenido del bloque que sigue al nuevo bloque. Siempre que el sistema tenga suficiente energía disponible para garantizar que la programación de ese bit tenga éxito o falle limpiamente, se garantiza un funcionamiento confiable en todos los escenarios de pérdida de energía.

Suponga que x está programado, y es en blanco y z es cualquier cosa. Debido a que el bloque de datos válido debe tener su propio indicador en blanco y el indicador del bloque anterior debe estar programado, X no puede ser un bloque válido (el indicador x está programado) y Z no puede ser un bloque válido (porque el indicador y está programado). En consecuencia, Y es el único bloque que puede contener datos válidos. El bloque X contiene la versión anterior de los datos, y no se puede confiar en que Z contenga nada. Cuando sea necesario almacenar nuevos datos, el código debe comenzar borrando Z (independientemente de si ya aparece en blanco) y programando todos los datos que deben contener. Si se pierde energía en cualquier momento durante este proceso, el estado del sistema será el mismo que antes de comenzar (según los indicadores, se presume que el contenido de Z no tiene sentido, por lo que su contenido no afecta el estado del sistema en absoluto).

Solo después de que todas las escrituras en Z estén completas y contenga datos válidos, se debe programar la marca y. Una vez que se escribe esa bandera, Z será reconocible como el bloque que contiene datos válidos ya que su propia bandera estará en blanco mientras se programa la bandera de los bloques anteriores (y); el hecho de que y ahora esté programado significará que Y ya no es válido.

La próxima vez que sea necesario almacenar nuevos datos, el bloque X debe borrarse y tener los datos almacenados allí; la finalización debe indicarse mediante el indicador de programación z. El tiempo después de eso, Y debe borrarse y tener los datos almacenados allí, con la finalización indicada por el indicador de programación x. Es vital que los intentos de programar los indicadores x, yyz se ejecuten hasta el final o no tengan ningún efecto, pero esas son las únicas operaciones que necesitan ser "atómicas garantizadas" a nivel de hardware. Todas las demás escrituras en la memoria se realizarán en un bloque cuyo contenido nunca se verá (*) a menos que se complete.

(*) El sistema generalmente no podrá evitar el acceso al bloque no válido, pero el comportamiento del sistema no se verá afectado por el valor leído.

Por cierto, si uno no confía en la capacidad de garantizar que las escrituras de banderas se ejecuten hasta su finalización, existen varios enfoques con bits de banderas redundantes que podrían ayudar un poco, pero la confiabilidad ya no estará asegurada. Suponga, por ejemplo, que el sistema pierde energía mientras el bit y está parcialmente programado, por lo que a veces se leerá como programado pero a veces como en blanco. Si en el primer encendido, y se lee en blanco, la próxima actualización borrará Z. Si durante ese borrado, el sistema pierde energía y en el siguiente encendido, y se lee como se programó, el sistema supondría que Z es el bloque válido. Si y hubiera leído como se programó en ambas ocasiones, entonces Z habría sido el bloque válido y el siguiente bloque borrado habría sido X. Si se hubiera leído en blanco en ambas ocasiones, entonces Z habría sido reconocido correctamente la segunda vez como bloque inválido. Aunque uno podría intentar protegerse contra estos peligros agregando bits de bandera redundantes, tales enfoques no ayudan mucho. Uno puede diseñar las cosas de modo que sea "improbable" que los indicadores parcialmente programados se comporten de manera problemática, pero eso es fundamentalmente diferente de la garantía de que si las escrituras de indicadores funcionan de forma atómica, nada que el chip pueda informar sobre otros datos parcialmente escritos causaría algún problema.



Esta pregunta y respuesta fue traducida automáticamente del idioma inglés.El contenido original está disponible en stackexchange, a quien agradecemos la licencia cc by-sa 3.0 bajo la que se distribuye.
Loading...