Keil基础知识同为嵌入式培训中心作者:古志东版本:V0.1时间:2013/8/251、ARM镜像文件结构ARM镜像文件的结构如下图所示:ARM映像文件各组成部分在存储系统中的地址有两种:一种是映像文件在运行之前,位于存储器中时的地址,称之为加载地址;一种是映像文件在运行时,位于存储器中的地址,称之为运行地址。之所以有这两种地址,是因为映像文件在运行时,其中的有些域是可以移动的新的存储区域。比如,已经初始化的RW属性的数据所在的段运行之前可能保存系统的ROM中,在运行时,他被移动至RAM中。一个映像文件,如我们将一个工程编译最终输出bin文件。这时我们将bin文件下载,下载地址被称为加载域,当程序要真正运行时还需要将加载域中的数据或代码进行搬移,这是会出现多个运行域。通常情况下有一个加载域,多个运行域,如下图所示。但是一个完整的镜像文件实际上可以包括有一个或多个加载域,一个或多个运行域,每个域包含一个或多个输出段,每个输出段包含一个或多个输入段,各输入段中包含了目标文件中的代码和数据。输入段:输入段中包含了四类内容:代码、已经初始化的数据、未经初始化的存储区域、内容初始化成0的存储区域。每个输入段有相应的属性,可以为只读的(RO)、可读写的(RW)以及初始化成0的(ZI)。ARM连接器根据每个输入段的属性将这些输入段分组,再组成不同的输出段及域。输出段:一个输出段中包含了一系列的具有相同的RO、RW和ZI属性的输入段。输出段的属性与其中包含的输入段的属性相同。在一个输出段的内部,各输入段是按照一定的规则排序的。域:一个域中包含1-3个输出段,其中个输出段的属性各不相同。各输出段的排列顺序是由其属性决定的。其中RO属性的输出段排在最前面,其次是RW属性的输出段,最后是ZI属性的输出段。一个域通常映射到一个物理存储器上,如ROM或RAM。在一个简单的嵌入式计算机系统中,存储器一般被分成ROM和RAM。连接器生成的映像被分成“Read-Only”段(包含代码和只读数据)和“Read-Write”段(包含已初始数据和未初始化数据,未初始化数据也叫ZI数据)。通常,在程序下载(烧入)的时候,它们会被一块下载到ROM上;而在程序开始执行时,Read-Write段会从ROM被Copy到RAM。如下图所示:通常,一个映像文件包含若干个域,各域又包含若干的输出段。ARM连接器需要知道如下的信息,已决定如何生成相应的映像文件。**分组信息决定如何将每个输入段组织成相应的输出段和域。**定位信息决定每个域在存储空间地址中的起始地址。根据映像文件中地址映射的复杂程度,有两种方法来告诉arm连接器这些相关信息。对于映像文件中地址映射关系比较简单的情况,可以使用命令行选项;对于映像文件中地址映射关系比较复杂的情况,可以使用一个配置文件,这就是keil的scatterfile。2、加载文件加载文件(即scatterfile后缀为.sct)是一个文本文件,当工程被成功编译之后,会生成一个加载文件,里面详细记录了每个加载域、运行域的地址和内容。例:LR_ROM10x300000000x02000000{;loadregionsize_regionER_ROM10x300000000x02000000{;loadaddress=executionaddress*.o(RESET,+First)*(InRoot$$Sections).ANY(+RO)}RW_RAM10x320000000x08000000{;RWdata.ANY(+RW+ZI)}}2.1Scatterfile语法Scatterfile(加载描述文件)用于指定映像文件内部各区域的装载域与运行域的位置。arm连接器将会根据scatterfile生成一些区域相关的符号,他们是全局的供用户建立运行时环境时使用。ScatterFile实际上是一个具有简单语法规则的文本文件,可以用来描述ARM连接器生成映像文件时所需的信息:各个加载时域的加载地址、最大尺寸和属性;从每个加载时域中分割出的运行时域;各个运行时域的起始地址、最大尺寸和属性;各个运行时域存储访问特性;各个运行时域中包含的输入段;典型的ScatterFile构成如下图所示(以一个加载时域为例):Load_region_nameBase_designator[attribute_list][max_size]{Exec_region1_nameBase_designator[attribute_list][max_size]{module_select_pattern(input_section_attr,input_section_pattern)}Exec_region2_nameBase_designator[attribute_list][max_size]{...}}2.1.1加载域:Load_region_name:加载时域名称,最大31个ASCII字符,只用于标识一个加载时域;Base_designator:本加载时域的起始地址,它有两种形式:1)base_address:本加载时域中的对象在连接时的起始地址,必须字对齐;2)+offset:本加载时域中的对象在连接时的起始地址是在前一个加载时域的结束地址后偏移offset(字节)处。attribute_list:本加载时域的属性,可选项;属性包括:PI,OVERLAY,ABSOLUTE,FIXED,UNINIT。PI:位置独立。与位置无关的代码OVERLAY:覆盖。只执行一次的代码,可以指示被覆盖ABSOLUTE:绝对地址。FIXED:固定地址,下载地址与执行地址具有该地址指示确定。UNINIT:未初始化数据。说明内存中数据杂乱RELOC:无法明确指定执行区域具有该属性,而只能通过继承前一个执行区或父区域获得。可重新定位的域对于PI,OVERLAY,ABSOLUTE,FIXED,我们只能选择一个,缺省属性为ABSOLUTE。一个执行区域要么直接继承其前面的执行区域的属性或者具有属性为ABSOLUTE。具有PI,OVERLAY,RELOC属性的执行区域允许其地址空间重叠,对于BSOLUTE,FIXED属性执行区域地址空间重叠Armlink会报错。max_size本加载时域的最大尺寸(字节)。若实际尺寸越界,连接器将会报错。默认为0xFFFFFFFF。可选项;2.1.2运行域(输出段)Exec_region_name:运行时域名称,最大31个ASCII字符,除了用于标识一个运行时域外,还用来构成连接器生成的连接符号;Base_designator:本运行时域的起始地址,同“加载时域”:attribute_list:本加载时域的属性,默认为ABSOLUTE。可选项;常用的FIXED表明本域的加载地址和运行地址是相同的,都通过base_designator指定,且必须是绝对地址或offset为0;max_size:本加载时域的最大尺寸(字节)。若实际尺寸越界,连接器将会报错。默认为0xFFFFFFFF。可选项;2.1.3输入段module_select_pattern:选择的模块名称(目标文件,库文件成员,库文件),模块名可以使用通配符(*匹配任意多个字符,?匹配任意一个字符),名称不区分字母大小写,如:-disp*.o(+RO):匹配所有以disp开始的.o目标文件作为输入段;-*pmic.lib(+RW):匹配所有以pmic结束的.lib文件作为输入段;-.ANY(+ZI):匹配所有前面未匹配到的输入段,并指定连接器自行安排该输入段的位置;每一个模块都有自己的属性,一个用逗号分割的模式列表跟随在,模块名后面。该列表中的每个模式定义了输入段名称或输入段属性的匹配方式:input_section_attr:按属性加载。输入段属性选择,表示该链接文件中,具有某种属性的部分内容。input_section_attr:每个input_section_attr必须跟随在“+”后;且大小写不敏感;RO-CODE或CODERO-DATA或CONSTRO或TEXT同时包含RO-CODE和RO-DATARW-DATARW-CODERW或DATA同时包含RW-CODE和RW-DATAZI或BSSENTRY说明在当前段中存在入口点.还有两个伪属性:FIRST,如果各段的先后顺序比较重要时,可以使用FIRST标示一个执行区域的第一个段。LAST,如果各段的先后顺序比较重要时,可以使用LAST标示一个执行区域的最后一个段。例1:os_main_init.o(INIT,FIRST)FIRST表示放于本执行区域的开始处。例2:*libtx.a(RO)RO表示*libtx.a的只读部分。input_section_pattern:按段名称加载,通常为汇编代码某个区域,前面不用“+”,用于声明某个链接文件中的特定段。例1:os_main_init.o(INIT,FIRST)只包含os_main_init.o文件中的INIT段,而且需要放到最前面。例2:os_stackheap.o(heap)只包含os_stackheap.o中的heap段。2.1.4分段加载通常情况下一个镜像文件只有一个加载域,部分情况下需要将镜像文件进行分散加载。如下图所示:下面介绍需要分散加载的情况:1、存在复杂的地址映射:例如代码和数据需要分开放在在多个区域。2、存在多种存储器类型:例如包含Flash,ROM,SDRAM,快速SRAM。我们根据代码与数据的特性把他们放在不同的存储器中,比如中断处理部分放在快速SRAM内部来提高响应速度,而把不常用到的代码放到速度比较慢的Flash内。3、函数的地址固定定位:可以利用Scatterfile实现把某个函数放在固定地址,而不管其应用程序是否已经改变或重新编译。4、利用符号确定堆与栈:5、内存映射的IO:采用scatterfile可以实现把某个数据段放在精确的地指处。因此对于嵌入式系统来说scatterfile是必不可少的,因为嵌入式系统采用了ROM,RAM,和内存映射的IO。6、存储器容量不足2.1.5分段加载的方法在KEIL4中配置分段加载方法:就是不选择UseMemoryLayoutfromTargetDialog,然后指定一个ScatterFile,点击Edit按钮,编辑文件,如下图所示:2.1.6分段加载的输出编译后keil会以加载域为单位输出多个文件,而且文件会放进一个文件夹中,如图所示:例如,uart文件夹中会出现2个文件。为了实现这一目的,须在USER选项中添加命令如下:如图所示,输出应为文件夹。如下图所示,文件内容表示,将2440init.s的文件代码放到第一个装载域0x0的地方,将uart.o的代码放到第二个装载域0x4800的地址,将变量放到RAM装载域0x30008000的地址。说明:*(InRoot$$Sections)段为keil库的一段声明,必须包含在第一个执行域中,其中包含了一段数据搬移指令,可是选数据段的搬移2.2内建变量连接器使用Scatterloadingfile后创建的符号表与前面的些不同。具体如图所示。其规则如下:对于RO和RW段Load$$region_name$$Base表示region_name区域的装载首地址;Image$$region_name$$Base表示region_name区域的执行首地址Image$$region_name$$Length表示region_name区域的长度(单位:字节)Image$$region_name$$Limit表示region_name区域的执行结束地址.对于ZI段Image$$region_name$$ZI$$Base表示region_name区域的执行首地址Image$$region_name$$ZI$$Length表示region_name区域的长度(单位:字节)Image$$region_name$$ZI$$Limit表示region_name区域的ZI段的执行结束地址.Se