大家好,我是痞子衡,是正经搞技术的痞子。今天痞子衡给大家分享的是在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 TableGlobal SymbolsSymbol 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 imageExecution 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        Object0x30005428   0x30005428   0x0000001c   Code   RO           17    .text.critical_func1  critical_code.o0x30005444   0x30005444   0x00000004   PAD0x30005448   0x30005448   0x00000020   Code   RO           19    .text.critical_func2  critical_code.o0x30005468   0x30005468   0x00000024   Code   RO           21    .text.critical_func3  critical_code.o0x3000548c   0x3000548c   0x00000004   PAD
===============================================================================
Image component sizesCode (inc. data)   RO Data    RW Data    ZI Data      Debug   Object Name96         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 TableGlobal SymbolsSymbol 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 imageExecution 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        Object0x20000000   0x30005f60   0x0000001c   Code   RO           17    .criticalFunc       critical_code.oExecution 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        Object0x30005428   0x30005428   0x00000020   Code   RO           19    .text.critical_func2  critical_code.o0x30005448   0x30005448   0x00000024   Code   RO           21    .text.critical_func3  critical_code.o0x3000546c   0x3000546c   0x00000004   PAD
===============================================================================
Image component sizesCode (inc. data)   RO Data    RW Data    ZI Data      Debug   Object Name96         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 TableGlobal SymbolsSymbol 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 imageExecution 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        Object0x20000000   0x30005f50   0x00000040   Code   RO           17    .criticalFunc       critical_code.oExecution 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        Object0x30005428   0x30005428   0x00000024   Code   RO           19    .text.critical_func3  critical_code.o0x3000544c   0x3000544c   0x00000004   PAD
===============================================================================
Image component sizesCode (inc. data)   RO Data    RW Data    ZI Data      Debug   Object Name100         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 TableGlobal SymbolsSymbol 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 imageExecution 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        Object0x20000000   0x30005f30   0x0000001c   Code   RO           17    .text.critical_func1  critical_code.o0x2000001c   0x30005f4c   0x00000004   PAD0x20000020   0x30005f50   0x00000020   Code   RO           19    .text.critical_func2  critical_code.o0x20000040   0x30005f70   0x00000024   Code   RO           21    .text.critical_func3  critical_code.o
===============================================================================
Image component sizesCode (inc. data)   RO Data    RW Data    ZI Data      Debug   Object Name96         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, 0xE000ED08ldr     r0, =VTORldr     r1, =__Vectorsstr     r1, [r0]ldr     r2, [r1]msr     msp, r2ldr   r0,=SystemInitblx   r0cpsie   ildr   r0,=__mainbx    r0

  至此,在MDK开发环境下将关键函数重定向到RAM中执行的几种方法痞子衡便介绍完毕了,掌声在哪里~~~

欢迎订阅

文章会同时发布到我的 博客园主页、CSDN主页、知乎主页、微信公众号 平台上。

微信搜索"痞子衡嵌入式"或者扫描下面二维码,就可以在手机上第一时间看了哦。

痞子衡嵌入式:在MDK开发环境下将关键函数重定向到RAM中执行的几种方法相关推荐

  1. 痞子衡嵌入式:在IAR开发环境下将关键函数重定向到RAM中执行的三种方法

    大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家分享的是在IAR开发环境下将关键函数重定向到RAM中执行的三种方法. 嵌入式项目里应用程序代码正常是放在 Flash 中执行的,但有时候也需要将 ...

  2. 痞子衡嵌入式:在IAR开发环境下将整个源文件代码重定向到任意RAM中的方法

    大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家分享的是在IAR开发环境下将整个源文件代码重定向到任意RAM中的方法. 痞子衡旧文 <在IAR下将关键函数重定向到RAM中执行的方法> ...

  3. 痞子衡嵌入式:把玩i.MXRT1062 TencentOS Tiny EVB_AIoT开发板(1) - 开发环境搭建与点灯...

    大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家介绍的是i.MXRT1062 TencentOS Tiny EVB_AIoT开发板环境搭建与点灯. 腾讯 TencentOS 团队于2021年1 ...

  4. 痞子衡嵌入式:超级下载算法(RT-UFL)开发笔记(1) - 执行在不同CM内核下

    大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家介绍的是超级下载算法开发笔记(1)之执行在不同CM内核下. 文接上篇 <RT-UFL - 一个适用全平台i.MXRT的超级下载算法设计&g ...

  5. 痞子衡嵌入式:IAR环境下无法直接下载调试i.MXRT分散链接工程的解决方案(宏文件.mac+双Flashloader)...

    大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家分享的是IAR环境下无法直接下载调试i.MXRT分散链接工程的解决方案. 分散链接与加载一直是嵌入式领域比较劝退新手的难题,在恩智浦 i.MXR ...

  6. 痞子衡嵌入式:MCUXpresso IDE下工程链接文件配置管理与自动生成机制

    大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家介绍的是MCUXpresso IDE下工程链接文件配置管理与自动生成机制. 痞子衡在 2018 年初写过一个专题 <嵌入式开发文件系列&g ...

  7. 痞子衡嵌入式:超级下载算法RT-UFL v1.0在恩智浦MCUXpresso IDE下的使用

    痞子衡主导的"学术"项目 <RT-UFL - 一个适用全平台i.MXRT的超级下载算法设计> v1.0 版发布近 4 个月了,部分客户已经在实际项目开发调试中用上了这个 ...

  8. 痞子衡嵌入式:超级下载算法RT-UFL v1.0发布,附J-Link下安装教程

    痞子衡主导的"学术"项目 <RT-UFL - 一个适用全平台i.MXRT的超级下载算法设计> 历时 8 个月终于迎来了 v1.0 版发布,因为是第一个正式版,为了保证质 ...

  9. 痞子衡嵌入式:飞思卡尔Kinetis开发板OpenSDA调试器那些事(上)- 背景与架构

    大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家介绍的是飞思卡尔Kinetis MCU开发板板载OpenSDA调试器(上篇). 众所周知,嵌入式软件开发几乎离不开调试器,因为写一个稍有代码规模 ...

最新文章

  1. 基于YOLOv5的智慧工地安全帽检测(1)
  2. pycharm的anaconda配置
  3. oracle ddl会被什么阻塞,MySQL Online DDL与DML并发阻塞关系总结
  4. OpenGL编程指南13:光源移动
  5. Git在公司内部的使用规范
  6. linux下挂接fat32分区
  7. 《影视特效镜头跟踪技术精粹(第2版)》——导读
  8. python watchdog休眠_python watchdog
  9. 用Maven创建第一个web项目Struts2项目
  10. “杀死” APP 的留白设计!
  11. java获取中文拼音首字母
  12. aix linux tcp连接数,修改windows、linux、aix等系统TCP/IP 参数
  13. 《圈圈教你玩USB》 第二章 USB 硬件系统设计_测试程序的编写和调试——看书笔记
  14. 8光12电ERPS工业级环网交换机 PLC自动化环网柜一键环网交换机
  15. Citespace和vosviewer【理工、经管、法学、教育、农学、文史、医学、艺术】
  16. 爱立信实习总结之面试心得
  17. 怎样锁定oracle用户名,oracle 锁定用户
  18. 【爬虫学习笔记day44】5.2. (scrapy案例二)阳光热线问政平台爬虫
  19. 科技传播杂志科技传播杂志社科技传播编辑部2022年第9期目录
  20. 基于社会工程学的网络攻击手段分析

热门文章

  1. DevOps进阶(十三)初识JFrog Artifactory
  2. SmsForwarder-短信转发器
  3. GIS算法:3_拓扑空间关系计算模型DE-9IM
  4. 【OCR炼丹】解析CASIA数据集OLHWDB部分Python版完整代码
  5. Maven下载 安装和配置,以及阿里远程仓库的配置
  6. 面试题整理|50个React面试题及解析
  7. 【自定义View】抖音网红文字时钟-上篇
  8. CentOS8设置时间同步
  9. Tier和Layer
  10. 太阳软件站长丨Python比动态ip代理更适合人工智能