一 概述
Scatter file ( 分散加载描述文件 ) 用于 armlink 的输入参数,他指定映像文件内部各区域的 download 与运行时位置。 Armlink 将会根据 scatter file 生成一些区域相关的符号,他们是全局的供用户建立运行时环境时使用。
(注意:当使用了 scatter file 时将不会生成以下符号:
Image$$RW$$Base,
Image$$RW$$Limit,
Image$$RO$$Base,
Image$$RO$$Limit,
Image$$ZI$$Base,
Image$$ZI$$Limit )
二 什么时候使用 scatter file
当然首要的条件是你在利用 ADS 进行项目开发,下面我们看看更具体的一些情况。
1 存在复杂的地址映射:例如代码和数据需要分开放在在多个区域。
2 存在多种存储器类型:例如包含 Flash,ROM,SDRAM, 快速 SRAM 。我们根据代码与数据的特性把他们放在不同的存储器中,比如中断处理部分放在快速 SRAM 内部来提高响应速度,而把不常用到的代码放到速度比较慢的 Flash 内。
3 函数的地址固定定位:可以利用 Scatter file 实现把某个函数放在固定地址,而不管其应用程序是否已经改变或重新编译。
4 利用符号确定堆与堆栈:
5 内存映射的 IO :采用 scatter file 可以实现把某个数据段放在精确的地指处。
因此对于嵌入式系统来说 scatter file 是必不可少的,因为嵌入式系统采用了 ROM , RAM ,和内存映射的 IO 。
三 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 放在第二执行区域 )
}
}
2.1 BNF 符号与语法
" :由引号赖标示的符号保持其字面原意,如 A”+”B 标示 A+B 。
A ::= B :定义 A 为 B 。
[A] :标示可选部分,如 A[B]C 用来标示 ABC 或 AC 。
A+ :用来标示 A 可以重复任意次,如 A+ 可标示 A,AA,AAA, …
A* :同 A+ 。
A | B :用来标示选择其一,不能全选。如 A|B 用来标示 A 或者 B 。
(A B) :标示一个整体,当和 | 符号或复杂符号的多次重复一起使用时尤其强大,如( AB ) +(C|D) 标示 ABC,ABD,ABABC,ABABD, …
2.2 分散加载文件各部分描述
(2.1)
如图 2.1 所示为一个完整的分散加载脚本描述结构图。下面我们对图示中各个部分进行讲述。
2.2.1 加载区描述
每个加载区有:
名称:供连接器确定不同下载区域
基地址:相对或绝对地址
属性:可选
最大字节数:可选
执行区域列:确定执行时各执行区域的类型与位置
load_region_name (base_address | ("+" offset)) [attribute_list] [ max_size ]
"{"
execution_region_description+
"}"
load_region_name :下载区域名称,最大有效字符数 31 。(并不像执行区域段名用于 Load$$region_name ,而是 仅仅用于标示下载区域)。
base_address :本区域内部目标被连接到的地址(按字对齐)。
+offset :相对前一个下载区域的偏移量( 4 的整数倍,如果为第一个区域)。
2.2.2 执行区 描述
每个执行区有:
名称:供连接器确定不同下载区域
基地址:相对或绝对地址
属性:确定执行区域的属性
最大字节数:可选
输入段:确定放在该执行区域的模块
exec_region_name (base_address | "+" offset) [attribute_list] [max_size]
"{"
input_section_description+
"}"
exec_region_name :执行区域名称,最大有效字符数 31 。
base_address :本执行区域目标要被联接到的位置,按字对齐。
+offset :相对于前一个执行区域结束地址的偏移量, 4 的整数倍;如果没有前继之能够行区域(本执行区域为该下载区域的第一个执行区域),则该偏移量是相对于该下载区域的基址偏移量。
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 :可选,他用于指使 Armlink 在实际分配空间大于指定值时报错。
input_section_description :指示输入段的内容。
基本语法 2
2.2.3 输入段 描述
输入段:
ó 模块名:目标文件名,库成员名,库文件名。名称可以使用通配符。
ó 输入段名,或输入段属性 (READ-ONLY,CODE) 。
module_select_pattern
["("
("+" input_section_attr | input_section_pattern)
([","] "+" input_section_attr | "," input_section_pattern))*
")"]
2.2.3.1
module_select_pattern :选择的模块名称(目标文件,库文件成员,库文件),模块名可以使用通配符( * 匹配任意多个字符,?匹配任意一个字符),名称不区分字母大小写,它是供选择的样本。
例 1 : *libtx.a (+RO)
libtx.a 为 threadX 库文件。
例 2 : tx_ill.o (INIT)
tx_ill.o 为 threadX 中断向量目标文件。
2.2.3.2
input_section_attr :输入段属性选择子,每个选择子以” + ”开头,选择子不区分大小写字符。
选择子可选:
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 )。
以下同义词可以选择:
CODE (for RO-CODE) ,
CONST( for RO-DATA) ,
TEXT (for RO) ,
DATA (for RW) ,
BSS (for ZI) 。
还有两个伪属性: FIRST , LAST 。如果各段的先后顺序比较重要时,可以使用 FIRST , LAST 标示一个执行区域的第一个和最后一个段。
例 1 : os_main_init.o (INIT ,+FIRST)
FIRST 表示放于本执行区域的开始处。
例 2 : *libtx.a (+RO)
RO 表示 *libtx.a 的只读部分。
2.2.3.3
input_section_pattern :输入段名。
例 1 : os_main_init.o (INIT ,+FIRST)
INIT 为 os_main_init.o 的一个段。
例 2 : os_stackheap.o (heap)
heap 为 os_stackheap.o 的一个段。
例 3 : os_stackheap.o (stack)
stack 为 os_stackheap.o 的一个段。
提高篇
3.1 在 scatter file 中指定胶合段
胶合段用于实现 ARM 代码到 Thumb 代码的切换或者实现代码的长转移。使用 scatter file 可以指定怎样放置胶合输入段。通常,在 scatter file 中一个执行区域可以拥有胶合段选择 *(Venner$$Code) 。
Armlink 把胶合输入段放到拥有段选择符 *(Veneer$$Code) 的执行区域中,这样做是安全的。
可能由于地址范围问题或者受执行区域大小限制无法完成把胶合段分配个某个执行区域。如果当发生胶合段无法加到指定区域时,他将会被加到那些包含了生成胶合段的可重载输入段的执行区域。
3.2 创建根执行区域
根执行区域就是指那些执行与加载时地址相同的区域。
当你为映像文件指定初始化入口点或者由于你只使用一个 ENTRY 导向符而使得连接器创建初始化入口位置时,你就必须确保该入口点位于根执行区域。如果入口点不在根执行区域,连接将会失败,连接器会报错。
如: ENTRY point (0x00000000) lies within non-root region ER_ROM
可以通过以下方式实现在 scatter file 中指定根执行区域。
① 显示或缺省的指定执行区的属性为 ABSOLUTE ,同时使得加载区域与第一个执行区域具有相同的地址。
② 使用 FIXED 属性使得执行区域的加载地址与其执行时地址保持不变。
3.3 创建根执行区域
可以通过在 scatter file 中为某个执行区域指定 FIXED 属性来实现该区域加载于运行时地址保持不变。
FIXED 可以用来在一个加载区中创建多个根执行区域。因此我们可以通过它实现把某个函数或一段数据放到目标地址,从而可以通过指针方便地访问该地址。比如,我们可以实现把常量表和 checksum 放到 ROM 上的固定地址处。
?
注意:
① 为了使得代码更加易于维护和调试,请尽量少使用 scatter file 来指定放置位置,而是应该尽可能多地让连接器来确定函数和数据的位置。
②
3.3.1 怎样把函数或数据放到指定地址
通常,编译器处理来自单个源文件的 RO,RW, 和 ZI 段。这些区域包括源文件的代码与数据。如果打算把单个函数或数据项放到某个固定地址,我们就必须让编译器单独处理这些函数和数据。
我么可以通过以下方式处理单个目标:
① 把函数和数据放到其源文件。
② 使用编译选项 –zo 为每个函数单独生成一个目标文件。 ( 参看 ARM Compiler Guide)
③ 在 C,C++ 源文件内利用 #pragma arm section 来生成多命名段。
④ 在汇编源文件内利用 AREA 导向符来生成可重载段。
3.3.2 怎样放置单个目标文件的内容
3.3.3 怎样使用 ARM 的 section pragma
通常把函数和数据放到其源代码文件,然后放到其目标文件的相应段中。然而,我们也可以 #pragma 和 scatter file 实现单独处理某个命名段。
// file adder.c
int x1 = 5; // in.data
int y1[100];// in.bss
int const z1[3] = {1,2,3}; // in.constdata
int sub1(int x)// in.text
{
return x-1;
}
#pragma arm section rwdata = "foo", code ="foo"
int x2 = 5;// in foo (data part of region)
char *s3 = "abc";// s3 in foo, "abc" in .constdata
int add1(int x)
{
return x+1;
} // in foo (.text part of region)
#pragma arm section code, rwdata // return to default placement
FLASH 0x24000000 0x4000000
{
FLASH 0x24000000 0x4000000
{
init.o (Init, +First) ; place code from init.o first
* (+RO) ; sub1(), z1[]
}
32bitRAM 0x0000
{
vectors.o (Vect, +First)
* (+RW,+ZI) ; x1, y1
}
ADDER 0x08000000
{
adder.o (foo) ; x2, string s3, and add1()
}
}
源文档 < http://blog.csdn.net/jianshe999/archive/2008/01/25/2065080.aspx >