如何将Flash模拟成EEPROM (EEPROM Emulation)
Posted 嵌入式软件实战派
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何将Flash模拟成EEPROM (EEPROM Emulation)相关的知识,希望对你有一定的参考价值。
如何将Flash模拟成EEPROM (EEPROM Emulation)
温馨提示:
本文参考《EEPROM Emulation with Qorivva MPC55xx, MPC56xx, and MPC57xx Microcontrollers》
关注公众号“嵌入式软件实战派”,并回复“EEP”可获得本文PDF版和《EEPROM Emulation with Qorivva MPC55xx, MPC56xx, and MPC57xx Microcontrollers》文档。
EEPROM与Flash Memory 特性
EEPROM 和Flash都是非易失性存储器,这意味着它们在断电后仍保留其数据。 它们都由一组存储单元(memory cells )组成,其中每个存储单元保存一位或多位信息。 在一些设计中,多个单元组合形成一个位。
在每个存储单元保存一位信息的 EEPROM 和Flash中,(对于Qorivva MPC55xx/MPC56xx/MPC57xx 等芯片)存储单元的默认擦除状态读取为二进制 1。对存储单元进行编程会将其值从二进制 1 更改为二进制 0,并且擦除存储单元会更改其值 值从 0 到 1。编程不能反向工作:只有擦除操作才能将存储单元的值从 0 更改为 1。
EEPROM通常是以一个Byte或者Word为单位写入和擦除的,即擦写是以Byte或Word为边界,这样就使得EEPROM擦写比较独立自由,也不会影响其他数据。另外,EEPROM的擦写寿命(PE cycles)是比较高的,达百万次。
而Flash(以Qorivva Device为例)通常是以Word或Double-Word为写入单位,但是擦除却以Block为单位,这个Block大小往往又是以KB衡量的。这些都是半导体制造工艺或设计特性有关,这也使得Flash的容量非常大,但擦写寿命很短(通常只有十万次PE cycles)。
使用Flash Memory模拟EEPROM
将Flash模拟成EEPROM来使用,会遇到两个问题:
- 修改Flash中存储的值需要擦除整个内存块。
- 频繁更新数据的应用程序必须注意PE cycles要求。
那么,怎么可以完美解决这两个问题呢?
可以通过Record(记录)列表的方式来实现,每个Record都是一个或多个变量的组合。每条Record还有一个标签(Tag),用于标识Record中包含的变量、Record的大小以及Record是否有效。
一组Flash Block被设定为 EEPROM emulation block。从这组块中,选择一个块作为Active Block。Record被编程到Active Block中的连续内存位置。为了更新Record,软件将Record的更新版本添加到Active Block中的下一个可用位置。读取Record是通过检索带有匹配标记的最近写入的Record(即最后一条Record)来完成的。
当Active Block变满时,软件将Active Block内的所有有效Record复制到其他 EEPROM emulation block之一中。这个新Block成为Active Block,然后擦除前一个Active Block。由于此块交换过程会清除过时的Record,因此新的Active Block将有空间用于进一步的Record更新。
Record结构
Record分为两个主要部分:
- Data Segment,即数据部分,其中保存应用程序变量,
- Metadata Segment,即元数据部分,其中包含管理和检索Record中数据所需的所有信息。
Metadata Segment进一步细分为其他字段:
- Record Status Field,即Record状态字段,该字段用于从错误中恢复和保持数据一致性。
- ID Field,即ID 字段,此字段用于标识Record中包含的数据。
- Size Field,即大小字段,该字段用于确定数据段内的数据字节数。
以上的ID Field和Size Field的长度是可以根据应用而设置不一样的,但是Record Status Field却不能随意设置,这个跟Flash的ECC有关。
下图,根据Flash的ECC不同,有两种不同的设计:
ECC和Record Status
纠错码 (ECC) 与存储设备一起使用,以纠正或检测由于辐射或电磁干扰导致的内存损坏。 一般IC(如Qorivva Device)上的Flash采用单bit校正、双bit检测ECC算法。
ECC 的工作原理是对写入Flash的数据计算固定数量的奇偶校验位。 稍后从闪存读取数据时,数据位与奇偶校验位组合,任何错误都会得到适当处理。 在 Qorivva 设备上,每个 4 字节或 8 字节边界都被指定为一个校验基,并且有自己的一组奇偶校验位(校验基大小是特定于设备的,如下表所示)。
在以上Flash模拟EEPROM的方案中,那个Record Status在不同状态变化时是需要写入不同的值的。通常,在不擦除的前提下,多次写入Flash是不允许的,但是,这里有个方法。
如果这个IC没有ECC,只要写入时将Flash里面的bit位从1改成0是没有问题的,因为写入的过程就是讲1改成0,但是注意,如果不擦除,将0改成1是不行的哦。虽然说,在已经擦除过的数据位中,一个bit只有一次从1改成0的机会,但是Record Status是有很多bit的,是不是可以改写很多次?例如:
0xFFFFFFFF → 0xFFEF3FFF → 0x0FEE1ABC → 0x02AE1AB0
但是,IC是有包括 ECC的,因此重复写一个值也会尝试重写校验库中的 ECC 奇偶校验位。 即使数据位仅从 1 更改为 0,新数据也可能需要将奇偶校验位从 0 更改为 1。
由于奇偶校验位是闪存的一部分,因此编程操作将失败。
这里就看ECC是怎么做保护的了,有些IC(如Qorivva Device),如果重写(reprogram)是按group(如8/16bits)进行Clear的话,那是没问题的。例如,具有 8-byte ECC checkbase的设备可以使用以下双字序列reprogram,允许清除 16-bit group:
0xFFFFFFFFFFFFFFFF → 0x0000FFFF0000FFFF → 0x000000000000FFFF
通过这个特性,这个Record Status就可以reprogram多次了。以下汇总了Qorivva Device的ECC情况:
Device | Checkbase size | 1/0 bit groupings | Example value |
---|---|---|---|
MPC55xx | 8 bytes | 16 bits | 0xFFFF_0000_FFFF_0000 |
MPC56xx (instruction flash) | 8 bytes | 16 bits | 0xFFFF_0000_FFFF_0000 |
MPC56xx (data flash) | 4 bytes | 8 bits | 0xFF00_00FF |
MPC57xx | 8 bytes | 8 bits | 0xFFFF_0000_FF00_00FF |
添加和更新Record
在向Active Block添加新Record之前,EEPROM emulation软件定位块中第一个擦除的存储位置。 Record按顺序写入Active Block,因此软件知道剩余的存储位置也被擦除。 然后,软件从该位置确定是否有足够的空间来保存Record。
实际的写入过程分为以下几个步骤:
- 软件将Record状态写为
$invalid
。 - 软件将 ID 和Size字段以及打包到同一个 ECC checkbase中的任何其他数据写入。
- 软件写入剩余数据。
- 软件将Record状态写为
$valid
。
选择 $valid
和 $invalid
值,使它们遵循上一节中解释的 ECC 规则。下表给出了status字段的建议值。 注意,在写入$invalid
值之前,Record状态被认为是$empty
,即被擦除内存的值。
Status | 4-Byte ECC boundary | 8-Byte ECC boundary |
---|---|---|
$empty | 0xFFFF_FFFF | 0xFFFF_FFFF_FFFF_FFFF |
$invalid | 0x0000_FFFF | 0x0000_0000_FFFF_FFFF |
$valid | 0x0000_0000 | 0x0000_0000_0000_0000 |
要更新已写入Flash的Record,软件只需将更新的Record添加到Record列表的末尾。 在搜索具有特定 ID 的Record时,软件知道具有匹配 ID 的最后一条Record是最新的Record。
下图显示了写入多条Record后Active Block内存内容的示例。 在此示例中,在添加第二条Record时Reset了设备。 由于Record添加过程未完成,第二条Record的状态为 $invalid
。
读Record
当应用软件请求读取一条Record时,EEPROM emulation软件必须定位该Record。 应用软件提供Record ID,EEPROM emulation软件遍历Record列表,沿途比较每个RecordID。 EEPROM emulation软件返回具有匹配 ID 的最后一个有效Record的地址。
遍历Record列表使用以下步骤,从块中的第一条Record开始:
- EEPROM emulation软件检查Record状态。 如果Record状态为
$empty
,则软件位于列表末尾并返回最近匹配Record(如果有)的地址。 - 软件检查Record的ID。 如果 ID 与请求Record的 ID 匹配,并且Record状态为
$valid
,则软件会Record此记录。 由于Active Block中可能同时存在多个具有相同 ID 的记录,因此软件必须继续遍历列表,直到到达末尾。 - 软件读取Record的大小,并用它来计算列表中下一条Record的地址。
- 软件移至下一条Record,从步骤 1 重新开始该过程。
在Block交换操作期间或添加Record时,EEPROM emulation软件以类似的方式遍历Record列表。 在块交换的情况下,记录 ID 被忽略,因为软件只关心查找有效Record。 在添加Record的情况下,软件遍历Record列表只找到列表的末尾。
在添加Record中断中恢复
某些事件(例如设备断电)可能会在任何时间点中断Record添加过程。 正确使用Record状态字段允许应用程序优雅地恢复,即使易失性上下文信息丢失。每当 EEPROM emulation软件遍历Record列表时,它就会跳过标记为 $invalid
的记录。标记为 $invalid
的Record会在块交换过程中从Record列表中清除,因为它们不会写入新的Flash块。
下表将Record添加过程的每个阶段与每个阶段完成后的Record状态配对。
Stage | Operation | Record status | ID/Size field |
---|---|---|---|
A | 写操作之前的Record的状态 | $empty | blank |
B | 软件写Record status 为$invalid. | $invalid | blank |
C | 软件将 ID 和Size字段以及打包到同一个checkbase中的任何数据写入。 | $invalid | written |
D | 软件对剩余的Record数据进行写入。 这需要多次写入,具体取决于数据的大小。 | $invalid | written |
E | 软件写Record status 为$valid | $valid | written |
$invalid
Record 状态有两个目的:标记损坏Record的存在,以及防止边缘写错误。
如果系统正在写入时被中断,可能存在写入不成功,即边界写入情况。此时的Flash状态处于已擦除和已写入之间,如果以相同的值再次写入,这是没有问题的。
但是,真实的情况不仅仅这样。当读取的数据来源于已擦除的位置,那么应用程序是不知道这个值是擦除值还是正常写入的值。那么要依赖Record Status的值了,这样可以通过$invalid
来防止边界写入的情况了。如上表中的State B,第一步就写入$invalid
状态,然后再写数据,最后在State E才将状态改为$valid
。
举例说明:
如果软件在State B 完成之前被中断(如上表所述),$invalid
值将被写入。由于下次软件尝试添加Record 时会写入相同的值,因此操作将成功完成。
当跳过 $invalid
块时,EEPROM emulation软件会查看Record Size以了解下一条Record 在内存中的位置。如果软件在阶段 C 完成之前中断,则Record ID 和Size不可用。出于这个原因,软件将任何具有空白大小字段的Record 视为保存零字节数据的Record 。由于没有写入任何Record 数据,软件使用紧跟在 ID 和Size字段之后的字/双字(取决于checkbase大小)作为下一条Record 的开始。阶段 C 完成后,软件通过跳过不完整的 $invalid
Record 从任何中断中恢复。
交换(Swapping)Block
在多次Record 添加和更新之后,活动块中将没有足够的擦除内存来执行进一步的更新。在此阶段,EEPROM emulation软件通过将有效Record 从活动块复制到备用块来执行块交换。
每当软件擦除一个块时,软件随后会通过检查块中的每个存储位置是否都读回全1来验证该块是否已被擦除。任何被验证为已擦除的块都可以用作块交换中的替代块。验证擦除的块可防止软件使用未完全擦除的块作为替代块。尝试对未完全擦除的块进行编程可能会导致错误。
选择备用块后,软件会读取活动块的内容并将每个 ID 的最新Record 复制到备用块中。然后将备用块标记为任何后续访问的Active Block,软件会擦除并验证前一个Active Block以备将来使用。每个块在块的开始处保留一个字/双字(参考“添加和更新Record”章节的图示)来保存块Status。块Status字段的使用方式与Record Status字段的使用方式相同,并在四种可能的状态之间依次循环:
$erased
- 擦除块的默认值(全 1)$verified
- 块已被验证为已擦除$copy
- 块被用作块交换中的替代块$active
- 块用作Active Block
与Record Status字段一样,必须特别选择立即值 $verified
、$copy
和 $active
,以便遵守 ECC 规则。 下表提供了一些建议值。
Status | 4-Byte checkbase, groups of 8 bits | 8-Byte checkbase, groups of 16 bits |
---|---|---|
$erased | 0xFFFF_FFFF | 0xFFFF_FFFF_FFFF_FFFF |
$verified | 0x00FF_FFFF | 0x0000_FFFF_FFFF_FFFF |
$copy | 0x0000_FFFF | 0x0000_0000_FFFF_FFFF |
$active | 0x0000_00FF | 0x0000_0000_0000_FFFF |
下表显示了块交换操作的不同阶段,以及该过程中涉及的每个块的状态。 State A 代表正常操作条件,其中Flash块 A 是Active Block。 其他阶段是仅存在于块交换过程中的短暂瞬间阶段。 如果块交换过程在任何时候中断,EEPROM emulation软件能够恢复并识别用于后续访问和更新的块。
Stage | Block A status | Block B status | Operation |
---|---|---|---|
A | $active | $verified | Block A 激活且Block B 验证擦除的初始状态。 |
B | $active | $verified → $copy | 软件将区Bloc B 的Status字段设置为 $copy,表示区块交换操作开始 |
C | $active | $copy | 软件从Block A复制active records到Block B. |
D | $active | $copy → $active | 软件完成将Active Record从Block A 复制到Block B。软件将Block B 设置为新的Active Block。 |
E | $active → $erased | $active | 软件擦除Block A. |
F | $erased → $verified | $active | 软件验证Block A 已被擦除并将Block A 的Status设置为 $verified。 |
在Block Swap中断中恢复
与Record添加一样,可以从块交换操作被中断并且所有易失性上下文信息丢失的情况中恢复。软件将状态不是 $verified
或 $active
的任何块视为处于故障状态,并且这种情况下的任何块都应该被擦除并验证为空白。有关本节中使用的State的说明,请参阅上表。
如果块交换过程在State B 或 C 中断,则Flash Block A 仍标记为Active Block,从而仍然可以正确访问Record。标记为 $copy
的Flash Block B 包含可能损坏或部分编程的数据Record。为了将Block B 返回到已知的有用状态,EEPROM emulation软件会擦除Block B 并验证它是否为空白。然后软件重新启动块交换过程。
如果块交换过程在State D 中断,则两个块都标记为 $active
。软件识别哪个块具有最大数量的未使用(即擦除)Flash并将该块用作Active Block。另一个块已完全或几乎完全已满,这是需要进行块交换过程的最初原因。软件通过擦除完整块并验证其为空白来完成Block Swap过程。
如果Block Swap过程在State E 或 F 中断,则Block A 处于部分擦除状态。这种状态可以通过非法Block/Record状态值或可能发生的 ECC 错误来检测。恢复包括重新擦除Block A 并验证它是空白的。与Record添加一样,可以从块交换操作被中断并且所有易失性上下文信息丢失的情况中恢复。软件将状态不是 $verified
或 $active
的任何块视为处于故障状态,并且这种情况下的任何块都应该被擦除并验证为空白。有关本节中使用的State的说明,请参阅上表。
模拟EEPROM的软件实现和变种
前几章节描述了同个Record和Block的方式模拟EEPROM的方案,以下讲讲其软件实现,会涉及到几个关键技术:
- 将数据分组到标记Record列表中
- 将多条Record打包到一个块中以减少块擦除次数
- 覆盖Status字段时正确处理 ECC
- 当活动块变满时进行Block Swap
- 以允许从意外中断中恢复的方式处理Record
Record Cache
添加到 EEPROM emulation软件的一项可能功能是能够将Record缓存到 RAM 中。 此功能可实现在驱动程序中。 在 RAM 中保留一个区域来保存几个最常用(或最近使用)的Record具有以下几个优点:
- 读取和写入 RAM 的速度比各自的Flash访问速度快。
- Record更新可以就地进行。
- 将更多的Record操作移入 RAM 意味着更少的Block Swap操作,从而减少块擦除的次数。
添加此功能会增加 EEPROM emulation软件的复杂性,因为该软件现在必须处理Flash和 RAM 之间的移动记录。
软件可以构建一个查找表,保存给定Record ID 的Record的内存位置,而不是将整个Record保存在 RAM 中。 这减少了搜索Record所需的时间(特别是对于接近满的块),因为软件只需要参考查找表而不是遍历整个Record列表。
固定的Record长度
在某些情况下,不需要可变Record长度方案的灵活性,固定Record长度方案就足够了。 如果应用程序中的每条Record的长度都相同,或者如果多个大小混合的变量总是组合在一起形成一个固定长度的Record,则Record长度字段携带冗余信息,可以删除。 由于已知Record总是从内存中的固定偏移量开始,因此降低了搜索固定长度Record所涉及的软件复杂性。 不再需要读取每条Record的大小字段来计算下一条Record的地址。
单Flash Block
某些应用程序可能只有(或想要)一个可用于 EEPROM emulation的块。 由于只有一个块可用,EEPROM emulation软件必须使用不同的块来做Block Swap。 当单个块用完可用空间时,软件会在继续擦除过程之前将活动的有效Record复制到 RAM 中。 在块被擦除和验证后,软件将Record写回闪存。
仅使用单个Flash Block进行 EEPROM emulation会带来很大的数据丢失风险。 在Block Swap过程中,如果发生严重错误或在软件将每条Record写回Flash之前设备断电,这些Record中的数据将丢失。 对于大多数应用程序,建议至少使用两个Flash Block,以便数据的非易失性副本始终可用以从此类问题中恢复。
CRC
除了硬件 ECC,EEPROM emulation软件还可以用CRC来检测Record错误。下次访问此数据时,将重新计算校验值并与之前的值进行比较。如果两个检查值不匹配,则已知数据已更改。存在许多 CRC 规范,不同的规范提供不同级别的保护。
例如可以让 EEPROM emulation软件使用 CRC-32-IEEE 标准。软件每次写入Record时,软件都会使用RecordID、大小和数据来计算一个32位的校验值。然后,软件会在Record之后立即将此校验值写入Flash(同时正确处理 ECC checkbase大小),从而有效地将校验值附加到Record中。每当软件读取Record时,它都会重新计算校验值。如果重新计算的校验值与先前的校验值不匹配,则软件知道Record ID、大小、数据或校验值已更改。
擦写寿命(Program-Erase Endurance)
可模拟的 EEPROM 数量取决于Flash Block大小和预期的数据访问次数。由于使用大量Flash来实现一定数量的模拟 EEPROM,Flash看到的program/erase (PE) cycles 减少了。这允许以比原生Flash规范更高的 PE 耐久性来模拟EEPROM。
例如,假设设备上的 16 KB Flash块被指定为至少 100,000 个program/erase (PE) cycles 。这意味着在编程然后擦除 100,000 次后不能保证Block工作。但是,这并不意味着只能进行 100,000 次记录操作。
如果 EEPROM 模拟数据只有一个 256 字节的Record(包括Record Status、ID 和Size字段等开销),则该Record可以在 16 KB 块中更新 63 次,然后才需要擦除该块(16 KB Byte/ 256 Byte = 64,但块Status字段也消耗了一些内存)。如果系统使用两个 16 KB 块来保存 EEPROM 模拟数据,那么在块达到 100,000 个 PE cycles之前,这个 256 Byte的记录可以更新 12,600,000 次。使用为 100,000 个 PE cycles指定的两个 64 KB 块可以维持四倍的记录更新。
随着所需模拟 EEPROM 数据量的增加,所需Block Swap操作的频率迅速增加。最终用户必须根据诸如Record更新频率和Flash的 PE 耐用性规范等输入来估计模拟 EEPROM 的最大数量。
通过使用尽可能多的Flash Block进行 EEPROM 模拟,应用程序可以进一步延长Flash寿命。 如下图所示,通过循环方式遍历多个块,进一步减少了每个块上的擦除。
本文参考《EEPROM Emulation with Qorivva MPC55xx, MPC56xx, and MPC57xx Microcontrollers》
关注公众号“嵌入式软件实战派”,并回复“EEP”可获得本文PDF版和《EEPROM Emulation with Qorivva MPC55xx, MPC56xx, and MPC57xx Microcontrollers》文档。
以上是关于如何将Flash模拟成EEPROM (EEPROM Emulation)的主要内容,如果未能解决你的问题,请参考以下文章