大家好,我是痞子衡,是正經(jīng)搞技術(shù)的痞子。今天痞子衡給大家分享的是IAR環(huán)境下無(wú)法直接下載調(diào)試i.MXRT分散鏈接工程的解決方案。
分散鏈接與加載一直是嵌入式領(lǐng)域比較勸退新手的難題,在恩智浦 i.MXRT 系列為代表的多存儲(chǔ)器架構(gòu)的 MCU 上,分散鏈接問(wèn)題體現(xiàn)得尤為明顯,畢竟你在鏈接應(yīng)用程序各種段(section)時(shí)可能會(huì)面對(duì)包括內(nèi)部 ITCM/DTCM/OCRAM 和外部 Flash/SDRAM/PSRAM/HyperRAM 等多種存儲(chǔ)器空間選擇。
雖然存儲(chǔ)器空間選擇很多,但是一個(gè)最終可離線啟動(dòng)的 i.MXRT 程序(即能被下載進(jìn)外部非易失存儲(chǔ)器,且能被 BootROM 加載啟動(dòng))其 readonly 段應(yīng)該是一段連續(xù)的數(shù)據(jù)(SREC/HEX格式鏡像文件里僅能包含一段空間地址),即要鏈接在一個(gè)主存儲(chǔ)器空間里,這也意味著其它鏈接在非主存儲(chǔ)器空間的 .text 段應(yīng)該使用重定向方法來(lái)實(shí)現(xiàn),不可直接原地鏈接,參考痞子衡文章 《IAR下將源文件代碼重定向到任意RAM中的方法》。
最近有一個(gè) i.MXRT1060 客戶,他們就遇到了分散鏈接工程調(diào)試問(wèn)題,工程 readonly 段被直接分散鏈接到了兩個(gè)不同的外部存儲(chǔ)器空間,沒(méi)有用重定向方法,這雖然不符合離線啟動(dòng)要求,但是在 IAR 下直接下載調(diào)試也會(huì)報(bào)錯(cuò),這是怎么回事?
一、引出客戶問(wèn)題
我們?cè)龠M(jìn)一步描述客戶工程分散鏈接問(wèn)題,下圖包含了 i.MXRT 架構(gòu)下程序段的全部鏈接選擇,根據(jù)這些選擇組合,我們能產(chǎn)生多種不同的工程鏈接文件。
先來(lái)看不涉及分散鏈接的簡(jiǎn)單情況,即 readonly 段全在 Flash 里,readwrite 段在一個(gè)或多個(gè) RAM 空間里,這種情況下 IAR 下載調(diào)試沒(méi)有什么特殊注意事項(xiàng),flashloader 會(huì)負(fù)責(zé)外部 Flash 初始化,并將 readonly 段數(shù)據(jù)下載進(jìn) Flash,然后宏文件負(fù)責(zé)外部 RAM 初始化,在線調(diào)試一切正常。
Case1:APP readonly text/data1 + APP readwrite data2/3/4
再來(lái)看第二種情況,這里開(kāi)始涉及分散鏈接,readonly 段分散在多個(gè) RAM 空間,readwrite 段在一個(gè)或多個(gè) RAM 空間里。這種情況下因?yàn)闆](méi)有鏈接在 Flash 空間,因此無(wú)需 flashloader,完全由宏文件將相關(guān)外部 RAM 初始化好,多個(gè) readonly 段都能正常下載,在線調(diào)試一切正常。
Case2:APP readonly text/data2/3/4 + APP readonly text/data2/3/4 + APP readwrite data2/3/4
第三種情況再?gòu)?fù)雜一點(diǎn),readonly 段除了在 Flash 空間外,還有一部分放在了內(nèi)部 RAM 里,然后 readwrite 段依然在一個(gè)或多個(gè) RAM 空間里。這種情況下 IAR 下載調(diào)試感覺(jué)上應(yīng)該沒(méi)問(wèn)題,因?yàn)閮?nèi)部 RAM 無(wú)需初始化可直接訪問(wèn),兩個(gè)不連續(xù) readonly 段原則上可以下載,但是很遺憾,IAR 會(huì)報(bào)錯(cuò),其 flashloader 無(wú)法處理放在內(nèi)部 RAM 的 readonly 段,調(diào)試無(wú)法進(jìn)行。
Case3:APP readonly text/data1 + APP readonly text/data2 + APP readwrite data2/3/4
最后一種分散鏈接的情況最復(fù)雜,也是客戶的問(wèn)題所在,readonly 段除了在 Flash 空間外,還有一部分放在了外部 RAM,然后 readwrite 段在一個(gè)或多個(gè) RAM 空間里。這種情況下 IAR 下載調(diào)試一定會(huì)出問(wèn)題,默認(rèn) flashloader 只做了 Flash 初始化,并不負(fù)責(zé)初始化外部 RAM,因此部分 readonly 段往外部 RAM 下載時(shí)會(huì)報(bào)錯(cuò),工程宏文件雖然負(fù)責(zé)初始化外部 RAM,但其執(zhí)行階段在 flashloader 作用之后,鞭長(zhǎng)莫及。
Case4:APP readonly text/data1 + APP readonly text/data3/4 + APP readwrite data2/3/4
二、復(fù)現(xiàn)客戶問(wèn)題
我們?cè)诙髦瞧止俜?MIMXRT1060-EVK 板上復(fù)現(xiàn)一下客戶問(wèn)題,使用 SDK_2.11.0_EVK-MIMXRT1060boardsevkmimxrt1060demo_appshello_worldiar 工程,原工程有很多 Build,我們就選用 flexspi_nor_sdram build,它用到了兩塊外部存儲(chǔ)器,符合客戶場(chǎng)景。在這個(gè) build 里 readonly 段都鏈在外部 Flash 里,readwrite 段都鏈接在外部 SDRAM 里,顯然這個(gè)情況屬于第一節(jié)介紹的 case1:
1. Flash初始化工作:IAR SystemsEmbedded Workbench 9.10.2armconfigflashloaderNXPFlashIMXRT1060_FlexSPI.out
2. SDRAM初始化工作:SDK_2.11.0_EVK-MIMXRT1060boardsevkmimxrt1060demo_appshello_worldiarevkmimxrt1060_sdram_init.mac
我們現(xiàn)在要將工程稍微改動(dòng)一下,在工程源文件里定義一個(gè) sw_delay() 函數(shù)(記得要在 main 函數(shù)里調(diào)用一下),并且將其指定在自定義 .sdramCodeSection 段里:
#pragma default_function_attributes = @ ".sdramCodeSection"
void sw_delay(void)
{
__NOP();
}
#pragma default_function_attributes =
然后在工程鏈接文件里將這個(gè)自定義 .sdramCodeSection 段放到 SDRAM 空間里,這樣我們?cè)谕獠?Flash 和 SDRAM 空間里就都有 readonly 段了,跟客戶情況一致了。
place in DATA3_region { section .sdramCodeSection };
板卡上電,直接用板載 DAP-Link 調(diào)試器在線下載工程(為了減少對(duì)板子設(shè)置的依賴,我們將調(diào)試器復(fù)位類型改為 Core),下載過(guò)程中 IAR 果然一直在報(bào)錯(cuò),如果你忽略錯(cuò)誤繼續(xù)調(diào)試,雖然斷點(diǎn)會(huì)停在 main 函數(shù),但是只要單步進(jìn)放到 SDRAM 空間的函數(shù)里時(shí),程序就會(huì)跑飛進(jìn) hardfault,因?yàn)?SDRAM 中根本就沒(méi)有正確的 .sdramCodeSection 段數(shù)據(jù)。
三、嘗試僅借助宏文件(.mac)解決問(wèn)題
分析到這里,其實(shí)你應(yīng)該知道問(wèn)題出在哪里了,工程配套宏文件 evkmimxrt1060_sdram_init.mac 本應(yīng)負(fù)責(zé) SDRAM 初始化,但是其執(zhí)行順序在 FlashIMXRT1060_FlexSPI.out 作用之后,所以沒(méi)有產(chǎn)生其該有的效果,這個(gè)具體可見(jiàn)痞子衡舊文 《IAR內(nèi)部C-SPY調(diào)試組件配套宏文件(.mac)用法介紹》 3.1 小節(jié),有非常詳細(xì)的解釋。
現(xiàn)在的解決思路就是,如何讓 evkmimxrt1060_sdram_init.mac 里的 SDRAM 初始化語(yǔ)句在 flashloader 作用之前生效,所以我們很自然地想在 flashloader 配套的宏文件 FlashIMXRT1060_FlexSPI.mac 里的 execUserFlashInit() 接口里將 SDRAM 初始化語(yǔ)句都加上,但是很遺憾,這招不湊效,其實(shí)在第一節(jié)介紹的 case3 里就應(yīng)該認(rèn)清現(xiàn)實(shí)了,內(nèi)部 RAM 無(wú)需初始化 IAR 也無(wú)法正常下載。
四、借助雙Flashloader解決問(wèn)題
其實(shí) IAR 軟件設(shè)計(jì)里,對(duì)于兩個(gè) readonly 段,只要其中有一個(gè)段被放入了 Flash 里(即需要 flashloader),那么另外一個(gè)段不管是不是放在 Flash 里也需要有相應(yīng) flashloader,這里痞子衡要吐槽下 IAR 的設(shè)計(jì),有點(diǎn)呆板了。
所以本文案例里解決問(wèn)題的關(guān)鍵就是為 SDRAM 也設(shè)計(jì)一個(gè) flashloader,具體制作方法可以參考痞子衡舊文 《串行NOR Flash下載算法(IAR EWARM篇)》。因?yàn)?SDRAM 擦寫(xiě)其實(shí)也不需要什么特殊命令時(shí)序,就是單純 AHB 方式地寫(xiě)就行了,所以這個(gè) SDRAM 版本的 flashloader 就是個(gè)傀儡 flashloader 而已。
痞子衡為了讓這個(gè)傀儡 flashloader 更通用一些,是按如下方式實(shí)現(xiàn)三個(gè)主要 flashloader API 的,其中 FlashInit() 函數(shù)里故意沒(méi)有加 SEMC 模塊初始化代碼,就是為了讓這個(gè) flashloader 適用所有類型的 RAM(ITCM/DTCM/OCRAM/SDRAM/PSRAM/HyperRAM),外設(shè)初始化工作放在傀儡 flashloader 配套宏文件里去完成。
FlashInit() - 什么都不做,直接返回
FlashWrite() - 用 memcpy 函數(shù)實(shí)現(xiàn)
FlashErase() - 用 memset 函數(shù)實(shí)現(xiàn)
最終 RAM 型通用 flashloader 源碼工程地址如下:
https://github.com/JayHeng/imxrt-tool-flash-algo/tree/master/boards/nxp_evkmimxrt1060_rev.a1/ram_algo/IAR
我們把新生成的 SDRAM flashloader 相關(guān)的所有文件(.out/.flash/.mac)放到對(duì)應(yīng) IAR 系統(tǒng)目錄下,并且修改原來(lái)的 FlashIMXRT1060_EVK_FlexSPI.board 文件,加入 SDRAM 相關(guān)的部分:
1. FlashIMXRT1060_SEMC.mac 文件基本沿用 evkmimxrt1060_sdram_init.mac 文件,只是 setup 宏函數(shù)從 execUserPreload 換到 execUserFlashInit
2. FlashIMXRT1060_SEMC.flash 文件內(nèi)容按 FlashIMXRT1060_FlexSPI1.flash 寫(xiě)即可,注意文件后綴一定要是 .flash, IAR 只認(rèn)這個(gè)后綴。
現(xiàn)在再去下載調(diào)試,就一切正常了,說(shuō)明雙 Flashloader 解決方案生效了。本例是以 IAR flashloader 為例的,如果用 J-Link flashloader 也是可以的,一樣的原理制作兩個(gè) Flashloader 即可。
至此,IAR環(huán)境下無(wú)法直接下載調(diào)試i.MXRT分散鏈接工程的解決方案痞子衡便介紹完畢了,掌聲在哪里~~~