痞子衡嵌入式:在IAR开发环境下将关键函数重定向到RAM中执行的三种方法
大家好,我是痞子衡,是正经搞技术的痞子。今天痞子衡给大家分享的是在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: 0x10
RW 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: 0x10
RW 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: 0x10
RW 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_Handler
CPSID I
LDR R0, =0xE000ED08
LDR R1, =__vector_table
STR R1, [R0]
LDR R2, [R1]
MSR MSP, R2
LDR R0, =SystemInit
BLX R0
CPSIE I
LDR R0, =__iar_program_start
BX 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 中调用 __iar_program_start 之前增加一个 init_data_bss() 函数调用:
Reset_Handler
CPSID I
LDR R0, =0xE000ED08
LDR R1, =__vector_table
STR R1, [R0]
LDR R2, [R1]
MSR MSP, R2
LDR R0, =SystemInit
BLX R0
LDR R0, =init_data_bss ; 新增初始化函数
BLX R0
CPSIE I
LDR R0, =__iar_program_start
BX 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中执行的三种方法的更多相关文章
- 痞子衡嵌入式:在MDK开发环境下将关键函数重定向到RAM中执行的几种方法
大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家分享的是在MDK开发环境下将关键函数重定向到RAM中执行的几种方法. 这个关键函数重定向到 RAM 中执行系列文章,痞子衡已经写过 <IA ...
- 痞子衡嵌入式:MCUXpresso IDE下将关键函数重定向到RAM中执行的几种方法
大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家分享的是MCUXpresso IDE下将关键函数重定向到RAM中执行的几种方法. 前段时间痞子衡写了一篇 <在IAR开发环境下将关键函数重 ...
- 痞子衡嵌入式:在IAR开发环境下RT-Thread工程函数重定向失效分析
大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家分享的是在IAR开发环境下RT-Thread工程函数重定向失效分析. 痞子衡旧文 <在IAR下将关键函数重定向到RAM中执行的方法> ...
- 痞子衡嵌入式:在IAR开发环境下将整个源文件代码重定向到任意RAM中的方法
大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家分享的是在IAR开发环境下将整个源文件代码重定向到任意RAM中的方法. 痞子衡旧文 <在IAR下将关键函数重定向到RAM中执行的方法> ...
- 痞子衡嵌入式:在IAR开发环境下为工程开启CRC完整性校验功能的方法
大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家分享的是在IAR开发环境下为工程开启CRC完整性校验功能的方法. CRC校验在嵌入式领域里的应用非常广,比如在通信领域,CRC检验值可以作为数据 ...
- 痞子衡嵌入式:IAR内部C-SPY调试组件配套宏文件(.mac)用法介绍
大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家分享的是IAR内部C-SPY调试组件配套宏文件(.mac)用法. 痞子衡之前写过一篇 <JLink Script文件基础及其在IAR下调用 ...
- 痞子衡嵌入式:IAR在线调试时设不同复位类型可能会导致i.MXRT下调试现象不一致(J-Link / CMSIS-DAP)
大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家分享的是IAR在线调试时设不同复位类型可能会导致i.MXRT下调试现象不一致. 做Cortex-M内核MCU嵌入式软件开发,可用的集成开发环境( ...
- 痞子衡嵌入式:其实i.MXRT下改造FlexSPI driver同样支持AHB方式去写入NOR Flash
大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家介绍的是i.MXRT下改造FlexSPI driver以AHB方式去写入NOR Flash. 痞子衡前段时间写过一篇 <串行NAND Fl ...
- python 全栈开发,Day94(Promise,箭头函数,Django REST framework,生成json数据三种方式,serializers,Postman使用,外部python脚本调用django)
昨日内容回顾 1. 内容回顾 1. VueX VueX分三部分 1. state 2. mutations 3. actions 存放数据 修改数据的唯一方式 异步操作 修改state中数据的步骤: ...
随机推荐
- 如何获取微信小程序for循环的index
在微信小程序开发中,对于wx:for,可以使用wx:for-index="index"来获取数组中的元素的索引值(下标). <view class="item&qu ...
- 【Java集合】ArrayList源码分析
ArrayList是日常开发中经常使用到的集合,其底层采用数组实现,因此元素按序存放.其优点是可以使用下标来访问元素,时间复杂度是O(1).其缺点是删除和增加操作需要使用System.arraycop ...
- C#/VB.NET 设置PDF跨页表格重复显示表头行
在创建表格时,如果表格内容出现跨页显示的时候,默认情况下该表格的表头不会在下一页显示,在阅读体验上不是很好.下面分享一个方法如何在表格跨页时显示表格的表头内容,在C#中只需要简单使用方法grid.Re ...
- java:替换List集合中的某个任意值(对象)
定义replaceAll方法,将传入的新值替换集合中的老值(list,old,new) private static <E> void replaceAll(List<E> l ...
- Linux:jar服务部署
1.进入jar包所在文件夹中 2.启动jar,将jar在后台运行,并且记录jar的pid 命令为 : nohup java -jar test.jar (同jar包的配置文件要在jar包同级目录 ...
- 19 shell代码块重定向
代码块是由多条语句组成的一个整体,for.while.until循环或者if-else.case-in选择结构,或者由{ }包围起来的命令都可以称为代码块. 将重定向命令放在代码块的结尾处,就可以对代 ...
- (学习心路历程)Vue过渡/动画 VS. 过渡/动画
[此篇为本人的个人见解和哔哔赖赖,如果有观点不对的地方,还请大家指出来哇!!] 最近实习在做一个项目,里面应用的动画效果还蛮复杂的,因为本身对Vue框架比较熟悉,所以最终选择了Vue框架. 自己之前从 ...
- idea本地调式tomcat源码
前言 上篇文章中一直没搞定的tomcat源码调试终于搞明白了,p神的代码审计星球里竟然有,真的好b( ̄▽ ̄)d ,写一下过程,还有p神没提到的小坑 准备阶段 1.去官网下东西:https://tomc ...
- JS高阶函数的使用
1.何为高阶函数呢? JavaScript的函数其实都指向某个变量.既然变量可以指向函数,函数的参数能接收变量,那么一个函数就可以接收另一个函数作为参数,这种函数就称之为高阶函数.简单来说,就是对其他 ...
- GO系列-ioutil包
ioutil包提供给外部使用的一共有1个变量,7个方法. // Discard 是一个 io.Writer 接口,调用它的 Write 方法将不做任何事情 // 并且始终成功返回. var Disca ...