大家好,我是痞子衡,是正經(jīng)搞技術(shù)的痞子。今天痞子衡給大家分享的是恩智浦 i.MXRT1170 上 Cortex-M7 內(nèi)核的 FlexRAM ECC 功能。
ECC 是“Error Correcting Code”的簡(jiǎn)寫,ECC 能夠?qū)崿F(xiàn)錯(cuò)誤檢查和糾正,含有 ECC 功能的內(nèi)存一般稱為 ECC 內(nèi)存,使用了 ECC 內(nèi)存的系統(tǒng)在穩(wěn)定性和可靠性上得到很大提升。相比前幾代不帶 ECC 的 i.MXRT10xx 型號(hào),新一代 i.MXRT1170 在 ECC 上做了全面武裝,從 eFuse 到 FlexRAM,從 OCRAM 到外部存儲(chǔ)空間全都加上了 ECC 功能。如下表所示,不同類型的存儲(chǔ)由不同的 ECC 控制器來守護(hù):
今天痞子衡就先給大家簡(jiǎn)單介紹一下 i.MXRT1170 上 Cortex-M7 內(nèi)核下的 FlexRAM ECC 功能:
一、FlexRAM ECC 功能簡(jiǎn)介
1.1 FlexRAM v2 特點(diǎn)
i.MXRT1170 上的 FlexRAM 模塊是 v2 版本,相比 i.MXRT10xx 上的 FlexRAM v1 版本,主要就是增加了 ECC 功能。關(guān)于 FlexRAM v1 基本功能,建議你先閱讀痞子衡之前寫過的文章?《恩智浦 i.MX RT1xxx 系列 MCU 外設(shè)那些事(2)- 百變星君 FlexRAM》,痞子衡今天主要聊 v2 新增的功能。
我們知道 i.MXRT1170 是 Cortex-M7 和 Cortex-M4 雙核架構(gòu),我們看下它的 CM7 內(nèi)核系統(tǒng)框圖,F(xiàn)lexRAM 本質(zhì)上是為 CM7 內(nèi)核設(shè)計(jì)的,因?yàn)槠涔芾淼?TCM 空間僅能由 CM7 訪問的,不過如果將 FlexRAM 也分配出一部分給 OCRAM,這部分 OCRAM(即文章開頭 memory map 表里從 0x20360000 開始的空間)其實(shí) CM4 也能正常訪問。
在框圖中你還會(huì)發(fā)現(xiàn),F(xiàn)lexRAM 中物理 SRAM 總大小是 640KB,不是我們知道的可自由配置的 512KB,這是因?yàn)樾枰~外 128KB RAM 來存放 ECC 校驗(yàn)值,關(guān)于這個(gè)細(xì)節(jié)在下一節(jié)里展開聊。
?
?
1.2 關(guān)于 ECC 設(shè)計(jì)細(xì)節(jié)
關(guān)于 ECC,大家最早接觸的應(yīng)該是 ONFI Raw NAND 上的應(yīng)用,NAND Flash 因?yàn)槠湮锢硖匦缘脑颍ㄔ试S壞塊的存在),必須要包含 ECC 功能,痞子衡寫過的一篇文章?《并行 NAND 接口標(biāo)準(zhǔn)(ONFI)及 SLC Raw NAND 簡(jiǎn)介》, 里面的?2.6 Raw NAND 壞塊與 ECC?簡(jiǎn)單介紹了 ECC 在 Raw NAND 上的應(yīng)用。
在 Raw NAND 上,通常是每寫入 512bytes 數(shù)據(jù)計(jì)算出一個(gè) ECC 校驗(yàn)值(4bytes),讀取時(shí)也是讀完全部 512bytes 數(shù)據(jù)后進(jìn)行 ECC 校驗(yàn),ECC 校驗(yàn)?zāi)芰σ话阌?(n+1) bit 檢錯(cuò),n bit 糾錯(cuò)?來表達(dá),即這 512bytes 數(shù)據(jù)里如果發(fā)生 n+1 bit 及以下的錯(cuò)誤時(shí)能夠檢查出來,如果發(fā)生 n bit 及以下的錯(cuò)誤時(shí),能夠自動(dòng)糾正。比如典型的 Micron MT29F4G08 就是 5-bit 檢錯(cuò),4-bit 糾錯(cuò)。
但是 FlexRAM 本質(zhì)上是 RAM,跟 Flash(尤其是 NAND)讀寫機(jī)制不一樣,RAM 是隨機(jī)地址按 Byte 讀寫,NAND 是以 Page 為單位順序讀寫,所以 NAND 上 ECC 實(shí)現(xiàn)機(jī)制不適用于 RAM。那么 FlexRAM 到底采用的是什么樣的 ECC 設(shè)計(jì)呢?
1.2.1 ECC 檢驗(yàn)?zāi)芰?/h5>
好了,不賣關(guān)子了,公布答案。FlexRAM 中每 4ytes(針對(duì) DTCM)或 8bytes(針對(duì) ITCM/OCRAM)數(shù)據(jù)就會(huì)計(jì)算出一個(gè) ECC 校驗(yàn)值(7/8bits),ECC 校驗(yàn)值都被放在了 ECC RAM 里。因?yàn)樾r?yàn)數(shù)據(jù)塊分割得很小,所以此時(shí)不需要提供多 bit 糾錯(cuò)能力,F(xiàn)lexRAM 采用的是最經(jīng)典的 Single-bit Error Correction and Dual-bit Error Detection(SEC-DED,漢明校驗(yàn)碼)。關(guān)于 SEC-DED,你可以去 Linux 內(nèi)核里看其在 NAND 上應(yīng)用的源碼實(shí)現(xiàn) nand_ecc.c,寫得非常經(jīng)典。
存儲(chǔ)類型 | ECC 校驗(yàn)數(shù)據(jù)塊大小 | ECC 校驗(yàn)值長(zhǎng)度 | ECC 校驗(yàn)?zāi)芰?/th> |
---|---|---|---|
Raw NAND | 512 bytes | 4 bytes | 5-bit 檢錯(cuò),4-bit 糾錯(cuò) |
FlexRAM | 4/8 bytes | 7/8 bits | 2-bit 檢錯(cuò),1-bit 糾錯(cuò) |
?
1.2.2 ECC RAM 分配
128KB ECC RAM 并不是只能用來存儲(chǔ) ECC 校驗(yàn)值的,其也可以當(dāng)做普通 OCRAM 來使用,ECC RAM 具體功能是根據(jù)當(dāng)前 FlexRAM 分配以及 ECC 是否開啟而定的。eFuse 里默認(rèn)的 512KB FlexRAM 配置是 256KB ITCM, 256KB DTCM, 沒有 OCRAM。
在默認(rèn) FlexRAM 配置情況下,如果 TCM ECC 沒有開啟,那么 128KB ECC RAM 全部都是普通 OCRAM,其地址空間如下表所示:
在默認(rèn) FlexRAM 配置情況下,如果 TCM ECC 已經(jīng)開啟,那么 128KB ECC RAM 全部都用來存儲(chǔ) ECC 校驗(yàn)值,這個(gè)區(qū)域僅能由 FlexRAM 模塊內(nèi)部訪問,用戶無法訪問這 128KB 區(qū)域(不在 memory map 空間里),畢竟 ECC 校驗(yàn)值不能被隨便改。
如果我們?cè)?eFuse 或者 IOMUXC_GPR 寄存器里將 FlexRAM 配置改掉,分配出一部分給 OCRAM(比如 256KB TCM, 256KB OCRAM),那這 128KB ECC RAM 功能分配就稍微復(fù)雜一些了。最簡(jiǎn)單的情況是 TCM 和 OCRAM ECC 都沒有開啟,那么這 128KB ECC RAM 還是普通 OCRAM,map 地址是緊跟在 FlexRAM OCRAM 后面的,如下表所示:
如果 TCM 和 OCRAM ECC 都開啟了,那么這 128KB ECC RAM 就還是全部用來存儲(chǔ) ECC 校驗(yàn)值了,用戶無法訪問。
如果 OCRAM ECC 沒有開啟,不管 TCM ECC 是否開啟,這 128KB ECC RAM 中本用于存儲(chǔ) OCRAM ECC 的區(qū)域都將被劃分為普通 OCRAM,另一部分用于存儲(chǔ) TCM ECC 的區(qū)域(無論是否真的要存儲(chǔ) ECC 校驗(yàn)值)用戶則無法訪問。區(qū)域劃分比例與 OCRAM 在總 FlexRAM 大小中占比保持一致,比如 512KB FlexRAM(一共 16 個(gè) bank,每個(gè) bank 32KB)劃分出了 256KB OCRAM,那么 128KB ECC RAM(也是 16 個(gè) bank,每個(gè) bank 8KB)則劃出了 64KB 作為普通 OCRAM,如下表所示:
1.2.3 ECC 錯(cuò)誤觸發(fā)處理
ECC 錯(cuò)誤分兩種,分別是 1-bit 錯(cuò)誤和 2-bit 錯(cuò)誤。從軟件層面來看,1-bit 錯(cuò)誤可以不用管,F(xiàn)lexRAM 模塊會(huì)自動(dòng)糾錯(cuò)。我們主要處理 2-bit 錯(cuò)誤,由于 2-bit 錯(cuò)誤僅能檢錯(cuò),無法糾錯(cuò),所以發(fā)生了這個(gè)錯(cuò)誤,就意味著讀取的數(shù)據(jù)不可靠了,需要丟棄并重新寫一次(丟棄之前可以再 retry read 一次看是否還是報(bào)錯(cuò))。
關(guān)于 ECC 錯(cuò)誤處理,可根據(jù)如下 FlexRAM 寄存器來操作,首先當(dāng)然是在 INT_SIG_EN 寄存器中使能 multi-bit ECC Error,當(dāng)有 2-bit 錯(cuò)誤發(fā)生時(shí),系統(tǒng)會(huì)觸發(fā) FLEXRAM_IRQn(中斷號(hào)是 50),在中斷處理程序里找到相應(yīng)的 ECC_MULTI_ERROR_ADDR,對(duì)這個(gè)地址重新寫一次初始化數(shù)據(jù)(按 ECC 校驗(yàn)塊長(zhǎng)度一次性寫入),最后清除 INT_STATUS 寄存器里的相應(yīng)狀態(tài)位。
需要注意的是,上述處理流程僅對(duì) FlexRAM 中存放的是普通業(yè)務(wù)數(shù)據(jù)且發(fā)生 ECC 錯(cuò)誤時(shí)有效,如果 ECC 錯(cuò)誤發(fā)生在關(guān)鍵代碼段或變量段中,這個(gè)處理是不適用的,因?yàn)檫@種 ECC 錯(cuò)誤可能會(huì)造成程序崩潰。
Offset | Register |
---|---|
10h | Interrupt Status Register (INT_STATUS) |
14h | Interrupt Status Enable Register (INT_STAT_EN) |
18h | Interrupt Enable Register (INT_SIG_EN) |
30h | OCRAM multi-bit ECC Error Address Register (OCRAM_ECC_MULTI_ERROR_ADDR) |
50h | ITCM multi-bit ECC Error Address Register (ITCM_ECC_MULTI_ERROR_ADDR) |
6Ch | D0TCM multi-bit ECC Error Address Register (D0TCM_ECC_MULTI_ERROR_ADDR) |
84h | D1TCM multi-bit ECC Error Address Register (D1TCM_ECC_MULTI_ERROR_ADDR) |
?
二、開啟 FlexRAM ECC 的步驟
FlexRAM ECC 需要按照標(biāo)準(zhǔn)步驟去開啟,需要特別注意的是開啟 ECC 操作的代碼不能放在待開啟 ECC 的 FlexRAM 空間里(比如 TCM ECC 要開啟,那么開啟 ECC 操作的代碼不能使用任何 TCM 空間),因此不管是 XIP 還是 Non-XIP 應(yīng)用程序,最好是用一個(gè)二級(jí) loader(這個(gè) loader 可以鏈接在固定 OCRAM1/2 空間里,或者 XIP)來完成 ECC 開啟操作然后再加載應(yīng)用程序執(zhí)行。痞子衡給了如下示例 loader 代碼工程,代碼里主要有四個(gè)步驟:
參考代碼:https://github.com/JayHeng/cortex-m-apps/blob/master/apps/coremark_imxrt1176/loader/loader.c
2.1 使能 TCM 的 RMW(可選)
如果需要開啟 TCM ECC,那么首先需要在 CM7 內(nèi)核寄存器里開啟 TCM RMW(Read-Modify-Write)功能,這是 ARM 的規(guī)定,可在 Cortex-M7 Technical RM 手冊(cè)里找到相關(guān)信息如下。手冊(cè)里明確寫了 RMW 位同時(shí)也控制了外部邏輯(即 MCU 廠商的設(shè)計(jì))來支持 ECC 功能。
操作函數(shù)代碼如下:
void enable_cm7_tcm_rmw(void)
{
SCB->ITCMCR |= SCB_ITCMCR_RMW_Msk;
SCB->DTCMCR |= SCB_DTCMCR_RMW_Msk;
}
2.2 使能 FlexRAM 的 ECC
現(xiàn)在需要開啟 FlexRAM ECC,在 i.MXRT1170 參考手冊(cè)里的 FlexRAM 章節(jié)可以找到 FLEXRAM_CTRL 寄存器定義,其中 bit5 和 bit4 就是用來分別控制 TCM 和 OCRAM 的 ECC 開關(guān)。
操作函數(shù)代碼如下:
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 開啟了之后,此時(shí)還不能隨機(jī)訪問 FlexRAM,因?yàn)槌跏?ECC 校驗(yàn)值還沒有填充,如果這時(shí)候去讀 FlexRAM 會(huì)產(chǎn)生錯(cuò)誤。我們首先需要將會(huì)用到的 FlexRAM 空間全部初始化一遍(就是以 ECC 校驗(yàn)數(shù)據(jù)塊大小對(duì)齊方式從頭到尾寫入一遍,寫入內(nèi)容不限,正常用全 0)。
操作函數(shù)代碼如下:
#define ITCM_START 0x00000000
#define ITCM_SIZE (256*1024U) // 只是示例長(zhǎng)度,根據(jù)實(shí)際情況修改
#define DTCM_START 0x20000000
#define DTCM_SIZE (256*1024U) // 只是示例長(zhǎng)度,根據(jù)實(shí)際情況修改
#define OCRAM_START 0x20360000
#define OCRAM_SIZE (256*1024U) // 只是示例長(zhǎng)度,根據(jù)實(shí)際情況修改
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 加載應(yīng)用程序執(zhí)行
當(dāng) FlexRAM 初始 ECC 校驗(yàn)值已經(jīng)被填充之后,此時(shí)便可以正常隨機(jī)讀寫 FlexRAM 了。如果此時(shí)加載的是一個(gè)在 ITCM 里執(zhí)行并且 data 段在 DTCM 里的應(yīng)用程序,可以參考痞子衡前面給出的示例 loader 工程。
這是 loader 工程完整主函數(shù)代碼,其中 memcpy 那一句代碼里的 app_code 是應(yīng)用程序 binary 數(shù)組(用 Python 腳本將應(yīng)用程序工程生成的 .bin 文件轉(zhuǎn)換成 C 語言數(shù)組放到 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 對(duì)內(nèi)存訪問性能的影響
FlexRAM 開了 ECC 后,訪問性能會(huì)有一定降低,畢竟數(shù)據(jù)訪問中插入了額外的 ECC 校驗(yàn)工作,不過這個(gè)影響非常小,因?yàn)橐淮?ECC 校驗(yàn)僅增加 1-2 個(gè)機(jī)器 cycle。下面是 FlexRAM 分配出的不同存儲(chǔ)類型的基本情況,其中 OCRAM 可以被 L1 Cache 加速,所以從應(yīng)用程序角度開 ECC 對(duì)其訪問性能影響就更小了,我們主要討論 ECC 對(duì) TCM 性能的影響。
FlexRAM 分配類型 | ECC 校驗(yàn)數(shù)據(jù)塊大小 | 總線類型 | 訪問速度 | L1 Cache 加速 |
---|---|---|---|---|
ITCM | 8 bytes | ITCM_ITF 64-bits | 與 CM7 同頻 | 否 |
DTCM | 4 bytes | DTCM_ITF 2x32-bit | 與 CM7 同頻 | 否 |
OCRAM | 8 bytes | AXI64 | 與 CM7 頻率的 1/4 | 是 |
為了簡(jiǎn)化測(cè)試,痞子衡就用經(jīng)典的 benchmark 程序(Coremark 和 Dhrystone)來測(cè)試 ECC 對(duì) TCM 的影響,測(cè)試工程如下:
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 內(nèi)核最高可以配置到 1GHz,但是開了 TCM ECC 后,為了保證訪問可靠性,此時(shí) CM7 內(nèi)核最好是工作在 800MHz,下面的 benchmark 結(jié)果也是在 800MHz 主頻下得到的:
Benchmark 類型 | TCM ECC 開關(guān) | Benchmark 結(jié)果 |
---|---|---|
coremark | 關(guān)閉 | 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 | 關(guān)閉 | 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 結(jié)果來看,ECC 是否開啟對(duì)性能影響特別小,可以忽略,當(dāng)然 benchmark 測(cè)試并不是特別精確地反映了性能影響,底下有空痞子衡會(huì)再專門用 memcpy 函數(shù)來測(cè)試性能影響。
至此,恩智浦 i.MXRT1170 上 Cortex-M7 內(nèi)核的 FlexRAM ECC 功能痞子衡便介紹完畢了,掌聲在哪里~~~