大家好,我是痞子衡,是正經搞技術的痞子。今天痞子衡給大家介紹的是在序列槽波特率識别執行個體裡逐漸展示i.MXRT上提升代碼執行性能的十八般武藝。
恩智浦 MCU SE 團隊近期一直在加班加點趕 SBL 項目(解決客戶産品 OTA 需求),這個項目裡內建了 ISP 本地更新(UART/USB)功能,其中 UART 口下載下傳更新實作裡加入了自動波特率識别支援,具體識别方法見 《序列槽(UART)自動波特率識别程式設計與實作(中斷)》 一文,這一套 ISP 代碼其實是移植于 i.MXRT Flashloader(更早期的時候叫 KBOOT)。
ISP 代碼放在 SBL 工程裡會出現高波特率(比如115200)無法識别的問題,但在低波特率的情況下(比如9600,19200),ISP 代碼是功能正常的,說明代碼本身并不存在邏輯缺陷,但高波特率下就異常了,大機率是遇到了代碼執行性能瓶頸。今天痞子衡就嘗試在 i.MXRT 上使用各種方法去提升性能來解決這個高波特率無法識别問題:
SBL 項目是支援全系列 i.MXRT 平台的,為了具體化問題,我們就選取 i.MXRT1062 型号為例,官方配套 MIMXRT1060-EVK 闆子上搭配了一顆四線串行 NOR Flash(芯成IS25WP064A)用于存放代碼。
SBL 程式主體是 XIP 執行的,僅部分涉及 IAP 操作的代碼被分散加載到了 RAM 裡。SBL 中 ISP 功能代碼主體當然也是 XIP 為主,且在 SBL 程式裡是最先執行的(本地更新逾時後才進入 SBL 主體),SBL 工程裡跟序列槽波特率識别相關的源檔案一共如下三個:
MIMXRT1060-EVK 闆子上序列槽是 GPIO1[13:12],其中 RXD - GPIO1[13] 是核心的用于波特率識别的引腳,為了便于直覺地感受代碼執行性能,我們用另一個 GPIO1[12] 來輔助,将其配置為 GPIO 輸出模式,初值為高電平,在 GPIO 中斷處理函數裡保持低電平來标示執行總時間:
Note :下述代碼裡中斷處理函數實際上有點小缺陷,《中斷處理函數(IRQHandler)的标準流程》 一文裡給出了改進方法,但這裡為了觀察中斷處理代碼是否能在下一次中斷來臨前執行完畢特意舍棄了文中 2.2.2 小節裡的改進)
現在我們用示波器同時抓取 GPIO1[13:12] 信号,分别測試 9600 低波特率(下圖一)和 115200 高波特率(下圖二)下實際波形,根據測量第一次 GPIO 中斷處理執行時間大概是 32.8us(7 次中斷因代碼分支執行不同略有差別),這個時間對于 9600 波特率下單 bit 傳輸耗時約 104us 的情況來說是足夠快的,但是對于 115200 波特率下單 bit 傳輸耗時約 8.68us 的情況來說就顯得有點慢了(最小的下降沿之間間隔是 2bit 傳輸耗時 17.36us ),這也是 115200 無法被識别的原因,因為有 4 個下降沿中斷被漏掉了。

Note: ISP 功能代碼裡配置的系統環境是:396MHz CPU 主頻、不使能 L1 Cache、100MHz Flash 工作頻率,普通 SPI 下 Fast Read Quad I/O SDR Non-Continuous 工作模式,并且使能了 FlexSPI 的 Prefetch 特性(AHB RX Buffer 為 1KB)。
既然代碼執行性能不夠,那就努力提升性能,文章标題叫十八般武藝,這隻是一種誇張說法,不過痞子衡确實收集了如下六種提升性能的方法,讓我們一一嘗試吧,注意下述結果都是疊加前面方法而得的(所有測試均是在 115200 波特率下進行)。
ISP 功能代碼裡配置的 CPU 主頻是 396MHz,實際上這是根據 BootROM 預設運作配置而來的,而 i.MXRT1062 是可以跑到 600MHz 主頻的,将 SDK 代碼裡 armPllConfig_BOARD_BootClockRUN.loopDivider 由 66 調大到 100 即可。
CPU 主頻提升後第一次 GPIO 中斷處理執行時間從 32.8us 下降到了 32.2us,性能僅有微小提升,看來此時主要性能瓶頸不在 CPU 主頻上,應該是 Flash 通路性能在拖後腿。
SBL 工程裡啟動頭 FDCB 配置的是 100MHz Flash 工作頻率,但 MIMXRT1060-EVK 闆載 Flash(芯成IS25WP064A)最大工作頻率是 133MHz,是以我們可以提升 Flash 工作頻率。修改 qspiflash_config.memConfig.serialClkFreq 為 kFlexSpiSerialClk_133MHz 即可。不了解 FDCB 結構體工作機制的可以翻閱痞子衡舊文 《從頭開始認識i.MXRT啟動頭FDCB裡的lookupTable》 。
Flash 工作頻率提升後第一次 GPIO 中斷處理執行時間從 32.2us 下降到了 27.8us,這次的性能提升算有點明顯了,但是還是不夠,解決不了問題。
讓我們繼續從 Flash 傳輸模式上做文章,ISP 功能代碼裡配置的是普通 SPI 下 Fast Read Quad I/O SDR Non-Continuous 工作模式,這個模式已經算是非常高效的傳輸模式了,如果還想改進,要麼是切換到 QPI 模式(将 CMD 子序列也從一線變到四線)要麼是使能 Continuous Read(除了第一個 CMD 子序列,其後 CMD 子序列全部省掉),綜合考慮應該是使能 Continuous Read 性能提升更大一些,具體方法參考 《在i.MXRT啟動頭FDCB裡使能串行NOR Flash的Continuous read模式》。
使能 Flash Continuous Read 後第一次 GPIO 中斷處理執行時間從 27.8us 下降到了 27.4us,性能僅有微小提升,這應該跟我們使能了 FlexSPI prefetch 特性有關,1KB AHB RX Buffer 的存在導緻 CMD 子序列在總傳輸時序中占比不明顯。不過有點收獲的是漏掉的下降沿中斷從 4 個減少到了 3 個。
對于 XIP 工程來說,不開 L1 I-Cache 加速性能是非常吃虧的一件事,i.MXRT1062 内部有 32KB I-Cache,不把這個 Cache 用起來簡直是暴殄天物。雖然工程 SystemInit() 函數裡會執行一次 SCB_EnableICache(),但這隻是一個 Cache 總開關,要想 Cache 對 Flash 映射位址(0x60000000 之後)産生作用還得借助 BOARD_ConfigMPU() 函數來具體配置 MPU。關于 Cache 對 Flash 讀取的性能提升見 《實抓Flash信号波形來看i.MXRT的FlexSPI外設下AHB讀通路情形(全加速)》 。
使能 Cache 後第一次 GPIO 中斷處理執行時間從 27.4us 下降到了 19us,後面的 GPIO 中斷執行耗時更是大大縮短(原因是中斷處理函數相關代碼在第一次中斷觸發執行時被順便放到 Cache 裡了),這時候 115200 高波特率已經能夠被正常識别了。
到這裡問題已經解決了,但我們還沒有榨幹 MCU 最後一滴血,優化繼續。上圖波形裡第一次 GPIO 中斷處理執行時間相比其後面的 6 次中斷執行耗時要明顯長,這還是有風險的,比如再高的波特率 256000 還是無法正常識别(至少第一次識别會失敗,後面上位機再重複發暗号做第二次識别就可以了)。為了讓第一次 GPIO 中斷處理時間也大大縮短,我們可以在系統初始化的時候故意調用一下這些中斷處理相關函數,将這些代碼事先裝載到 I-Cache裡。
将中斷處理函數相關代碼預裝載到 I-Cache 後第一次 GPIO 中斷處理執行時間從 19us 銳降到了 2.12us,跟其他中斷處理執行差不多的耗時,現在即使是 256000 高波特率也能一次識别成功。
靠 Cache 這種無法精準控制的優化政策始終讓我們無法放心,還是将中斷處理相關代碼直接放到 TCM 裡更可靠,我們在工程連結檔案(MIMXRT1062xxxxx_flexspi_nor.icf)裡做如下修改将第一節裡列出了三個源檔案全部弄到 RAM 區裡執行(對于 XIP 工程來說,RAM 區是 DTCM, 當然對于代碼來說 ITCM 效率要更高,不過 DTCM 也夠用了)。
将中斷處理函數相關代碼重定位到 DTCM 執行後第一次 GPIO 中斷處理執行時間從 2.12us 再降到了 520ns,這下 1M 超高波特率也能被識别了。
性能提升結束了嗎?痞子衡還有一招,參見 《連結函數到8位元組對齊位址或可進一步提升i.MXRT1xxx核心執行性能》 一文,将中斷處理相關函數全部連結到八位元組對齊位址還可以再利用 Cortex-M7 核心指令雙發射特性。我們檢視下工程映射檔案(sbl.map),三個相關函數僅有計時函數 microseconds_get_ticks() 被自動配置設定到了八位元組對齊的位址,其他兩個函數不是,是以還有提升空間。
将非八位元組位址對齊的中斷處理相關函數調整到八位元組位址對齊後(具體方法這裡就不展開介紹了),第一次 GPIO 中斷處理執行時間從 520ns 降到了 480ns,這幾乎是性能極限了。
至此,在序列槽波特率識别執行個體裡逐漸展示i.MXRT上提升代碼執行性能的十八般武藝痞子衡便介紹完畢了,掌聲在哪裡~~~
文章會同時釋出到我的 部落格園首頁、CSDN首頁、知乎首頁、微信公衆号 平台上。
微信搜尋"痞子衡嵌入式"或者掃描下面二維碼,就可以在手機上第一時間看了哦。
最後歡迎關注痞子衡個人微信公衆号【痞子衡嵌入式】,一個專注嵌入式技術的公衆号,跟着痞子衡一起玩轉嵌入式。
衡傑(痞子衡),目前就職于恩智浦MCU系統部門,擔任嵌入式系統應用工程師。
專欄内所有文章的轉載請注明出處:http://www.cnblogs.com/henjay724/
與痞子衡進一步交流或咨詢業務合作請發郵件至 [email protected]
可以關注痞子衡的Github首頁 https://github.com/JayHeng,有很多好玩的嵌入式項目。
關于專欄文章有任何疑問請直接在部落格下面留言,痞子衡會及時回複免費(劃重點)答疑。
痞子衡郵箱已被私信擠爆,技術問題不推薦私信,堅持私信請先掃碼付款(5元起步)再發。