百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术文章 > 正文

ECC给i.MXRT1170 FlexRAM带来了哪些变化?

myzbx 2025-04-08 16:51 27 浏览

大家好,我是痞子衡,是正经搞技术的痞子。今天痞子衡给大家分享的是恩智浦i.MXRT1170上Cortex-M7内核的 FlexRAMECC功能。

ECC 是“Error Correcting Code”的简写,ECC 能够实现错误检查和纠正,含有 ECC 功能的内存一般称为 ECC 内存,使用了 ECC 内存的系统在稳定性和可靠性上得到很大提升。相比前几代不带 ECC 的 i.MXRT10xx 型号,新一代 i.MXRT1170 在 ECC 上做了全面武装,从 eFuse 到 FlexRAM,从 OCRAM 到外部存储空间全都加上了 ECC 功能。如下表所示,不同类型的存储由不同的 ECC 控制器来守护:

今天痞子衡就先给大家简单介绍一下 i.MXRT1170 上 Cortex-M7 内核下的 FlexRAM ECC功能:

一、FlexRAM ECC 功能简介

1.1 FlexRAM v2 特点

i.MXRT1170 上的 FlexRAM 模块是 v2 版本,相比 i.MXRT10xx 上的 FlexRAM v1 版本,主要就是增加了 ECC 功能。关于 FlexRAM v1 基本功能,建议你先阅读痞子衡之前写过的文章 《恩智浦 i.MX RT1xxx 系列 MCU 外设那些事(2)- 百变星君 FlexRAM》,痞子衡今天主要聊 v2 新增的功能。

我们知道 i.MXRT1170 是 Cortex-M7 和 Cortex-M4 双核架构,我们看下它的 CM7 内核系统框图,FlexRAM 本质上是为 CM7 内核设计的,因为其管理的 TCM 空间仅能由 CM7 访问的,不过如果将 FlexRAM 也分配出一部分给 OCRAM,这部分 OCRAM(即文章开头 memory map 表里从 0x20360000 开始的空间)其实 CM4 也能正常访问。

在框图中你还会发现,FlexRAM 中物理 SRAM 总大小是 640KB,不是我们知道的可自由配置的 512KB,这是因为需要额外 128KB RAM 来存放 ECC 校验值,关于这个细节在下一节里展开聊。

1.2 关于 ECC 设计细节

关于 ECC,大家最早接触的应该是 ONFI Raw NAND 上的应用,NAND Flash 因为其物理特性的原因(允许坏块的存在),必须要包含 ECC 功能,痞子衡写过的一篇文章 《并行 NAND 接口标准(ONFI)及 SLC Raw NAND 简介》, 里面的 2.6 Raw NAND 坏块与 ECC 简单介绍了 ECC 在 Raw NAND 上的应用。

在 Raw NAND 上,通常是每写入 512bytes 数据计算出一个 ECC 校验值(4bytes),读取时也是读完全部 512bytes 数据后进行 ECC 校验,ECC 校验能力一般用 (n+1) bit 检错,n bit 纠错 来表达,即这 512bytes 数据里如果发生 n+1 bit 及以下的错误时能够检查出来,如果发生 n bit 及以下的错误时,能够自动纠正。比如典型的 Micron MT29F4G08 就是 5-bit 检错,4-bit 纠错。

但是 FlexRAM 本质上是 RAM,跟 Flash(尤其是 NAND)读写机制不一样,RAM 是随机地址按 Byte 读写,NAND 是以 Page 为单位顺序读写,所以 NAND 上 ECC 实现机制不适用于 RAM。那么 FlexRAM 到底采用的是什么样的 ECC 设计呢?

1.2.1 ECC 检验能力

好了,不卖关子了,公布答案。FlexRAM 中每 4ytes(针对 DTCM)或 8bytes(针对 ITCM/OCRAM)数据就会计算出一个 ECC 校验值(7/8bits),ECC 校验值都被放在了 ECC RAM里。因为校验数据块分割得很小,所以此时不需要提供多 bit 纠错能力,FlexRAM 采用的是最经典的 Single-bit Error Correction and Dual-bit Error Detection(SEC-DED,汉明校验码)。关于 SEC-DED,你可以去 Linux 内核里看其在 NAND 上应用的源码实现 nand_ecc.c,写得非常经典。

存储类型ECC 校验数据块大小ECC 校验值长度ECC 校验能力
Raw NAND512 bytes4 bytes5-bit 检错,4-bit 纠错
FlexRAM4/8 bytes7/8 bits2-bit 检错,1-bit 纠错

1.2.2 ECC RAM 分配

128KB ECC RAM 并不是只能用来存储 ECC 校验值的,其也可以当做普通 OCRAM 来使用,ECC RAM 具体功能是根据当前 FlexRAM 分配以及 ECC 是否开启而定的。eFuse 里默认的 512KB FlexRAM 配置是 256KB ITCM, 256KB DTCM, 没有 OCRAM。

在默认 FlexRAM 配置情况下,如果 TCM ECC 没有开启,那么 128KB ECC RAM 全部都是普通 OCRAM,其地址空间如下表所示:

在默认 FlexRAM 配置情况下,如果 TCM ECC 已经开启,那么 128KB ECC RAM 全部都用来存储 ECC 校验值,这个区域仅能由 FlexRAM 模块内部访问,用户无法访问这 128KB 区域(不在 memory map 空间里),毕竟 ECC 校验值不能被随便改。

如果我们在 eFuse 或者 IOMUXC_GPR 寄存器里将 FlexRAM 配置改掉,分配出一部分给 OCRAM(比如 256KB TCM, 256KB OCRAM),那这 128KB ECC RAM 功能分配就稍微复杂一些了。最简单的情况是 TCM 和 OCRAM ECC 都没有开启,那么这 128KB ECC RAM 还是普通 OCRAM,map 地址是紧跟在 FlexRAM OCRAM 后面的,如下表所示:

如果 TCM 和 OCRAM ECC 都开启了,那么这 128KB ECC RAM 就还是全部用来存储 ECC 校验值了,用户无法访问。

如果 OCRAM ECC 没有开启,不管 TCM ECC 是否开启,这 128KB ECC RAM 中本用于存储 OCRAM ECC 的区域都将被划分为普通 OCRAM,另一部分用于存储 TCM ECC 的区域(无论是否真的要存储 ECC 校验值)用户则无法访问。区域划分比例与 OCRAM 在总 FlexRAM 大小中占比保持一致,比如 512KB FlexRAM(一共 16 个 bank,每个 bank 32KB)划分出了 256KB OCRAM,那么 128KB ECC RAM(也是 16 个 bank,每个 bank 8KB)则划出了 64KB 作为普通 OCRAM,如下表所示:

1.2.3 ECC 错误触发处理

ECC 错误分两种,分别是 1-bit 错误和 2-bit 错误。从软件层面来看,1-bit 错误可以不用管,FlexRAM 模块会自动纠错。我们主要处理 2-bit 错误,由于 2-bit 错误仅能检错,无法纠错,所以发生了这个错误,就意味着读取的数据不可靠了,需要丢弃并重新写一次(丢弃之前可以再 retry read 一次看是否还是报错)。

关于 ECC 错误处理,可根据如下 FlexRAM 寄存器来操作,首先当然是在 INT_SIG_EN 寄存器中使能 multi-bit ECC Error,当有 2-bit 错误发生时,系统会触发 FLEXRAM_IRQn(中断号是 50),在中断处理程序里找到相应的 ECC_MULTI_ERROR_ADDR,对这个地址重新写一次初始化数据(按 ECC 校验块长度一次性写入),最后清除 INT_STATUS 寄存器里的相应状态位。

需要注意的是,上述处理流程仅对 FlexRAM 中存放的是普通业务数据且发生 ECC 错误时有效,如果 ECC 错误发生在关键代码段或变量段中,这个处理是不适用的,因为这种 ECC 错误可能会造成程序崩溃。

OffsetRegister
10hInterrupt Status Register (INT_STATUS)
14hInterrupt Status Enable Register (INT_STAT_EN)
18hInterrupt Enable Register (INT_SIG_EN)
30hOCRAM multi-bit ECC Error Address Register (OCRAM_ECC_MULTI_ERROR_ADDR)
50hITCM multi-bit ECC Error Address Register (ITCM_ECC_MULTI_ERROR_ADDR)
6ChD0TCM multi-bit ECC Error Address Register (D0TCM_ECC_MULTI_ERROR_ADDR)
84hD1TCM multi-bit ECC Error Address Register (D1TCM_ECC_MULTI_ERROR_ADDR)

二、开启 FlexRAM ECC 的步骤

FlexRAM ECC 需要按照标准步骤去开启,需要特别注意的是开启 ECC 操作的代码不能放在待开启 ECC 的 FlexRAM 空间里(比如 TCM ECC 要开启,那么开启 ECC 操作的代码不能使用任何 TCM 空间),因此不管是 XIP 还是 Non-XIP 应用程序,最好是用一个二级 loader(这个 loader 可以链接在固定 OCRAM1/2 空间里,或者 XIP)来完成 ECC 开启操作然后再加载应用程序执行。痞子衡给了如下示例 loader 代码工程,代码里主要有四个步骤:

参考代码:
https://github.com/JayHeng/cortex-m-apps/blob/master/apps/coremark_imxrt1176/loader/loader.c

2.1 使能 TCM 的 RMW(可选)

如果需要开启 TCM ECC,那么首先需要在 CM7 内核寄存器里开启 TCM RMW(Read-Modify-Write)功能,这是 ARM 的规定,可在 Cortex-M7 Technical RM 手册里找到相关信息如下。手册里明确写了 RMW 位同时也控制了外部逻辑(即 MCU 厂商的设计)来支持 ECC 功能。

操作函数代码如下:

void enable_cm7_tcm_rmw(void)
{
    SCB->ITCMCR |= SCB_ITCMCR_RMW_Msk;
    SCB->DTCMCR |= SCB_DTCMCR_RMW_Msk;
}

2.2 使能 FlexRAM 的 ECC

现在需要开启 FlexRAM ECC,在 i.MXRT1170 参考手册里的 FlexRAM 章节可以找到 FLEXRAM_CTRL 寄存器定义,其中 bit5 和 bit4 就是用来分别控制 TCM 和 OCRAM 的 ECC 开关。

操作函数代码如下:

void enable_flexram_tcm_ecc(void)
{
    *(uint32_t *)(FLEXRAM_BASE + 0x108) |= (1u << 5);
}

void enable_flexram_ocram_ecc(void)
{
    *(uint32_t *)(FLEXRAM_BASE + 0x108) |= (1u << 4);
}

2.3 初始化 FlexRAM 的 ECC 值

FlexRAM ECC 开启了之后,此时还不能随机访问 FlexRAM,因为初始 ECC 校验值还没有填充,如果这时候去读 FlexRAM 会产生错误。我们首先需要将会用到的 FlexRAM 空间全部初始化一遍(就是以 ECC 校验数据块大小对齐方式从头到尾写入一遍,写入内容不限,正常用全 0)。

操作函数代码如下:

#define ITCM_START   0x00000000
#define ITCM_SIZE    (256*1024U)  // 只是示例长度,根据实际情况修改
#define DTCM_START   0x20000000
#define DTCM_SIZE    (256*1024U)  // 只是示例长度,根据实际情况修改
#define OCRAM_START  0x20360000
#define OCRAM_SIZE   (256*1024U)  // 只是示例长度,根据实际情况修改

void init_flexram_itcm_ecc(void)
{
    for (uint32_t i = 0; i < ITCM_SIZE; i += sizeof(uint64_t))
    {
        *(uint64_t *)(ITCM_START + i) = 0;
    }
}

void init_flexram_dtcm_ecc(void)
{
    for (uint32_t i = 0; i < DTCM_SIZE; i += sizeof(uint32_t))
    {
        *(uint32_t *)(DTCM_START + i) = 0;
    }
}

void init_flexram_ocram_ecc(void)
{
    for (uint32_t i = 0; i < OCRAM_SIZE; i += sizeof(uint64_t))
    {
        *(uint64_t *)(OCRAM_START + i) = 0;
    }
}

2.4 加载应用程序执行

当 FlexRAM 初始 ECC 校验值已经被填充之后,此时便可以正常随机读写 FlexRAM 了。如果此时加载的是一个在 ITCM 里执行并且 data 段在 DTCM 里的应用程序,可以参考痞子衡前面给出的示例 loader 工程。

这是 loader 工程完整主函数代码,其中 memcpy 那一句代码里的 app_code 是应用程序 binary 数组(用 Python 脚本将应用程序工程生成的 .bin 文件转换成 C 语言数组放到 loader 工程源文件里)。

#define APP_START 0U

int main(void)
{
    enable_cm7_tcm_ecc;
    enable_flexram_tcm_ecc;
    init_flexram_itcm_ecc;
    init_flexram_dtcm_ecc;

    // Copy image to RAM.
    memcpy((void *)APP_START, app_code, APP_LEN);
    
    uint32_t appStack = *(uint32_t *)(APP_START);
    uint32_t appEntry = *(uint32_t *)(APP_START + 4);

    // Turn off interrupts.
    __disable_irq;

    // Set the VTOR to default.
    SCB->VTOR = APP_START;

    // Memory barriers for good measure.
    __ISB;
    __DSB;

    // Set main stack pointer and process stack pointer.
    __set_MSP(appStack);
    __set_PSP(appStack);

    // Jump to app entry point, does not return.
    void (*entry)(void) = (void (*)(void))appEntry;
    entry;
}

三、ECC 对内存访问性能的影响

FlexRAM 开了 ECC 后,访问性能会有一定降低,毕竟数据访问中插入了额外的 ECC 校验工作,不过这个影响非常小,因为一次 ECC 校验仅增加 1-2 个机器 cycle。下面是 FlexRAM 分配出的不同存储类型的基本情况,其中 OCRAM 可以被 L1 Cache 加速,所以从应用程序角度开 ECC 对其访问性能影响就更小了,我们主要讨论 ECC 对 TCM 性能的影响。

FlexRAM 分配类型ECC 校验数据块大小总线类型访问速度L1 Cache 加速
ITCM8 bytesITCM_ITF 64-bits与 CM7 同频
DTCM4 bytesDTCM_ITF 2x32-bit与 CM7 同频
OCRAM8 bytesAXI64与 CM7 频率的 1/4

为了简化测试,痞子衡就用经典的 benchmark 程序(Coremark 和 Dhrystone)来测试 ECC 对 TCM 的影响,测试工程如下:

Coremark 工程:
https://github.com/JayHeng/cortex-m-apps/tree/master/apps/coremark_imxrt1176/loader Dhrystone 工程:
https://github.com/JayHeng/cortex-m-apps/tree/master/apps/dhrystone_imxrt1176/loader

需要特别提醒的是,我们知道 i.MXRT1170 CM7 内核最高可以配置到 1GHz,但是开了 TCM ECC 后,为了保证访问可靠性,此时 CM7 内核最好是工作在 800MHz,下面的 benchmark 结果也是在 800MHz 主频下得到的:

Benchmark 类型TCM ECC 开关Benchmark 结果
coremark关闭Total ticks : 2023600
Total time (secs): 20.236000
Iterations/Sec : 3953.350465
Iterations : 80000
CoreMark 1.0 : 3953.350465
coremark开启Total ticks : 2023657
Total time (secs): 20.236570
Iterations/Sec : 3953.239111
Iterations : 80000
CoreMark 1.0 : 3953.239111
dhrystone关闭Dhrystones per Second: 3622138.51
DMIPS: 2061.5472
DMIPS/MHz: 2.0698
dhrystone开启Dhrystones per Second: 3621977.04
DMIPS: 2061.4553
DMIPS/MHz: 2.0697

从 benchmark 结果来看,ECC 是否开启对性能影响特别小,可以忽略,当然 benchmark 测试并不是特别精确地反映了性能影响,底下有空痞子衡会再专门用 memcpy 函数来测试性能影响。

至此,恩智浦 i.MXRT1170 上 Cortex-M7 内核的 FlexRAM ECC 功能痞子衡便介绍完毕了,掌声在哪里~~~

相关推荐

怎么恢复7z文件 7z文件删除了怎么恢复

7z是一种压缩格式的文件,它运用LZMA压缩算法,该压缩算法的输出稍后被算数编码进行处理以便后续进一步压缩,压缩比十分高。我们可以将文件压缩成这种格式,便于传输,保存,占空间少。了解更多7z文件知识...

郎酒让消费者喝得明明白白 算术题里有答案

日前,『郎酒酱香产品企业内控准则』颁布,郎酒首次公开酱香产品生产全过程,公布酱香产品产能、储能及投放计划。随后,郎酒官微向消费者发出「品控算术题」有奖问答。郎酒亮出家底,消费者踊跃留言。8天后,谜底揭...

学龄前,比识字、算术更重要的是这三件事

“为了给孩子选择一家合适的幼儿园,我曾穿梭于纽约各家幼儿园的开放日,这些幼儿员既包括主流的公立幼儿园,还包括那些遥不可及的私人幼儿园。我的目的就是想了解他们的教育理念是什么,到底厉害在哪里,看看对于我...

参加CSP-J信奥赛需要掌握数学知识

在C++语法的学习中需要储备的数学知识如下①数据类型:需要知道整数、正整数、负整数、小数、判断对错②算术运算符:加法、减法、乘法、除法、取模运算③关系表达式:大于、大于等于、小于、小...

1g米饭能做多少深蹲?今天我们来算一算

减重我们都知道3分在练,7分在吃,吃这件事情上,真的是每一口都算数。今天我们来算一笔账,1粒米饭可以做多少事情?本着认真负责的态度,今天在食物秤上称了1g米饭,是16粒。根据能量换算:100g米饭是4...

web 自动化测试,一定得掌握的 8 个核心知识点

使用cypress进行端对端测试,和其他的一些框架有一个显著不同的地方,它使用JavaScript作为编程语言。传统主流的selenium框架是支持多语言的,大多数QA会的pytho...

大话C语言:赋值运算符(c语言中赋值运算符是什么)

赋值运算符是最基本的运算符之一,用于将右侧的值或表达式的计算结果赋给左侧的变量。它是一个二元运算符,意味着它需要两个操作数:一个是目标变量(左侧),另一个是要赋给该变量的值或表达式(右侧)。赋值运算符...

Vue进阶(幺幺伍):js 将字符串转换为boolean

Boolean();参数为0、null和无参数返回false,有参数返回true。Boolean("");//输出为:falseBoolean(null);//输出为...

mongodb查询的语法(大于,小于,大于或等于,小于或等于等等)

1).大于,小于,大于或等于,小于或等于$gt:大于$lt:小于$gte:大于或等于$lte:小于或等于例子:db.collection.find({"field":{$gt:valu...

Python学不会来打我(21)python表达式知识点汇总

在Python中,表达式是由变量、运算符、函数调用等组合而成的语句,用于产生值或执行特定操作。以下是对Python中常见表达式的详细讲解:1.1算术表达式涉及数学运算的表达式。例如:a=5b...

C|数据存储地址与字节偏移、数据索引

话说C是面向内存的编程语言。数据要能存得进去,取得出来,且要考虑效率。不管是顺序存储还是链式存储,其寻址方式总是很重要。顺序存储是连续存储。同质结构的数组通过其索引表示位置偏移,异质结构的结构体通过其...

下班后累懵?4 个 JS 手写题帮你搞定前端面试高频考点

打工人下班后最痛苦的事,莫过于拖着疲惫的身子还要啃前端面试题吧?看着那些密密麻麻的JS代码,脑子都快转不动了!别担心,今天咱就用轻松的方式,带你吃透4道高频手写题,让你在面试时自信满满,再也不...

嵌入式数据库sqlite3【进阶篇】-子句和函数的使用,小白一文入门

sqlite在《嵌入式数据库sqlite3命令操作基础篇-增删改查,小白一文入门》一文中讲解了如何实现sqlite3的基本操作增删改查,本文介绍一些其他复杂一点的操作。比如where、orderby...

前缀表达式与后缀表达式(前缀表达式后缀表达式中缀表达式计算)

昨天晚上和儿子一起学习了前缀表达式和后缀表达式。这应该是字符串算式如何被计算机识别并计算的2种方法。本来是想先给他讲一个逆波兰式(后缀表达式),以后再讲前缀表达式。没想到他还挺聪明,很快就把2个都掌握...

Python快速入门教程1:基本语法、数据类型、运算符、数字字符串

Python3的基础教程,涵盖了基本语法、数据类型、类型转换、解释器、注释、运算符、数字和字符串等内容,并附有使用实例场景。Python3的基础教程,涵盖了基本语法、数据类型、类型转换、解释器、注释、...