大家好,我是痞子衡,是正經搞技術的痞子。今天痞子衡給大家分享的是在IAR開發環境下RT-Thread工程函式重定向失效分析。
痞子衡舊文 《在IAR下將關鍵函式重定向到RAM中執行的方法》 裡介紹了三種關鍵函式重定向方法,不過這三種方法只是寫法形式不同,本質上沒啥區別,都是利用 IAR 連結器特性將函式重定向到工程資料段(RW)所在 RAM 裡。
對於 i.MXRT 這種擁有多塊地址非連續的 RAM 的晶片,其實我們也可以單獨將這些重定向函式放到一個指定的 RAM 裡,不一定非得跟資料段放在同一個 RAM 裡。具體實現也很簡單,只需要在連結檔案裡額外加一句 place in 語句處理即可,恩智浦官方 SDK 包裡就是這麼做的。
然而痞子衡最近在移植一個 i.MXRT1170 RT-Thread 工程時發現,在 IAR 連結檔案裡用自定義段來單獨指定重定向函式到 ITCM 竟然失效了,這是怎麼回事?今天我們一起來看一下:
- Note 1: 閱讀本文前需要對 《IAR連結檔案(.icf)》、《IAR對映檔案(.map)》 這兩種檔案有所瞭解。
- Note 2: 本文使用的 IAR EWARM 軟體版本是 v9.10.2。
一、回顧SDK裡函式重定向做法
我們以最經典的 \SDK_2.11.0_MIMXRT1170-EVK\boards\evkmimxrt1170\demo_apps\hello_world\cm7\iar 例程來看,工程 Build 選擇 flexspi_nor_sdram_debug(僅該 build 預編譯巨集裡有 XIP_BOOT_HEADER_DCD_ENABLE=1),即程式碼段放在 Flash 裡(0x30000000 - ),資料段放在 SDRAM 裡(0x80000000 - )。
在時鐘初始化函式 BOARD_BootClockRUN() 裡會呼叫如下 UpdateSemcClock() 函式,這個函式需要重定向到 RAM 裡執行,在程式碼裡先將它放到自定義 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 段單獨放在 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() 函式相關的內容如下,顯然這是符合預期的。這裡特別注意一下,CodeQuickAccess 的類別顯示的是 inited,表明其不是常見的 ro code,而是經過重定向的,而且 UpdateSemcClock() 函式所在 clock_config.o 裡包含了 60個位元組的 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下函式重定向失效問題
現在來看 RT-Thread 工程,也是一個簡單的 hello world(具體工程略去不表),其中 i.MXRT1170 晶片 BSP 部分直接來自於官方 SDK,連結檔案也與 SDK 裡一致,但是編譯連結工程後檢視其對映檔案,發現跟 UpdateSemcClock() 函式相關的內容如下,CodeQuickAccess 的類別顯示的是 ro code, UpdateSemcClock() 函式所在 clock_config.o 裡乾脆連 rw code 都沒有。顯然函式重定向失效了,連結檔案裡 initialize by copy { section CodeQuickAccess }; 語句沒起作用,這顯然就是一個分散連結而已。
*******************************************************************************
*** 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下函式重定向失效分析
第一節裡 SDK 裸機環境下函式重定向做法不會失效,RT-Thread 環境下同樣的做法就失效了,難道 IAR 對 RTOS 支援不友好?但是痞子衡在 \SDK_2.11.0_MIMXRT1170-EVK\boards\evkmimxrt1170\rtos_examples\freertos_hello 下做了相同實驗,FreeRTOS 下這種函式重定向方式也是沒有問題的(FreeRTOS 核心啟動是在 main() 裡),所以這個問題主要跟 RT-Thread 核心程式碼結構設計有關。
經過裸機工程、RT-Thread 工程、FreeRTOS 工程三者對比,痞子衡找到了問題所在。RT-Thread 核心啟動是在 /src/components.c 檔案中的 __low_level_init() 函式裡,而這個 __low_level_init() 函式本應是 IAR 入口函式 __iar_program_start() 中的一部分(IAR 系統庫裡有一個內建 PUBWEAK 版本),但是 RT-Thread 重實現了這個 __low_level_init() 函式,很不幸的是 IAR 連結器對於自定義段的函式重定向認定與原內建 __low_level_init() 函式設計有某種內在關聯。
當痞子衡將核心啟動函式 rtthread_startup() 放到 main() 裡,而將 components.c 檔案裡的 __low_level_init() 函式臨時刪掉時,函式重定向失效問題就解決了,不過這只是驗證分析,並不是真正的解決方案。
四、RT-Thread下函式重定向失效解決方案
經過痞子衡的一番嘗試(嘗試結果見下表),在 RT-Thread 重寫 __low_level_init() 函式的情況下,IAR 僅僅是無法正常處理自定義段的重定向函式程式碼體,而如果將那些需要重定向的函式用 __ramfunc 修飾,統一放到 IAR 內建預設的 .textrw 段裡,IAR 是可以正常處理的(感覺更像是 IAR 的一個缺陷)。
原始檔處理 | 連結檔案處理 | IAR 最終連結結果 |
---|---|---|
放入自定義程式段 CodeQuickAccess | 僅 initialize by copy { section CodeQuickAccess }; | 函式直接鏈在程式碼段所在 Flash 區,無效重定向 |
放入自定義程式段 CodeQuickAccess | initialize by copy { section CodeQuickAccess }; place in ITCM_region { section CodeQuickAccess }; |
函式直接鏈在 ITCM 裡,無效重定向 |
__ramfunc 修飾放入預設段 .textrw | 僅 initialize by copy { section .textrw }; | 函式體儲存在程式碼段所在區,但連結在資料段所在 RAM 區,有效重定向 |
__ramfunc 修飾放入預設段 .textrw | initialize by copy { section .textrw }; place in ITCM_region { section .textrw }; |
函式體儲存在程式碼段所在區,但連結在 ITCM 裡,有效重定向 |
分析到這裡,解決方案清晰了,首先是棄用 AT_QUICKACCESS_SECTION_CODE 巨集,而改用 __ramfunc 來修飾 UpdateSemcClock() 函式:
#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 };
這時候再編譯連結工程檢視對映檔案,函式重定向結果就符合預期了。.textrw 的類別顯示的是 inited,而且 UpdateSemcClock() 函式所在 clock_config.o 裡也包含了 60個位元組的 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開發環境下RT-Thread工程函式重定向失效分析痞子衡便介紹完畢了,掌聲在哪裡~~~
歡迎訂閱
文章會同時釋出到我的 部落格園主頁、CSDN主頁、知乎主頁、微信公眾號 平臺上。
微信搜尋"痞子衡嵌入式"或者掃描下面二維碼,就可以在手機上第一時間看了哦。