大家好,我是痞子衡,是正經搞技術的痞子。今天痞子衡給大家介紹的是恩智浦i.MX RT1xxx系列MCU的性能。
在前面的文章 i.MXRT1xxx微控制器概覽 里,痞子衡給大家簡介過恩智浦半導體在2017年推出的新一代跨界微控制器i.MX RT1xxx系列,該系列第一款芯片i.MXRT105x性能完爆同時期市面上所有的微控制器,官方公布的CoreMark跑分高達3020,有人可能不明白這個數字意味着什么,作為對比,我們再來看看意法半導體最流行的芯片STM32F103RB,它的CoreMark是108(此處應該有類似My God的尖叫),真是沒有對比就沒有傷害,那么i.MXRT105x性能到底有沒有這么強?今天痞子衡為大家實測一下。
關於CoreMark標准的基本知識,痞子衡之前專門寫過一篇文章 微控制器CPU性能測試基准(EEMBC-CoreMark) ,本篇就是基於了解CoreMark基本知識之后的一次實踐。來,讓我們開始吧。
一、准備工作
1.1 硬件平台NXP i.MX RT1050 EVK
要開始實測i.MXRT105x的CoreMark,首先你得有一塊開發板,恩智浦官網上有i.MXRT105x配套的評估板,如下圖所示,痞子衡今天用的就是這塊板子,板載主芯片型號為iMXRT1052DVL6。

1.2 集成開發環境IAR EWARM
ARM Cortex-M微控制器的集成開發環境有很多,其中IAR EWARM憑借優良的特性備受廣大工程師青睞,且痞子衡在CoreMark標准基本知識介紹文章的最后貼出了一張iMXRT1050與STM32H743性能對比圖,其中CoreMark跑分就是在IAR下得到的,所以今天痞子衡也選用IAR作為軟件環境,具體版本為IAR EWARM v8.20.2。
1.3 官方軟件開發包NXP MCUXpresso SDK
在開始移植CoreMark程序到i.MXRT1052上之前,我們需要先有一個i.MXRT1052的基本hello world的例程,當然我們可以對着數據手冊自己從頭寫一個,但是這里痞子衡推薦使用官方軟件開發包。
注冊並登錄恩智浦官網,來到 MCUXpresso SDK Builder 頁面,在"Select Development Board"里選擇EVKB-IMXRT1050后點擊Build MCUXpresso SDK后跳轉到下一個頁面,在"Developer Environment Settings"里選擇IAR並點擊Download SDK后便可得到SDK_2.3.1_EVKB-IMXRT1050.zip,下面是痞子衡下載的開發包具體版本信息:

二、開始實測
2.1 跑通hello world
給i.MXRT1050 EVK板子供電(J2口接5V輸出的電源),並且使用USB線連接電腦與板子的J28 USB口,此時在設備管理器應該可以看到USB虛擬的串口(EVK板載OpenSDA調試器內含USB轉串口功能,如果看不到串口,請自行安裝PE Micro驅動)。
打開前一步下載的開發包里的\SDK_2.3.1_EVKB-IMXRT1050\boards\evkbimxrt1050\demo_apps\hello_world\iar\hello_world.eww工程,確認工程option里linker文件選擇的是MIMXRT1052xxxxx_ram.icf,然后J21 JTAG口連接上Jlink Plus直接將工程下載進主芯片的RAM運行。
如果工程運行正常,你在串口調試助手(115200,8N1)里應該能看到"hello world."打印輸出。
2.2 移植CoreMark程序
以hello_world工程為基礎,將從EEMBC官網下載到的coremark_v1.0.tgz包解壓,將\coremark_v1.0\以及\coremark_v1.0\barebones\路徑下的如下所有源文件(.c或.h)全部拷貝到hello world工程目錄下
\coremark_v1.0
\barebones --移植到裸機下需要修改的文件
\core_portme.h -- 移植平台工程具體配置信息
\core_portme.c -- 計時以及板級初始化實現
\cvt.c
\ee_printf.c -- 打印函數串口發送實現
core_main.c --主程序入口
core_state.c --狀態機控制子程序
core_list_join.c --列表操作子程序
core_matrix.c --矩陣運算子程序
core_util.c --CRC計算子程序
coremark.h --工程配置與數據結構定義
將上面的所有coremark源文件全部添加進hello_world工程,並從工程中移除原主函數入口文件hello_world.c,此時基本coremark工程就完成了。但注意此時工程無法編譯,因為從\coremark_v1.0\barebones\下拷貝的源文件還需要進一步修改。
2.2.1 板級初始化
程序移植的第一步是實現板載初始化,編譯上一步建好的coremark工程會報這樣的錯誤error "Call board initialization routines in portable init (if needed), in particular initialize UART!\n"。其實這是coremark作者故意在core_portme.c里的portable_init()函數里留下的提示,我們需要用i.MXRT1052的板級初始化函數替代這個#error,打開之前移除的hello_world.c文件,將其中板級初始化函數調用拷貝到portable_init函數中:
void portable_init(core_portable *p, int *argc, char *argv[])
{
// 刪除#error提示
//#error "Call board initialization routines in portable init (if needed), in particular initialize UART!\n"
// 添加板級初始化調用
/* Init board hardware. */
BOARD_InitHardware();
//...
}
原hello_world里的BOARD_InitHardware已經包含了系統時鍾以及UART初始化,所以這里的移植工作就算結束了。
2.2.2 計時功能
程序移植的第二步是實現計時功能,由於CoreMark得分其實是單位時間內跑了多少次CoreMark程序,所以CoreMark工程必須要有計時功能。編譯coremark工程會報這樣的錯誤error "You must implement a method to measure time in barebones_clock()! This function should return current time.\n",這也是coremark作者故意在core_portme.barebones_clock()函數里留下的提示。
關於計時器,第一個想到的自然是Cortex-M內核里的SysTick,不過考慮到coremark程序是要跑在600MHz的主頻下,而SysTick計時器只有24bit,也就是說即使SysTick->LOAD設最大的reload值0xFFFFFF,也將每隔0.02796s(0x1000000/600MHz)產生一次SysTick中斷,而CoreMark程序至少要跑10s以上,在coremark運行的10s內會產生357次(10/0.02796)SysTick中斷,這無疑會降低coremark得分,所以SysTick直接被pass。(備注:SysTick->CTRL[2]用於選擇SysTick的時鍾源,默認1'b0為Core Clock,1'b1為外部clock,暫未研究這里的外部clock在i.MXRT105x上是否能用)。
翻看i.MXRT1052的參考手冊,其支持的計時器種類很多,有GPT、PIT、TMR、WDOG,就選擇比較常用的32bit計時器PIT吧。
之前下載的軟件包里也有PIT的例程\SDK_2.3.1_EVKB-IMXRT1050\boards\evkbimxrt1050\driver_examples\pit,打開pit.c文件將其中PIT初始化相關代碼放入新定義的timer_pit_init()函數中並在portable_init()中調用timer_pit_init()一次以完成PIT初始化。
barebones_clock()函數中的#error也需要被PIT_GetCurrentTimerCount()函數替換,此外還需要添加定義#define CLOCKS_PER_SEC (24000000),因為我們會在clock_config.c文件里的BOARD_BootClockRUN()函數最后添加如下語句以選擇OSC作為PIT計時時鍾,而EVK板載外部XTALOSC晶振是24MHz。
/* Configure PIT divider */
CLOCK_SetMux(kCLOCK_PerclkMux, 1U); /* Set PERCLK_CLK source to OSC_CLK*/
CLOCK_SetDiv(kCLOCK_PerclkDiv, 0U); /* Set PERCLK_CLK divider to 1 */
有朋友可能會好奇,我們似乎沒有使能和實現PIT中斷,其實在當前coremark工程下沒有必要,因為PIT中斷每隔178s(0x100000000/24MHz)才會觸發一次,而我們只需要執行幾十秒就夠了,所以即使使能和實現PIT中斷也用不到,當然如果能實現PIT中斷更好,萬一我們讓coremark程序執行超過178s,不實現PIT中斷就會導致計時出錯。
#define CLOCKS_PER_SEC (24000000)
void timer_pit_init(void)
{
/* Structure of initialize PIT */
pit_config_t pitConfig;
PIT_GetDefaultConfig(&pitConfig);
/* Init pit module */
PIT_Init(PIT, &pitConfig);
/* Set max timer period for channel 0 */
PIT_SetTimerPeriod(PIT, kPIT_Chnl_0, (uint32_t)~0);
/* Start channel 0 */
PIT_StartTimer(PIT, kPIT_Chnl_0);
}
CORETIMETYPE barebones_clock() {
// 刪除#error提示
#error "You must implement a method to measure time in barebones_clock()! This function should return current time.\n"
// 添加PIT計數值返回語句
return (~PIT_GetCurrentTimerCount(PIT, kPIT_Chnl_0));
}
void portable_init(core_portable *p, int *argc, char *argv[])
{
// 刪除#error提示
//#error "Call board initialization routines in portable init (if needed), in particular initialize UART!\n"
// 添加板級初始化調用
/* Init board hardware. */
BOARD_InitHardware();
// 添加PIT計時器初始化調用
/* Init timer for microsecond function. */
timer_pit_init();
//...
}
2.2.3 串口打印功能
程序移植的第三步是實現串口打印功能,由於hello_world工程已經包含串口打印功能,所以這里的修改比較簡單。
void uart_send_char(char c) {
// 刪除#error提示
//#error "You must implement the method uart_send_char to use this file!\n";
// 添加UART發送函數調用
if (c == '\n')
{
char tmp = '\r';
LPUART_WriteBlocking((LPUART_Type *)BOARD_DEBUG_UART_BASEADDR, (const uint8_t *)&tmp, 1);
}
LPUART_WriteBlocking((LPUART_Type *)BOARD_DEBUG_UART_BASEADDR, (const uint8_t *)&c, 1);
//...
}
2.3 CoreMark參數配置
2.2里只是將基本CoreMark功能移植完成,但此時coremark工程還是無法編譯,因為還有一些CoreMark配置沒有確定,都在coremark_portme.h里,需要重定義下面三個宏,並且IAR的優化選項也要設置如下圖所示:
#define ITERATIONS (40000)
#define COMPILER_VERSION "IAR EWARM v8.20.2"
#define COMPILER_FLAGS "High - Speed - No size constraints"

2.4 輸出CoreMark結果
到這里CoreMark的移植工作就完全結束了,此時coremark工程也應該能正常編譯了。為得到最高的CoreMark得分,最后需要再確定兩件事:一、Core Clock是否確定配置為600MHz;二、工程代碼段/數據段是否放在了ITCM/DTCM RAM。
打開clock_config.c文件,查看BOARD_BootClockRUN()函數,ARM Core/AHB時鍾確實都為600MHz。
void BOARD_BootClockRUN(void)
{
/* Boot ROM did initialize the XTAL, here we only sets external XTAL OSC freq */
CLOCK_SetXtalFreq(24000000U);
CLOCK_SetRtcXtalFreq(32768U);
CLOCK_SetMux(kCLOCK_PeriphClk2Mux, 0x1); /* Set PERIPH_CLK2 MUX to OSC */
CLOCK_SetMux(kCLOCK_PeriphMux, 0x1); /* Set PERIPH_CLK MUX to PERIPH_CLK2 */
/* Setting the VDD_SOC to 1.5V. It is necessary to config AHB to 600Mhz */
DCDC->REG3 = (DCDC->REG3 & (~DCDC_REG3_TRG_MASK)) | DCDC_REG3_TRG(0x12);
CLOCK_InitArmPll(&armPllConfig); /* Configure ARM PLL to 1200M */
CLOCK_SetDiv(kCLOCK_ArmDiv, 0x1); /* Set ARM PODF to 1, divide by 2 */
CLOCK_SetDiv(kCLOCK_AhbDiv, 0x0); /* Set AHB PODF to 0, divide by 1 */
CLOCK_SetDiv(kCLOCK_IpgDiv, 0x3); /* Set IPG PODF to 3, divede by 4 */
// ...
}
再打開MIMXRT1052xxxxx_ram.icf文件,查看text段確實放在了ITCM里(0x0 - 0x7FFFF),data段確實放在了DTCM里(0x20000000 - 0x2007FFFF)。
define symbol m_interrupts_start = 0x00000000;
define symbol m_interrupts_end = 0x000003FF;
define symbol m_text_start = 0x00000400;
define symbol m_text_end = 0x0001FFFF;
define symbol m_data_start = 0x20000000;
define symbol m_data_end = 0x2001FFFF;
還等什么?將coremark工程趕緊下載進芯片並打開串口調試助手看CoreMark得分啊。痞子衡實測的CoreMark得分為2952,這與官方標稱的3020有一點差距。
對於coremark得分沒上3000,痞子衡感到不服啊,那怎么提高coremark得分?痞子衡試盡方法(改編譯器選項、調coremark配置)均無功而返,后來無意中看到EEMBC上跑出3036得分的IDE(編譯器)是IAR 7.80,於是死馬當活馬醫吧,痞子衡也在IAR 7.80.4上運行了一次,得到了3017分,原來這微小差異取決於編譯器版本,真相大白。
想偷懶的朋友直接移步痞子衡的github https://github.com/JayHeng/Cortex-M-Apps 去下載移植好的工程,工程在\Cortex-M-Apps\apps\coremark_imxrt1052\bsp\下面,build8202和build7804分別對應不同的IAR版本。
至此,恩智浦i.MX RT1xxx系列MCU的CoreMark性能與實測痞子衡便介紹完畢了,掌聲在哪里~~~
歡迎訂閱
文章會同時發布到我的 博客園主頁、CSDN主頁、微信公眾號 平台上。
微信搜索"痞子衡嵌入式"或者掃描下面二維碼,就可以在手機上第一時間看了哦。