周立功单片机分散加载文件浅释.docx
- 文档编号:24620205
- 上传时间:2023-05-29
- 格式:DOCX
- 页数:25
- 大小:189.12KB
周立功单片机分散加载文件浅释.docx
《周立功单片机分散加载文件浅释.docx》由会员分享,可在线阅读,更多相关《周立功单片机分散加载文件浅释.docx(25页珍藏版)》请在冰豆网上搜索。
周立功单片机分散加载文件浅释
分散加载文件浅释
ARM嵌入式开发
广州周立功单片机科技有限公司
1.适用范围...................................................................................................................1
2.基础知识...................................................................................................................2
2.1基本概念...................................................................................................................2
3.分散加载文件概述...................................................................................................3
4.分散加载文件语法...................................................................................................4
4.1
4.2
4.3
5.1
5.2
5.3
5.4
5.5
5.6加载时域的描述.......................................................................................................4运行时域的描述.......................................................................................................5输入段描述...............................................................................................................6一个普通的分散加载配置.......................................................................................8多块RAM的分散加载文件配置............................................................................8多块Flash的分散加载文件配置..........................................................................10Flash特殊要求应用...............................................................................................13段在分散加载文件中的应用.................................................................................13程序拷贝到RAM中执行应用..............................................................................145.分散加载应用实例...................................................................................................8工程技术笔记©2008GuangzhouZHIYUANElectronicsCO.,LTD.
1
1.适用范围
有时候用户希望将不同代码放在不同存储空间,也就是通过编译器生成的映像文件需要包含多个域,每个域在加载和运行时可以有不同的地址。
要生成这样的映像文件,必须通过某种方式告知编译器相关的地址映射关系。
在Keil/ADS/IAR等编译工具中,可通过分散加载机制实现。
分散加载通过配置文件实现,这样的文件称为分散加载文件。
本文重点介绍Keil的分散加载文件配置。
工程技术笔记©2008GuangzhouZHIYUANElectronicsCO.,LTD.
1
2.基础知识
2.1基本概念
要了解分散加载文件前首先需要对以下各个概念进行了解,可参考程序清单2.1。
●Code:
为程序代码部分;
●RO-Data:
表示程序定义的常量及const型数据;
●RW-Data:
表示已经初始化的静态变量,变量有初值;
●ZI-Data:
表示未初始化的静态变量,变量无初值。
程序清单2.1各类型数据声明#defineDATA
charconst
char
char(0x10000000)/*RO-Data*//*RO-Data*//*ZI-Data*/GcChar=5;GcZero;GcStr[]="string.";/*RW-Data*/
Keil工程编译完成后,查看其的map文件,可得到结果如程序清单2.2类似。
程序清单2.2map文件信息
==============================================================================TotalROSize(Code+RO-Data)768(0.75kB)
TotalRWSize(RW-Data+ZI-Data)2060(2.01kB)
TotalROMSize(Code+RO-Data+RW-Data)780(0.76kB)
==============================================================================
由程序清单2.2所示的map文件可看出:
ROM(Flash)Size=Code+RO-Data+RW-Data=0.76KB;
RAMSize=RW-Data+ZI-Data=2.01KB。
为什么上述的RW-Data既占用Flash又占用RAM么,变量不是放在RAM中的么,为什么会占用Flash?
因为RW数据不能像ZI那样“无中生有”的,ZI段数据只要求其所在的区域全部初始化为零,所以只需要程序根据编译器给出的ZI基址及大小来将相应的RAM清零。
但RW段数据却不这样做,所以编译器为了完成所有RW段数据赋值,其先将RW段的所有初值,先保存到Flash中,程序执行时,再Flash中的数据搬运到RAM中,所以RW段即占用Flash又占用RAM,且占用的空间大小是相等的。
这里有必要再了解一下,ZI和RW段数据的赋值在一工程中是在什么地方实现的?
首先,变量必先要初始化才能使用,否则初值不正确,而在main()函数后变量已经可以正常使用,那就是说变量的初始化是在之前完成的,查看这之前的代码只有__main()一个函数,除了赋初值外,都还做了什么呢?
__main()这个函数主要由以下两部分功能组成,如下所示。
●_main():
完成代码和数据的拷贝,并把ZI数据区清零。
代码拷贝可将代码拷贝到
另外一个映射空间并执行,如将代码拷贝到RAM执行;数据拷贝完成RW段数据赋值;数据区清零完成ZI段数据赋值。
以上的代码和分散加载文件密切相关。
●_rt_entry():
进行STACK和HEAP等的初始化。
最后_rt_entry跳进main()函数入口。
当main()函数执行完后,_rt_entry又将控制权交还给调试器。
工程技术笔记©2008GuangzhouZHIYUANElectronicsCO.,LTD.
2
3.分散加载文件概述
分散加载(scatter)文件是一个文本文件,它可以用来描述连接器生成映像文件时需要的信息。
通过编写一个分散加载文件来指定ARM连接器在生成映像文件时如何分配Code、RO-Data,RW-Data,ZI-Data等数据的存放地址。
如果不用分散加载文件指定,那么ARM连接器会按照默认的方式来生成映像文件。
一般情况下我们不需要使用分散加载文件。
但对一些特殊的情况例如需要将不同的程序代码存储到不同的地址区域时需要修改分散加载文件。
1.何时使用分散加载
链接器的命令行选项提供了一些对数据和代码位置的控制,但要对位置进行全面控制,则需要使用比命令行中的输入内容更详细的指令。
需要或最好使用分散加载描述的情况包括:
2.复杂内存映射
如果必须将代码和数据放在多个不同的内存区域中,则需要使用详细指令指定将哪些数据放在哪个内存空间中。
3.不同类型的内存
许多系统都包含多种不同的物理内存设备,如闪存、ROM、SDRAM和快速SRAM。
分散加载描述可以将代码和数据与最适合的内存类型相匹配。
例如,可以将中断代码放在快速SRAM中以缩短中断等待时间,而将不经常使用的配置信息放在较慢的闪存中。
4.内存映射的I/O
分散加载描述可以将数据节准确放在内存映射中的某个地址,以便能够访问内存映射的外围设备。
5.位于固定位置的函数
可以将函数放在内存中的固定位置,即使已修改并重新编译周围的应用程序。
6.使用符号标识堆和堆栈
链接应用程序时,可以为堆和堆栈位置定义一些符号。
工程技术笔记©2008GuangzhouZHIYUANElectronicsCO.,LTD.
3
4.分散加载文件语法
分散加载文件主要由一个加载时域和多个运行时域组成,其大致结构如图4.1所示。
图4.1分散加载文件语法结构
下面分别介绍一下加载时域和运行时域特点及用法。
4.1加载时域的描述
加载时域语法格式如程序清单4.1所示,其每项含义解释如下。
程序清单4.1加载时域描述语法描述
load_region_name(base_address|("+"offset))[attribute_list][max_size]
{
execution_region_description+
}
●load_region_name:
为本加载时域的名称,名称可以按照用户意愿自己定义,该名
称中只有前31个字符有意义;
●base_designator:
用来表示本加载时域的起始地址,可以有下面两种格式中的一种:
◆base_address:
表示本加载时域中的对象在连接时的起始地址,地址必须是字对齐的;
◆+offset:
表示本加载时域中的对象在连接时的起始地址是在前一个加载时域的结束地址后偏移量offset字节处。
本加载时域是第一个加载时域,则它的起始地址即为offset,offset的值必须能被4整除。
●attribute_list:
指定本加载时域内容的属性,包含以下几种,默认加载时域的属性是
ABSOLUTE。
◆ABSOLUTE:
绝对地址;
◆PI:
与位置无关;
◆RELOC:
可重定位;
◆OVERLAY:
覆盖;
工程技术笔记©2008GuangzhouZHIYUANElectronicsCO.,LTD.
4
◆NOCOMPRESS:
不能进行压缩。
●max_size:
指定本加载时域的最大尺寸。
如果本加载时域的实际尺寸超过了该值,
连接器将报告错误,默认取值为0xFFFFFFFF;
●execution_region_description:
表示运行时域,后面有个+号,表示其可以有一个或
者多个运行时域,关于运行时域的介绍请看后面。
注:
程序清单4.1中’|’、‘[]’符号的使用请参考BNF语法。
4.2运行时域的描述
加载时域语法格式如程序清单4.2所示,其每项含义解释如下。
程序清单4.2运行时域语法描述
exec_region_name(base_address|"+"offset)[attribute_list][max_size|""length]
{
input_section_description*
}
●exec_region_name:
为为本加载时域的名称,名称可以按照用户意愿自己定义,该
名称中只有前31个字符有意义;
●base_address:
用来表示本加载时域的起始地址,可以有下面两种格式中的一种:
◆base_address:
表示本加载时域中的对象在连接时的起始地址,地址必须是字对齐的;
◆+offset:
表示本加载时域中的对象在连接时的起始地址是在前一个加载时域的结束地址后偏移量offset字节处,offset的值必须能被4整除。
●attribute_list:
指定本加载时域内容的属性:
◆ABSOLUTE:
绝对地址;
◆PI:
与位置无关;
◆RELOC:
可重定位;
◆OVERLAY:
覆盖;
◆FIXED:
固定地址。
区加载地址和执行地址都是由基址指示符指定的,基址指示符必须是绝对基址,或者偏移为0。
◆ALIGNalignment:
将执行区的对齐约束从4增加到alignment。
alignment必须为2的正数幂。
如果执行区具有base_address,则它必须为alignment对齐。
如果执行区具有offset,则链接器将计算的区基址与alignment边界对齐;
◆EMPTY:
在执行区中保留一个给定长度的空白内存块,通常供堆或堆栈使用。
◆ZEROPAD:
零初始化的段作为零填充块写入ELF文件,因此,运行时无需使用零进行填充;
◆PADVALUE:
定义任何填充的值。
如果指定PADVALUE,则必须为其赋值;◆NOCOMPRESS:
不能进行压缩;
◆UNINIT:
未初始化的数据。
●max_size:
指定本加载时域的最大尺寸。
如果本加载时域的实际尺寸超过了该值,
连接器将报告错误,默认取值为0xFFFFFFFF;
工程技术笔记©2008GuangzhouZHIYUANElectronicsCO.,LTD.
5
●length:
如果指定的长度为负值,则将base_address作为区结束地址。
它通常与
EMPTY一起使用,以表示在内存中变小的堆栈。
●input_section_description:
指定输入段的内容。
4.3输入段描述
输入段语法描述如程序清单4.3所示。
程序清单4.3输入段语法描述
module_select_pattern["("input_section_selector(","input_section_selector)*")"]
("+"input_section_attr|input_section_pattern|input_symbol_pattern)
●module_select_pattern:
目标文件滤波器,支持使用通配符“*”与“?
”。
其中符号
“*”代表零个或多个字符,符号“?
”代表单个字符。
进行匹配时所有字符不区分大小写。
当module_select_pattern与以下内容之一相匹配时,输入段将与模块选择器模式相匹配:
◆包含段和目标文件的名称;
◆库成员名称(不带前导路径名);
◆库的完整名称(包括路径名)。
如果名称包含空格,则可以使用通配符简化搜索。
例如,使用*libname.lib匹配C:
\libdir\libname.lib。
●nput_section_attr:
属性选择器与输入段属性相匹配。
每个input_section_attr的前面
有一个“+”号。
如果指定一个模式以匹配输入段名称,名称前面必须有一个“+”号。
可以省略紧靠“+”号前面的任何逗号。
选择器不区分大小写。
可以识别以下选择器:
◆RO-CODE;
◆RO-DATA;
◆RO,同时选择RO-CODE和RO-DATA;
◆RW-DATA;
◆RW-CODE;
◆RW,同时选择RW-CODE和RW-DATA;
◆ZI;
◆ENTRY:
即包含ENTRY点的段。
可以识别以下同义词:
◆CODE表示RO-CODE;
◆CONST表示RO-DATA;
◆TEXT表示RO;
◆DATA表示RW;
◆BSS表示ZI。
可以识别以下伪属性:
◆FIRST;
◆LAST。
通过使用特殊模块选择器模式.ANY可以将输入段分配给执行区,而无需考虑其父模块。
工程技术笔记©2008GuangzhouZHIYUANElectronicsCO.,LTD.
6
可以使用一个或多个.ANY模式以任意分配方式填充运行时域。
在大多数情况下,使用单个.ANY等效于使用*模块选择器。
工程技术笔记©2008GuangzhouZHIYUANElectronicsCO.,LTD.
7
5.分散加载应用实例
5.1一个普通的分散加载配置
假设,一个Cortex-M3内核的LPC17xx微控制器有Flash、RAM的资源如下:
●Flash基址:
0x00000000,大小:
256KByte;
●RAM基址:
0x10000000,大小:
32Kbyte。
那这一个分散加载文件应该怎样描述呢?
可参考如程序清单5.1所示的配置。
程序清单5.1一个普通的分散加载文件配置LR_IROM10x000000000x00040000{;定义一个加载时域,域基址:
0x00000000,域大
*.o(RESET,+First);小为0x00040000,对应实际Flash的大小;时域起始地址相同,否则库不能加载到该时域的;将RESET段最先加载到本域的起始地址外,即ER_IROM10x000000000x00040000{;定义一个运行时域,第一个运行时域必须和加载;错误,其域大小一般也和加载时域大小相同;RESET的起始地址为0,RESET存储的是向量表.ANY(+RO);加载所有匹配目标文件的只读属性数据,包含:
}
RW_IRAM10x100000000x00008000{
*(+RW+ZI)
}
};定义一个运行时域,域基址:
0x10000000,域大;小为0x00008000,对应实际RAM大小;加载所有区配目标文件的RW-Data、ZI-Data;这里也可以用.ANY替代*号;Code、RW-Code、RO-Data。
5.2多块RAM的分散加载文件配置
还是上述的MCU,假设其增加了另外一块RAM,其资源如下:
●Flash基址:
0x00000000,大小:
256KByte;
●RAM1基址:
0x10000000,大小:
32Kbyte;
●RAM2基址:
0x2007C000,大小:
32Kbyte。
如果我想将这两块不连续的RAM都使用起来(可使用64KBRAM)?
分散加载文件应怎样描述呢?
可参考如程序清单5.2所示配置。
程序清单5.2双RAM的加载文件配置
LR_IROM10x000000000x00040000{
ER_IROM10x000000000x00040000{
*.o(RESET,+First)
.ANY(+RO)
}
工程技术笔记©2008GuangzhouZHIYUANElectronicsCO.,LTD.
8
;定义RAM1的运行时域
;使用.ANY进行随意分配变量,这里不能使用*号
;替代,*表示匹配有所有的目标文件,这样变量就
;无法分配到第二块RAM空间了RW_IRAM10x100000000x00008000{.ANY(+RW+ZI)
}
RW_IRAM20x2007C0000x00008000{
}
};定义第RAM2的运行时域.ANY(+RW+ZI);同样使用.ANY随意分配变量的方式;如果还有另多的RAM块,在这里增加新的运行;时域即可,格式和RAM2的定义相同
如程序清单5.2所示的分散加载文件配置,确实可将两块RAM都使用起来,即有64KB的RAM可以使用,但其并不能完全等价于一个64KB的RAM,实际应用可能会碰到如下的问题。
如我在main.c文件中声明了1个40KB的数组,如程序清单5.3所示,程序中红色部分已注释,可暂不理会。
程序清单5.3定义大数组出错
//main.c文件
…
unsignedcharGucTest0[40*1024];//定义一个40KB的数组
//unsignedcharGucTest1[20*1024];//定义第一个20KB数组
//unsignedcharGucTest2[20*1024];//定义第二个20KB数组
…
//endoffile
如程序清单5.3所示的程序在编译时会出现错误,并提示没有足够的空间,为什么呢?
原因为数组是一个整体,其内部元素的地址是连续的,不能分割的,但是在两个不连续的32KB空间中,是没办法分配出一个连续的40KB地址空间,所以编译会提示空间不足,分配40KB数组失败。
还是程序清单5.3所示的程序,去掉40KB的数据,换成2个20KB的数组(即红色注释代码部分),编译结果又会如何呢?
编译结果还是提示空间不足,这又是为什么呢?
这里出错的原因其实和上面的原因是相同的,首先,重温一个,.ANY的作用,.ANY是一个通配符,当其与以下内容之一相匹配时将进行选择。
●包含段和目标文件的名称;
●库成员名称(不带前导路径名);
●库的完整名称(包括路径名)。
如果名称包含空格,则可以使用通配符简化搜索。
例如,使用*libname.lib匹配C:
\libdir\libname.lib。
后面两个和本次讨论的话题无关,再仔细的看第一个匹配项为:
包含段和目标文件,关于段的解释请参考“5.5段在分散加载文件中的应用”小节。
段这里先不用理会,因为这里没有用到段,所以只剩下目标文件。
要注意是“目标文件”,不是其它,即是说一个C文件工程技术笔记©2008GuangzhouZHIYUANElectronicsCO.,LTD.
9
编译后,其所有的变量、代码都会作为一个整体。
所以定义两个20KB和定义了一个40KB,在编译器看来都是一样,就是这个C文件总共定义了40KB的空间,我要用40KB的空间来分配它,因此会出现同样的错误。
关于大数组分配的解决方法,有两种,分别如下:
●将数组分开在不同的C文件中定义,避免在同一个C文件定义的数据大小总量超
过其中最大的分区;
●将一个C数组,使用段定义,使其从该C文件中独立出来,这样编译器就不会将
它们作为一个整体来划分空间了,其示例如程序清单5.4所示,解释请参考“5.5段在分散加载文件中的应用”小节。
程序清单
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 立功 单片机 分散 加载 文件