痞子衡嵌入式:MCUXpresso IDE下将关键函数重定向到RAM中执行的几种方法
大家好,我是痞子衡,是正经搞技术的痞子。今天痞子衡给大家分享的是MCUXpresso IDE下将关键函数重定向到RAM中执行的几种方法。
前段时间痞子衡写了一篇 《在IAR开发环境下将关键函数重定向到RAM中执行的三种方法》,有读者在文章下面留言,希望也讲一讲 MCUXpresso IDE 下函数重定向到 RAM 中执行的方法。我们知道函数重定向的实现需要借助 IDE 中链接器,不同 IDE 下虽然链接器原理差不多,但具体链接语法不太一样。MCUXpresso IDE 的底层工具链是 Arm GCC,所以今天的主题其实跟 Arm GCC 链接器语法及用法有关。
- Note:本篇基于 《MCUXpresso IDE下工程链接文件配置管理与自动生成机制》 一文基础,除非特殊申明,否则下文均指使用 IDE 自动生成的链接文件。
一、准备工作
首先需要准备好环境,包含必要的软件,痞子衡的环境如下:
然后按照 《MCUXpresso IDE下SDK工程导入与workspace管理机制》 一文步骤从 SDK 包里导入生成一个工程(就选最简单的 hello_world 吧)。工程导入成功后,会在 \MCUXpressoIDE_11.4.0_6224\workspace\evkmimxrt1170_hello_world_demo_cm7 下看到 .project 工程文件,在 MCUXpresso IDE 下打开这个工程,然后调整工程设置 Memory 定义中顺序如下:
现在我们再创建一个新源文件 critical_code.c 用于示例关键函数,将这个源文件添加进工程里,critical_code.c 文件中只有如下三个测试函数(它们在 main 函数里会被调用):
void critical_func1(uint32_t n) { PRINTF("Arg = %d .\r\n", n); } void critical_func2(uint32_t n) { PRINTF("Arg * 2 = %d .\r\n", 2 * n); } void critical_func3(uint32_t n) { PRINTF("Arg * 3 = %d .\r\n", 3 * n); }
编译链接修改后的工程,然后查看其映射文件(evkmimxrt1170_hello_world_demo_cm7.map)找到跟 critical_code.c 文件相关的内容如下,显然 critical_code.c 中的三个函数都会被链在 BOARD_FLASH 空间里(均在 .text 段里,函数体本身总大小为 52bytes)。
Linker script and memory map LOAD ./source/critical_code.o **************************************************** ad8 ********** .text 0x30002000 0x4f30 .text.critical_func1 0x300026dc 0x10 ./source/critical_code.o 0x300026dc critical_func1 .text.critical_func2 0x300026ec 0x10 ./source/critical_code.o 0x300026ec critical_func2 .text.critical_func3 0x300026fc 0x14 ./source/critical_code.o 0x300026fc critical_func3 .rodata.critical_func1.str1.4 0x30005ea4 0xd ./source/critical_code.o *fill* 0x30005eb1 0x3 ff .rodata.critical_func2.str1.4 0x30005eb4 0x11 ./source/critical_code.o *fill* 0x30005ec5 0x3 ff .rodata.critical_func3.str1.4 0x30005ec8 0x11 ./source/critical_code.o *fill* 0x30005ed9 0x3 ff
二、重定向到RAM中方法
我们现在要做的事就是将 critical_code.c 文件中的函数重定向到 RAM 里执行,原 MCUXpresso IDE 工程链接配置里指定的是 SRAM_DTC_cm7 空间来存放 readwrite 段,那我们就尝试将关键函数重定向到 SRAM_DTC_cm7 里(如需改到 SRAM_ITC_cm7、SRAM_OC1/2 等空间方法类似)。
2.1 __RAMFUNC() 修饰函数
第一种方法是借助 MCUXpresso IDE 自带的头文件 cr_section_macros.h 里的宏。用 __RAMFUNC(RamAliasName) 宏来修饰函数定义。这种方法主要适用重定向单个关键函数,比如我们用它来修饰 critical_func1() 函数:
- Note: __RAMFUNC() 仅重定向被修饰的函数体本身代码,而该函数中调用的其他函数体本身并不受影响
#include <cr_section_macros.h> __RAMFUNC(RAM) void critical_func1(uint32_t n) { PRINTF("Arg = %d .\r\n", n); } void critical_func2(uint32_t n) { PRINTF("Arg * 2 = %d .\r\n", 2 * n); } void critical_func3(uint32_t n) { PRINTF("Arg * 3 = %d .\r\n", 3 * n); }
编译链接修改后的工程,然后查看其映射文件(evkmimxrt1170_hello_world_demo_cm7.map)找到跟 critical_code.c 文件相关的内容如下,此时 critical_func1() 已经被放到了 MCUXpresso IDE 内置的 .ramfunc.$RAM 段里,这个段是 MCUXpresso IDE 底层链接器专门用来收集重定向到 RAM 里的函数。
Linker script and memory map LOAD ./source/critical_code.o ************************************************************** .text 0x30002000 0x4f28 .text.critical_func2 0x300026dc 0x10 ./source/critical_code.o ad8 0x300026dc critical_func2 .text.critical_func3 0x300026ec 0x14 ./source/critical_code.o 0x300026ec critical_func3 .rodata.str1.4 0x30005e9c 0xd ./source/critical_code.o *fill* 0x30005ea9 0x3 ff .rodata.critical_func2.str1.4 0x30005eac 0x11 ./source/critical_code.o *fill* 0x30005ebd 0x3 ff .rodata.critical_func3.str1.4 0x30005ec0 0x11 ./source/critical_code.o *fill* 0x30005ed1 0x3 ff ************************************************************** .data 0x20000000 0x54 load address 0x30006f28 *(SORT_BY_ALIGNMENT(.ramfunc*)) .ramfunc.$RAM 0x20000000 0x10 ./source/critical_code.o 0x20000000 critical_func1 // 变化处
2.2 自定义section指定函数
第二种方法是借助 GNU C 里的 __attribute__ 机制,即用 __attribute__((section("UserSectionName"))) 语法来修饰函数定义,将其放到自定义程序段里。比如我们将 critical_func1() 函数放到名为 .criticalFunc 的自定义段里:
__attribute__((section(".criticalFunc"))) void critical_func1(uint32_t n) { PRINTF("Arg = %d .\r\n", n); } void critical_func2(uint32_t n) { PRINTF("Arg * 2 = %d .\r\n", 2 * n); } void critical_func3(uint32_t n) { PRINTF("Arg * 3 = %d .\r\n", 3 * n); }
然后在 MCUXpresso IDE 链接配置设置界面 Extra linker script input sections 框里,将自定义程序段指定到具体 RAMx 里:
编译链接修改后的工程,然后查看其映射文件(evkmimxrt1170_hello_world_demo_cm7.map)你会发现效果其实跟第一种方法是一模一样的,唯一的区别就是一个用 MCUXpresso IDE 内置的 .ramfunc.$RAM 段名,一个是用自定义段名 .criticalFunc 而已。
Linker script and memory map LOAD ./source/critical_code.o ************************************************************** .text 0x30002000 0x4f28 .text.critical_func2 0x300026dc 0x10 ./source/critical_code.o 0x300026dc critical_func2 .text.critical_func3 0x300026ec 0x14 ./source/critical_code.o 0x300026ec critical_func3 .rodata.str1.4 0x30005e9c 0xd ./source/critical_code.o *fill* 0x30005ea9 0x3 ff .rod 119 ata.critical_func2.str1.4 0x30005eac 0x11 ./source/critical_code.o *fill* 0x30005ebd 0x3 ff .rodata.critical_func3.str1.4 0x30005ec0 0x11 ./source/critical_code.o *fill* 0x30005ed1 0x3 ff **************** 566 ********************************************** .data 0x20000000 0x54 load address 0x30006f28 *(SORT_BY_ALIGNMENT(criticalFunc)) .criticalFunc 0x20000038 0x10 ./source/critical_code.o // 变化处 0x20000038 critical_func1 .criticalFunc.__stub 0x20000048 0x8 linker stubs
2.3 针对源文件中全部函数
前两种重定向方法都适用单个关键函数(如果是多个关键函数,按方法逐一添加修饰当然也行),但如果某个源文件里函数特别多,并且我们希望将这个源文件里函数全部重定向到 RAM 里,有没有更便捷的方法呢?当然有!
我们现在将 critical_code.c 文件里全部函数都重定向,首先需要在 MCUXpresso IDE 链接配置设置界面去掉 Manage linker script 选项的勾选,将自动生成的 evkmimxrt1170_hello_world_demo_cm7_Debug.ld 文件在同路径下拷贝一份重新命名,然后在 Linker script 路径里指定新的链接文件。
打开链接文件 evkmimxrt1170_hello_world_demo_cm7_Debug_User.ld,在里面分别找到 Main .text/.data S ad8 ECTION 执行域,将 critical_code.o 从 .text 中移除(注意原单独 *(.text*) 语句需要删除),并加进 .data 即可。
编译链接修改后的工程,然后查看其映射文件(evkmimxrt1170_hello_world_demo_cm7.map)找到跟 critical_code.c 文件相关的内容如下,此时 critical_func1/2/3() 都链接在 RAM 里了。
Linker script and memory map LOAD ./source/critical_code.o ************************************************************** *(SORT_BY_ALIGNMENT(EXCLUDE_FILE(*critical_code.o) .text*)) .rodata.critical_func1.str1.4 0x30005e8c 0xd ./source/critical_code.o *fill* 0x30005e99 0x3 ff .rodata.critical_func2.str1.4 0x30005e9c 0x11 ./source/critical_code.o *fill* 0x30005ead 0x3 ff .rodata.critical_func3.str1.4 0x30005eb0 0x11 ./source/critical_code.o *fill* 0x30005ec1 0x3 ff ************************************************************** .data 0x20000000 0x7c load address 0x30006f18 *critical_code.o(SORT_BY_ALIGNMENT(.text.*)) .text.critical_func1 0x20000038 0x10 ./source/critical_code.o 0x20000038 critical_func1 .text.critical_func2 0x20000048 0x10 ./source/critical_code.o 0x20000048 critical_func2 .text.critical_func3 0x20000058 0x14 ./source/critical_code.o 0x20000058 critical_func3 *fill* 0x2000006c 0x4 ff .text.critical_func3.__stub 0x20000070 0x8 linker stubs
这里顺便说一下,本来不想手动改链接文件,想借助如下 IDE 选项配置把要增加的语句放到自动生成的链接文件里,但实测达不到预期效果,因为 .text 执行域里的原 *(.text*) 语句依旧存在,看起来这个 Extra linker script input sections 框只能添加新语句进链接文件,缺少删除语句的功能。
三、启动文件中拷贝过程
三种函数重定向方法都介绍完了,不知道你是否曾有过这样的疑问,这些关键函数机器码到底是什么时候怎么从 Flash 中拷贝到 RAM 里的?这要从工程启动文件 startup_mimxrt1176_cm7.c 谈起,在复位函数 ResetISR() 里有全部的 data/bss 等段初始化过程:
22daextern unsigned int __data_section_table; extern unsigned int __data_section_table_end; extern unsigned int __bss_section_table; extern unsigned int __bss_section_table_end; __attribute__ ((naked, section(".after_vectors.reset"))) void ResetISR(void) { // Disable interrupts __asm volatile ("cpsid i"); __asm volatile ("MSR MSP, %0" : : "r" (&_vStackTop) : ); #if defined (__USE_CMSIS) // If __USE_CMSIS defined, then call CMSIS SystemInit code SystemInit(); #endif // (__USE_CMSIS) // Copy the data sections from flash to SRAM. unsigned int LoadAddr, ExeAddr, SectionLen; unsigned int *SectionTableAddr; // Load base address of Global Section Table SectionTableAddr = &__data_section_table; // Copy the data sections from flash to SRAM. while (SectionTableAddr < &__data_section_table_end) { LoadAddr = *SectionTableAddr++; ExeAddr = *SectionTableAddr++; SectionLen = *SectionTableAddr++; data_init(LoadAddr, ExeAddr, SectionLen); } // At this point, SectionTableAddr = &__bss_section_table; // Zero fill the bss segment while (SectionTableAddr < &__bss_section_table_end) { ExeAddr = *SectionTableAddr++; SectionLen = *SectionTableAddr++; bss_init(ExeAddr, SectionLen); } // Reenable interrupts __asm volatile ("cpsie i"); #if defined (__REDLIB__) // Call the Redlib library, which in turn calls main() __main(); #endif while (1); }
至此,MCUXpresso IDE下将关键函数重定向到RAM中执行的几种方法痞子衡便介绍完毕了,掌声在哪里~~~
欢迎订阅
文章会同时发布到我的 博客园主页、CSDN主页、知乎主页、微信公众号 平台上。
微信搜索"痞子衡嵌入式"或者扫描下面二维码,就可以在手机上第一时间看了哦。
- 痞子衡嵌入式:在MDK开发环境下将关键函数重定向到RAM中执行的几种方法
- 痞子衡嵌入式:MCUXpresso IDE下添加新路径下源文件进工程编译的方法
- 痞子衡嵌入式:MCUXpresso IDE下使用J-Link下载算法在Flash调试注意事项(i.MXRT500为例)
- 痞子衡嵌入式:MCUXpresso IDE下SDK工程导入与workspace管理机制
- JavaScript中OnLoad几种使用方法,onload 后面执行的函数不能加括号 加了就立即执行。
- TGraphicControl(自绘就2步,直接自绘自己,不需要调用VCL框架提供的函数重绘所有子控件,也不需要自己来提供PaintWindow函数让管理框架来调用)与TControl关键属性方法速记(Repaint要求父控件执行详细代码来重绘自己,还是直接要求Invalidate无效后Update刷新父控件,就看透明不透明这个属性,因为计算显示的区域有所不同)
- 痞子衡嵌入式:恩智浦i.MX RTxxx系列MCU启动那些事(4)- OTP及其烧写方法
- 痞子衡嵌入式:链接函数到8字节对齐地址或可进一步提升i.MXRT内核执行性能
- 函数执行改变this的几种方法
- LPC1768 MCUXpresso IDE环境下使用完整64K内存的方法
- 牛客_{}大括号里面的内容都会执行,如果它不是成员函数的时候,看成是构造函数中的方法;
- 几种函数调用方法(关键字)
- Java类中各成员及函数方法的执行顺序
- 通过data-func='aa'配置函数方法给某个节点,拿到方法名字符串之后怎样执行该方法?
- asp.net 重定向的几种方法和性能
- Linux下的函数执行时间的统计方法
- 网页中JS函数自动执行常用三种方法
- 正试图在 os 加载程序锁内执行托管代码。不要尝试在 DllMain 或映像初始化函数内运行托管代码,这样做会导致应用程序挂起。问题的解决方法!
- 痞子衡嵌入式:恩智浦i.MX RTxxx系列MCU启动那些事(8)- 从Serial(1-bit SPI) NOR恢复启动
- selenium IDE 定时执行case及log保存的方法