大家好,我是痞子衡,是正經搞技術的痞子。今天痞子衡給大家介紹的是MCUXpresso IDE下工程鏈接文件配置管理與自動生成機制。
痞子衡在 2018 年初寫過一個專題 《嵌入式開發文件系列》,用 8 篇文章系統地介紹了 ARM Cortex-M 內核微控制器開發過程中所要了解的主要文件類型:源文件、鏈接文件、工程文件、可重定向文件、映射文件、可執行文件、反匯編文件、鏡像文件。
上述 8 種文件中,大家對源文件、工程文件以及鏡像文件這三種應該是最熟悉的,而其余文件類型,很多人應該都沒有深入研究過,但痞子衡一直認為只有深入了解了鏈接文件才算是真正步入嵌入式開發老手行列。
我們知道不同 IDE 下鏈接文件語法是不一樣的,而恩智浦 MCUXpresso IDE 底層編譯器是 Arm GCC,因此其鏈接文件就是標准 GCC 下 .ld 文件。如果你對 .ld 文件語法非常精通,當然可以自己從頭開始寫鏈接文件,如果不太熟的話,也不要緊張,MCUXpresso IDE 早就為你掃清了障礙,在這個 IDE 下能夠支持圖形界面里做鏈接配置,然后自動生成相應鏈接文件的。今天痞子衡就和大家聊聊這個特性:
- Note: 在開始今天的主題之前,可以先看一下痞子衡的舊文 《MCUXpresso IDE下SDK工程在Build配置上與IAR,MDK差異》,本篇實際上就是這篇文章的后續。
一、准備開發環境
首先需要准備好環境,包含必要的軟件,痞子衡的環境如下:
二、MCUXpresso IDE下鏈接文件配置
現在進入正題,我們先按照 《MCUXpresso IDE下SDK工程導入與workspace管理機制》 一文步驟從 SDK 包里導入生成一個工程(就選最簡單的 hello_world 吧)。工程導入成功后,會在 \MCUXpressoIDE_11.4.0_6224\workspace\evkmimxrt1170_hello_world_demo_cm7 下看到 .project 工程文件,在 MCUXpresso IDE 下打開這個工程。
2.1 Memory 空間定義
在工程名上右擊選擇 Properties 進入選項配置界面,其中 MCU settings 一欄里定義的就是 MCU 實際存儲空間,這是鏈接文件的空間分配基礎,我們后面會將程序里全部的段都鏈接在這些區域里。
存儲空間屬性(Type)分為兩類:一類是 Flash(存放 RO 段),一類是 RAM(存放 RW 段)。每個屬性的空間都可以被定義很多個,但其中僅 Alias 名為 Flash 和 RAM 的空間才是默認被選中用於鏈接程序段的(可通過上下移動按鈕將指定空間調整到前排 Flash 和 RAM 的位置)。

2.2 默認程序段分配
還是在上一節打開的 Properties 選項配置界面,其中 Settings / Managed Linker Script 頁面就是鏈接文件里具體程序段鏈接設置,這個頁面的最上面 Manage linker script 要保持勾選,勾選上則代表使用 IDE 的鏈接文件自動生成功能。
在一個具體應用程序項目工程里,如果源文件僅包含標准 C 和匯編代碼,那么程序段會被默認歸納為三大類:RO 段(函數代碼,常量,全局變量初值等),RW 段(全局變量,重定向到 RAM 中函數代碼等),Heap/Stack。IDE 里分別提供了這三類程序段的空間指定:
1. Link application to RAM 勾選框:
- 不勾選,則 RO 段放在 2.1 節圖中 Alias 名為 Flash 的空間里
- 勾選上,則 RO 段放在 2.1 節圖中 Alias 名為 RAM 的空間里
2. Heap and Stack placement 配置框:
- 可以按需調整 Heap/Stack 里的 Region,Location,Size,其中 Region 可以是 2.1 節圖中屬性為 RAM 的任意空間
- Heap/Stack 默認大小均為 4KB,放在 2.1 節圖中 Alias 名為 RAM 的空間里
3. Global data placement 下拉框:
- 可以按需鏈接 RW 段到 2.1 節圖中屬性為 RAM 的任意空間
- RW 段默認放在 2.1 節圖中 Alias 名為 RAM 的空間里

2.3 自定義程序段分配
除了上一節鏈接器默認的程序段名外,我們也可以自定義一些用戶段名,方便一些特殊代碼處理。這里需要使用 __attribute__((section("UserSectionName"))) 鏈接器語法來修飾指定函數/變量,這樣該函數/變量就會被放在 UserSectionName 段里,然后我們在上一節圖中鏈接設置框的最后 Extra linker script input sections 框里單獨為自定義 UserSectionName 段指定鏈接空間。
比如工程 clock_config.c 文件里如下函數 UpdateSemcClock(),這個函數在默認的 RO .text 段里,RO 段都是鏈接在 Flash 里的,但是我們希望將這個函數重定向到 ITCM 里執行,所以我們可以使用 __attribute__((section("CodeQuickAccess"))) 鏈接器語法來修飾這個函數,然后在 Extra linker script input sections 框里將 CodeQuickAccess 段放到 SRAM_ITC_cm7 空間里即可。
#define AT_QUICKACCESS_SECTION_CODE(func) __attribute__((section("CodeQuickAccess"), __noinline__)) func
#define AT_QUICKACCESS_SECTION_DATA(func) __attribute__((section("DataQuickAccess"))) func
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;
}
2.4 自動生成的鏈接文件
鏈接設置完成后,會在工程目錄 \MCUXpressoIDE_11.4.0_6224\workspace\evkmimxrt1170_hello_world_demo_cm7\Debug 下自動生成最終鏈接文件(假定用得 debug build),文件一共有三個,其中 evkmimxrt1170_hello_world_demo_cm7_Debug.ld 是主鏈接文件,感興趣的可以打開這個鏈接文件學習一下(如果看不懂語法可以結合這篇文章 https://www.embedded.com/building-bare-metal-arm-systems-with-gnu-part-3/ )。

2.5 查看Map文件確認
按照 2.1 節和 2.2 節 圖中的鏈接設置,我們編譯鏈接 evkmimxrt1170_hello_world_demo_cm7 工程可以得到如下鏈接結果,從空間占用上來看是符合預期的。

現在我們可以打開生成的 evkmimxrt1170_hello_world_demo_cm7.map 文件具體分析一下最終鏈接情況,痞子衡摘錄了最核心部分如下:
Memory Configuration
Name Origin Length Attributes
/------------------------------------------------------------/
// RO .text 段在 BOARD_FLASH:0x30000000 - 0x30006eaf,共 28336 個字節
// RO CodeQuickAccess 段在 BOARD_FLASH:0x30006eb0 - 0x30006ee7,共 56 個字節
// RO .data_init 段在 BOARD_FLASH:0x30006ee8 - 0x30006eeb,共 4 個字節
.boot_hdr 0x30000000 0x2000
.text 0x30002000 0x4eb0
// RW CodeQuickAccess 段在 SRAM_ITC_cm7:0x00000000 - 0x00000037,共 56 個字節
.data_RAM4 0x00000000 0x38 load address 0x30006eb0
CodeQuickAccess
0x00000000 0x38 ./board/clock_config.o
0x00000000 UpdateSemcClock
// RW .data 段在 BOARD_SDRAM:0x80000000 - 0x80000003,共 4 個字節
.data 0x80000000 0x4 load address 0x30006ee8
.data.SystemCoreClock
0x80000000 0x4 ./device/system_MIMXRT1176_cm7.o
// RW .bss 段在 BOARD_SDRAM:0x80000004 - 0x80000107,共 260 個字節
.bss 0x80000004 0x104
// Heap 在 BOARD_SDRAM:0x80000108 - 0x80001107,共 4KB
.heap 0x80000108 0x1000
// Stack 在 BOARD_SDRAM:0x82fff000 - 0x82ffffff,共 4KB
.stack 0x82fff000 0x0
0x82fff000 _vStackBase = .
0x82fff000 . = ALIGN (0x4)
0x83000000 _vStackTop = (. + _StackSize)
簡單總結下,RO 段一般從 Flash 的最前面開始鏈接的,.text 段在最前面,然后是 ramfunc 函數實體,最后是 .data_init 段(全局變量初值)。RW 段也是從 RAM 的最前面開始鏈接,.data 段在前,.bss 在后,然后是 Heap 和 Stack(Heap/Stack的具體位置是可以設置的,有 Start、End、Post Data 三種選擇)。
三、MCUXpresso IDE下鏈接小實驗
根據上面的知識,我們現在來做些鏈接設置小實驗,在做實驗前,我們調整下 Memory 定義,把 SDRAM 相關空間移到后面去,默認 RAM 用 SRAM_DTC_cm7 空間,這樣看起來習慣一點。

3.1 默認 XiP 鏈接
調整過 Memory 空間順序后的鏈接結果如下:

3.2 Non-XiP 鏈接
現在我們嘗試使能 Link application to RAM 選項,其余不變,此時可以看到 28396 Bytes 的 RO 段也到了 SRAM_DTC_cm7 空間里,BOARD_FLASH 空間完全沒有任何占用:

3.3 XiP 鏈接,調整 Stack 大小並放置到 OCRAM1
我們嘗試調整 Stack 大小到 1KB 並放置到 SRAM_OC1,其余不變,此時可以看到 SRAM_DTC_cm7 空間消耗相比 3.1 節里少了 4KB,但 SRAM_OC1 空間消耗多了 1KB:

3.4 XiP 鏈接,調整 RW 段到 OCRAM2
我們嘗試調整 RW 段到 SRAM_OC2,其余不變,此時可以看到 SRAM_DTC_cm7 空間消耗相比 3.1 節里少了 264 Bytes,但 SRAM_OC2 空間消耗多了 264 Bytes:

至此,MCUXpresso IDE下工程鏈接文件配置管理與自動生成機制痞子衡便介紹完畢了,掌聲在哪里~~~
歡迎訂閱
文章會同時發布到我的 博客園主頁、CSDN主頁、知乎主頁、微信公眾號 平台上。
微信搜索"痞子衡嵌入式"或者掃描下面二維碼,就可以在手機上第一時間看了哦。

