大家好,我是痞子衡,是正經(jīng)搞技術(shù)的痞子。今天痞子衡給大家分享的是在IAR開(kāi)發(fā)環(huán)境下將整個(gè)源文件代碼重定向到任意RAM中的方法。
痞子衡舊文 《在IAR下將關(guān)鍵函數(shù)重定向到RAM中執(zhí)行的方法》 里介紹了三種關(guān)鍵函數(shù)重定向方法,不過(guò)這三種方法只是寫(xiě)法形式不同,本質(zhì)上沒(méi)啥區(qū)別,都是利用 IAR 鏈接器特性將函數(shù)重定向到工程數(shù)據(jù)段(RW)所在 RAM 里。
對(duì)于 i.MXRT 這種擁有多塊地址非連續(xù)的 RAM 的芯片,其實(shí)我們也可以單獨(dú)將這些重定向函數(shù)放到一個(gè)指定的 RAM 里,不一定非得跟數(shù)據(jù)段放在同一個(gè) RAM 里。具體實(shí)現(xiàn)也很簡(jiǎn)單,只需要在鏈接文件里額外加一句 place in 語(yǔ)句處理即可,恩智浦官方 SDK 包里就是這么做的。
然而痞子衡最近在支持 i.MXRT1170 客戶過(guò)程中,不使用恩智浦 SDK 里那種自定義函數(shù)段處理的方法,而是在 IAR 鏈接文件里使用指定源文件 object 的方式將代碼重定向到 ITCM 竟然失效了,這是怎么回事?今天我們一起來(lái)看一下:
- Note 1:閱讀本文前需要對(duì) 《IAR鏈接文件(.icf)》、《IAR映射文件(.map)》 這兩種文件有所了解。Note 2:本文使用的 IAR EWARM 軟件版本是 v9.10.2。
一、回顧SDK里函數(shù)重定向做法
我們以最經(jīng)典的 SDK_2.11.0_MIMXRT1170-EVKboardsevkmimxrt1170demo_appshello_worldcm7iar 例程來(lái)看,工程 Build 選擇 flexspi_nor_debug,即代碼段放在 Flash 里(0x30000000 - ),數(shù)據(jù)段放在 DTCM 里(0x20000000 - )。
我們現(xiàn)在新建一個(gè)名為 ram_code.c 的源文件,在這個(gè)源文件里定義如下兩個(gè) delay1/2() 函數(shù),然后將這個(gè)源文件添加進(jìn)工程使用。按照 SDK 里做法,如果我們想將這兩個(gè)函數(shù)重定向,需要加 AT_QUICKACCESS_SECTION_CODE 宏來(lái)修飾,其實(shí)就是將函數(shù)放到名為 CodeQuickAccess 的自定義段里。
#define AT_QUICKACCESS_SECTION_CODE(func) func @"CodeQuickAccess"
AT_QUICKACCESS_SECTION_CODE(void delay1(void));
void delay1(void)
{
__NOP();
}
AT_QUICKACCESS_SECTION_CODE(void delay2(void));
void delay2(void)
{
__NOP();
__NOP();
}
然后工程鏈接文件 MIMXRT1176xxxxx_cm7_flexspi_nor.icf 里(僅摘錄部分),CodeQuickAccess 段單獨(dú)放在 ITCM 里(0x00000000 - ),這就是官方 SDK 里的實(shí)現(xiàn)。
define symbol m_data_start = 0x20000000;
define symbol m_data_end = 0x2003FFFF;
define symbol m_itcm_start = 0x00000000;
define symbol m_itcm_end = 0x0003FFFF;
define region DATA_region = mem:[from m_data_start to m_data_end-__size_cstack__];
define region ITCM_region = mem:[from m_itcm_start to m_itcm_end];
define block RW { first readwrite, section m_usb_dma_init_data };
define block QACCESS_CODE { section CodeQuickAccess };
initialize by copy { readwrite, section .textrw, section CodeQuickAccess };
place in DATA_region { block RW };
place in ITCM_region { block QACCESS_CODE };
編譯鏈接 hello_world_demo_cm7.ewp 工程,然后查看其映射文件(hello_world_demo_cm7.map)找到跟 delay1/2() 函數(shù)相關(guān)的內(nèi)容如下,顯然這是符合預(yù)期的。這里特別注意一下,CodeQuickAccess 的類別顯示的是 inited,表明其不是常見(jiàn)的 ro code,而是經(jīng)過(guò)重定向的,而且 delay1/2() 函數(shù)所在 ram_code.o 里包含了 10 個(gè)字節(jié)的 rw code。
*******************************************************************************
*** PLACEMENT SUMMARY
***
define block QACCESS_CODE { section CodeQuickAccess };
"P8": place in [from 0x0 to 0x3'ffff] { block QACCESS_CODE };
Section Kind Address Size Object
------- ---- ------- ---- ------
"P8": 0xc
QACCESS_CODE 0x0 0xc <Block>
QACCESS_CODE-1 0x0 0xa <Init block>
CodeQuickAccess inited 0x0 0xa ram_code.o [6]
- 0xc 0xc
*******************************************************************************
*** MODULE SUMMARY
***
Module ro code rw code ro data rw data
------ ------- ------- ------- -------
ram_code.o 10 10
*******************************************************************************
*** ENTRY LIST
***
Entry Address Size Type Object
---- ------- ---- ---- ------
delay1 0x1 0x4 Code Gb ram_code.o [6]
delay2 0x5 0x6 Code Gb ram_code.o [6]
如果你打開(kāi)工程鏡像文件 hello_world_demo_cm7.srec 查看,這里也只有一段連續(xù)的 Flash 地址空間數(shù)據(jù),沒(méi)有 RAM 地址空間數(shù)據(jù),所以這個(gè)鏡像文件是符合獨(dú)立工具(比如 MCUBootUtility)下載以及 BootROM 啟動(dòng)條件的。
二、引出源文件Object方式重定向失效問(wèn)題
現(xiàn)在來(lái)看客戶遇到的問(wèn)題,客戶不想在源文件里逐一修飾需要重定向的函數(shù)(即 ram_code.c 文件里 delay1/2() 函數(shù)不加 AT_QUICKACCESS_SECTION_CODE 修飾),這種情況下我們需要改動(dòng)一下鏈接文件,將 object ram_code.o 放到 initialize by copy 語(yǔ)句和 place in ITCM_region 語(yǔ)句里。
initialize by copy { readwrite, section .textrw, section CodeQuickAccess, object ram_code.o };
place in ITCM_region { object ram_code.o };
這時(shí)候重新編譯鏈接工程,查看映射文件,找到跟 delay1/2() 函數(shù)相關(guān)的內(nèi)容如下,這個(gè)結(jié)果跟第一小節(jié)里結(jié)果有點(diǎn)區(qū)別,雖然 delay1/2() 確實(shí)鏈接在了 ITCM 里(0x00000000 - ),但是同時(shí)也增加了 0x0 - 0xb 區(qū)域的 Initializer bytes。
*******************************************************************************
*** PLACEMENT SUMMARY
***
define block QACCESS_CODE { section CodeQuickAccess };
"P8": place in [from 0x0 to 0x3'ffff] { block QACCESS_CODE };
"P9": place in [from 0x0 to 0x3'ffff] { object ram_code.o };
Section Kind Address Size Object
------- ---- ------- ---- ------
"P8-P9": 0x18
Initializer bytes const 0x0 0xc <for P8-P9-1>
P8-P9-1 0xc 0xa <Init block>
.text inited 0xc 0xa ram_code.o [6]
- 0x16 0x16
*******************************************************************************
*** MODULE SUMMARY
***
Module ro code ro data rw data
------ ------- ------- -------
ram_code.o 10 10
*******************************************************************************
*** ENTRY LIST
***
Entry Address Size Type Object
---- ------- ---- ---- ------
delay1 0xd 0x4 Code Gb ram_code.o [6]
delay2 0x11 0x6 Code Gb ram_code.o [6]
如果此時(shí)再打開(kāi)工程鏡像文件 hello_world_demo_cm7.srec 查看,除了 Flash 地址空間數(shù)據(jù),還新增了 RAM 地址空間數(shù)據(jù),很顯然這個(gè)鏡像文件不符合獨(dú)立工具(比如 MCUBootUtility)下載以及 BootROM 啟動(dòng),僅能用于 IDE 中在線下載調(diào)試(即分散加載了)。
三、源文件Object方式重定向失效分析
我們?cè)僮鲆粋€(gè)實(shí)驗(yàn),按 《在IAR下將關(guān)鍵函數(shù)重定向到RAM中執(zhí)行的方法》 一文 2.3 針對(duì)源文件中全部函數(shù) 方法,在鏈接文件中僅將 object ram_code.o 放到 initialize by copy 語(yǔ)句里,那么 ram_code.o 中的內(nèi)容會(huì)被統(tǒng)一重定向到工程數(shù)據(jù)段 RW 所在 RAM 區(qū)域里(即 DTCM 0x20000000 -),這種情況下 delay1/2() 函數(shù)重定向是成功的。
initialize by copy { readwrite, section .textrw, section CodeQuickAccess, object ram_code.o };
//place in ITCM_region { object ram_code.o };
所以我們能夠得出結(jié)論,在不自定義函數(shù)段名的情況下,object ram_code.o 中內(nèi)容會(huì)在默認(rèn) RO、RW 段里,在做函數(shù)重定向時(shí),IAR 鏈接器無(wú)法將對(duì)應(yīng) ram_code.o 的 Initializer bytes 從默認(rèn) RO 段里單獨(dú)提取出來(lái)拷貝到非 RW 段所在區(qū),它只能統(tǒng)一處理 RO 段 Initializer bytes 到 RW 區(qū)的拷貝。如果硬要將 object ram_code.o 重定向到非 RW 所在 RAM 區(qū),IAR 鏈接器會(huì)直接將其 Initializer bytes 也從 RO 段里抽出來(lái),與其 RW 屬性的 .text 放在一起,這其實(shí)幾乎等效于分散加載了。
四、源文件Object方式重定向失效解決方案
分析到這里,解決方案清晰了,還是需要自定義程序段,不過(guò)既然不想單個(gè)函數(shù)加修飾,那有沒(méi)有整個(gè)文件范圍內(nèi)代碼統(tǒng)一加修飾呢?當(dāng)然是有的,這時(shí)候需要借助如下 #pragma default_function_attributes 語(yǔ)法,將這一對(duì)語(yǔ)句放置到源文件首行和末行,那么該源文件里所有函數(shù)都進(jìn)入了 .myCodeSection 自定義段里:
// 作用全部函數(shù)
#pragma default_function_attributes = @ ".myCodeSection"
// 作用全部變量(如有必要)
//#pragma default_variable_attributes = @ ".myVariSection"
void delay1(void)
{
__NOP();
}
void delay2(void)
{
__NOP();
__NOP();
}
#pragma default_function_attributes =
//#pragma default_variable_attributes =
然后在工程鏈接文件 MIMXRT1176xxxxx_cm7_flexspi_nor_sdram.icf 里直接將 section .myCodeSection 放到 ITCM 里就可以了:
initialize by copy { readwrite, section .textrw, section CodeQuickAccess, section .myCodeSection };
place in ITCM_region { section .myCodeSection };
這時(shí)候再編譯鏈接工程查看映射文件,函數(shù)重定向結(jié)果就符合預(yù)期了,這個(gè)結(jié)果跟第一小節(jié)里的結(jié)果一致。
*******************************************************************************
*** PLACEMENT SUMMARY
***
"P8": place in [from 0x0 to 0x3'ffff] { block QACCESS_CODE };
"P9": place in [from 0x0 to 0x3'ffff] { section .myCodeSection };
Section Kind Address Size Object
------- ---- ------- ---- ------
"P8-P9": 0xc
P8-P9 0x0 0xc <Init block>
.myCodeSection inited 0x0 0xa ram_code.o [6]
- 0xc 0xc
*******************************************************************************
*** MODULE SUMMARY
***
Module ro code rw code ro data rw data
------ ------- ------- ------- -------
ram_code.o 10 10
*******************************************************************************
*** ENTRY LIST
***
Entry Address Size Type Object
---- ------- ---- ---- ------
delay1 0x1 0x4 Code Gb ram_code.o [6]
delay2 0x5 0x6 Code Gb ram_code.o [6]
至此,在IAR開(kāi)發(fā)環(huán)境下將整個(gè)源文件代碼重定向到任意RAM中的方法痞子衡便介紹完畢了,掌聲在哪里~~~