痞子衡嵌入式:在MDK开发环境下将关键函数重定向到RAM中执行的几种方法
大家好,我是痞子衡,是正经搞技术的痞子。今天痞子衡给大家分享的是在MDK开发环境下将关键函数重定向到RAM中执行的几种方法。
这个关键函数重定向到 RAM 中执行系列文章,痞子衡已经写过 《IAR篇》、《MCUXpresso IDE篇》,今天一鼓作气把 Keil MDK 篇也写了,做个全家桶。
把 Keil MDK 放到最后来写,其实痞子衡是有用意的。第一篇写 IAR,我们基本上是要纯手工改链接文件。第二篇写 MCUXpresso IDE,我们除了手工改链接文件,也在利用它的链接文件配置自动生成功能。现在到了 Keil MDK,这个 IDE 其实跟 MCUXpresso IDE 一样也支持链接文件配置自动生成,但是具体功能设计上有各有千秋,今天我们就来了解下:
- Note: 本文使用的 Keil uVision 软件版本是 v5.31.0.0。
一、准备工作
为了便于描述后面的函数重定向方法实现,我们先做一些准备工作,选定的硬件平台是恩智浦 MIMXRT1170-EVK,主芯片内部有2MB RAM,外挂了 16MB Flash 和 2 片 32MB SDRAM。这些存储设备在芯片系统中映射地址空间如下:
NOR Flash: 0x30000000 - 0x30FFFFFF (16MB)
ITCM RAM: 0x00000000 - 0x0003FFFF (256KB)
DTCM RAM: 0x20000000 - 0x2003FFFF (256KB)
OCRAM: 0x20200000 - 0x2037FFFF (1.5MB)
SDRAM: 0x80000000 - 0x83FFFFFF (64MB)
我们随便选择一个测试例程:\SDK_2.10.0_EVK-MIMXRT1170\boards\evkmimxrt1170\demo_apps\hello_world\cm7\mdk,其中 flexspi_nor 工程是最典型的代码链接场景(见 MIMXRT1176xxxxx_cm7_flexspi_nor.scf 文件),全部的 readonly 段分配在 0x30000000 - 0x30FFFFFF 空间(在 Flash 中),全部的 readwrite 段分配在 0x20000000 - 0x2003FFFF 空间(在 DTCM 中)。链接文件精简如下:
LR_m_text 0x30002000 0x00FFE000 {
VECTOR_ROM 0x30002000 FIXED 0x00000400 {
* (.isr_vector,+FIRST)
}
ER_m_text 0x30002400 FIXED 0x00FFDC00 {
* (InRoot$$Sections)
.ANY (+RO)
}
RW_m_data 0x20000000 0x0003F800 {
.ANY (+RW +ZI)
}
ARM_LIB_HEAP +0 EMPTY 0x00000400 {
}
ARM_LIB_STACK 0x20040000 EMPTY -0x00000400 {
}
}
现在我们再创建一个新源文件 critical_code.c 用于示例关键函数,将这个源文件添加进 hello_world_demo_cm7.uvprojx 工程里,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);
}
编译链接修改后的工程,然后查看其映射文件(hello_world_demo_cm7.map)找到跟 critical_code.c 文件相关的内容如下,显然 critical_code.c 中的三个函数都会被链在 Flash 空间里(均在 .text 段里)。
===============================================================================
Image Symbol Table
Global Symbols
Symbol Name Value Ov Type Size Object(Section)
critical_func1 0x30005429 Thumb Code 28 critical_code.o(.text.critical_func1)
critical_func2 0x30005449 Thumb Code 32 critical_code.o(.text.critical_func2)
critical_func3 0x30005469 Thumb Code 36 critical_code.o(.text.critical_func3)
===============================================================================
Memory Map of the image
Execution Region ER_m_text (Exec base: 0x30002400, Load base: 0x30002400, Size: 0x00003b68, Max: 0x00fbdc00, ABSOLUTE, FIXED)
Exec Addr Load Addr Size Type Attr Idx E Section Name Object
0x30005428 0x30005428 0x0000001c Code RO 17 .text.critical_func1 critical_code.o
0x30005444 0x30005444 0x00000004 PAD
0x30005448 0x30005448 0x00000020 Code RO 19 .text.critical_func2 critical_code.o
0x30005468 0x30005468 0x00000024 Code RO 21 .text.critical_func3 critical_code.o
0x3000548c 0x3000548c 0x00000004 PAD
===============================================================================
Image component sizes
Code (inc. data) RO Data RW Data ZI Data Debug Object Name
96 56 0 0 0 903 critical_code.o
二、重定向到RAM中方法
我们现在要做的事就是将 critical_code.c 文件中的函数重定向到 RAM 里执行,原链接文件 MIMXRT1176xxxxx_cm7_flexspi_nor.scf 中指定的是 DTCM 来存放 readwrite 段,那我们就尝试将关键函数放到 DTCM 里(如需改到 ITCM、OCRAM、SDRAM,方法类似)。
2.1 自定义section指定函数 - 针对单个函数
第一种方法是用 __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);
}
然后在工程链接文件 MIMXRT1176xxxxx_cm7_flexspi_nor.scf 里将这个自定义的 section .criticalFunc 也放进 RW_m_data 执行域中:
LR_m_text 0x30002000 0x00FFE000 {
; ...
RW_m_data 0x20000000 0x0003F800 {
.ANY (+RW +ZI)
* (.criticalFunc) ;添加 .criticalFunc 段
; 第二种写法:*.o (.criticalFunc)
}
; ...
}
编译链接修改后的工程,然后查看其映射文件(hello_world_demo_cm7.map)找到跟 critical_code.c 文件相关的内容如下,此时 critical_func1() 已经被放到自定义段 .criticalFunc 里,并且这个段被 MDK 底层链接器链接到了 RAM 里(RW_m_data 执行域空间)。
===============================================================================
Image Symbol Table
Global Symbols
Symbol Name Value Ov Type Size Object(Section)
critical_func1 0x20000001 Thumb Code 28 critical_code.o(.criticalFunc)
critical_func2 0x30005429 Thumb Code 32 critical_code.o(.text.critical_func2)
critical_func3 0x30005449 Thumb Code 36 critical_code.o(.text.critical_func3)
===============================================================================
Memory Map of the image
Execution Region RW_m_data (Exec base: 0x20000000, Load base: 0x30005f60, Size: 0x00000078, Max: 0x0003f800, ABSOLUTE)
Exec Addr Load Addr Size Type Attr Idx E Section Name Object
0x20000000 0x30005f60 0x0000001c Code RO 17 .criticalFunc critical_code.o
Execution Region ER_m_text (Exec base: 0x30002400, Load base: 0x30002400, Size: 0x00003b60, Max: 0x00fbdc00, ABSOLUTE, FIXED)
Exec Addr Load Addr Size Type Attr Idx E Section Name Object
0x30005428 0x30005428 0x00000020 Code RO 19 .text.critical_func2 critical_code.o
0x30005448 0x30005448 0x00000024 Code RO 21 .text.critical_func3 critical_code.o
0x3000546c 0x3000546c 0x00000004 PAD
===============================================================================
Image component sizes
Code (inc. data) RO Data RW Data ZI Data Debug Object Name
96 56 0 0 0 903 critical_code.o
2.2 自定义section指定函数 - 针对同一文件里的多个函数
第二种方法是利用 #pragma 语法来修饰函数定义(注意 AC5 编译器 Armcc 和 AC6 编译器 Armclang 语法不太一样),将同一源文件里紧挨在一起的多个关键函数放到自定义段里。比如我们将 critical_func1() 和 critical_func2() 函数放到名为 .criticalFunc 的自定义段里:
- Note: 这种方法一般情况下不太推荐,代码可移植性较差。
#pragma clang section text = ".criticalFunc" // 适用 AC6 编译器(范围开始)
//#pragma arm section code = ".criticalFunc" // 适用 AC5 编译器(范围开始)
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);
}
#pragma clang section text = "" // 适用 AC6 编译器(范围结束)
//#pragma arm section code // 适用 AC5 编译器(范围结束)
void critical_func3(uint32_t n)
{
PRINTF("Arg * 3 = %d .\r\n", 3 * n);
}
然后也是同样在工程链接文件 MIMXRT1176xxxxx_cm7_flexspi_nor.scf 里将这个自定义的 section .criticalFunc 也放进 RW_m_data 执行域中:
LR_m_text 0x30002000 0x00FFE000 {
; ...
RW_m_data 0x20000000 0x0003F800 {
.ANY (+RW +ZI)
* (.criticalFunc) ;添加 .criticalFunc 段
}
; ...
}
编译链接修改后的工程,然后查看其映射文件(hello_world_demo_cm7.map)找到跟 critical_code.c 文件相关的内容如下,此时 critical_func1/2() 均已经被放到自定义段 .criticalFunc 里,并且这个段被 MDK 底层链接器链接到了 RAM 里(RW_m_data 执行域空间)。
===============================================================================
Image Symbol Table
Global Symbols
Symbol Name Value Ov Type Size Object(Section)
critical_func1 0x20000001 Thumb Code 28 critical_code.o(.criticalFunc)
critical_func2 0x20000021 Thumb Code 32 critical_code.o(.criticalFunc)
critical_func3 0x30005429 Thumb Code 36 critical_code.o(.text.critical_func3)
===============================================================================
Memory Map of the image
Execution Region RW_m_data (Exec base: 0x20000000, Load base: 0x30005f50, Size: 0x0000009c, Max: 0x0003f800, ABSOLUTE)
Exec Addr Load Addr Size Type Attr Idx E Section Name Object
0x20000000 0x30005f50 0x00000040 Code RO 17 .criticalFunc critical_code.o
Execution Region ER_m_text (Exec base: 0x30002400, Load base: 0x30002400, Size: 0x00003b4c, Max: 0x00fbdc00, ABSOLUTE, FIXED)
Exec Addr Load Addr Size Type Attr Idx E Section Name Object
0x30005428 0x30005428 0x00000024 Code RO 19 .text.critical_func3 critical_code.o
0x3000544c 0x3000544c 0x00000004 PAD
===============================================================================
Image component sizes
Code (inc. data) RO Data RW Data ZI Data Debug Object Name
100 60 0 0 0 887 critical_code.o
2.3 针对源文件中全部函数
前两种重定向方法都是针对具体函数的(如果是多个关键函数分散在多个文件里,按方法逐一添加修饰当然也行),但如果某个库源文件特别多,并且我们希望将这些源文件里函数全部重定向到 RAM 里,有没有更便捷的方法呢?当然有!
我们现在将 critical_code.c 文件里全部函数都重定向,只需要在工程链接文件 MIMXRT1176xxxxx_cm7_flexspi_nor.scf 里做如下修改:
LR_m_text 0x30002000 0x00FFE000 {
; ...
RW_m_data 0x20000000 0x0003F800 {
.ANY (+RW +ZI)
critical_code.o (+RO +RW +ZI) ;添加 critical_code.o 全部目标
}
; ...
}
编译链接修改后的工程,然后查看其映射文件(hello_world_demo_cm7.map)找到跟 critical_code.c 文件相关的内容如下,此时 critical_func1/2/3() 都链接在 RAM 里了。
===============================================================================
Image Symbol Table
Global Symbols
Symbol Name Value Ov Type Size Object(Section)
critical_func1 0x20000001 Thumb Code 28 critical_code.o(.text.critical_func1)
critical_func2 0x20000021 Thumb Code 32 critical_code.o(.text.critical_func2)
critical_func3 0x20000041 Thumb Code 36 critical_code.o(.text.critical_func3)
===============================================================================
Memory Map of the image
Execution Region RW_m_data (Exec base: 0x20000000, Load base: 0x30005f30, Size: 0x000000c0, Max: 0x0003f800, ABSOLUTE)
Exec Addr Load Addr Size Type Attr Idx E Section Name Object
0x20000000 0x30005f30 0x0000001c Code RO 17 .text.critical_func1 critical_code.o
0x2000001c 0x30005f4c 0x00000004 PAD
0x20000020 0x30005f50 0x00000020 Code RO 19 .text.critical_func2 critical_code.o
0x20000040 0x30005f70 0x00000024 Code RO 21 .text.critical_func3 critical_code.o
===============================================================================
Image component sizes
Code (inc. data) RO Data RW Data ZI Data Debug Object Name
96 56 0 0 0 903 critical_code.o
三、链接文件自动生成功能
第二节里介绍的方法都是基于用户自己提供的链接文件,如果想启动 MDK 的链接文件自动生成功能,需要在工程 Option / Linker 里将 Use Memory Layout from Target Dialog 选项勾选上,这时候用户提供的 MIMXRT1176xxxxx_cm7_flexspi_nor.scf 文件就失效了,MDK 会自动生成一个名为 hello_world_demo_cm7.sct 的链接文件。
自动生成的 hello_world_demo_cm7.sct 链接文件配置非常简单,在工程 Option / Target 里有 Read/Only Memory Areas 和 Read/Write Memory Areas 指定,这里仅简单提供了 RO 和 RW 段空间指定设置,没有关于用户自定义段的单独设置。
不过比较特色的是,在 MDK 里可以单独为某个文件指定 Memory Assignment,这样我们也能跟 2.3 节里的方法一样实现整个文件里的函数重定向。
四、启动文件中拷贝过程
三种函数重定向方法都介绍完了,不知道你是否曾有过这样的疑问,这些关键函数机器码到底是什么时候怎么从 Flash 中拷贝到 RAM 里的?这要从工程启动文件 startup_MIMXRT1176_cm7.S 谈起。在复位函数 Reset_Handler 的最后调用了 MDK 内置函数 __main,这个函数中隐藏着玄机,我们可以在 ARM CMSIS 库中找到该函数原型,顺着原型你应该可以发现其中的奥秘。
Reset_Handler:
cpsid i
.equ VTOR, 0xE000ED08
ldr r0, =VTOR
ldr r1, =__Vectors
str r1, [r0]
ldr r2, [r1]
msr msp, r2
ldr r0,=SystemInit
blx r0
cpsie i
ldr r0,=__main
bx r0
至此,在MDK开发环境下将关键函数重定向到RAM中执行的几种方法痞子衡便介绍完毕了,掌声在哪里~~~
欢迎订阅
文章会同时发布到我的 博客园主页、CSDN主页、知乎主页、微信公众号 平台上。
微信搜索"痞子衡嵌入式"或者扫描下面二维码,就可以在手机上第一时间看了哦。

痞子衡嵌入式:在MDK开发环境下将关键函数重定向到RAM中执行的几种方法的更多相关文章
- 痞子衡嵌入式:在IAR开发环境下将关键函数重定向到RAM中执行的三种方法
大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家分享的是在IAR开发环境下将关键函数重定向到RAM中执行的三种方法. 嵌入式项目里应用程序代码正常是放在 Flash 中执行的,但有时候也需要将 ...
- 痞子衡嵌入式:MCUXpresso IDE下将关键函数重定向到RAM中执行的几种方法
大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家分享的是MCUXpresso IDE下将关键函数重定向到RAM中执行的几种方法. 前段时间痞子衡写了一篇 <在IAR开发环境下将关键函数重 ...
- 痞子衡嵌入式:在IAR开发环境下RT-Thread工程函数重定向失效分析
大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家分享的是在IAR开发环境下RT-Thread工程函数重定向失效分析. 痞子衡旧文 <在IAR下将关键函数重定向到RAM中执行的方法> ...
- 痞子衡嵌入式:其实i.MXRT下改造FlexSPI driver同样支持AHB方式去写入NOR Flash
大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家介绍的是i.MXRT下改造FlexSPI driver以AHB方式去写入NOR Flash. 痞子衡前段时间写过一篇 <串行NAND Fl ...
- (一)keil4 MDK 开发环境下编写裸机程序 (参考杨铸 北航) (开发板只需要连接JLNK 就行了)
首先用的是 keil4 位与 ( F:\ARM+LINUX\MDK-ARM_v4.10.exe ) 1. 新建工程名project 为 led circle ,放在 我的文档\ l ...
- 痞子衡嵌入式:在IAR开发环境下将整个源文件代码重定向到任意RAM中的方法
大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家分享的是在IAR开发环境下将整个源文件代码重定向到任意RAM中的方法. 痞子衡旧文 <在IAR下将关键函数重定向到RAM中执行的方法> ...
- 痞子衡嵌入式:深扒IAR启动函数流程之段初始化函数__iar_data_init3实现
大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家分享的是IAR启动函数流程里的段初始化函数__iar_data_init3实现. 本篇是 <IAR启动函数流程及其__low_level_ ...
- 痞子衡嵌入式:在IAR开发环境下为工程开启CRC完整性校验功能的方法
大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家分享的是在IAR开发环境下为工程开启CRC完整性校验功能的方法. CRC校验在嵌入式领域里的应用非常广,比如在通信领域,CRC检验值可以作为数据 ...
- 痞子衡嵌入式:一个奇怪的Keil MDK下变量链接强制对齐报错问题(--legacyalign)
大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家分享的是一个奇怪的Keil MDK下变量链接强制对齐报错问题. 痞子衡最近一直在参与恩智浦SBL项目(就是一个适用LPC和i.MXRT的完整OT ...
随机推荐
- 5G时代,视频会议的未来
过去,2G打开了了移动互联网天下,3G促成了即时通信,诞生了QQ.微信等巨头,4G 带来了短视频兴起,字节跳动等公司崛起.2.3.4G的出现促成了移动互联网10年繁荣.而5G的出现,也会促成至少10年 ...
- 🏆【Java技术专区】「编译器专题」重塑认识Java编译器的执行过程(消除数组边界检查+公共子表达式)!
前提概要 Java的class字节码并不是机器语言,要想让机器能够执行,还需要把字节码翻译成机器指令.这个过程是Java虚拟机做的,这个过程也叫编译.是更深层次的编译. 在编译原理中,把源代码翻译成机 ...
- 记一次 .NET 某智慧水厂API 非托管内存泄漏分析
一:背景 1. 讲故事 七月底的时候有位朋友在wx上找到我,说他的程序内存占用8G,托管才占用1.5G,询问剩下的内存哪里去了?截图如下: 从求助内容看,这位朋友真的太客气了,动不动就谈钱,真伤感情, ...
- 操作系统思考 第十一章 C语言中的信号量
第十一章 C语言中的信号量 作者:Allen B. Downey 原文:Chapter 11 Semaphores in C 译者:飞龙 协议:CC BY-NC-SA 4.0 信号量是学习同步的一个好 ...
- Build a ZenTao Server on Linux
Download xampp for linux on Apache Friends, file name: xampp-linux-1.8.3-2-installer.run; Intallatio ...
- Goland 这些技巧,学会开发效率翻倍!
hi, 大家好,我是 hhf. <Goland 这些实操技巧,你可能还不会!>介绍了日常开发中一些比较好用的技巧.本篇文章继续介绍一些其他比较好用的技巧. 自定义结构 tag Goland ...
- 【笔记】偏差方差权衡 Bias Variance Trade off
偏差方差权衡 Bias Variance Trade off 什么叫偏差,什么叫方差 根据下图来说 偏差可以看作为左下角的图片,意思就是目标为红点,但是没有一个命中,所有的点都偏离了 方差可以看作为右 ...
- CNVD-2021-14536 锐捷 RG-UAC 统一上网行为管理审计系统信息泄露漏洞
漏洞简介 锐捷 RG-UAC 统一上网行为管理审计系统存在信息泄露,攻击者通过网页源代码可间接获取管理用户账号密码,登录管理后台. 漏洞复现 fofa搜索以下关键字 title="RG-UA ...
- MeteoInfo-Java解析与绘图教程(三)
MeteoInfo-Java解析与绘图教程(三) 上文我们说到简单绘制色斑图(卫星云图),但那种效果可定不符合要求,一般来说,客户需要的是在地图上色斑图的叠加,或者是将图片导出分别是这两种效果 当然还 ...
- 【Azure 应用服务】App Service .NET Core项目在Program.cs中自定义添加的logger.LogInformation,部署到App Service上后日志不显示Log Stream中的问题
问题描述 在.Net Core 5.0 项目中,添加 Microsoft.Extensions.Logging.AzureAppServices 和 Microsoft.Extensions.Logg ...