大家好,我是痞子衡,是正经搞技术的痞子。今天痞子衡给大家介绍的是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主页、知乎主页、微信公众号 平台上。
微信搜索"痞子衡嵌入式"或者扫描下面二维码,就可以在手机上第一时间看了哦。