大家好,我是痞子衡,是正經(jīng)搞技術(shù)的痞子。今天痞子衡給大家分享的是在IAR開發(fā)環(huán)境下RT-Thread工程函數(shù)重定向失效分析。
痞子衡舊文 《在IAR下將關(guān)鍵函數(shù)重定向到RAM中執(zhí)行的方法》 里介紹了三種關(guān)鍵函數(shù)重定向方法,不過這三種方法只是寫法形式不同,本質(zhì)上沒啥區(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 語句處理即可,恩智浦官方 SDK 包里就是這么做的。
然而痞子衡最近在移植一個(gè) i.MXRT1170 ?RT-Thread 工程時(shí)發(fā)現(xiàn),在 IAR 鏈接文件里用自定義段來單獨(dú)指定重定向函數(shù)到 ITCM 竟然失效了,這是怎么回事?今天我們一起來看一下:
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 例程來看,工程 Build 選擇 flexspi_nor_sdram_debug(僅該 build 預(yù)編譯宏里有 XIP_BOOT_HEADER_DCD_ENABLE=1),即代碼段放在 Flash 里(0x30000000 - ),數(shù)據(jù)段放在 SDRAM 里(0x80000000 - )。
在時(shí)鐘初始化函數(shù) BOARD_BootClockRUN() 里會(huì)調(diào)用如下 UpdateSemcClock() 函數(shù),這個(gè)函數(shù)需要重定向到 RAM 里執(zhí)行,在代碼里先將它放到自定義 CodeQuickAccess 段里。
#define?AT_QUICKACCESS_SECTION_CODE(func)?func?@"CodeQuickAccess"
#if?defined(XIP_BOOT_HEADER_ENABLE)?&&?(XIP_BOOT_HEADER_ENABLE?==?1)
#if?defined(XIP_BOOT_HEADER_DCD_ENABLE)?&&?(XIP_BOOT_HEADER_DCD_ENABLE?==?1)
AT_QUICKACCESS_SECTION_CODE(void?UpdateSemcClock(void));
void?UpdateSemcClock(void)
{
????SEMC->IPCMD?=?0xA55A000D;
????while?((SEMC->INTR?&?0x3)?==?0);
????SEMC->INTR????????????????????????????????=?0x3;
????SEMC->DCCR????????????????????????????????=?0x0B;
????CCM->CLOCK_ROOT[kCLOCK_Root_Semc].CONTROL?=?0x602;
}
#endif
#endif
然后在工程鏈接文件 MIMXRT1176xxxxx_cm7_flexspi_nor_sdram.icf 里(僅摘錄部分),再將 CodeQuickAccess 段單獨(dú)放在 ITCM 里(0x00000000 - ),這就是官方 SDK 里的做法。
define symbol m_data3_start = 0x80000000;
define symbol m_data3_end = 0x82FFFFFF;
define symbol m_qacode_start = 0x00000000;
define symbol m_qacode_end = 0x0003FFFF;
define region DATA3_region = mem:[from m_data3_start to m_data3_end-__size_cstack__];
define region QACODE_region = mem:[from m_qacode_start to m_qacode_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 DATA3_region { block RW };
place in QACODE_region { block QACCESS_CODE };
編譯鏈接 hello_world_demo_cm7.ewp 工程,然后查看其映射文件(hello_world_demo_cm7.map)找到跟 UpdateSemcClock() 函數(shù)相關(guān)的內(nèi)容如下,顯然這是符合預(yù)期的。這里特別注意一下,CodeQuickAccess 的類別顯示的是 inited,表明其不是常見的 ro code,而是經(jīng)過重定向的,而且 UpdateSemcClock() 函數(shù)所在 clock_config.o 里包含了 60個(gè)字節(jié)的 rw code。
*******************************************************************************
*** PLACEMENT SUMMARY
***
define block QACCESS_CODE { section CodeQuickAccess };
"P7": place in [from 0x0 to 0x3'ffff] { block QACCESS_CODE };
Section Kind Address Size Object
------- ---- ------- ---- ------
"P7": 0x3c
QACCESS_CODE 0x0 0x3c <Block>
QACCESS_CODE-1 0x0 0x3c <Init block>
CodeQuickAccess inited 0x0 0x3c clock_config.o [1]
- 0x3c 0x3c
*******************************************************************************
*** MODULE SUMMARY
***
Module ro code rw code ro data rw data
------ ------- ------- ------- -------
clock_config.o 2'644 60 844
*******************************************************************************
*** ENTRY LIST
***
Entry Address Size Type Object
---- ------- ---- ---- ------
UpdateSemcClock 0x1 0x3c Code Gb clock_config.o [1]
二、引出RT-Thread下函數(shù)重定向失效問題
現(xiàn)在來看 RT-Thread 工程,也是一個(gè)簡(jiǎn)單的 hello world(具體工程略去不表),其中 i.MXRT1170 芯片 BSP 部分直接來自于官方 SDK,鏈接文件也與 SDK 里一致,但是編譯鏈接工程后查看其映射文件,發(fā)現(xiàn)跟 UpdateSemcClock() 函數(shù)相關(guān)的內(nèi)容如下,CodeQuickAccess 的類別顯示的是 ro code, UpdateSemcClock() 函數(shù)所在 clock_config.o 里干脆連 rw code 都沒有。顯然函數(shù)重定向失效了,鏈接文件里 initialize by copy { section CodeQuickAccess }; 語句沒起作用,這顯然就是一個(gè)分散鏈接而已。
*******************************************************************************
*** PLACEMENT SUMMARY
***
define block QACCESS_CODE { section CodeQuickAccess };
"P7": place in [from 0x0 to 0x3'ffff] { block QACCESS_CODE };
Section Kind Address Size Object
------- ---- ------- ---- ------
"P7": 0x3c
QACCESS_CODE 0x0 0x3c <Block>
CodeQuickAccess ro code 0x0 0x3c clock_config.o [4]
- 0x3c 0x3c
*******************************************************************************
*** MODULE SUMMARY
***
Module ro code ro data rw data
------ ------- ------- -------
clock_config.o 2'768 784
*******************************************************************************
*** ENTRY LIST
***
Entry Address Size Type Object
---- ------- ---- ---- ------
UpdateSemcClock 0x1 0x3c Code Gb clock_config.o [4]
三、RT-Thread下函數(shù)重定向失效分析
第一節(jié)里 SDK 裸機(jī)環(huán)境下函數(shù)重定向做法不會(huì)失效,RT-Thread 環(huán)境下同樣的做法就失效了,難道 IAR 對(duì) RTOS 支持不友好?但是痞子衡在 SDK_2.11.0_MIMXRT1170-EVKboardsevkmimxrt1170rtos_examplesfreertos_hello 下做了相同實(shí)驗(yàn),F(xiàn)reeRTOS 下這種函數(shù)重定向方式也是沒有問題的(FreeRTOS 內(nèi)核啟動(dòng)是在 main() 里),所以這個(gè)問題主要跟 RT-Thread 內(nèi)核代碼結(jié)構(gòu)設(shè)計(jì)有關(guān)。
經(jīng)過裸機(jī)工程、RT-Thread 工程、FreeRTOS 工程三者對(duì)比,痞子衡找到了問題所在。RT-Thread 內(nèi)核啟動(dòng)是在 /src/components.c 文件中的 __low_level_init() 函數(shù)里,而這個(gè) __low_level_init() 函數(shù)本應(yīng)是 IAR 入口函數(shù) __iar_program_start() 中的一部分(IAR 系統(tǒng)庫里有一個(gè)內(nèi)置 PUBWEAK 版本),但是 RT-Thread 重實(shí)現(xiàn)了這個(gè) __low_level_init() 函數(shù),很不幸的是 IAR 鏈接器對(duì)于自定義段的函數(shù)重定向認(rèn)定與原內(nèi)置 __low_level_init() 函數(shù)設(shè)計(jì)有某種內(nèi)在關(guān)聯(lián)。
RT-Thread 代碼:https://gitee.com/rtthread/rt-thread/blob/gitee_master/src/components.c
當(dāng)痞子衡將內(nèi)核啟動(dòng)函數(shù) rtthread_startup() ?放到 main() 里,而將 components.c 文件里的 __low_level_init() 函數(shù)臨時(shí)刪掉時(shí),函數(shù)重定向失效問題就解決了,不過這只是驗(yàn)證分析,并不是真正的解決方案。
四、RT-Thread下函數(shù)重定向失效解決方案
經(jīng)過痞子衡的一番嘗試,在 RT-Thread 重寫 __low_level_init() 函數(shù)的情況下,IAR 僅僅是無法正常處理自定義段的重定向函數(shù)代碼體,而如果將那些需要重定向的函數(shù)用 __ramfunc 修飾,統(tǒng)一放到 IAR 內(nèi)置默認(rèn)的 .textrw 段里,IAR 是可以正常處理的(感覺更像是 IAR 的一個(gè)缺陷)。
分析到這里,解決方案清晰了,首先是棄用 AT_QUICKACCESS_SECTION_CODE 宏,而改用 __ramfunc 來修飾 UpdateSemcClock() 函數(shù):
#if?defined(XIP_BOOT_HEADER_ENABLE)?&&?(XIP_BOOT_HEADER_ENABLE?==?1)
#if?defined(XIP_BOOT_HEADER_DCD_ENABLE)?&&?(XIP_BOOT_HEADER_DCD_ENABLE?==?1)
__ramfunc?void?UpdateSemcClock(void)
{
????SEMC->IPCMD?=?0xA55A000D;
????while?((SEMC->INTR?&?0x3)?==?0);
????SEMC->INTR????????????????????????????????=?0x3;
????SEMC->DCCR????????????????????????????????=?0x0B;
????CCM->CLOCK_ROOT[kCLOCK_Root_Semc].CONTROL?=?0x602;
}
#endif
#endif
然后在工程鏈接文件 MIMXRT1176xxxxx_cm7_flexspi_nor_sdram.icf 里直接將 section .textrw 放到 ITCM 里:
define symbol m_qacode_start = 0x00000000;
define symbol m_qacode_end = 0x0003FFFF;
define region QACODE_region = mem:[from m_qacode_start to m_qacode_end];
initialize by copy { readwrite, section .textrw };
place in QACODE_region { section .textrw };
這時(shí)候再編譯鏈接工程查看映射文件,函數(shù)重定向結(jié)果就符合預(yù)期了。.textrw 的類別顯示的是 inited,而且 UpdateSemcClock() 函數(shù)所在 clock_config.o 里也包含了 60個(gè)字節(jié)的 rw code。
*******************************************************************************
*** PLACEMENT SUMMARY
***
"P7": place in [from 0x0 to 0x3'ffff] { section .textrw };
Section Kind Address Size Object
------- ---- ------- ---- ------
"P7": 0x3c
P7 0x0 0x3c <Init block>
.textrw inited 0x0 0x3c clock_config.o [4]
- 0x3c 0x3c
*******************************************************************************
*** MODULE SUMMARY
***
Module ro code rw code ro data rw data
------ ------- ------- ------- -------
clock_config.o 2'708 60 846
*******************************************************************************
*** ENTRY LIST
***
Entry Address Size Type Object
---- ------- ---- ---- ------
UpdateSemcClock 0x1 0x3c Code Gb clock_config.o [4]
至此,在IAR開發(fā)環(huán)境下RT-Thread工程函數(shù)重定向失效分析痞子衡便介紹完畢了,掌聲在哪里~~~