大家好,我是痞子衡,是正经搞技术的痞子。今天痞子衡给大家分享的是在IAR开发环境下将关键函数重定向到RAM中执行的三种方法

  嵌入式项目里应用程序代码正常是放在 Flash 中执行的,但有时候也需要将代码中的一些函数重定向到 RAM 中去执行,这些函数包括 Flash 擦写操作函数(假定 Flash 本身有 RWW 限制),对执行时间要求特别高的中断响应函数或核心算法函数(假定 RAM 中代码执行速度超过 Flash)等等,这些被重定向到 RAM 中执行的函数我们通常称其为关键函数。

  前段时间痞子衡跟一个美国同事正好在关键函数重定向机制方面交流了一下,那个美国同事用的是 IAR 开发环境,我们知道函数重定向一般是需要借助 IDE 特性的(主要是其中链接器),今天痞子衡就和大家聊一聊 IAR 环境下关键函数重定向的几种方法及其实现机制:

  • Note: 阅读本文前需要对 《IAR链接文件(.icf)》、《IAR映射文件(.map)》 这两种文件有所了解。

一、准备工作

  为了便于描述后面的函数重定向方法实现,我们先做一些准备工作,选定的硬件平台是恩智浦 MIMXRT1060-EVK,主芯片内部有1MB RAM,外挂了 8MB Flash 和 32MB SDRAM。这些存储设备在芯片系统中映射地址空间如下:

NOR Flash: 0x60000000 - 0x607FFFFF (8MB)
ITCM RAM:  0x00000000 - 0x0001FFFF (128KB)
DTCM RAM:  0x20000000 - 0x2001FFFF (128KB)
OCRAM:     0x20200000 - 0x202BFFFF (768KB)
SDRAM:     0x80000000 - 0x81FFFFFF (32MB)

  我们随便选择一个测试例程:\SDK_2.9.1_EVK-MIMXRT1060\boards\evkmimxrt1060\demo_apps\led_blinky\iar,其中 flexspi_nor 工程是最典型的代码链接场景(见 MIMXRT1062xxxxx_flexspi_nor.icf 文件),全部的 readonly 段分配在 0x60000000 - 0x607FFFFF 空间(在 Flash 中),全部的 readwrite 段分配在 0x20000000 - 0x2001FFFF 空间(在 DTCM 中)。链接文件精简如下:

define memory mem with size = 4G;
define region TEXT_region = mem:[from 0x60002000 to 0x607FFFFF];
define region DATA_region = mem:[from 0x20000000 to 0x2001FBFF];
define region CSTACK_region = mem:[from 0x2001FC00 to 0x2001FFFF];define block CSTACK    with alignment = 8, size = 0x400   { };initialize by copy { readwrite, section .textrw };
do not initialize  { section .noinit };place at address mem: 0x60002000    { readonly section .intvec };
place in TEXT_region                { readonly };
place in DATA_region                { readwrite, zi };
place in CSTACK_region              { block CSTACK };

  现在我们再创建一个新源文件 critical_code.c 用于示例关键函数,将这个源文件添加进 iled_blinky.ewp 工程里,critical_code.c 文件中只有如下三个测试函数(它们在 main 函数里会被调用):

void critical_func1(uint32_t n)
{SysTick_DelayTicks(n*1);
}
void critical_func2(uint32_t n)
{SysTick_DelayTicks(n*2);
}
void critical_func3(uint32_t n)
{SysTick_DelayTicks(n*3);
}

  编译链接修改后的 iled_blinky.ewp 工程,然后查看其映射文件(iled_blinky.map)找到跟 critical_code.c 文件相关的内容如下,显然 critical_code.c 中的三个函数都会被链在 Flash 空间里(均在 .text 段里,总大小为 18bytes)。

*******************************************************************************
*** PLACEMENT SUMMARY
***
"P1":  place in [from 0x6000'2000 to 0x607f'ffff] { ro };Section              Kind         Address    Size  Object-------              ----         -------    ----  ------
"P1":                                        0x1f9a.text                ro code  0x6000'3da4    0x12  critical_code.o [1]*******************************************************************************
*** MODULE SUMMARY
***Module                              ro code  ro data  rw data------                              -------  -------  -------
D:\SDK_2.9.1_EVK-MIMXRT1060\boards\evkmimxrt1060\demo_apps\led_blinky\iar\flexspi_nor_debug\obj: [1]critical_code.o                          18*******************************************************************************
*** ENTRY LIST
***Entry                      Address   Size  Type      Object-----                      -------   ----  ----      ------
critical_func1          0x6000'3da5    0x4  Code  Gb  critical_code.o [1]
critical_func2          0x6000'3da9    0x6  Code  Gb  critical_code.o [1]
critical_func3          0x6000'3daf    0x8  Code  Gb  critical_code.o [1]

二、重定向到RAM中方法

  我们现在要做的事就是将 critical_code.c 文件中的函数重定向到 RAM 里执行,原链接文件 MIMXRT1062xxxxx_flexspi_nor.icf 中指定的是 DTCM 来存放 readwrite 段,那我们就尝试将关键函数放到 DTCM 里(如需改到 ITCM、OCRAM、SDRAM,方法类似)。

2.1 __ramfunc 修饰函数

  第一种方法是利用 __ramfunc 修饰符,这个修饰符是 IAR 链接器能特殊识别的,主要适用重定向单个关键函数。比如我们用它来修饰 critical_func1() 函数:

  • Note: __ramfunc 仅重定向被修饰的函数体本身代码,而该函数中调用的其他函数体本身并不受影响
__ramfunc void critical_func1(uint32_t n)
{SysTick_DelayTicks(n*1);
}
void critical_func2(uint32_t n)
{SysTick_DelayTicks(n*2);
}
void critical_func3(uint32_t n)
{SysTick_DelayTicks(n*3);
}

  编译链接修改后的 iled_blinky.ewp 工程,然后查看其映射文件(iled_blinky.map)找到跟 critical_code.c 文件相关的内容如下,此时 critical_func1() 已经被放到了 IAR 内置的 .textrw 段里,这个段是 IAR 链接器专门用来收集重定向到 RAM 里的函数。

*******************************************************************************
*** PLACEMENT SUMMARY
***
"P1":  place in [from 0x6000'2000 to 0x607f'ffff] { ro };
"P2":  place in [from 0x2000'0000 to 0x2001'fbff] { rw };Section              Kind         Address    Size  Object-------              ----         -------    ----  ------
"P2-P3|P5", part 1 of 2:                       0x10RW                            0x2000'0000    0x10  <Block>RW-1                        0x2000'0000    0x10  <Init block>.textrw          inited   0x2000'000c     0x4  critical_code.o [1]    // 变化处1
"P1":                                        0x1faa.text                ro code  0x6000'3dac     0xe  critical_code.o [1]*******************************************************************************
*** MODULE SUMMARY
***Module                              ro code  ro data  rw data------                              -------  -------  -------
D:\SDK_2.9.1_EVK-MIMXRT1060\boards\evkmimxrt1060\demo_apps\led_blinky\iar\flexspi_nor_debug\obj: [1]critical_code.o                          14        4        4           // 变化处2*******************************************************************************
*** ENTRY LIST
***Entry                      Address   Size  Type      Object-----                      -------   ----  ----      ------
critical_func1          0x2000'000d    0x4  Code  Gb  critical_code.o [1]   // 变化处3
critical_func2          0x6000'3dad    0x6  Code  Gb  critical_code.o [1]
critical_func3          0x6000'3db3    0x8  Code  Gb  critical_code.o [1]

  在 MODULE SUMMARY 里,原本 critical_code.o 只占 18bytes 的 ro code,现在变成了 14bytes ro code + 4bytes ro data + 4bytes rw data,因为原本占 4bytes ro code 的 critical_func1() 变成了 4bytes ro data + 4bytes rw data,是的,总消耗空间增大了,因为关键函数代码体本身依然需要占用 4bytes Flash 存储空间。

2.2 自定义section指定函数

  第二种方法是利用 #pragma location 语法,将要指定的关键函数放到自定义段里。比如我们将 critical_func1() 函数放到名为 .criticalFunc 的自定义段里:

#pragma location = ".criticalFunc"
void critical_func1(uint32_t n)
{SysTick_DelayTicks(n*1);
}
void critical_func2(uint32_t n)
{SysTick_DelayTicks(n*2);
}
void critical_func3(uint32_t n)
{SysTick_DelayTicks(n*3);
}

  然后在工程链接文件 MIMXRT1062xxxxx_flexspi_nor.icf 里将这个自定义的 section .criticalFunc 也放进 initialize by copy 语句中:

initialize by copy { readwrite, section .textrw, section .criticalFunc };  // 添加 .criticalFunc 段

  编译链接修改后的 iled_blinky.ewp 工程,然后查看其映射文件(iled_blinky.map)你会发现效果其实跟第一种方法是一模一样的,唯一的区别就是一个用 IAR 内置的 .textrw 段名,一个是用自定义段名 .criticalFunc 而已。

*******************************************************************************
*** PLACEMENT SUMMARY
***
"P1":  place in [from 0x6000'2000 to 0x607f'ffff] { ro };
"P2":  place in [from 0x2000'0000 to 0x2001'fbff] { rw };Section              Kind         Address    Size  Object-------              ----         -------    ----  ------
"P2-P3|P5", part 1 of 2:                       0x10RW                            0x2000'0000    0x10  <Block>RW-1                        0x2000'0000    0x10  <Init block>.criticalFunc    inited   0x2000'000c     0x4  critical_code.o [1]    // 变化处
"P1":                                        0x1faa.text                ro code  0x6000'3dac     0xe  critical_code.o [1]

2.3 针对源文件中全部函数

  前两种重定向方法都适用单个关键函数(如果是多个关键函数,按方法逐一添加修饰当然也行),但如果某个源文件里函数特别多,并且我们希望将这个源文件里函数全部重定向到 RAM 里,有没有更便捷的方法呢?当然有!

  我们现在将 critical_code.c 文件里全部函数都重定向,只需要在工程链接文件 MIMXRT1062xxxxx_flexspi_nor.icf 里做如下修改:

initialize by copy { readwrite, section .textrw, object critical_code.o, };  // 添加 critical_code.o 全部目标

  编译链接修改后的 iled_blinky.ewp 工程,然后查看其映射文件(iled_blinky.map)找到跟 critical_code.c 文件相关的内容如下,此时 critical_func1/2/3() 都链接在 RAM 里了,这里比较有意思的是 critical_code.c 中的函数依旧是在 .text 段里,不过这部分 .text 段的属性从 RO 换到了 RW。

*******************************************************************************
*** PLACEMENT SUMMARY
***
"P2":  place in [from 0x2000'0000 to 0x2001'fbff] { rw };Section              Kind         Address    Size  Object-------              ----         -------    ----  ------
"P2-P3|P5", part 1 of 2:                       0x10RW                            0x2000'0000    0x10  <Block>RW-1                        0x2000'0000    0x10  <Init block>.text            inited   0x2000'000c    0x12  critical_code.o [1]    // 变化处1*******************************************************************************
*** MODULE SUMMARY
***Module                              ro code  ro data  rw data------                              -------  -------  -------
D:\SDK_2.9.1_EVK-MIMXRT1060\boards\evkmimxrt1060\demo_apps\led_blinky\iar\flexspi_nor_debug\obj: [1]critical_code.o                                   18       18           // 变化处2*******************************************************************************
*** ENTRY LIST
***Entry                      Address   Size  Type      Object-----                      -------   ----  ----      ------
critical_func1          0x2000'000d    0x4  Code  Gb  critical_code.o [1]   // 变化处3
critical_func2          0x2000'0011    0x6  Code  Gb  critical_code.o [1]
critical_func3          0x2000'0017    0x8  Code  Gb  critical_code.o [1]

三、启动文件中拷贝过程

  三种函数重定向方法都介绍完了,不知道你是否曾有过这样的疑问,这些关键函数机器码到底是什么时候怎么从 Flash 中拷贝到 RAM 里的?这要从工程启动文件 startup_MIMXRT1062.s 谈起。在复位函数 Reset_Handler 的最后调用了 IAR 内置函数 __iar_program_start,这个函数中隐藏着玄机,我们可以在 \IAR Systems\Embedded Workbench 8.50.6\arm\src\lib\thumb\cstartup_M.c 文件中找到该函数原型,顺着原型你应该可以发现其中的奥秘。

Reset_HandlerCPSID   ILDR     R0, =0xE000ED08LDR     R1, =__vector_tableSTR     R1, [R0]LDR     R2, [R1]MSR     MSP, R2LDR     R0, =SystemInitBLX     R0CPSIE   ILDR     R0, =__iar_program_startBX      R0

  不过痞子衡并不打算过多介绍 IAR 内置函数 __iar_program_start 实现细节,我们可以尝试自己写初始化代码来替代 __iar_program_start 中的拷贝过程。

  先在工程链接文件 MIMXRT1062xxxxx_flexspi_nor.icf 里做如下修改,即弃用 __iar_program_start 的拷贝动作。

// initialize by copy { readwrite, section .textrw };
initialize manually { readwrite, section .textrw };

  然后我们在启动文件复位函数 Reset_Handler 中调用 main 之前增加一个 init_data_bss() 函数调用:

Reset_HandlerCPSID   ILDR     R0, =0xE000ED08LDR     R1, =__vector_tableSTR     R1, [R0]LDR     R2, [R1]MSR     MSP, R2LDR     R0, =SystemInitBLX     R0LDR     R0, =init_data_bss   ; 新增初始化函数BLX     R0CPSIE   ILDR     R0, =mainBX      R0

  在这个 init_data_bss() 函数里我们来自己完成 .data, .bss, .textrw 段的初始化,示例代码如下。其中最后的 .textrw 段的初始化就是关键函数从 Flash 到 RAM 的拷贝过程:

#pragma section = ".data"
#pragma section = ".data_init"
#pragma section = ".bss"
#pragma section = ".textrw"
#pragma section = ".textrw_init"void init_data_bss(void)
{uint32_t n;// 拷贝 .data 段uint8_t *data_ram     = __section_begin(".data");uint8_t *data_rom     = __section_begin(".data_init");uint8_t *data_rom_end = __section_end(".data_init");n = data_rom_end - data_rom;if (data_ram != data_rom){while (n){*data_ram++ = *data_rom++;n--;}}// 清零 .bss 段uint8_t *bss_start = __section_begin(".bss");uint8_t *bss_end   = __section_end(".bss");n = bss_end - bss_start;while (n){*bss_start++ = 0;n--;}// 拷贝 .textrw 段(适用第一种函数重定向方法)uint8_t *code_relocate_ram     = __section_begin(".textrw");uint8_t *code_relocate_rom     = __section_begin(".textrw_init");uint8_t *code_relocate_rom_end = __section_end(".textrw_init");n = code_relocate_rom_end - code_relocate_rom;while (n){*code_relocate_ram++ = *code_relocate_rom++;n--;}
}

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

欢迎订阅

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

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

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

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

    大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家分享的是在MDK开发环境下将关键函数重定向到RAM中执行的几种方法. 这个关键函数重定向到 RAM 中执行系列文章,痞子衡已经写过 <IA ...

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

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

  3. 痞子衡嵌入式:IAR内部C-SPY调试组件配套宏文件(.mac)用法介绍

    大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家分享的是IAR内部C-SPY调试组件配套宏文件(.mac)用法. 痞子衡之前写过一篇 <JLink Script文件基础及其在IAR下调用 ...

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

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

  5. Arduino环境下对NodeMCU ESP8266将文件直接传入flash的三种方式

    flash存储简答介绍 参考:https://www.elecfans.com/consume/572040.html flash存储器又称闪存(快闪存储器),就其本质而言,flash存储器属于EEP ...

  6. 痞子衡嵌入式:大话双核i.MXRT1170之单独在线调试从核工程的方法(IAR篇)

    大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家分享的是i.MXRT1170下单独在线调试从核工程的方法(基于IAR). 两年前痞子衡写过一篇<双核i.MXRT1170之Cortex-M ...

  7. 痞子衡嵌入式:大话双核i.MXRT1170之在线联合调试双核工程的三种方法(IAR篇)

    大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家分享的是i.MXRT1170下在线联合调试双核工程的方法(基于IAR). 前段时间痞子衡写过一篇<双核i.MXRT1170之单独在线调试从 ...

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

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

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

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

最新文章

  1. 如何使用OpenCV和Socket进行视频聊天?
  2. WebLogic集群案例分析
  3. AjaxControlToolkit中CalendarExtender日历控件的用法
  4. 对话嬴彻科技CEO马喆人:L3才是自动驾驶货运的本质拐点
  5. Uncaught TypeError: Object #Document has no method 'load'
  6. sql server 2008学习6 更复杂的查询
  7. ios(safar/微信)返回不执行js
  8. JS 在 HTML 中做加减乘除
  9. 第二百二十一节,jQuery EasyUI,Form(表单)组件
  10. 如何写软件设计文档?
  11. 邮箱超大附件最大是多少?什么邮箱能发超大附件?
  12. Flutter 旋转动画 — RotationTransition
  13. 从计算机移到u盘如何加快速度,小技巧:如何无成本提高优盘拷贝速度
  14. Java编程学习-Math类
  15. 在Octane中提升渲染速度的技巧(第2部分)
  16. HTML5期末大作业:红酒销售网页网站设计——品牌红酒销售网页模板(4页) html网页设计期末大作业_网页设计平时作业
  17. Agora Talk 回顾 | 深入聊聊不一样的 Flutter
  18. bootstrap4笔记
  19. bug:ValueError: multi_class must be in (‘ovo‘, ‘ovr‘)
  20. 【原创】720p,1080i,1080p的区别

热门文章

  1. 教你50行代码实现前端路由小轮子
  2. 1-20的两个数把和告诉A,积告诉B,A说不知道是多少,B也说不知道,这时A说我知道了,B接着说我也知道了,问这两个数是多少?
  3. 基于TDOA的Chan氏算法代码解释
  4. 县域面板数据(2000-2019)一:国民经济核算+人口(面板数据,stata或excel版)
  5. python女性素描_不给你的女朋友来张素描吗?使用python将图片转素描
  6. Calendar类常用api
  7. 报告上、下集 |《认文识字●中文字信息精准化》
  8. [基于STM32底盘控制与ROS上层导航小车制作] 第五节 实现STM32与ubuntu系统下的ROS串口DMA通信,传输底盘速度等信息
  9. I2CSCCB协议的快速使用
  10. matlab实现RBF的相关函数