NXP

[学习笔记 ]IAR的链接配置.icf文件详解——RT1052芯片的链接文件

2019-07-12 12:03发布

本文主要通过以下几个部分解释IAR中的链接配置icf文件:
1、参考解析IAR的ILink链接器icf配置文件介绍icf文件作用以及icf文件主要内容;
2、介绍icf常使用的命令
3、RT1052芯片存储空间分配以及IAR8.30.1的系统预定义的section和block
4、结合野火RT1052mini开发板的链接配置文件MIMXRT1052xxxxx_itcm_txt_ram.icf详细分析icf文件
作为学习笔记,里面应该有很多问题,欢迎批评指正,谢谢!

icf文件作用以及icf文件主要内容

icf文件是什么

.首先说说什么是icf文件(即ILINK链接器的配置文件)的作用,其实在IAR5.x之前,IAR是采用的叫XLINK的链接器(它相应的配置文件为xcl文件),5.x之后才采用了新版ILINK链接器,所以咱们开发Kinetis的IAR6.x自然也采用的是ILINK链接器,配置文件为icf文件,咳咳,如果有人问什么是链接器,先谷歌一下补补,我这里就引用IAR官方手册里的一句话简要说明一下什么是链接器及其相应配置文件的作用吧: ”EWARM 5.xx 中的链接器称为ILINK。ILINK 可以从 ELF/DWARF 格式的目标文件中提取代码和数据, 并生成可执行的输出镜像。对于 ELF/DWARF 格式而言,基本的链接单元是section,section 的类型有code和data,属性可以是readonly (ro),readwrite (rw)和zeroinit (zi)。ILINK 根据 ILINK Configuration File(.icf)来分配和定位这些sections。“
.icf文件(ILINK Configuration File)是ILINK链接器的配置文件,IAR5.x以后采用ILINK链接器,以前使用XLINK链接器(对应配置文件为.xcl文件)。想详细了解icf文件必须要了解IAR的ILINK链接器的连接过程IAR 链接ILINK链接过程
1、决定哪些模块需要包含到应用程序中。目标文件提供的模块通常包含进去。
2、选择链接目标文件用到的标准库文件。
3、决定目标文件哪些section需要包含进来,没有用到的自然不会包含进来。当然也可以通过特殊的编译器指令如__root来使section链接到目标文件(及时程序中没有用到)。
4、完成RAM中的代码和变量的初始化。初始化指令可以让链接器产生额外的代码能够copy ROM中的内容到RAM中。每个通过copy完成初始化的段被分成了两个段,一个在ROM,一个在RAM。如果没有人工初始化的话。链接器回自动产生启动代码来完成初始化。
5、通过 .icf文件的段放置指令来决定每个section存放的位置。(icf文件决定section的位置或者说地址
**)
6、生成最终的包含可执行映像和调试信息的最终文件。在重定位输入文件中,每个用到的section的内容是通过文件中的重定位信息和放置块时的地址计算出来的。
7、最后生成map文件,包括了各个section的的在存储器中的最终地址,global symbol的地址和用到的存储器和库汇总。

icf文件的主要内容

(1)定义可用的可编址空间(memory)
为链接器提供有关可能地址的最大大小的信息,并定义可用的物理内存,以及处理可以以不同方式寻址的内存。
(2)定义ROM或RAM的可用内存区域(region)
给定每个region的起始地址。
(3)段组(section group)或者Block
根据段需要将段分成块(blocks)或覆盖(overlays)
(4)定义如何处理应用程序初始化
提供关于哪些sections要被初始化和如何进行初始化的信息
(5)内存分配
定义section在存储空间的放置
(6)使用symbols, expressions, and numbers
expressing addresses and sizes等,symbols也可以在应用程序中进行定义
后面还有两个内容这里涉及不到,结构配置和名字中的特殊字符。大家可以根据上面的几个要点对照着实际的icf文件进行分析,就能够对icf文件有个整体把握。

icf文件常使用的命令

对于icf的常使用的命令,网上介绍也有很多,我这里从这篇博客中抄录过来,做了格式整理,方便大家阅读。icf命令相当于C语言中的语法,如果想详细了解icf文件,建议诵读一番。
(1)define [ exported ] symbol name = expr;
作用:指定某个符号的值。
参数:
exported: 导出该symbol,使其对可执行镜像可用
name:符号名
expr:符号值
举例: define symbol RAM_START_ADDRESS = 0x40000000; /* 定义 RAM 起始地址 */ define symbol RAM_END_ADDRESS = 0x4000FFFF; /* 定义 RAM 结束地址 */ (2)define memory name with size = expr [, unit-size];
作用:
定义一个可编址的存储地址空间(memory)。
参数:
name :memory的名称
expr:地址空间的大小
unit-size:expr的单位,可以是位(unitbitsize),缺省是字节(unitbytesize)
举例: define memory MEM with size = 4G; (3)define region name = region-expr;
作用:
定义一个存储地址区域(region)。一个区域可由一个或多个范围组成,每个范围内地址必须连续,但几个范围之间不必是连续的。
参数:
name:region的名称
region-expr:memory:[from expr { to expr | size expr}],可以定义起止范围,也可以定义起始地址和region的大小
举例: define region ROM = MEM:[from 0x0 size 0x10000]; /* 定义 ROM region,位于地址空间MEM 中,起始地址为0x0,大小为0x10000 字节 */ define region ROM = MEM:[from 0x0 to 0xFFFF]; /* 定义 ROM region,位于地址空间MEM 中,起始地址为0x0,结束地址为0xFFFF */ (4)define block name [ with param, param… ]
{
extended-selectors
};

作用: 定义一个地址块(block);它可以是个只保留指定大小的地址空间的空块,比如栈、堆;也可以包含一系列的sections,由extended-selectors 选择。
参数:
name block 的名称
param 可以是: size = expr (块的大小)
maximum size = expr (块大小的上限)
alignment = expr (最小对齐字节数)
fixed order (按照固定顺序放置sections)
extended-selector [ first | last ] { section-selector | block name | overlay name }
first 最先存放
last 最后存放
section-selector [ section-attribute ][ section sectionname ][object filename ]
section-attribute [ readonly [ code | data ] | readwrite [ code | data ] | zeroinit ]
sectionname section的名称
filename 目标文件的名称
name block或overlay的名称
注:这里可以按照section的属性,名称及其所在目标文件这三个过滤条件中,任意选取一个条件或多个条件进行组合,来圈定所要求的sections。
举例: define block HEAP with size = 0x1000, alignment = 4 { }; /* 定义 HEAP block,大小为0x1000,4 字节对齐,没有内容 */ define block MYBLOCK1 = { section mysection1, section mysection2, readwrite }; /* 定义 MYBLOCK1 block,含有mysection1,mysection2,以及所有readwrite 属性的sections */ define block MYBLOCK2 = { readwrite object myfile2.o }; /* 定义 MYBLOCK2 block,含有目标文件myfile2.o 中所有readwrite 属性的sections */ define block MYBLOCK3 = { readonly code object myfile3.o }; /* 定义 MYBLOCK3 block,含有目标文件myfile3.o 中所有readonly 属性的code sections */ (5)initialize { by copy | manually } [ with param, param… ]
{
section-selectors
};
作用: 初始化sections
参数:
by copy 在程序启动时自动执行初始化
manually 在程序启动时不自动执行初始化
param 可以是: packing = { none | compress1 | compress2 | auto } copy routine = functionname
packing表示是否压缩数据,缺省是auto
functionname表示是否使用自己的拷贝函数来取代缺省的拷贝函数
section-selector 同上
举例: initialize by copy { readwrite }; /* 在启动时初始化所有属性为 readwrite 的sections */ (6)do not initialize
{
section-selectors
};

作用: 规定在程序启动时不需要初始化的sections;一般用于__no_init 声明的变量段(.noinit)
参数:
section-selector 同上
举例: do not initialize { .noinit }; /* 在启动时不要初始化.noinit section */ (7)place at { address memory [:expr] | start of region_expr | end of region_expr }
{
extended-selectors
};

作用: 把section 或 block 放置在某个具体的起始地址处,或者一个 region 的开始或结束处
参数:
memory memory 的名称
expr 地址值,该地址必须在 memory 所定义的范围内
region_expr region 的名称
extended-selector 同上
举例: place at end of ROM { section .checksum }; /* 把.checksum 放在 ROM region 的最后 */ place at address MEM:0x0 { section .intvec }; /* 把.intvec 放在地址 0x0 */ place at address MEM:0x1000 { section .text object myfile.o }; /* the .text section of myfile.o */ place at address MEM:0x1000 { readonly object myfile.o }; /* all read-only sections of myfile.o */ place at address MEM:0x1000 { readonly data object myfile.o }; /* all read-only data sections of myfile.o */ (8)place in region-expr
{
extended-selectors
};

作用: 把section 或 block (按任意顺序)放置在某个region 中
参数:
region-expr region 的名称
extended-selector 同上
举例: place in ROM { readonly }; /* all readonly sections */ place in RAM { readwrite }; /* all readwrite sections */ place in RAM { block HEAP, block CSTACK, block IRQ_STACK }; /* heap and stacks */ place in ROM { section .text object myfile.o }; /* the .text section of myfile.o */ place in ROM { readonly object myfile.o }; /* all read-only sections of myfile.o */ place in ROM { readonly data object myfile.o }; /* all read-only data sections myfile.o */

芯片存储空间分配以及IAR软件的系统预定义的sections和blocks

IAR软件系统预定义sections和blocks

下图在IAR软件(版本8.30.1)的帮助手册中截图,具体在Compiler Reference ->Section Reference -> Summury of Sections
IAR编译工具链的ELF中的sections和blocks

RT1052 芯片存储空间介绍

RT1052芯片是NXP公司基于ARM Cortex M7的CPU做的一块价廉物美的跨界微处理器,唯一的缺点就是参考资料少,让很多人望而却步。由于下面介绍icf文件是基于RT1052芯片的链接配置文件进行,所以需要简答了解一下Cortex-M7存储器空间分配。这里主要参考野火RT1052开发板资料介绍M7存储器映射以及RT1052芯片存储具体分配。

Cortex-M7 存储器映射

这里贴出Cortex-M7通用用户指南中的存储器映射图
Cortex-M7存储器空间映射表
M7处理器32为总线处理器,可寻址空间为4G,ARM将这4G存储空间分为了8块,每块都规定了具体用途(见下图)大部分块的大小都有512MB以上,显然这是非常大的,芯片厂商在每个块的范围内设计各具特 {MOD}的外设时并不一定都用得完,都是只用了其中的一部分而已。
ARM内核划分的存储器功能分块
在这8个Block里面,有这3个块非常重要,也是我们最关心的三个块。Block0主要用于存储程序代码,一般采用FLASH存储器,Block1主要用于运行时的内存,一般采用SRAM存储器,Block2用来设计成片上的外设,内核通过相应的地址访问片上外设。下面简单介绍一下Block0和Block1里面的具体功能划分

Block0内部区域功能划分

Block0主要用于存储程序代码,RT1052芯片对block0内部区域功能划分如图表所示:
Block0内部区域功能划分
首先是ITCM,ITCM是Instruction Tightly-Coupled Memory的缩写,译为指令紧耦合内存。所谓紧耦合是指该内存与内核连接紧密,有非常高的访问速度,而“指令”则表示该内存用于缓存指令,在芯片启动时,内核会从外部的FLASH等非易失存储器加载指令至ITCM然后执行。这种方式一方面提高了对指令的访问速度,另一方面取消了对外部非易失存储器类型的限制,即不具备XIP功能的NandFlash、SD卡等都可以存储程序。 注:XIP(executed in place)是指代码直接在存储器上运行,不拷贝到RAM中。
第二部分是ROMCP,这是一小段ROM空间,用于存储芯片启动时的加载代码,即bootloader,bootloader负责把指令从外部存储器加载至ITCM。
第三部分中的SEMC及FlexSPI是RT1052可用于控制外部并行及串行NorFlash的两个外设,此处把它们映射到此代码空间,是为了支持XIP功能(即指令直接在NorFlash上运行,不需要加载到内部的ITCM)。

Block1内部区域功能划分

Block1主要用于设计片内的RAM,即芯片运行时的内存区域,具体功能划分如下图表所示。
Block1内部区域功能划分
第一种类型为DTCM,是Data Tightly-Coupled Memory的缩写,译为数据紧耦合内存,它跟ITCM类似,有着极高的访问速度,不过它是专门用来存储程序数据的,即代码中变量的存储位置。
第二种类型为OCRAM,它是On-chip RAM的缩写,即片上内存,它的功能实际上跟DTCM一样,也是用来存储程序数据的,不过由于它们跟内核连接的总线不同,导致了访问速度的差异,OCRAM的访问速度只有DTCM的1/4。
注意:这里对ITCM、DTCM、OCRAM进行说明,在RT1052芯片中,前面提到的ITCM、DTCM及OCRAM地址范围均分配了512KB,但这并不是说这三种存储器都有512KB大小。实际上这三种存储器共享内部SRAM的空间,而这个内部SRAM空间在RT1052芯片中为512KB,每部分大小可以动态调整,默认ITCM和DTCM各占128KB,OCRAM占256KB。

结合野火RT1052mini开发板链接配置文件MIMXRT1052xxxxx_itcm_txt_ram.icf详细分析icf

/****定义可读性的符号,包括中断向量的起始地址,ROM、RAM1、RAM2的起始地址以及堆栈大小 beg********/ //中断异常起始地址符号定义 define symbol m_interrupts_start = 0x00000000; define symbol m_interrupts_end = 0x000003FF; //代码起始地址符号定义,即ROM起始地址 define symbol m_text_start = 0x00000400; define symbol m_text_end = 0x0001FFFF; //RAM1起始地址符号定义 define symbol m_data_start = 0x20000000; define symbol m_data_end = 0x2001FFFF; //RAM2起始地址符号定义 define symbol m_data2_start = 0x20200000; define symbol m_data2_end = 0x2023FFFF; //栈大小定义 /* Sizes */ if (isdefinedsymbol(__stack_size__)) { define symbol __size_cstack__ = __stack_size__; } else { define symbol __size_cstack__ = 0x0400; } //堆大小定义 if (isdefinedsymbol(__heap_size__)) { define symbol __size_heap__ = __heap_size__; } else { define symbol __size_heap__ = 0x0400; } /****定义可读性的符号,包括中断向量的起始地址,ROM、RAM1、RAM2的起始地址以及堆栈大小 end********/ /****** 定义可导出的符号,以便在ELF文件或者全局标签中使用,应用程序或者调试器将其引用设置 ******/ define exported symbol __VECTOR_TABLE = m_interrupts_start; define exported symbol __VECTOR_RAM = m_interrupts_start; define exported symbol __RAM_VECTOR_TABLE_SIZE = 0x0; /********* 以下是icf 文件的主要内容 *********/ //(1) 定义可编址空间大小 define memory mem with size = 4G; //(2)定义不同存储region的地址 define region TEXT_region = mem:[from m_interrupts_start to m_interrupts_end] | mem:[from m_text_start to m_text_end]; define region DATA_region = mem:[from m_data_start to m_data_end-__size_cstack__]; define region DATA2_region = mem:[from m_data2_start to m_data2_end]; define region CSTACK_region = mem:[from m_data_end-__size_cstack__+1 to m_data_end]; //(3)生成不同的block define block CSTACK with alignment = 8, size = __size_cstack__ { }; //生成一个栈,大小为0x0400,8字节对齐 define block HEAP with alignment = 8, size = __size_heap__ { }; //生成一个堆,大小为0x0400,8字节对齐 define block RW { readwrite }; //定义RW为读写段,即RAM sections define block ZI { zi }; //定义数据段中初始化为zero的块 define block NCACHE_VAR { section NonCacheable , section NonCacheable.init }; //NonCacheable为初始化零不可缓存的变量的段,NonCacheable.init 对应不初始化为零的不可缓存的变量的段 initialize by copy { readwrite, section .textrw };//对属性为readwrite的段和.textrw段,在程序启动时执行初始化 do not initialize { section .noinit };//对.noinit段,在程序启动时不进行初始化 place at address mem: m_interrupts_start { readonly section .intvec };//将只读的中断向量表放在m_interrupts_start 即0x00000000 place in TEXT_region { readonly }; //将.rodata and .data_init放在TEXT区域 place in DATA_region { block RW }; //将.data、.bss、.noinit放在DATA区域 place in DATA_region { block ZI }; //放置初始化为零的区域 place in DATA_region { last block HEAP }; //最后放置堆段 place in DATA_region { block NCACHE_VAR }; //放置上面定义的NCACHE_VAR块 place in CSTACK_region { block CSTACK }; //放置定义的栈块 下面是xip(代码存储、运行在FlexSPI) 终于写完了,自己对icf文件整体有了了解。