小猫爪:嵌入式小知识05-IAR icf链接文件操作-链接代码至RAM

1 前言

在前面一章我们具体介绍了IAR icf链接文件的功能以及相关语法,接下来我们来进行实际操作来证明一下这些操作是确实可用的。下面的例子都是将一些readonly性质的code和data定义到RAM中的操作。

2 相关例子

现在以i.MX RT1050的IAR工程为例来做说明。

在这里我们先贴上RT的存储映射地址,等下会用到。
在这里插入图片描述在这里插入图片描述我们提取出我们接下来用到的地址空间:

起始地址 描述
0x2000 0000 DTCM (相当于内部SRAM)
0x6000 0000 FlexSPI(相当于FLASH)
0x8000 0000 SDRAM

2.1 定义数据不指定位置

在函数中定义一个数组如下:
在这里插入图片描述
编译后通过map文件查看其地址:

在这里插入图片描述
可以看到my_code的链接地址在0x8000 0000,查看其地址处于外部SDRAM,为什么IAR会把my_code放到了外部SDRAM,这中间发生了什么,让我们看到该项目的链接文件就可以找到答案:

/*定义SDRAM的起始地址和终止地址*/
define symbol m_data3_start            = 0x80000000;
define symbol m_data3_end              = 0x81DFFFFF;

/*定义SDRAM的region空间,名字为DATA3_region*/
define region DATA3_region  = mem:[from m_data3_start to m_data3_end-__size_cstack__];

/*定义ZI block,并将ZI性质的数据放入该block*/
define block ZI         with alignment = 32  {
    
     first zi, section m_usb_dma_noninit_data };

/*将ZI block放入SDRAM的region空间*/
place in DATA3_region                       {
    
     block ZI };

定义的my_code数组属于zi性质的数据,经过以上的操作,最终my_code被链接到了SDRAM中。

接下来我们加入const关键字改变其性质为readonly,如下:

在这里插入图片描述
编译后通过map文件查看其地址:

在这里插入图片描述可以看到my_code的链接地址在0x60000 4074,查看其地址处于FLASH,这中间又发生了什么:

/*定义FLASH区的一段空间的起始地址和终止地址*/
define symbol m_text_start             = 0x60002400;
define symbol m_text_end               = 0x63FFFFFF;

/*定义位于FLASH的region空间,名字为TEXT_region */
define region TEXT_region = mem:[from m_interrupts_start to m_interrupts_end]
                          | mem:[from m_text_start to m_text_end];

/*将readonly性质的数据放入TEXT_region区*/
place in TEXT_region                        {
    
     readonly };

定义的my_code数组属于readonly性质的数据,经过以上的操作,最终my_code被链接到了FLASH中。

2.2 定义数组指定位置

接下来我们自定义section的方式来指定数据的链接地址。

使用#pragma关键词自定义Section,如下:

#pragma location = ".my_code_section"
uint8_t my_code[] = "my code\r\n";

在链接文件中使用place at语法放置my_code_section的位置,链接文件代码如下:

/*将.my_code_section section的数据链接到地址0x20200100 */
place at address mem:0x20210000 {
    
     section .my_code_section };

编译后通过map文件查看其地址:

在这里插入图片描述

可以看到成功的将其链接到了我们想要的地址。

我们再使用建立region的方式来实现,链接文件代码如下:

define region DATA2_region = mem:[from m_data2_start to 0x20210000];
define region my_code_region= mem:[from 0x20210000 to m_data2_end];

place in my_code_region                     {
    
     section .my_code_section };

编译后通过map文件查看其地址:

在这里插入图片描述

可以看到这样操作也可以成功的将其链接到了我们想要的地址。

对于函数链接地址的操作同样可以通过以上方法去实现,在这里则不再展开细说,大家可以照着这种方法试试手。(对于函数,如果想指定其在RAM中运行且不指定地址的情况,还有一种更加方便的方法,就是使用__ramfunc关键字对函数进行限定,这样则将该函数归纳在section .textrw中,这样在IAR的初始化函数中则可直接将其拷贝至RAM。)

2.3 批量操作

上面介绍的方法(即使用#pragma的方式)只能一个一个的去定义,这样略显麻烦,如果我想批量操作,该如何去操作呢?

首先将目标文件和数据全部放在一个C文件中,在链接文件中我们则可以使用place in或者place at将这个文件编译后生成的全部section放入指定链接地址。示例链接文件代码如下:

place in my_code_region {
    
     readonly object my_code.o };

上面代码的意思就是将my_code.c文件中的readonly性质的section放入my_code_region中,这里我就不举例子说明了。

我们也可以使用initialize by copy,通过IAR的初始化函数在APP运行前将相关code和data拷贝至RAM,所以这种方法适用于将代码链接到RAM,但是不能指定明确的地址,示例链接文件代码如下(强烈推荐使用这种方法,非常简单,好用,还不容易出错):

initialize by copy {
    
    readwrite, section .textrw, readonly object my_code.o};

上面代码的意思就是IAR初始化函数会将所有readwrite性质的data,.textrw section的数据以及my_code.c文件中的readonly性质的section拷贝至RAM区。还可以使用这个函数将整个代码链接至RAM中运行。

3 实例操作之链接代码至RAM

接下来我以RT1050的项目来具体实验一下(FLASH启动地址0x60000000, RAM起始地址为0x20000000)

3.1 链接单个section至RAM

首先自定义section:

#pragma location = ".my_code_section"
void my_code_fun(void)
{
    
    
    printf("hello world.\r\n");
}

再使用initialize by copy将这个section链接至RAM:

initialize by copy {
    
     readwrite, section .textrw ,section .my_code_section};

编译后查看map文件:
在这里插入图片描述
运行代码查看跳转地址:
在这里插入图片描述

3.2 链接单个.o文件至RAM

使用initialize by copy将fsl_gpio.o链接至RAM:

initialize by copy {
    
     readwrite, section .textrw, readonly object fsl_gpio.o};

编译后查看map文件发现fsl_gpio.c中的文件全部链接至了RAM中:
在这里插入图片描述
运行代码查看跳转地址:
在这里插入图片描述

3.3 链接整个代码至RAM

使用以下代码将除了与启动相关的代码(也就是进入main函数之前的代码)之外的所有代码链接至RAM:

initialize by copy {
    
     rw, ro} 
   except {
    
      
        ro section .boot_hdr.conf, 
        ro section .boot_hdr.ivt, 
        ro section .boot_hdr.boot_data, 
        ro section .boot_hdr.dcd_data,
        section .intvec,
        section .init_array,
        ro object ABImemcpy.o,
        ro object startup_MIMXRT1024.o,
        ro object system_MIMXRT1024.o
};

编译后查看map文件发现除了没有except里面的部分全部链接至了RAM中:
在这里插入图片描述
运行代码查看跳转地址:
在这里插入图片描述

注意1:我们不能将所有的代码都链接至RAM中,换句话说就是有些代码必须得链接至FLASH,因为必须运行完IAR的程序初始化函数__iar_program_start,而这个函数执行了代码拷贝至RAM的工作,所以需要在FLASH中执行完__iar_program_start后,RAM中才会有代码,之后再跳转进入RAM中运行,所有至少需要将__iar_program_start之前包括它的函数链接至FLASH才能保证正常进入main函数。如果想完全将代码放入RAM中运行,则是可以直接将代码下载至RAM中,或者像RT1050这种支持non-XIP启动的MCU,则需要修改头信息,让BootROM实现所有代码的拷贝,类似于non-XIP启动。)

注意2:如果想在正常启动过程中将中断向量表链接至RAM,则需要注意两个点,第一点就是将中断向量链接至RAM的同时还要保证芯片的正常启动,我们可以通过将与启动相关的部分中断向量表拷贝副本链接至FLASH保证正常启动,再将完整的中断向量链接至RAM。第二点则是需要通过修改SCB->VTOR寄存器改变中断向量映射地址。)

END

番外篇(initialize by copy的实现者)

关于链接文件initialize by copy这个函数,我们可以对其进行深挖一下。
IAR给芯片运行main函数设计了一个入口函数__iar_program_start,该函数在Reset_Handler中被调用,下面是该函数的内部组成:
在这里插入图片描述
可以看到其内部调用了__iar_copy_init3 ,该函数就是initialize by copy实现者,目的就是将相关数据拷贝至RAM。

个人猜想:

第一点:
请大家妥善使用关键词ro和rw,因为这两个属性会涵盖一些别的属性的数据,也就是说它是一个大的集合,包含很多section的数据,举个例子:

现使用以下代码将我自定义的section拷贝至RAM中运行:

/*初始化函数中拷贝readwrite, .textrw段和.my_code_section至RAM*/
initialize by copy {
    
     readwrite, section .textrw  ,section .my_code_section};

但是当同时拥有SDRAM和SRAM的情况下,所以数据将被拷贝在哪里?
答案:是拷贝到你指定rw属性数据所在的RAM区。

再举个例子:
如果使用以下语句将ro属性的数据放入了RAM区,那么IAR就会认为这个区域默认是FLASH。所产生的bin文件的排布也会偏移你的预期。

/*将ro属性的数据放入XXXXX_region*/
place in RAM_region                        {
    
     ro };

总之如果使用place in语句放置ro段和rw段,ro一定要放在FLASH区域, rw一定要放在RAM区。

猜你喜欢

转载自blog.csdn.net/Oushuwen/article/details/109284140
今日推荐