大家好,我是痞子衡,是正經(jīng)搞技術(shù)的痞子。今天痞子衡給大家介紹的是在i.MXRT1060-EVK上利用memtester程序給SDRAM做壓力測試。
我們知道恩智浦i.MXRT1xxx系列是高性能MCU的代表,對于這個超高主頻(>=500MHz)的怪獸,不做點人機交互圖形顯示或者跑點算法方面的高階應(yīng)用實在有點浪費它的能力,高階應(yīng)用往往需要大容量緩存(RAM),i.MXRT1xxx系列內(nèi)部RAM最大也就1-2MB,不是特別寬裕,因此我們在板級設(shè)計時往往會為i.MXRT1xxx再搭配一塊外部RAM。外部RAM的種類選擇很多,可通過FlexSPI外設(shè)掛HyperRAM/PSRAM或者通過SEMC外設(shè)掛SDRAM/SRAM,其中SDRAM是最常見的選擇,官方EVK上默認也是選了SDRAM。
有了外部SDRAM雖然解決了系統(tǒng)緩存容量危機,但也同時可能引入潛在的系統(tǒng)穩(wěn)定性問題,畢竟SDRAM是外部器件,容易受干擾(比如PCB走線或者環(huán)境溫度等),如果SDRAM的讀寫訪問不可靠,那么系統(tǒng)就會有不可預(yù)知的奇怪問題發(fā)生,因此我們需要在板子出廠時對SDRAM做一次壓力測試(既測試了SDRAM芯片和PCB設(shè)計,也測試了對應(yīng)的SEMC外設(shè)配置),今天痞子衡就選用memtester程序給SDRAM上一次強度。
關(guān)于memtester程序的基本知識,痞子衡之前專門寫過一篇文章 《內(nèi)存讀寫正確性壓力測試程序(memtester)》 ,本篇就是基于了解memtester基本知識之后的一次實踐。來,讓我們開始吧。
一、準備工作
1.1 硬件平臺NXP i.MX RT1060 EVK
要開始給SDRAM做壓力測試,首先你得有一塊開發(fā)板,恩智浦官網(wǎng)上有i.MXRT1060評估板,板載SDRAM芯片型號為鎂光的MT48LC16M16A2,我們今天就來測試它。
1.2 集成開發(fā)環(huán)境IAR EWARM
ARM Cortex-M微控制器的集成開發(fā)環(huán)境有很多,其中IAR EWARM憑借優(yōu)良的特性備受廣大工程師青睞,今天痞子衡就選用IAR作為軟件環(huán)境,具體版本為IAR EWARM v8.50.6。
1.3 官方軟件開發(fā)包NXP MCUXpresso SDK
在開始移植memtester程序到i.MXRT1062上之前,我們需要先有一個i.MXRT1062的基本hello world的例程,當(dāng)然我們可以對著數(shù)據(jù)手冊自己從頭寫一個,但是這里痞子衡推薦使用官方軟件開發(fā)包。
注冊并登錄恩智浦官網(wǎng),來到 MCUXpresso SDK Builder 頁面,在"Select Development Board"里選擇EVK-MIMXRT1060后點擊Build MCUXpresso SDK后跳轉(zhuǎn)到下一個頁面,點擊Download SDK后便可得到SDK_2.9.1_EVK-MIMXRT1060.zip,下面是痞子衡下載的開發(fā)包具體版本信息:
二、開始實測
2.1 跑通hello world
給i.MXRT1060 EVK板子供電(J2口接5V輸出的電源),并且使用USB線連接電腦與板子的J41 USB口,此時在設(shè)備管理器應(yīng)該可以看到USB虛擬的串口(EVK板載LPC-LINK2調(diào)試器內(nèi)含USB轉(zhuǎn)串口功能,如果看不到串口,請自行安裝LPC-LINK2驅(qū)動)。
打開前一步下載的開發(fā)包里的SDK_2.9.1_EVK-MIMXRT1060boardsevkimxrt1060demo_appshello_worldiarhello_world.eww工程,確認工程option里linker文件選擇的是MIMXRT1062xxxxx_ram.icf,然后J21 JTAG口連接上Jlink Plus直接將工程下載進主芯片的RAM運行。
如果工程運行正常,你在串口調(diào)試助手(115200,8N1)里應(yīng)該能看到"hello world."打印輸出。
2.2 移植memtester程序
以hello_world工程為基礎(chǔ),將從官方網(wǎng)站下載到的memtester-4.5.0.tar包解壓,將memtester-4.5.0路徑下的如下源文件(.c或.h)全部拷貝到hello world工程目錄下:
memtester-4.5.0
memtester.h
memtester.c --主程序入口
sizes.h --關(guān)于系統(tǒng)位數(shù)(32/64bit)的一些定義
types.h --所用數(shù)據(jù)類型的定義
tests.h
tests.c --測試算法子程序
將上面所有memtester源文件全部添加進hello_world工程并將工程更名為memtester,然后再將工程中原主函數(shù)入口文件hello_world.c更名為main.c,此時基本memtester工程就完成了。但注意此時工程無法編譯,因為memtester源文件還需要進一步修改。
2.2.1 適配嵌入式平臺
我們下載的memtester源碼本用作在Unix-like系統(tǒng)上運行的,所以源碼里面有一些僅適用于Unix-like系統(tǒng)上運行的代碼,需要將這些代碼全部刪除以適合在嵌入式平臺運行。
關(guān)于頭文件引用部分,需要刪除memtester.c和tests.c文件里的一些不適用的#include語句,改用SDK里的標準頭:
// 下述Unix系統(tǒng)頭需刪除
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
// 下述SDK標準頭需添加
#include "fsl_common.h"
#include "fsl_debug_console.h"
整個memtester源碼其實最主要(唯一)的改動就是memtester.c里的原main()函數(shù),本來main函數(shù)是接受控制臺傳來的用戶選項完成解析獲取必要的參數(shù)(內(nèi)存起始地址,測試長度,壓力測試循環(huán)次數(shù)),然后運行tests.c里各種算法子程序。我們需要做的就是將main函數(shù)里代碼各種刪減,把不適用于MCU上運行的代碼全部拿掉,并且將main函數(shù)原型改為如下:
/*******************************************************************************
* Input parameters:
* ---- phystestbase : memory base address
* ---- wantraw : memory size
* ---- memsuffix : memory unit, B,K,M,G for B, KB, MB, GB
* ---- loop : memory test code loop times
* ---- pagesize : memory pase size (Bytes)
*/
int memtester_main(ul phystestbase, ul wantraw, char *memsuffix, ul loops, ul pagesize)
{}
最后memtester_main里跑tests.c里具體算法子程序時,如果遇到失敗的情況,默認是繼續(xù)執(zhí)行下一個算法子程序,我們可以加一個fail_stop控制變量,決定遇到失敗是直接結(jié)束整個測試還是繼續(xù)往下跑。
2.2.2 板級初始化
上一節(jié)里已經(jīng)將memtester.c里面的main()函數(shù)改成了memtester_main(),所以我們直接在原h(huán)ello_world.c改成的main.c里的main函數(shù)中增加memtester_main()的調(diào)用即可,注意按需設(shè)置測試參數(shù)(下述代碼里的設(shè)置就是一個示例,測試0x8000_0000地址開始的64KB內(nèi)存,循環(huán)一次即可)。此外Cache是否使能對測試影響很大,建議關(guān)掉DCache測試。
typedef struct _semc_test_config {
uint32_t baseAddr;
uint32_t testSize;
uint32_t loopNum;
uint32_t dramFreq;
uint32_t enableCache;
} semc_test_config_t;
int fail_stop = 1;
int main(void)
{
char memsuffix = 'B';
/* Init board hardware. */
BOARD_InitHardware();
/* --------------- stress test --------------- */
semc_test_config_t testConfig;
testConfig.baseAddr = 0x80000000;
testConfig.testSize = 64 * 1024;
testConfig.loopNum = 1;
testConfig.dramFreq = CLOCK_GetFreq(kCLOCK_SemcClk);
testConfig.enableCache = 0;
if (!testConfig.enableCache) {
/* Disable D cache */
SCB_DisableDCache();
}
PRINTF("rn########## Print out from target board ##########rn");
PRINTF("rnSDRAM r/w test settings:rn");
PRINTF(" Base Addr: 0x%x;rn", testConfig.baseAddr);
PRINTF(" Test Size: %d Bytes;rn", testConfig.testSize);
PRINTF(" Test Loop: %d;rn", testConfig.loopNum);
PRINTF(" SDRAM Freq: %d Hz;rn",testConfig.dramFreq);
PRINTF(" Enable Cache: %d;rnrn", testConfig.enableCache);
/* Run memory stress test: 64KByte, loop=1, page_size = 1kbyte */
memtester_main(testConfig.baseAddr, testConfig.testSize, &memsuffix, testConfig.loopNum, (1*1024));
while (1)
{
}
}
2.2.3 串口打印功能
串口打印功能的改動比較簡單,直接把原memtester.c和tests.c文件里的fprintf()全部替換成PRINTF()即可,PRINTF函數(shù)在原h(huán)ello world工程里已經(jīng)實現(xiàn)了。
2.3 memtester參數(shù)配置
痞子衡在memtester程序的基本知識介紹里說過,memtester幾乎沒有參數(shù)配置,就是需要在sizes.h文件里把如下兩個宏加進去:
#define ULONG_MAX (4294967295UL)
#define TEST_NARROW_WRITES
2.4 輸出memtester結(jié)果
到這里memtester的移植工作就完全結(jié)束了,此時memtester工程(代碼和變量都鏈接在TCM里)也應(yīng)該能正常編譯了。你可能會疑問,SEMC外設(shè)的初始化代碼在哪里???別急,工程選項調(diào)試器設(shè)置里提供了兩種SDRAM初始化腳本,我們隨便選用一種即可:
另外需要注意的是,在工程預(yù)編譯選項里需要加上SKIP_SYSCLK_INIT宏,因為工程時鐘初始化函數(shù)BOARD_BootClockRUN()會根據(jù)這個宏來決定要不要重配SEMC,我們已經(jīng)有SDRAM初始化腳本了,不需要在工程里再配SEMC時鐘了。
現(xiàn)在還等什么?將memtester工程趕緊下載進芯片并打開串口調(diào)試助手看memtester結(jié)果啊。痞子衡為了盡快得到結(jié)果,僅測試了64KB的空間,按說應(yīng)該測試全部32MB空間才對,但這個測試時間是很漫長的,如果循環(huán)次數(shù)loopNum大于1,那等待時間就更長了。如果測試中遇到了失敗,除了要檢查板級硬件外,還可以調(diào)整SDRAM初始化腳本里的配置再次測試,直至通過壓力測試。
想偷懶的朋友直接移步痞子衡的github https://github.com/JayHeng/cortex-m-apps 去下載移植好的工程,工程在cortex-m-appsappsmemtester_imxrt1062bsp下面。
至此,在i.MXRT1060-EVK上利用memtester程序給SDRAM做壓力測試痞子衡便介紹完畢了,掌聲在哪里~~~