一 概述
Scatter file (分散加载描述文件)用于armlink的输入参数,他指定映像文件内部各区域的download与运行时位置。Armlink将会根据scatter file生成一些区域相关的符号,他们是全局的供用户建立运行时环境时使用。(注意:当使用了scatter file 时将不会生成以下符号 Image$$RW$$Base, Image$$RW$$Limit, Image$$RO$$Base, Image$$RO$$Limit, Image$$ZI$$Base, and Image$$ZI$$Limit_br>
亿什么时候使用scatter file
当然首要的条件是你在利用ADS进行项目开发,下面我们看看更具体的一些情况?br>
1 存在复杂的地址映射:例如代码和数据需要分开放在在多个区域?br>
2 存在多种存储器类型:例如包含 Flash,ROM,SDRAM,快速SRAM。我们根据代码与数据的特性把他们放在不同的存储器中,比如中断处理部分放在快速SRAM内部来提高响应速 度,而把不常用到的代码放到速度比较慢的Flash内?br>
3 函数的地址固定定位:可以利用Scatter file实现把某个函数放在固定地址,而不管其应用程序是否已经改变或重新编译?br>
4 利用符号确定堆与堆栈_br>
5 内存映射的IO:采用scatter file可以实现把某个数据段放在精确的地指处?br>
因此对于嵌入式系统来说scatter file是必不可少的,因为嵌入式系统采用了ROM,RAM,和内存映射的IO?br>
丿scatter file 实例
1 简单的内存映射
LOAD_ROM 0x0000 0x8000
{
EXEC_ROM 0x0000 0x8000
{
*( RO)
}
RAM 0x10000 0x6000
{
*( RW, ZI)
}
}
LOAD_ROM(下载区域名称) 0x0000(下载区域起始地址) 0x8000(下载区域最大字节数)
{
EXEC_ROM(第一执行区域名称) 0x0000(第一执行区域起始地址) 0x8000(第一执行区域最大字节数)
{
*( RO(代码与只读数捿)
}
RAM(第二执行区域名称) 0x10000(第二执行区域起始地址) 0x6000(第二执行区域最大字节数)
{
*( RW(读写变量), ZI(未初始化变量))
}
}
2 复杂内存映射
LOAD_ROM_1 0x0000
{
EXEC_ROM_1 0x0000
{
program1.o( RO)
}
DRAM 0x18000 0x8000
{
program1.o ( RW, ZI)
}
}
LOAD_ROM_2 0x4000
{
EXEC_ROM_2 0x4000
{
program2.o( RO)
}
SRAM 0x8000 0x8000
{
program2.o ( RW, ZI)
}
}
LOAD_ROM_1 0x0000(下载区域一起始地址)
{
EXEC_ROM_1 0x0000(第一执行区域开始地址)
{
program1.o( RO) (program1.o内的Code与RO data 放在第一执行区域)
}
DRAM 0x18000(第二执行区域开始地址) 0x8000(第二执行区域最大字节数)
{
program1.o ( RW, ZI) (program1.o内的RW data丿ZI data 放在第二执行区域)
}
}
LOAD_ROM_2 0x4000(下载区域二起始地址)
{
EXEC_ROM_2 0x4000
{
program2.o( RO) (program2.o内的Code与RO data 放在第一执行区域)
}
SRAM 0x8000 0x8000
{
program2.o ( RW, ZI) (program2.o内的RW data丿ZI data 放在第二执行区域)
}
}
Part2 基本语法
2.1 BNF 符号与语泿br>
" :由引号赖标示的符号保持其字面原意,如A” ”B标示A B?br>
A ::= B :定义A为B?br>
[A] :标示可选部分,如A[B]C用来标示ABC或AC?br>
A :用来标示A可以重复任意次,如A 可标示A,AA,AAA, …
A* :同A ?br>
A | B :用来标示选择其一,不能全选。如A|B用来标示A或者B?br>
(A B) :标示一个整体,当和|符号或复杂符号的多次重复一起使用时尤其强大,如(AB_(C|D)标示ABC,ABD,ABABC,ABABD, …
2.2 分散加载文件各部分描迿nbsp;
(2.1)
如图2.1所示为一个完整的分散加载脚本描述结构图。下面我们对图示中各个部分进行讲述?br>
2.2.1 加载区描迿br>
每个加载区有_br>
ó名称:供连接器确定不同下载区埿br>
ó基地址:相对或绝对地址
ó属性:可逿br>
ó最大字节数:可逿br>
ó执行区域列:确定执行时各执行区域的类型与位置
load_region_name (base_address | (" " offset)) [attribute_list] [ max_size ]
"{"
execution_region_deion
"}"
load_region_name:下载区域名称,最大有效字符数31。(并不像执行区域段名用于Load$$region_name,而是仅仅用于标示 下载区域)?br>
base_address:本区域内部目标被连接到的地址(按字对齐)?br>
offset:相对前一个下载区域的偏移量(4的整数倍,如果为第一个区域)?br>
2.2.2 执行区描迿br>
每个执行区有_br>
ó名称:供连接器确定不同下载区埿br>
ó基地址:相对或绝对地址
ó属性:确定执行区域的属怿br>
ó最大字节数:可逿br>
ó输入段:确定放在该执行区域的模块
exec_region_name (base_address | " " offset) [attribute_list] [max_size]
"{"
input_section_deion
"}"
exec_region_name:执行区域名称,最大有效字符数31?br>
base_address:本执行区域目标要被联接到的位置,按字对齐?br>
offset:相对于前一个执行区域结束地址的偏移量_的整数倍;如果没有前继之能够行区域(本执行区域为该下载区域的第一个执行区域),则该偏移量是相 对于该下载区域的基址偏移量?br>
attribute_list:PI,OVERLAY,ABSOLUTE,FIXED,UNINIT?br>
PI: 位置独立?br>
OVERLAY: 覆盖?br>
ABSOLUTE: 绝对地址?br>
FIXED: 固定地址,下载地址与执行地址具有该地址指示确定?br>
UNINIT: 未初始化数据?br>
RELOC:无法明确指定执行区域具有该属性,而只能通过继承前一个执行区或父区域获得?br>
对于PI,OVERLAY,ABSOLUTE,FIXED,我们只能选择一个,缺省属性为ABSOLUTE。一个执行区域要么直接继承其前面的执行区域的 属性或者具有属性为ABSOLUTE?br>
具有PI,OVERLAY,RELOC属性的执行区域允许其地址空间重叠,对于BSOLUTE,FIXED 属性执行区域地址空间重叠Armlink会报错?br>
max_size:可选,他用于指使Armlink在实际分配空间大于指定值时报错?br>
input_section_deion:指示输入段的内容?br>
2.2.3 输入段描迿br>
输入段:
ó模块名:目标文件名,库成员名,库文件名。名称可以使用通配符?br>
ó输入段名,或输入段属怿READ-ONLY,CODE)?br>
module_select_pattern
["("
(" " input_section_attr | input_section_pattern)
([","] " " input_section_attr | "," input_section_pattern))*
")"]
2.2.3.1
module_select_pattern:选择的模块名称(目标文件,库文件成员,库文件),模块名可以使用通配符(*匹配任意多个字符,?匹配任意 一个字符),名称不区分字母大小写,它是供选择的样本?br>
便_libtx.a ( RO)
libtx.a为threadX库文件?br>
便:tx_ill.o (INIT)
tx_ill.o为threadX中断向量目标文件?br>
2.2.3.2
input_section_attr:输入段属性选择子,每个选择子以” ”开头,选择子不区分大小写字符?br>
选择子可选RO-CODE,RO-DATA,RO_selects both RO-CODE and RO-DATA),RW-DATA,RW-CODE,RW_selects both RW-CODE and RW-DATA),ZI,ENTRY_that is a section containing an ENTRY point)?br>
以下同义词可以选择:CODE (for RO-CODE),CONST( for RO-DATA),TEXT (for RO),DATA (for RW),BSS (for ZI)?br>
还有两个伪属性:FIRST,LAST。如果各段的先后顺序比较重要时,可以使用FIRST,LAST标示一个执行区域的第一个和最后一个 段?br>
便:os_main_init.o (INIT , FIRST)
FIRST表示放于本执行区域的开始处?br>
便_libtx.a ( RO)
RO 表示*libtx.a的只读部分?br>
2.2.3.3
input_section_pattern:输入段名?br>
便:os_main_init.o (INIT , FIRST)
INIT 为os_main_init.o的一个段?br>
便:os_stackheap.o (heap)
heap 为os_stackheap.o的一个段?br>
便:os_stackheap.o (stack)
stack为os_stackheap.o的一个段?br>
//--------------------------------------------------------------------------------------------------------------------------
分散加载文件事例
ADS下的分散加载文件应用实例
load_region_name start_address | " "offset [attributes] [max_size]
{
execution_region_name start_address | " "offset [attributes][max_size]
{
module_select_pattern ["("
(" " input_section_attr | input_section_pattern)
([","] " " input_section_attr | "," input_section_pattern)) *
")"]
}
}
load_region_nbsp; 加载区,用来保存永久性数据(程序和只读变量)的区域;
execution_region_nbsp; 执行区,程序执行时,从加载区域将数据复制到相应执行区后才能被正确执行_br>
load_region_name_nbsp; 加载区域名,用于“Linker”区别不同的加载区域,最夿1个字符;
start_address_nbsp; 起始地址,指示区域的首地址_br>
offset_nbsp; 前一个加载区域尾地址+offset 做为当前的起始地址,且“offset”应为“0”房ldquo;4”的倍数_br>
attributes_nbsp; 区域属性,可设置如下属性:
PI 与地址无关方式存放_br>
RELOC 重新部署,保留定位信息,以便重新定位该段到新的执行区_br>
OVERLAY 覆盖,允许多个可执行区域在同一个地址,ADS不支持;
ABSOLUTE 绝对地址(默认)_br>
max_size_nbsp; 该区域的大小_br>
execution_region_name:执行区域名_br>
start_address_nbsp; 该执行区的首地址,必须字对齐_br>
offset_nbsp; 同上_br>
attributes_nbsp; 同上_br>
PI 与地址无关,该区域的代码可任意移动后执行;
OVERLAY 覆盖_br>
ABSOLUTE 绝对地址(默认)_br>
FIXED 固定地址_br>
UNINIT 不用初始化该区域的ZI段;
module_select_pattern_目标文件滤波器,支持通配笿ldquo;*”咿ldquo;?”_br>
*.o匹配所有目标,* (或“.ANY”)匹配所有目标文件和库?br>
input_section_attr_nbsp; 每个input_section_attr必须跟随圿ldquo;_rdquo;后;且大小写不 敏感;
RO-CODE 房CODE
RO-DATA 房CONST
RO或TEXT, selects both RO-CODE and RO-DATA
RW-DATA
RW-CODE
RW 房DATA, selects both RW-CODE and RW-DATA
ZI 房BSS
ENTRY, that is a section containing an ENTRY point.
FIRST,用于指定存放在一个执行区域的第一个或最后一个区域;
LAST,同上;
input_section_pattern_段名_br>
汇编中指定段_br>
AREA vectors, CODE, READONLY
C中指定段_br>
#pragma arm section [sort_type[[=]"name"]] [,sort_type="name"]*
sort_type: code、rwdata、rodata、zidata
如果“sort_type”指定了但没有指定“name”,那么之前的修改的段名将被恢复成默认值?br>
#pragma arm section // 恢复所有段名为默认设置?br>
应用_br>
#pragma arm section rwdata = "SRAM",zidata = "SRAM"
static OS_STK SecondTaskStk[256]; // “rwdata”“zidata”将定位在“sram”段中?br>
#pragma arm section // 恢复默认设置
分散加载文件中定义如下:
Exec_Sram 0x80000000 0x40000
{
* (sram)
}
“PI” 属性使用示例:
LR_1 0x010000 PI ; The first load region is at 0x010000.
{
ER_RO 0 ; The PI attribute is inherited from parent.
; The default execution address is 0x010000, but the code can be moved.
{
*( RO) ; All the RO sections go here.
}
ER_RW 0 ABSOLUTE ; PI attribute is overridden by ABSOLUTE.
{
*( RW) ; The RW sections are placed next. They cannot be moved.
}
ER_ZI 0 ; ER_ZI region placed after ER_RW region.
{
*( ZI) ; All the ZI sections are placed consecutively here.
}
}
LR_1 0x010000 ; The first load region is at 0x010000.
{
ER_RO 0 ; Default ABSOLUTE attribute is inherited from parent. The execution address
; is 0x010000. The code and ro data cannot be moved.
{
*( RO) ; All the RO sections go here.
}
ER_RW 0x018000 PI ; PI attribute overrides ABSOLUTE
{
*( RW) ; The RW sections are placed at 0x018000 and they can be moved.
}
ER_ZI 0 ; ER_ZI region placed after ER_RW region.
{
*( ZI) ; All the ZI sections are placed consecutively here.
}
}
程序中对某区域地址等的引用方法_br>
Load$$region_name$$Base Load address of the region.
Image$$region_name$$Base Execution address of the region.
Image$$region_name$$Length Execution region length in bytes (multiple of 4).
Image$$region_name$$Limit Address of the byte beyond the end of the execution region.
Image$$region_name$$ZI$$Base Execution address of the ZI output section in this region.
Image$$region_name$$ZI$$Length Length of the ZI output section in bytes (multiple of 4).
Image$$region_name$$ZI$$Limit Address of the byte beyond the end of the ZI output sectionin the execution region.
SectionName$$Base Input Address of the start of the consolidated section called SectionName.
SectionName$$Limit Input Address of the byte beyond the end of the consolidated section called SectionName.
Load_nbsp; 加载区,即存放地址_br>
Image_nbsp; 执行区,即运行地址_br>
Base_nbsp; 区首地址_br>
Limit_nbsp; 区尾地址_br>
Length_nbsp; 区长度;
region_name_nbsp; RO、RW、ZI、load_region_name、 execution_region_name_br>
例如_br>
“RAM1”区域的首地址_nbsp; Image$$RAM1$$Base
上例丿ldquo;sram”段首地址_nbsp; sram$$Base
汇编引用示例_br>
IMPORT |Load$$Exec_RAM1$$Base| // Exec_RAM1 丿ldquo;RW”殿br>
IMPORT |Image$$Exec_RAM1$$Base|
IMPORT |Image$$Exec_RAM1$$Length|
IMPORT |Image$$Exec_RAM1$$Limit|
LDR R0, =|Load$$Exec_RAM1$$Base|
LDR R1, =|Image$$Exec_RAM1$$Base|
LDR R2, =|Image$$Exec_RAM1$$Limit|
0
CMP R1, R2
LDRCC R3, [R0], #4
STRCC R3, [R1], #4
BCC %b0
C 引用_br>
extern unsigned char Load$$Exec_RAM1$$Base;
extern unsigned char Image$$Exec_RAM1$$Base;
extern unsigned char Image$$Exec_RAM1$$Length;
void MoveRO(void)
{
unsigned char * psrc, *pdst;
unsigned int count;
count = (unsigned int) &Image$$Exec_RAM1$$Length;
psrc = (unsigned char *)&Load$$Exec_RAM1$$Base;
pdst = (unsigned char *)&Image$$Exec_RAM1$$Base;
while (count--) {
*pdst = *psrc ;
}
}
加载文件示例一_br>
起始地址 大小
ROM: 0x00000000 256K _x1fc 保留为加密字,程序在ROM中运行;
RAM 0x40000000 16K ;用于全局变量及任务堆栈;
SRAM 0x80000000 512K ;SRAM速度慢,主要用于存放大的数据表;
LOAD_ROM1 0x00000000 0x1f8 ; 指定该加载区域首地址、大尿br>
{
EXEC_ROM1 0 0x1f8 ; 没有前一加载区域,所以该执行区域首地址为加载去首地址
; 并指定该区域长度
{
Startup.o (vectors, FIRST) ; 目标文件皿ldquo;vectors”段放在该执行区域的第一殿br>
irq.o ( RO) ; 目标文件的所朿ldquo;RO”段放在该执行区域
}
}
LOAD_ROM2 0x00000200 ; 第二个加载区埿br>
{
EXEC_ROM2 0 0x3e600
{
* ( RO) ; 所有目标文件和库文件中皿ldquo;RO”段存放在该区埿br>
}
RAM1 0x40000000 0x4000
{
* ( RW, ZI) ; 所有目标文件和库文件的“RW”咿ldquo;ZI”段存放在该区埿br>
}
SRAM2 0x80000000 0x80000
{
* (sram) ; 所有目标文件中皿ldquo;sram”段存放在该区埿br>
}
}
示例二:
“iap.o”定义圿ldquo;Exec_RAM1”中运行,所以设罿ldquo;PI”属性;
在调甿ldquo;iap.c”中函数之前应该将其从“Load$$Exec_IAP$$Base”复制到指定的“Exec_RAM1”区域 _br>
Load_region1 0x00000000 0x1fc
{
EXEC_ROM1 0
{
Startup.o (vectors, FIRST)
irq.o ( RO)
}
}
Load_region2 0x00000200 0x3e600
{
EXEC_ROM2 0
{
* ( RO)
}
Exec_IAP 0 PI // 可能引起链接器未使用该属性警告,忽略
{
iap.o ( RO)
}
Exec_RAM1 0x40000000 0x4000
{
* ( RW, ZI)
}
Exec_Sram 0x80000000 0x40000
{
* (SRAM)
}
}
// 移动“IAP.o”中的所有函数到“ImageExecIAPBase”加载区,并调用其中的函数
extern unsigned char Load$$Exec_IAP$$Base;
extern unsigned char Image$$Exec_IAP$$Length;
#define ImageExecIAPBase (0x40000000 0x1000) // 加载区首址
void MoveIAPRO(void)
{
unsigned char * psrc, *pdst;
unsigned int count;
count = (unsigned int) &Image$$Exec_IAP$$Length;
psrc = (unsigned char *)&Load$$Exec_IAP$$Base;
pdst = (unsigned char *)ImageExecIAPBase;
while (count--) {
*pdst = *psrc ;
}
}
// 调用“IAP.O”中的某函敿br>
{
void (* pfnIAPWrite)(unsigned long, int);
pfnIAPWrite = (void (*)(unsigned long, int))
(ImageExecIAPBase
(unsigned int)IAPWrite - // 被调用函数名
(unsigned int)&Load$$Exec_IAP$$Base);
pfnIAPWrite((int)((CUPDATA *)CODESTARTADDR)->data,
((CUPDATA *)CODESTARTADDR)->length);
}
//————————————————————————————————————————————————————————————
ARM编译程序参耿br>介绍ARM编译程序的ARM特有方面,包括:
Pragmas 编译指示
keywords 函数关键孿br>Variable declaration keywords 变量声明关键孿br>
Pragmas
ARM编译程序可识别一下格式的编译指示_br>#pragma [no_] feature-name
编译指示优于相关的命令行选项?br>能识别的编译选项如下_br>
Pragma name
Default
Reference
arm section
Off
Pragmas controlling code generation
check_printf_formats
Off
Pragmas controlling printf and scanf argument checking
check_scanf_formats
Off
Pragmas controlling printf and scanf argument checking
check_stack
On
Pragmas controlling code generation
debug
On
Pragmas controlling debugging
import
code generation
Ospace
optimization
Otime
optimization
Onum
optimization
softfp_linkage
Off
code generation
* check_printf_formats
该编译指示标记类似于printf的函数,如果存在文字格式串,则对照进行类型检查?br>#pragma check_printf_formats
extern void myprintf(const char *format, …);
#pragma no_check_printf_formats
* check_scanf_formats
该编译指示对声明为类似于scanf的函数做标记,以便对照文字格式串检查自变量的格式?br>#pragma check_scanf_formats
extern void myformat(const char *format, …);
#pragma no_check_scanf_formats
* debug 该编译指示可打开或关闭调试表生成, 如果指定#pragma no_debug,则不会为随后的声明和函数生成调试信息表条目,直到下一丿pragma debug出现?br> * Pragmas controlling optimization
Ospace
Otime
Onum
* Pragmas controlling code generation
o check_stack 如果已经使用亿pragma no_check_stack咿apcs/swst命令行选项禁止栈检查,则该编译指示可使的检查是否违反了栈限制的函数入口代码的重新生 成?br> o once 吿ifndef …#endif效果相类似,用于头文件。但一般推荐使甿ifndef…#define?br> o softfp_linkage 该编译指示指定了至下一丿pragma no_softfp_linkage之间的所有函数声明描述了使用软件浮点链接的函数。__softfp关键字与该编译指示的效果相同
o import(symbol_name) 该编译指示生成对symbol_name的导入引用。同如下汇编语言相同:IMPORT symbol_name。符号名作为外部符号放在映像的符号表中?br> o arm section section_sort_list This pragma specifies the code or data section name that used for subsequent or objects.This include definitions of anonymous objects the compiler creates for initializations.该编译指示可指定代码或数据段的名称用于随后的函数或对象。包括编译程序为初始化而创建的匿名对象的定义。该选项对一下 情况没有影响:
内联函数(及其局部静态变量)
模板实例(及其局部静态变量)
消除未使用的变量和函敿br>将定义写入目标文件中的顺庿br> 该编译指示完整语法为 _br> #pragma arm section [sort_type[[=]“name”]][,sort_type=
“name”]
此处name用于段名称,sort_type可为如下之一code, rwdata, rodata
和zidata。若指定sort_type,没有指定name,则sort_type的段名被
重新设置为默认值。单独输兿pragma arm section,则所以对象段皿br> 恢复为其默认倿br>int x1 = 5; // in .data (default)
int y1[100]; // in .bss (default)
int const z1[3] = {1,2,3}; // in .constdata (default)
#pragma arm section rwdata = "foo", rodata = "bar"
int x2 = 5; // in foo (data part of region)
int y2[100]; // in .bss
int const z2[3] ={1,2,3}; // in bar
char *s2 = "abc"; // s2 in foo, "abc" in bar
#pragma arm section rodata
int x3 = 5; // in foo
int y3[100]; // in .bss
int const z3[3] ={1,2,3}; // in .constdata
char *s3 = "abc"; // s3 in foo, "abc" in .constdata
#pragma arm section code = "foo"
int add1(int x) // in foo (code part of region)
{
return x 1;
}
#pragma arm section code
使用分散加载描述文件和链接程序,以控制将命名段放置在存储器中
的特定地址?br>· keywords
一些关键字指示编译程序对其某个函数进行特殊处理。包括函数内的声明,函数限定符及函数存储类限定符。即Declarations inside , qualifiers and storage.
__asm{assembler-code} 指示编译程序该语句是用汇编语言编写的?br> __irq This enables a C or C to be used as an interrupt routine called by the IRQ, or FIQ vectors. All corrupted registers except floating-point registers are preserved, not only those that are normally preserved under the ATPCS. The default ATPCS mode must be used. The exits by setting the pc to lr-4 and the CPSR to the value in SPSR. It is not available in tcc or tcpp. No arguments or return values can be used with __irq s.
__pure 指明函数声明为纯的。纯函数没有了公共子表达式。默认情况下,函数假定是不纯的(产生副作用)。纯函数需要满足:其 结果仅取决于其自变量的值;没有副作用,其不能调用非纯函数。不能使用全局变量或废弃指针,同一参数两次调用纯函数,返回应该相同