你們好,我是痞子衡,是正經搞技術的痞子。今天痞子衡給你們介紹的是恩智浦i.MX RTxxx系列MCU的性能。html
在前面的文章 i.MXRTxxx微控制器概覽 裏,痞子衡給你們簡介過恩智浦半導體在2018年推出的全新跨界微控制器i.MX RTxxx系列,該系列第一款芯片i.MXRT600搭載一顆Cortex-M33控制內核和一顆Tensilica HiFi4 DSP處理內核,該芯片可在超低功耗邊緣處理應用中實現高效本地音頻預處理、沉浸式3D音頻播放和支持語音的體驗。今天痞子衡先爲你們實測一下其Cortex-M33控制內核的性能,性能測試程序採用經典的Dhrystone算法。git
關於Dhrystone標準的基本知識,痞子衡以前專門寫過一篇文章 微處理器CPU性能測試基準(Dhrystone) ,本篇就是基於瞭解Dhrystone基本知識以後的一次實踐。來,讓咱們開始吧。github
要開始實測i.MXRT600的Dhrystone,首先你得有一塊開發板,恩智浦官網上有i.MXRT600配套的評估板,以下圖所示,痞子衡今天用的就是這塊板子,板載主芯片型號爲iMXRT685EVKA。算法
ARM Cortex-M微控制器的集成開發環境有不少,其中IAR EWARM憑藉優良的特性備受廣大工程師青睞,今天痞子衡就選用IAR做爲軟件環境,具體版本爲IAR EWARM v8.32.2。app
在開始移植Dhrystone程序到i.MXRT685上以前,咱們須要先有一個i.MXRT685的基本hello world的例程,固然咱們能夠對着數據手冊本身從頭寫一個,可是這裏痞子衡推薦使用官方軟件開發包。
註冊並登陸恩智浦官網,來到 MCUXpresso SDK Builder 頁面,在"Select Development Board"裏選擇EVK-IMXRT685後點擊Build MCUXpresso SDK後跳轉到下一個頁面,在"Developer Environment Settings"裏選擇IAR並點擊Download SDK後即可獲得SDK_2.5.0_EVK-MIMXRT685.zip,下面是痞子衡下載的開發包具體版本信息:less
使用USB線鏈接電腦與板子的J5 USB口,此時在設備管理器應該能夠看到USB虛擬的串口(EVK板載LPC-LINK2調試器內含USB轉串口功能,若是看不到串口,請自行安裝LPC-LINK2驅動)。
打開前一步下載的開發包裏的\SDK_2.5.0_EVK-MIMXRT685\boards\evkimxrt685\demo_apps\hello_world\iar\hello_world.eww工程,確認工程option裏linker文件選擇的是MIMXRT685Sxxxx_ram.icf,而後使用板載調試器直接將工程下載進主芯片的RAM運行。
若是工程運行正常,你在串口調試助手(115200,8N1)裏應該能看到"hello world."打印輸出。ide
以hello_world工程爲基礎,將從Roy Longbottom的網站下載到的classic_benchmarks.tar.gz包解壓,將\classic_benchmarks\source_code\dhrystone2\路徑下的以下全部源文件(.c或.h)所有拷貝到hello world工程目錄下函數
\classic_benchmarks\source_code\dhrystone2 \dhry.h --關於兼容性的原型定義 \dhry_1.c --主程序入口 \dhry_2.c --算法子程序
將上面全部Dhrystone源文件所有添加進hello_world工程並將工程改名爲dhrystone,而後再將工程中原主函數入口文件hello_world.c改名爲dhrystone.c,此時基本Dhrystone工程就完成了。但注意此時工程沒法編譯,由於Dhrystone源文件還須要進一步修改。性能
咱們下載的Dhrystone源碼本用做在PC上運行的,因此源碼裏面有一些僅適用於PC上運行的代碼,好比計時部分、文件I/O部分,須要將這些代碼所有刪除以適合在嵌入式平臺運行。
關於計時部分,須要刪除dhry_1.c文件裏的#include < time.h> 語句,而且刪除dhry.h文件裏跟TIME宏相關的以下代碼:測試
#ifndef TIME #define TIMES #endif /* Use times(2) time function unless */ /* explicitly defined otherwise */ #ifdef TIMES #include <sys/types.h> #include <sys/times.h> /* for "times" */ #endif
關於文件I/O部分,須要刪除dhry_1.c文件裏的#include < stdio.h> 語句,以及以下涉及文件I/O(主要是關於Dhry.txt的操做)的代碼:
#include <stdio.h> // 需刪除 void main (int argc, char *argv[]) // 需更改成void main(void) { // ... // 如下代碼需刪除 /* Initializations */ if (argc > 1) { switch (argv[1][0]) { case 'N': nopause = 0; break; case 'n': nopause = 0; break; } } if ((Ap = fopen("Dhry.txt","a+")) == NULL) { printf(" Can not open Dhry.txt\n\n"); printf(" Press Enter\n\n"); int g = getchar(); exit(1); } // ... { // ... /************************************************************************ * Add results to output file Dhry.txt * ************************************************************************/ fprintf (Ap, " #####################################################\n\n"); fprintf (Ap, " Dhrystone Benchmark 2.1 %s via C/C++ %s\n", options, timeday); // ... fclose(Ap); } }
上一節裏已經將dhry_1.c裏面的main函數形參int argc, char *argv[]改爲了void,因爲dhrystone.c(原hello_world.c)裏已有main函數,且該main函數中含有板級初始化代碼,因此咱們須要將dhry_1.c裏的main函數改名(好比可改名爲dhrystone()),使dhrystone源碼做爲子程序來調用,這樣便於往不一樣MCU平臺移植,最後直接dhrystone.c裏的main函數中增長dhrystone()的調用便可。
// dhry_1.c文件中 void main (void) // 需更改成void dhrystone(void) { ... } // dhrystone.c文件中 int main(void) { /* Init board hardware. */ BOARD_InitPins(); BOARD_BootClockRUN(); BOARD_InitDebugConsole(); // 增長的dhrystone函數調用 dhrystone(); while (1) { } }
原dhry_1.c文件裏的關於計時部分的代碼應該作一些微調整,time.h頭文件的包含應該去除,這是Windows系統裏的頭文件,start_time(), end_time()是基於time.h裏clock_gettime()函數而封裝的API,secs是用於記錄時間差的變量,這些API和全局變量均可以保留,但須要用MCU裏的計時器從新實現。
#include <time.h> // 需刪除 void dhrystone (void) { // ... do { start_time(); // ... end_time(); User_Time = secs; } while (count >0); }
關於計時器,第一個想到的天然是Cortex-M內核裏的SysTick,不過考慮到Dhrystone程序是要跑在300MHz的主頻下,而SysTick計時器只有24bit,也就是說即便SysTick->LOAD設最大的reload值0xFFFFFF,也將每隔0.05592s(0x1000000/300MHz)產生一次SysTick中斷,而Dhrystone程序至少要跑2s以上,在Dhrystone運行的2s內會產生35次(2/0.05592)SysTick中斷,這無疑會下降Dhrystone得分,因此SysTick直接被pass。(備註:SysTick->CTRL[2]用於選擇SysTick的時鐘源,默認1'b0爲Core Clock,1'b1爲外部clock,暫未研究這裏的外部clock在i.MXRT685上是否能用)。
翻看i.MXRT685的參考手冊,其支持的計時器種類不少,有CTIMER、SCT、MRT、UTICK、WWDT,就選擇比較經常使用的32bit計時器CTIMER吧。
以前下載的軟件包裏也有CTIMER的例程\SDK_2.5.0_EVK-MIMXRT685\boards\evkimxrt685\driver_examples\ctimer,打開simple_match_interrupt.c文件將其中CTIMER初始化相關代碼放入新定義的timer_ctimer_init()函數中並在start_time()中調用timer_ctimer_init()一次以完成CTIMER初始化。
此外還須要添加定義#define CLOCKS_PER_SEC (16000000),由於CTIMER選擇的時鐘源是16MHz。
#define CLOCKS_PER_SEC (16000000) volatile uint32_t s_timerHighCounter = 0; void ctimer_match0_callback(uint32_t flags) { s_timerHighCounter++; } void timer_ctimer_init(void) { ctimer_config_t config; /* Use 16 MHz clock for the Ctimer2 */ CLOCK_AttachClk(kSFRO_to_CTIMER2); CTIMER_GetDefaultConfig(&config); CTIMER_Init(CTIMER, &config); /* Configuration 0 */ matchConfig0.enableCounterReset = true; matchConfig0.enableCounterStop = false; matchConfig0.matchValue = (uint32_t)~0; matchConfig0.outControl = kCTIMER_Output_Toggle; matchConfig0.outPinInitState = false; matchConfig0.enableInterrupt = true; CTIMER_RegisterCallBack(CTIMER, &ctimer_callback_table[0], kCTIMER_SingleCallback); CTIMER_SetupMatch(CTIMER, CTIMER_MAT0_OUT, &matchConfig0); CTIMER_StartTimer(CTIMER); } void start_time(void) { timer_ctimer_init(); s_timerHighCounter = 0; } void end_time(void) { uint64_t retVal; uint32_t high; uint32_t low; do { high = s_timerHighCounter; low = CTIMER_GetTimerCountValue(CTIMER); } while (high != s_timerHighCounter); retVal = ((uint64_t)high << 32U) + low; CTIMER_StopTimer(CTIMER); secs = retVal / (CLOCKS_PER_SEC * 1.0); }
串口打印功能的改動比較簡單,直接把原dhry_1.c文件裏的printf()所有替換成PRINTF()便可,PRINTF函數在原hello world工程裏已經實現了。
痞子衡在Dhrystone標準的基本知識介紹裏說過,Dhrystone幾乎沒有參數配置,惟一須要注意的就是REG,Cortex-M33平臺支持register關鍵字,因此咱們在IAR工程option裏宏定義框內加上 REG=register。
此外咱們還須要在宏定義框內設置額外兩個宏 NUMBER_OF_RUNS、CORE_FREQ_MHz,前者表明跑dhrystone核心算法程序的總次數,後者是當前MCU實際運行主頻。最後還須要設置一下IAR的優化選項,以下圖所示:
到這裏Dhrystone的移植工做就徹底結束了,此時Dhrystone工程也應該能正常編譯了。爲獲得最高的Dhrystone得分,最後須要再肯定兩件事:1、Core Clock是否肯定配置爲300MHz;2、工程代碼段/數據段是否放在了SRAM。
打開clock_config.c文件,查看BOARD_BootClockRUN()函數,ARM Core/AHB時鐘僅保守地配了250MHz,讓咱們修改Pfd0的分頻係數,從19修改成16,直接超頻到300MHz。
void BOARD_BootClockRUN(void) { // ... CLOCK_InitSysPll(&g_configSysPll); /* Configure system PLL to 528Mhz. */ /* Valid PFD values are decimal 12-35. */ // 將分頻係數修改16 // CLOCK_InitSysPfd(kCLOCK_Pfd0, 19); /* Enable main PLL clock 500MHz. */ CLOCK_InitSysPfd(kCLOCK_Pfd0, 16); /* Enable main PLL clock 594MHz. */ // ... /* Let CPU run on SYS PLL PFD0 with divider 2. */ CLOCK_SetClkDiv(kCLOCK_DivSysCpuAhbClk, 2); CLOCK_AttachClk(kMAIN_PLL_to_MAIN_CLK); // ... }
再打開MIMXRT685Sxxxx_ram.icf文件,查看text/data段確實放在了SRAM裏(0x0 - 0x47FFFF):
define symbol m_interrupts_start = 0x00080000; define symbol m_interrupts_end = 0x00080143; define symbol m_text_start = 0x00080144; define symbol m_text_end = 0x001BFFFF; define symbol m_data_start = 0x001C0000; define symbol m_data_end = 0x002FFFFF;
還等什麼?將Dhrystone工程趕忙下載進芯片並打開串口調試助手看Dhrystone得分啊。痞子衡實測的Dhrystone得分爲411 DMIPS,1.37 DMIPS/MHz,這跟ARM官方公佈的CM33內核Dhrystone得分1.5 DMIPS/MHz有點差距。
對於Dhrystone得分沒上1.5 DMIPS/MHz,痞子衡固然不服,鑑於以前在 RT1052上測試CoreMark性能 的經驗,咱們知道低版本的IAR在速度優化上表現要更好,因此咱們繼續用IAR 7.80.4再測一遍,這一次獲得了440 DMIPS,1.466 DMIPS/MHz,這才差很少接近ARM官方指標。
想偷懶的朋友直接移步痞子衡的github https://github.com/JayHeng/Cortex-M-Apps 去下載移植好的工程,工程在\Cortex-M-Apps\apps\dhrystone_imxrt685\bsp\下面。
至此,恩智浦i.MX RTxxx系列MCU的Dhrystone性能與實測痞子衡便介紹完畢了,掌聲在哪裏~~~