当前位置:文档之家› Scatter分散加载文件详解

Scatter分散加载文件详解

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

分散加载(Scatlerloading),即工程里的.scf文件。在scatterfile中可以为每一个代码或数据区在装载和执行时指定不同的存储区域地址,Scatlertoading的存储区块可以分成二种类型:
装载区:当系统启动或加载时应用程序的存放区。
执行区:系统启动后,应用程序进行执行和数据访问的存储器区域,系统在实时运行时可以有一个或多个执行块。
映像中所有的代码和数据都有一个装载地址和运行地址(二者可能相同也可能不同,视具体情况而定)。

一、scatter文件语法:
scatter文件是一个简单的文本文件,包含一些简单的语法。
My Region 0x0000 0x1000
{
;the context of region
}

标题:
每个块由一个头标题开始定义,头中至少包含块的名字(自己定义)和起始地址,如0x0000,另外还有最大长度等其他一些属性选项(注:这些属性是可选的,如0x1000)

内容:
块定义的内容包括在紧接的一对花括号内,依赖于具体的系统情况。
一个加载块(加载时域)必须至少含有一个执行块(运行时域);实践中通常有多个执行块。
一个执行块必须至少含有一个代码或数据段;这些通常来自源文件或库函数等的目标文件;通配符号*可以匹配指定属性项中所有没有在文件中定义的余下部分。
一个映像文件由域(region)、输出段(output sections)和输入段(input sections)组成。不要想得太复杂,其实他们之间就是包含与被包好的关系。具体关系是这样的:映像文件 > 域 > 输出段 > 输入段

输入段:
输入段就是我们写的代码+初始化的数据+应该被初始化为0的数据+没有初始化的数据,用英文表示一下就是:RO(ReadOnly),RW(ReadWrite),ZI(ZeroInitialized),NOINIT(Not Initialized)。ARM连接器根据各个输入段不同的属性把相同的拿再一起组合一下就成为了输出段。

请看看平时写的东东:
AREA RESET, CODE, READONLY
AREA DSEG1, DATA, READWRITE
AREA HEAP, NOINIT, READWRITE
看出其属性没?

输出段:
为了简化编译过程和更容易取得各种段的地址,那么把多个同属性的输入段按照一定的规律组合在一起,当然这个输出段的属性就和它包含的输入段的属性一样咯。输入段的排放规律就是:最先排

放RO属性的输入段,然后是RW属性段,最后是ZI或NOINIT段。

域:
为什么还要加一层域,我的理解是由于代码的功能不同,那么我们有必要把不同功能的代码分类放。我们可以把需要高速执行的代码放在一起、把对速度要求不高的放在一起、把执行频率高的放在一起,把执行频率低的放在一起...那么按照这种方式放的代码就可以根据其具体需要放在不同的存储器中了。这样可以提高程序执行速度。一个域中包含1~3个输出段。

映像文件:
我暂时把映像文件理解成烧到存储器中的文件,由N个域组成。这些域其实可以看做是独立的模块,只是他们按照一定的顺序(这个顺序还是:RO+RW+ZI)被捆绑在一起,这样才方便烧写到非易失存储器中去。
好了,了解了映像文件的组成,那么来看看映像文件是怎么跑起来的。
映像文件就是有N节车厢的火车,车厢(域)里装着要送到不同站(不同类型的存储器)的货物。到相应的站了,那么就把相应的车厢拿下来。指挥拿这 个的就是scatter文件。拿下货物车厢后,我们就解开它,把里面的品牌为RO的货物提取出来,按照scatter的指示发给某个地址,然后再先后把品牌为RW和ZI的货物发到scatter指定的地址。

二、什么时候使用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_sec_desc+
"}"

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_sec_desc: 指示输入段的内容。

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 pra

gma
通常把函数和数据放到其源代码文件,然后放到其目标文件的相应段中。然而,我们也可以#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()
}
}

分散加载文件(.scf)格式
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: 加载区,用来保存永久性数据(程序和只读变量)的区域;有些手册称之为“加载时域”;
execution_region: 执行区,程序执行时,从加载区域将数据复制到相应执行区后才能被正确执行;有些手册称之为“运行时域”;
load_region_name: 加载区域名,用于“Linker”区别不同的加载区域,最多31个字符;
start_address: 起始地址,指示区域的首地址;
+offset: 前一个加载区域尾地址+offset 做为当前的起始地址,且“offset”应为“0”,或“4”的倍数;
attributes: 区域属性,可设置如下属性:
PI 与地址无关方式存放;
RELOC 重新部署,保留定位信息,以便重新定位该段到新的执行区;
OVERLAY 覆盖,允许多个可执行区域在同一个地址,ADS不支持;
ABSOLUTE 绝对地址(默认);
max_size: 该区域的大小;
execution_region_name: 执行区域名;
start_address: 该执行区的首地址,必须字对齐;
+offset:

同上;
attributes: 同上;
PI 与地址无关,该区域的代码可任意移动后执行;
OVERLAY 覆盖;
ABSOLUTE 绝对地址(默认);
FIXED 固定地址;
UNINIT 不用初始化该区域的ZI段;
module_select_pattern: 目标文件滤波器,支持通配符“*”和“?”;
*.o匹配所有目标,* (或“.ANY”)匹配所有目标文件和库。
input_section_attr: 每个input_section_attr必须跟随在“+”后;且大小写不敏感;
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: 段名;


汇编中指定段:
AREA vectors, CODE, READONLY
C中指定段:
#pragma arm section [sort_type[[=]"name"]] [,sort_type="name"]*
sort_type: code、rwdata、rodata、zidata
如果“sort_type”指定了但没有指定“name”,那么之前的修改的段名将被恢复成默认值。
#pragma arm section // 恢复所有段名为默认设置。
应用:
#pragma arm section rwdata = "SRAM",zidata = "SRAM"
static OS_STK SecondTaskStk[256]; // “rwdata”“zidata”将定位在“sram”段中。
#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.
}
}

程序中对某区域地址等的引用方法:
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: 加载区,即存放地址;
Image: 执行区,即运行地址;
Base: 区首地址;
Limit: 区尾地址;
Length: 区长度;
region_name: RO、RW、ZI、load_region_name、execution_region_name;

例如:
“RAM1”区域的首地址: Image$$RAM1$$Base
上例中“sram”段首地址: sram$$Base
汇编引用示例:
IMPORT |Load$$Exec_RAM1$$Base| // Exec_RAM1 为“RW”段
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|
CMP R1, R2
LDRCC R3, [R0], #4
STRCC R3, [R1], #4
BCC %b0

C 引用:
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 ;
}
}

加载文件示例一:
起始地址 大小
ROM: 0x00000000 256K ; 0x1fc 保留为加密字,程序在ROM中运行;
RAM 0x40000000 16K ; 用于全局变量及任务堆栈;
SRAM 0x80000000 512K ; SRAM速度慢,主要用于存放大的数据表;

LOAD_ROM1 0x00000000 0x1f8

; 指定该加载区域首地址、大小
{
EXEC_ROM1 0 0x1f8 ; 没有前一加载区域,所以该执行区域首地址为加载区首地址,并指定该区域长度
{
Startup.o (vectors, FIRST) ; 目标文件的“vectors”段放在该执行区域的第一段
irq.o ( RO) ; 目标文件的所有“RO”段放在该执行区域
}
}

LOAD_ROM2 0x00000200 ; 第二个加载区域
{
EXEC_ROM2 0 0x3e600
{
* ( RO) ; 所有目标文件和库文件中的“RO”段存放在该区域
}
RAM1 0x40000000 0x4000
{
* ( RW, ZI) ; 所有目标文件和库文件的“RW”和“ZI”段存放在该区域
}
SRAM2 0x80000000 0x80000
{
* (sram) ; 所有目标文件中的“sram”段存放在该区域
}
}

示例二:
“iap.o”定义在“Exec_RAM1”中运行,所以设置“PI”属性;在调用“iap.c”中函数之前应该将其从“Load$$Exec_IAP$$Base”复制到指定的“Exec_RAM1”区域;
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”中的某函数
{
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);
}

这里再用周立功的开发板附带例程里的分散加载文件做例子进行讲解:
硬件配置:
内部 64K RAM: 0x4000 0000 - 0x4000 ffff
外部 2M FLASH:0x8000 0000 - 0x801f ffff
外部 8M RAM: 0x8100 0000 - 0x807f ffff

ROM_LOAD 0x80000000 ;加载时域描述
{
ROM_EXEC 0x80000000

;第一个运行时域描述
{
Startup.o (vectors, +First) ;输入段描述:模块startup位于该域的开头(+First),vector为入口点
* (+RO) ;本域包含全部RO代码(*(+RO))
}

IRAM 0x40000000 ;第二个运行时域:将堆栈空间放入片内静态RAM中(0x40000000)
{
Startup.o (MyStacks) ;本域包含模块STARTUP 的MYSTACKS段
}

STACKS_BOTTOM +0 UNINIT ;将栈底放在堆栈的后面(+0)不进行初始化(UNINIT)
{
Startup.o (StackBottom)
}

STACKS 0x40004000 UNINIT ;将STACKS放入40004000 此处地址不能访问,如访问将产生预取中止和数据中止异常
{
Startup.o (Stacks)
}

ERAM 0x81000000 ;将所有RWZI(*(+RW,+ZI))段放入外部RAM中
{
* (+RW,+ZI)
}

HEAP +0 UNINIT ;在RWZI段后放入堆底
{
Startup.o (Heap)
}

HEAP_BOTTOM 0x81080000 UNINIT
{
Startup.o (HeapTop) ;堆顶:放入了外部RAM中(0x81080000)
}
}

(1)、样例中,只有一个加载块ROM_LOAD,包含了所有的代码和数据(存放在ROM),起始地址为0x800000000。这个加载块一共对应七个执行块(ROM_EXEC, IRAM,STACKS_BOTTOM, STAKS,ERAM,HEAP,HEAP_BOTTOM)。
(2)、RO的代码和数据会从ROM_EXEC开始执行,执行地址与装载地址相同。
(3)、Startup.o是Startup.s的目标文件(Startup.s也在这个工程下),vectors是在Startup.s定义的段,整个句子的意思是把整个Startup.s编译生成的目标文件(向量表)放在0x8000 0000的第一个位置,即从vectors开始依次从ROM_EXEC的顶部放下来。
(4)、所有的RW和ZI数据包含在外部RAM执行块里,起始地址为0x81080000。
(5)、RW数据是从ROM_LOAD copy 过来的,ZI数据是在RAM中初始化的,其位置在RW之上。
(6)、HEAP是用来定位堆栈的底的,堆栈底的位置在ZI之上所以使用"+0",heap会从此地址增加。
(7)、STACKS是用来定位堆栈顶的,堆栈顶的位置在可以用来作为存储的内存的顶部。Stacks会从堆栈顶的地址下降。

相关主题
文本预览
相关文档 最新文档