1. i.MX 8M Plus 在线 ECC 功能
i.MX 8M Plus DDR 控制器支持在线 ECC,可保护系统免受软错误(例如由 α 射线、中子等引起的 DRAM 位翻转)的影响。它为每 64 位数据存储 8 位 ECC(纠错码),从而能够纠正单比特错误并检测双比特错误。
由于没有专门用于存储 ECC 的内存,因此大约 1/8 的 DDR 容量被用作 ECC 区域。
由于 32 位数据总线在数据和 ECC 之间共享,启用内联 ECC 功能会降低 DDR 访问性能。
1.1.单比特纠错 (SEC)
SEC 是 DDR 控制器的一项功能,用于检测单比特错误并将其纠正为正确的值。
图1 SEC示意图
1.2.双比特错误检测 (DED)
DED 是 DDR 控制器的一项功能,用于检测双比特错误。
图2 DED概念图
笔记:
实际上,SEC 和 DED 是在 64 字节对齐的数据区(加上 8 字节的 ECC)中检测的,而不是在 8 字节中检测的。
2.内联 ECC 内存映射(概要)
根据 DDR 容量的不同,设置内联 ECC 内存映射的方法也会有所不同。
2.1.DDR密度是2的幂(二进制对齐密度)
例如,对于 1、2、4、8、16GB 的情况,
-
DDR的低地址7/8用作数据区,高地址1/8用作ECC区。ECC区通常不可访问。
-
数据区域最多分为八个区域:区域 0 至 6 和其他区域。每个区域都可以启用或禁用 ECC 功能。
-
区域 0 至 6 的容量相同,分别设置为 DDR 容量的 1/8、1/16、1/32 或 1/64。其余区域均为其他区域。
以下是当区域 0 到 6 的容量设置为 1/8 时的内存映射。在这种情况下,没有其他区域。
图 3内存映射示例 (1/8)
下面显示的是当区域 0 到 6 的容量设置为 1/16 时的内存映射。
2.2.非 2 的幂次方的 DDR 密度(非二进制对齐密度)
例如,对于 3、6 或 12 GB 的情况(i.MX 8M Plus LPDDR4 EVK 为 6 GB,因此这适用于这种情况)。
-
将整个 DDR 分成三份,每份的容量是 2 的幂。
-
3GB内存,1GB x 3
-
6GB内存,2GB x 3
-
12GB版本,4GB x 3
-
-
对于分为三部分的低地址区域,进行如下设置:2.1. 当 DDR 容量为 2 的幂时。
-
三段式地址划分中,上两个地址区域的设置与下地址区域的设置相同。
下面显示的是设置为 1/8 时的内存映射。
图 5 DDR 容量不是 2 的幂(1/8)时的内存映射示例
3.使用 DDR 寄存器编程辅助工具进行设置
使用DDR 寄存器编程辅助工具(DDR RPA) 配置内联 ECC。
笔记:
请准备一个 DDR RPA,该 RPA 可以运行禁用内联 ECC 的 DDR 压力测试(并且还可以验证其是否可以运行 u-boot)。
由于存在漏洞,i.MX8MP LPDDR4/DDR4 RPA v6 或更早版本无法正常工作内联 ECC。请使用 v7 或更高版本。除非有特殊情况,否则请使用最新版本的 DDR RPA。(截至 2024 年 9 月,最新版本为 v9。)
Linux BSP 的 U-Boot 中包含的 i.MX 8M Plus EVK 的lpddr4_timing.c 文件。以及ddr4_timing.c虽然乍一看似乎可以正常工作,因为它包含了定义
CONFIG_IMX8M_DRAM_INLINE_ECC,但内联 ECC 设置存在问题,无法正常工作。即使您使用的是 EVK 进行测试,也请使用 DDR RPA 和 DDR Tool 生成 lpddr4_timing.c/ddr4_timing.c 文件。
3.1.启用在线 ECC
在 DDR RPA 寄存器配置表中,将内联 ECC 项设置为启用。
3.2.内存映射
3.2.1.DDR密度是2的幂(二进制对齐密度)
如果 DDR 容量是 2 的幂,请使用 ECC_Config_BinaryAligned 表。
-
指定区域 0 至 6 的容量。(1/8、1/16、1/32 或 1/64)
-
启用或禁用 0 至 6 区及其他区域的 ECC(受保护或未受保护)。
这是区域 0 到 6 的容量设置为 1/8 时的示例。
图 6 DDR RPA 设置 (1/8)
这是一个将 0 到 6 区的容量设置为 1/16 的示例。
图 7 DDR RPA 设置 (1/16)
3.2.2.非 2 的幂次方的 DDR 密度(非二进制对齐密度)
如果 DDR 容量不是 2 的幂,请使用 ECC_Config_nonBinaryAligned 表(i.MX 8M Plus LPDDR4 EVK 就是这种情况,容量为 6GB)。
-
指定区域 0 至 6 的容量。(1/8、1/16、1/32 或 1/64)
-
ECC 启用或禁用内存区域 0 中的区域 0 到 6 以及其他区域(受保护或未受保护)的 ECC。
ECC 内存区域 1/2 将自动设置为与 ECC 内存区域 0 相同的设置。
这是区域 0 到 6 的容量设置为 1/8 时的示例。
图 8 DDR RPA 设置 (1/8)
这是一个将 0 到 6 区的容量设置为 1/32 的示例。
图 9 DDR RPA 设置 (1/32)
4.使用 DDR 工具进行测试并生成 lpddr4_timing.c/ddr4_timing.c 文件
您可以使用 DDR 工具来检查 DDR RPA 中所做的设置的运行情况。
如果 DDR 压力测试通过,则生成 lpddr4_timing.c/ddr4_timing.c 以合并到 U-Boot 中。
5. U-Boot 中的内联 ECC 实现
5.1.替换 lpddr4_timing.c/ddr4_timing.c
lpddr4_timing.c 或者将ddr4_timing.c替换为 DDR RPA 和 DDR Tool 生成的文件。
5.2.添加配置
将CONFIG_IMX8M_DRAM_INLINE_ECC=y添加到您的 U-Boot 配置文件中。
CONFIG_IMX8M_DRAM_INLINE_ECC=y
笔记:
U-Boot 配置文件
imx8mp_evk_inline_ecc_defconfig是在 Linux BSP 5.4 中添加的,但由于维护不当,已被弃用。使用 EVK 进行测试时,更安全的做法是将配置添加到imx8mp_evk_defconfig中并使用它。
5.3.更改Linux保留区域¶
软件禁止访问 ECC 区域。因此,设备树会动态修改,以便 Linux 内核(以及其他系统软件)将 ECC 区域视为保留区域。默认值硬编码为与 EVK 的 DDR 容量相匹配(LPDDR4 为 6GB,DDR4 为 4GB),因此如果 DDR 容量与 EVK 的容量不同,则必须修改代码。
使用uboot-imx/board/freescale/imx8mp_evk/imx8mp_evk.c中的函数ft_board_setup来更改 ECC 区域,使其与实际 DDR 容量相匹配。例如,对于 DDR4,ECC 区域的起始地址和大小是硬编码的,假定 DDR 容量为 4GB。
phys_addr_t ecc_start = 0x120000000;
size_t ecc_size = 0x20000000;
如果实际 DDR 容量为 2GB,则从物理地址 0x40000000 到 0xc0000000 的 1/8 (256MB) 高地址保留为 ECC 区域。
phys_addr_t ecc_start = 0xb0000000;
size_t ecc_size = 0x10000000;
imx8mp_evk.c 如果您不使用它,则需要添加与ft_board_setup函数等效的处理。
5.4.搬迁支持
如果 DDR 容量为 2GB 或更小,且未安装 OPTEE,U-Boot 会尝试重定位到 DDR 的最高地址,但由于该地址与 ECC 区域重叠,因此会在重定位过程中卡住。为避免这种情况,请添加 imx8mp_evk.h 文件。将 ECC 区域容量从 DDR 容量定义PHYS_SDRAM_SIZE中减少 (1/8)。
-#define PHYS_SDRAM_SIZE 0x80000000
+#define PHYS_SDRAM_SIZE 0x70000000
5.5.排除对 ECC 区域的访问处理
如果 U-Boot 中还有其他代码访问了 ECC 区域,则需要排除对 ECC 区域的访问。例如,如果内存测试访问了整个 DDR 区域,则需要修改该测试,使其不再访问 ECC 区域。
参考: https ://github.com/nxp-imx/uboot-imx/blob/lf-6.1.55-2.2.0/configs/imx8mp_evk_defconfig#L10-L11
6. Linux 系统运行检查
6.1.查看启动日志
确保在 Linux 启动时已安装 EDAC 驱动程序。(EDAC = 错误检测和纠正)i.MX 8M Plus 的 EDAC 驱动程序位于linux-imx/drivers/edac/synopsys_edac.c 。
root@imx8mp-lpddr4-evk:~# dmesg | grep EDAC
[ 0.116733] EDAC MC: Ver: 3.0.0
[ 1.874689] EDAC MC0: Giving out device to module 1 controller synps_ddr_controller: DEV synps_edac (INTERRUPT)
6.2.检查内存映射
使用命令cat /proc/iomem确认 ECC 区域已按照5.3 节“更改 Linux 保留区域”中的配置设置为保留。以下是 i.MX 8M Plus LPDDR4 EVK(DDR 容量 6GB)的示例。
root@imx8mp-lpddr4-evk:~# cat /proc/iomem | grep reserved
...
b0000000-bfffffff : reserved
...
130000000-13fffffff : reserved
...
1b0000000-1bfffffff : reserved
6.3.检查何时发生 ECC 错误
根据应用说明AN13566 - i.MX 8 系列上的 ECC ,i.MX 8M Plus 中的 DDR 控制器没有故意生成 ECC 错误的功能。
3.2.9通过软件注入ECC错误
ECC 错误注入是系统级软件验证的一个有用的可选功能。与边带 ECC 不同,它没有专门的硬件支持。但是,可以通过软件注入错误,方法是通过“ECC_REGION_PARITY_LOCK”寄存器解锁 ECC 区域并覆盖 ECC 奇偶校验位。当从受保护的内存区域读取相应的地址时,根据引入的错误类型,会产生可纠正的或不可纠正的 ECC 错误。
注意:DDR 控制器不支持 ECC 数据中毒。参考手册将进行更新,移除此功能。
因此,我们解锁ECC区域,并从内核层面修改ECC区域以模拟ECC错误并进行测试。基本思路如下:
-
将 8 字节值 0xffffffff_ffffffff 写入 DataAddr。DDRC 向 DDR 写入数据的同时,还会写入 1 字节的 ECC 纠错码。
-
解锁 ECC 区域,允许核心访问 ECC 区域。
-
获取与 DataAddr 对应的 ECC 字节的地址,并读取 ECC 的一个字节。
-
将反转后的值 0xffffffff_fffffffe 写入 DataAddr。同时更新 ECC 1 字节。
-
ECC 1 字节,ECC 读取于 3。将字节值写回。
-
锁定 ECC 区域,禁止从核心访问 ECC 区域。
-
读取 DataAddr 的值。此时,由于 ECC 不一致,单比特纠错被激活,并读取纠错后的 8 字节值 0xffffffff_ffffffff。同时,内核也会收到一个可纠正错误中断通知。
笔记:
由于这涉及到访问 DDR,因此必须在非缓存区域执行测试。
图 10单比特纠错 (SEC) 测试方法¶
双比特错误检测 (DED) 调试也可以用同样的方法进行。
7.参考资料
-
应用说明: AN13566 - i.MX 8 系列的 ECC
-
应用说明:AN13616 - 验证 i.MX 8 直列式 ECC 引擎功能(请联系您的 NXP 代表以获取此文档)
8.注意
-
本文档是使用恩智浦产品的参考指南。
-
有关正式规格,请参阅产品手册和应用说明。
-
实际操作可能因各种条件(例如所使用的软件版本)的不同而与所述内容有所不同。
-
并非所有功能都已验证,因此请务必验证和测试产品,以确保其符合您的预期用途。