单片机内存保护:如何利用错误保护代码(ECC)管理实现内存保护
1. 绪论
本文介绍了与ECC机制相关的硬件和软件方面内容,探讨如何使用ECC机制用于保护内部存储器的内容。虽然可以使用ECC保护外部存储器,但该文档不涉及外部存储器的ECC实现。
文档内容包括:ECC保护的一般信息、详细的硬件ECC故障管理、以及ECC实现的细节,本文档还提出了软件安全解决方案的具体实现。
2. 一般信息
本文档适用于基于Arm®的微控制器(单片机)。
注:Arm是Arm Limited(或其子公司)在美国和/或其他地方的注册商标。
2.1 缩略语和术语
ECC 错误校正码
CPU 中央处理单元(MCU的一部分)
CRC 循环冗余校验
DED 双错误检测
DTCM 数据紧密耦合存储器
FAR 下降地址寄存器
ISR 中断服务例程
ITCM 指令紧密耦合存储器
MCU 微控制器单元
MDMA 主直接存储器访问
POR 电源开启重置
RAM 随机存取存储器
SEC 单错误校正
SRAM 静态随机存取存储器
3. ECC概述
数学家Richard Hamming发明了第一个ECC。原始的汉明码使用7位存储4位信息,使用冗余位进行错误校正和检测。在arm核MCU设备中,所选的RAM和闪存存储器使用基于汉明原理的SEC-DED算法进行保护,但通过增加一个额外的奇偶校验位进行了改进。ECC代码能够检测并纠正单个位错误,并检测存储数据字中的双位错误。在SRAM易失性存储器中,一个迷路的α粒子可能会使位值翻转。这是一个持续的威胁,且不论硬件年龄大小,单比特故障的概率都是一样的。单比特或双比特错误故障在某些应用中是一个问题,尤其是在长时间不重置的情况下存储大量数据的应用,例如电池供电的数据记录器。在闪存中,数据会随着时间的推移而衰减,特别是在高温下。存储温度对闪存数据有影响,但编程温度的影响更大。闪存只能承受对每个存储字的一定数量的重写,这就需要在数据存储的情况下实现磨损均衡。特定产品的典型保持时间和寿命周期在产品数据表中发布。两种类型的故障(单比特错误和双比特错误)都是不可避免的,但正确使用ECC可以防止数据丢失。
表3. SEC-DED使用的额外校验位数量
数据字宽度 | 冗余位数 |
---|---|
16位 | 6位 |
32位 | 7位 |
64位 | 8位 |
128位 | 9位 |
256位 | 10位 |
3.1 ECC影响
ECC是旨在符合IEC60730 Class C或IEC 61508 SIL2及更高安全标准要求的嵌入式系统中的关键元素。没有硬件ECC的系统仍然可以满足目标安全标准的合规性,但它需要部署相当大的软件开销。使用ECC内存将整体诊断覆盖率提高到90%以上是容易实现的,并且提高了系统符合高安全标准的准备性。使用ECC的另一个优势是可能提高安全性,因为ECC的使用可能导致检测到硬件篡改。
3.2 RAM ECC
arm核单片机设备的RAM ECC功能具有类似外设的接口,具有用于设置的寄存器和允许快速响应检测到的故障的中断。具备ECC功能的所有arm核单片机的SRAM和指令/数据缓存存储器都受到ECC的保护。AXI-SRAM和ITCM-RAM的数据宽度为64位。所有其他易失性存储器都通过32位总线宽度(字大小)进行访问。在MCU上,只有紧密耦合存储器和指令/数据缓存存储器受到ECC的保护。其他SRAM不受ECC保护。 基于Cortex®‑M33的单片机具有更简单的内存架构。它们在用于数据的SRAM区域上实现了ECC,这些区域是备份SRAM、SRAM2和SRAM3。在SRAM3的情况下,ECC的使用仅限于SRAM3内存范围的前256 KB。最后的64 KB块然后对用户不可用,用于ECC冗余存储。
表4. MCU的SRAM ECC覆盖范围(以STM32为例)
设备系列 | AXI | ITCM | DTCM | SRAM1 | SRAM2 | SRAM3 | SRAM4 | 备份RAM |
---|---|---|---|---|---|---|---|---|
H72x/H73x | 64b | 64b | 32b | 32b | 32b | X | 32b | 32b |
H74x/H75x | 64b | 64b | 32b | 32b | 32b | 32b | 32b | 32b |
H7Ax/H7Bx | X | 64b | 32b | X | X | – | – | X |
H5 | – | – | – | X | 32b | 32b(2) | – | 32b |
U5 | — | – | – | X | 32b | 32b(3) | X | 32b |
H7Rx/H7Sx | 64b(1) | 64b | 32b | 32b | X | 32b | 32b | 32b |
(1)AXI总线是64位宽,但相同的存储器也可以通过AHB总线访问。
(2)只有SRAM3的前256 KB;最后的存储块用于管理ECC。
(3)不是整个SRAM3,详情请参见文档[6]。
请注意,表中的"X"表示在相应的设备系列中,该存储器区域没有ECC保护。脚注1、2和3提供了额外的信息,以帮助理解表中的数据。
在大多数的微控制器中,RAM ECC不能关闭。ECC与RAM一起供电和时钟,它是RAM接口的一个组成部分。例如,备份SRAM可以被禁用,这也禁用了与之相关的RAM ECC控制器。
ECC在数据以字为单位进行计算,如果写入的数据小于字的规格,则在读写修改写的基础上进行修改。在数据字不完整的访问上,ECC不会立即写入值,而是等待接下来的字节或半字,即需要等待下一次写入访问。这一机制在处理备份SRAM的应用中特别重要。例如,当修改字符数组时,这对设备功耗有非常积极的影响。然而,如果在等待期间发生复位的情况下,则写入操作并未完成(内存内容保留了最后不完整的字写入)。
为确保数组始终完全更新的方法是在每个常规写入后,即在SRAM中写入一个虚拟的不完整字,虚拟写入地址必须在相同的内存内(在这种情况下就是写入备份SRAM)。
图1. 在保留的SRAM中处理未对齐的访问
图2. 存储单元的RAM ECC控制器接口
每个内部SRAM块都被分配有RAM ECC控制器。本文以arm核意法半导体STM32H7架构为例,说明ECC控制器的工作机制。在这款MCU中,控制器分布在三个系统域中:D1、D2和D3。所有内部SRAM单元/控制器的诊断被集中收集到一个全局控制模块中。这个全局控制模块共享一组配置寄存器和一个全局中断信号,因此,中断事件存在被掩蔽的可能性。
特定分配给特定SRAM块的RAM ECC控制器在每次对该SRAM块的读取访问时检查数据完整性。某些读取访问类型并不明显,因为某些写入包括一个隐含的读取阶段。不明显的读取访问的一个例子是作为两个周期的读写/修改/写入执行的不完整RAM写入。这可以是小于RAM字的数据写入,或者是未对齐的写入。
3.3 闪存ECC
对于MCU而言,通常情况闪存字(可编程的最小存储量)是256位,当然,有的MCU也会选择128位。针对闪存,MCU通过10个ECC位(有的MCU是9个或更少的ECC位)实现闪存字上的SEC和DED功能。对任何小于字的存储单元的写入访问只能在读写修改的基础上进行,这个操作导致对存储器硬件的更高压力。
ECC功能集成到闪存存储器控制器中,不能被禁用。如果应用程序没有设计来利用ECC,它可以禁用相关的中断并忽略闪存存储器状态寄存器中的标记位。 集成ECC解决方案的缺点是,如果不先擦除该字,则不可能在闪存存储器字中编程单个位。由于有时在EEPROM仿真或单调计数器的实现中使用无擦除编程,因此在ECC启用的存储器上进行此类应用程序时必须选择另一种算法。 众多的闪存存储器控制器还实现了硬件CRC完整性保护。CRC是一个补充机制,而不是ECC的替代品。如果激活了自动后台CRC检查,则对闪存存储器的读取访问也隐含地在整个范围内检查了ECC。
3.4 缓存存储器ECC
缓存是没有自己的地址范围的存储器。它的目的是通过保留频繁访问的内容(无论是代码还是数据)或可能很快需要的内容(例如当前地址+1)的副本,来减少访问所寻址存储器的延迟。物理上,它是一个具有不同寻址的SRAM。 只有STM32H7系列具有带ECC保护的缓存。默认情况下,Cortex®-M7 L1缓存也通过使用相同的SEC DED代码受到ECC保护。字宽度为256位,因为整个缓存行都被覆盖。缓存ECC保护可以被禁用。要修改缓存ECC的状态,代码必须首先禁用并刷新缓存。然后可以修改ECC设置,并以新的设置重新启用缓存。
CPU缓存只涉及AXIM总线访问,ITCM和DTCM地址范围不需要缓存。紧密耦合存储器几乎专门用于Cortex®‑M7核心的使用。Cortex®‑M7处理器可以自动从检测到的任何ECC故障中恢复指令缓存。单比特错误由自动校正覆盖。对于双比特错误,该行将失效,指令将从程序存储器重新加载。在数据缓存中,双比特错误检测可能导致在重新加载旧数据时丢失正在进行的修改。
注意:不建议将写入通过实践作为对这种罕见事件的对策。 支持Cortex®‑M4核心的ART加速缓存,在双核心STM32H7系列微控制器上不受ECC保护。由于没有中断通知或接口用于ECC检测结果的CPU缓存,检测CPU数据缓存中罕见的双比特ECC错误的唯一解决方案是监视IEBR和DEBR核心寄存器。在来自指令存储器(通过闪存或SRAM)的指令存储器发生双比特ECC错误中断后,还有必要刷新或使指令缓存失效。否则,非法操作码可能仍然在缓存中,导致硬故障。
3.5 ECC测试
可以测试对ECC错误的反应。在RAM中很容易实现,在冷启动时读取未初始化的RAM通常足以触发ECC错误并测试反应。效果是随机的,但仍然容易产生。 注意:在特定的MCU中,通常有一个基于软件的程序可用于测试ECC效率,请参阅相关MCU技术手册。该测试程序允许应用程序软件在存储器中注入ECC错误。对于非易失性存储器,没有通用解决方案,有的MCU读取专用地址会导致ECC错误,以便于ECC功能的测试,也有的MCU可以通过在不擦除之间两次写入相同地址或通过将存储器重新配置为不同格式来创建ECC错误。这是由于需要重新初始化以避免通过读取它触发ECC错误的高循环存储器。
4 ECC在应用中的使用
为了正确使用ECC功能,必须在固件中实现基本例程以立即处理检测到的错误。建议记录和监控错误出现情况,以便进行维护、故障预测和危险警告。这一建议对于安全和工业应用尤为重要。
4.1 在RAM中处理ECC错误
静态易失性存储器基于单极晶体管的对称排列。单极晶体管在代表逻辑0或1的两种状态之间翻转。进行此转换所需的能量很低,因此设备保持低功耗。一个迷路的α粒子可能会导致RAM中的一位改变其存储值。如果ECC机制没有正确使用,这些罕见的错误可能会随时间累积并导致数据损坏甚至系统故障。这些事件本质上是随机的,某个地址上的错误发生并不提供任何指示,下一次错误可能发生在何处或何时。
4.1.1 初始化
当在RAM中使用ECC时,所有将被代码访问的存储器必须被初始化。对未初始化存储器的读取访问或对齐不良的写入可能会触发ECC错误,因为存储值和冗余位的初始设置是随机的。任何模式都可以用于存储器初始化。下面是一个建议的步骤列表:
步骤1. 在POR后或从待机模式唤醒后或在域待机后进行RAM初始化。
步骤2. 在RAM初始化后清除RAM ECC状态寄存器标志。
步骤3. 激活ECC错误锁定。即使这是可选的,这个操作对于后续错误校正和可靠性错误很重要。
步骤4. 为错误校正和检测启用中断。通过使用特定RAM ECC控制器单元的寄存器标志,可以有选择地只为某些存储器区域启用中断。
步骤5. 启用全局RAM ECC中断。
3.1.2 ECC ISR
中断服务例程提供了立即对ECC错误事件做出反应的机会。MCU通常会生成中断开始的ISR向量表,中断发生后ISR分支到一个回调函数。该函数不是中断向量的一部分,本节提出了如何实现它。单比特错误由ECC控制器自动校正,但仅限于数据读取。然后需要将校正后的数据写回。在这种情况下,数据和地址锁定功能非常有用。适当地将校正后的数据写回到其地址。如果不这样做,可能会导致后来(如果同一字中的另一位被损坏)发生双比特故障。如果无论如何发生了双比特错误,随后的行动取决于确切的损坏内容。如果受影响的字是加载到RAM中的代码指令,则必须识别原始闪存存储器中的加载区域,并将代码重新加载到SRAM中。对于任何其他初始化部分,例如中断向量表的副本,也适合采取相同类型的行动。作为一般规则,如果SRAM和CPU之间存在指令缓存,则必须刷新缓存内容。如果损坏的地址属于堆栈区域,则为了避免在不正确的上下文中执行导致的进一步损坏,必须执行系统重置。如果受影响的字地址位于数据RAM的边界内(堆或全局变量),则由开发人员决定采取何种行动。通常建议重置,但风险分析的结论可能因情况而异。
RAM ECC中断处理流程
4.1.3 解释FAR(失败地址寄存器)
通常FAR(失败地址寄存器)不显示故障的绝对地址,而是显示相对位置。FADD[31:0]中的值也指向一个字,而不是一个字节。要计算故障的物理地址,请使用以下公式:
地址 = 存储器起始地址 + FADD x字节大小。
例如,AXI SRAM监视器FADD=0x2004表示0x2400 0000 + 0x2004 x 8 = 0x2401 0020(64位字)。但在SRAM1监视器中,FADD=0x2004的值解释为0x3000 0000 + 0x2004 x 4 = 0x3000 8010(32位字)。
特殊情况是DTCM和其他存储器,它们是交错的。以意法半导体STM32H7系列为例,情况如下: • DTCM从0x20000000开始 • D1TCM从0x20000004开始 在D0TCM中,
地址 = D0TCM起始地址 + FADD x 8,
而在D1TCM中,地址 = D1TCM起始地址 + FADD x 8。
DTCM的每个64位由D0TCM的32位后跟D1TCM的32位组成,形成共同的地址空间,但具有单独的ECC报告。
因此,具体的ECC失败地址寄存器请查阅MCU手册。
4.1.4 预防性RAM ECC措施
RAM ECC事件是随机的,因此通过定期检查使用的RAM区域,可以预防某些损坏。通过读取每个字激活ECC检查;如果检查周期适当,大多数错误的字在仍然只有一个错误位时就被检测到。适当的周期可以从几小时到几天不等,这取决于操作条件和微控制器的作用。预防性ECC检查不需要在一轮中完成。这是一个背景任务,可以在空闲时刻通过后台进程或低优先级DMA传输执行。单个循环或DMA传输是不可能的,因为SRAM被划分为不连续的地址范围。通常情况下,MDMA特别适合于此任务,因为它可以访问ITCM/DTCM。当使用Cortex®-M7 CPU进行存储器检查-通过读取时,如果启用了缓存(如果缓存已启用),则涉及缓存。通过Cortex®-M7缓存访问SRAM(因此ITCM和DTCM不受此规则限制),每个从存储器读取的操作都会填充256位的缓存行。激活对每个存储器字读取的ECC检查的循环只读取每个256位的第一个字,而缓存行加载则继续使用其余的字。这个操作降低了CPU负载,但总线仍然负载很重。
4.2 在闪存存储器中处理ECC错误
闪存存储器的典型故障是由于存储器单元磨损或由于电荷泄漏造成的。可能导致故障的一些因素包括相邻单元的干扰或编程期间的电压不稳定。与SRAM不同,特定闪存存储器地址中的故障可能表明在同一页面中随后发生故障的概率略高。新设备上的闪存存储器错误应该不存在,随着预期寿命结束,故障概率会增加。闪存存储器的寿命主要取决于温度条件和擦除周期的数量。
4.2.1 闪存存储器ECC ISR
对于MCU的闪存存储器,通知ECC错误的中断包含在闪存存储器全局中断向量中。ISR检查闪存存储器状态寄存器FLASH_SR1中的“单错误校正”和“双错误检测”标志,并采取适当的行动(这取决于闪存存储器的使用)。在ECCD的情况下,有必要刷新指令缓存并防止将损坏的操作码传递给CPU。这项任务需要特别注意,因为ECCD会在BusError上发出信号。鉴于中断向量与正常闪存存储器操作(例如编程结束)共享,ISR应将控制权传递给HAL以处理其他标志。
3.2.2 闪存存储器代码
片上非易失性存储器主要用于代码。代码不太可能经常被重写,因此如果发生损坏,很可能是由于老化和电荷泄漏造成的。在双存储器设备上,可以拥有相同代码的第二份副本,并在指示ECC错误时切换到这个第二份副本。这个解决方案意味着需要监控两个存储器的内容的健康状况。可以从健康的存储器重新编程另一个存储器中失败的内容,但无法保证这样做会如何提高设备寿命。
3.2.3 EEPROM仿真
如果失败的闪存存储器单元用于存储数据,故障原因可能与编程/擦除循环有关。高级EEPROM仿真实现包括处理失败的存储器单元的机制,并且能够将它们从循环中排除。应用说明EEPROM仿真技术和微控制器的软件涉及EEPROM仿真的问题,包括ECC的影响。
3.2.4 预防性闪存存储器ECC措施
CRC硬件模块是监控嵌入式闪存存储器健康状况的有用工具。CRC可以自主地检查整个存储器库或特定地址范围;读取时也会隐含地检查ECC。然后程序必须实现对检测到的问题的反应。对于没有此CRC特性的产品,可以使用DMA(例如)来检查代码的很少使用的部分。如果启用了安全启动,可以在此处实现对安装的应用程序和加载程序代码的定期检查。
随着微电子在系统中的集成度提高,存储器单元更容易发生故障,因此ECC存储器完整性保护变得更加重要。与常规外设相比,RAM ECC的主要区别在于RAM ECC不能关闭,因为它是RAM接口的一个组成部分。本应用说明描述了RAM、闪存存储器和缓存存储器中的ECC。它还提供了处理RAM和闪存存储器中ECC错误的过程。
作者:MUKAMO