具体加载哪个,在DebugInExram->ARM Linker->Scatter定义,链接类型选择Scattered,image entry point一定要跟ROM_LOAD值一样
//ROM_LOAD 为加载区的名称,其后面的0x00000000 表示加载区的起始地址(存放程序代码的起始地址)
ROM_LOAD 0x0
{
//ROM_EXEC 描述了执行区的地址,放在第一块位置定义
ROM_EXEC 0x00000000
{
//从起始地址开始放置向量表(即Startup.o(vectors, +First),其中Startup.o 为Startup.s 的目标文件)
//+First表示Vectors段放在最前面
Startup.o (vectors, +First)
//接着放置其它代码(即* (+RO)),* 是通配符,类似WINDOW下搜索用的通配符
* (+RO)
}
//变量区IRAM 的起始地址为0x40000000
IRAM 0x40000000
{
//放置mystacks.o (MyStack为段名)
mystacks.o (MyStacks)
}
//+0表示接着上一段,UNINIT 表示不初始化
STACKS_BOTTOM +0 UNINIT
{
//放置AREA StackBottom, DATA, NOINIT
Startup.o (StackBottom)
}
//接着从0x40004000 开始,放置 AREA Stacks, DATA, NOINIT,UNINIT 表示不初始化
STACKS 0x40004000 UNINIT
{
Startup.o (Stacks)
}
//外部RAM从0x80000000开始为变量区
//如果片外RAM起始地址不为0x8000 0000,则需要修改mem_.scf文件
ERAM 0x80000000
{
* (+RW,+ZI)
}
//+0表示接着上一段,UNINIT 表示不初始化
HEAP +0 UNINIT
{
//放置堆底, AREA Heap, DATA, NOINIT
Startup.o (Heap)
}
//接着在外部0x80080000 放置堆顶
//这个地址是片外RAM 的结束地址,根据实际情况修改
HEAP_BOTTOM 0x80080000 UNINIT
{
Startup.o (HeapTop)
}
}
//重定向__user_initial_stackheap 函数
//分配新的bottom_of_heap地址等,R0-R3是函数必须的返回值,返回bottom_of_heap的值
//通过分散加载描述文件,重定向其位置,bottom_of_heap等已经在Startup.s中定义为DATA类型
__user_initial_stackheap
LDR r0,=bottom_of_heap
; LDR r1,=StackUsr
LDR r2,=top_of_heap
LDR r3,=bottom_of_Stacks
MOV pc,lr
变量区IRAM 的起始地址为0x40000000,放置Startup.o (MyStacks);
变量区ERAM 的起始地址为0x80000000,放置除Startup.o 文件之外的其它文件的变量(即* (+RW,+ZI));
紧靠ERAM 变量区之后的是系统堆空间(HEAP),放置描述为Startup.o (Heap);
堆栈区STACKS 使用片内RAM,由于ARM 的堆栈一般采用满递减堆栈,所以堆栈区起始地址设置为0x40004000,
放置描述为Startup.o (Stacks)。
通过分散加载文件把代码从flash里拷贝到ram里运行, 基于LPC1788。
先贴下我的sct文件:
这里有两个加载域(load region)LR_IROM1和LR_IROM2,LR_IROM1是初始化程序,拷贝代码等,
从ROM的地址0开始,LR_ROM2是应用程序,从ROM的0x2000开始。+RO表示只读,代码或者只
读数据,一般用来表示代码,+RW表示可读可写的数据,+ZI表示初始化为0的数据。大括号里面的
为运行域(execution region),一个加载域可以包含几个运行域,LR_ROM2里面有两个运行域,
VECTOR和ER_IRAM1,我用VECTOR来表示中断向量区域,ER_IRAM1来表示应用程序区,+0表示
紧接着VECTOR排放,EMPTY表示空的,这里空出0xE4的大小,用来放中断向量,.ANY表示除了
上面用到的代码之外的代码,官网上有专门解释.ANY的一节。
下面用一张图来表示这个程序的加载域和执行域:
其实加载域的empty这块区域是不用空出来的,主要是运行域要空出来,用来拷贝中断向量,看个人喜好了,
我觉得空出来方便引用这块区域的执行域地址。
这样框架就比较清楚了,拷贝的程序清单如下:
其中拷贝中断向量的时候要指定中断向量的偏移地址。Load Base表示执行域ER_IRAM1的加载地址;Image Base表示执行域ER_IRAM1的执行地址;Image Length表示执行域ER_IRAM1的实际长度,VECTOR区域因为是EMPTY,所以实际长度是0,
ROM_LOAD 0x0000 0x4000
{
ROM_EXEC 0x0000 0x4000; Root region
{
* (+RO); All code and constant data
}
RAM 0x10000 0x8000
{
* (+RW, +ZI); All non-constant data
}
}
(3)分散加载文件语法
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 // 恢复默认设置
(4)程序中对区域地址引用的方法
Load
Image
Image
Image
Image
Image
SectionName
汇编引用示例:
IMPORT |Load
IMPORT |Image
IMPORT |Image
IMPORT |Image
LDR R0, =|Load
LDR R1, =|Image
LDR R2, =|Image
0
CMP R1, R2
LDRCC R3, [R0], #4
STRCC R3, [R1], #4
BCC %b0
C 引用:
extern unsigned char Load
extern unsigned char Image
extern unsigned char Image
void MoveRO(void)
{
unsigned char * psrc, *pdst;
unsigned int count;
count = (unsigned int) &Image
psrc = (unsigned char *)&Load
pdst = (unsigned char *)&Image
while (count--) {
*pdst++ = *psrc++;
}
}
二.分散加载应用
前面提到过,从NAND Flash启动,对于S3C2410而言,由于片内具有4K的称作
"SteppingStone"的SRAM,NAND FLASH的最低4K代码可以自动复制到
"SteppingStone",因此可以将初始化等代码放在NAND FLASH的低4K区域内,
其他的代码放置在4K以外,在初始化代码内将这些代码复制到外部SDRAM,
从而这些代码可以在外部SDRAM内运行。
1.应用实例描述
先完成初始化操作,并且在初始化代码中将NAND FLASH的4K范围以外的代码
(简单起见,这部分代码可以操作LED灯)复制到外部SDRAM中。主要目的
是使用分散加载文件以及将NAND FLASH中的数据代码复制到SDRAM中。
2.分散加载文件
NAND_FLASH_LOAD 0x0 0x1000
{
RAM_EXEC +0 0x1000
{
;参见前面的加载文件语法
}
}
NAND_FLASH_LOAD2 0x1000
{
SDRAM_EXEC 0x30000000
{
;参见前面的加载文件语法
}
}
(1)将一些初始化代码放在第一个加载区(根区:加载地址和执行地址相同的区域,
每一个分散加载描述文件必须至少要有一个根区。),地址范围为:0x0000~0x0fff的4K,
其执行区的地址范围也是0x0000~0x0fff的4K,这正好是NAND FLASH启动时自动复制的地址范围。
(2)其他代码放在第2个加载区,从地址0x1000开始,由于这一部分不能自动复制,
因此在初始化代码中应该将这一部分复制到外部SDRAM中,其执行区的起始地址为外部SRDAM的地址。
3. 二进制文件烧录
由于有2个加载区,因此生成的二进制文件有2个,文件名对应于相应的执行区名,分别是
RAM_EXEC和SDRAM_EXEC,需要注意的是,应该将存放初始化代码的加载区对应
的二进制文件RAM_EXEC烧录NAND FLASH的低4K区域,第二个加载区对应的二进
制文件SDRAM_EXEC烧录到4K以后的区域。这个可以通过修改Samsuang的sjf烧录
程序实现,原来的烧录程序是按BLOCK(16K)烧录,可以修改为按4K的Section烧录,
即将1个Block分为4个Section(4K)。主要修改 k9s1208.c中的K9S1208_Program函数,
需要注意的是,由于NAND FLASH写入前应该擦除,擦除是按Block擦除,由于现在是按
Section写,因此应该注意只有在第1次写某一块中的Section前进行擦除,以后再写着一块
的其它Section前不能再进行擦除。
这样RAM_EXEC烧录到0 SECTION,SDRAM_EXEC烧录到1 SECTION开始的以后的区域中,
完成后复位即可。
ARM映像文件是一个层次性结构的文件,其中包含了域(region)、输出段(output section)和输入段(input section)。各部分关系如下:
一个映像文件由一个或多个域组成
每个域包含一个或多个输出段
每个输出段包含一个或多个输入段
各输入段包含了目标文件中的代码和数据
输入段中包含了4类内容:代码、已经初始化的数据、未经初始化的存储区域、内容初始化成0的存储区域。每个输入段有相应的属性,可以为只读的(RO)、可读写的(RW)以及初始化成0的(ZI)。ARM连接器根据各输入段的属性将这些输入段分组,再组成不同的输出段以及域。
一个输出段中包含了一系列的具有相同的RO、RW和ZI属性的输入段。输出段的属性与其中包含的输入段的属性相同。在一个输出段内部,各输入段是按照一定的规则排序的,这个后面再补充。
一个域中包含了1~3个输出段,其中各输出段的属性各不相同。各输出段的排列顺序是由其属性决定的。其中,RO属性的输出段排在最前面,其次是RW属性的输出段,最后是ZI属性的输出段。一个域通常映射到一个物理存储器上,如ROM和RAM等。
2.ARM映像文件各组成部分的地址映射
分散加载机制允许为链接器指定映像的存储器映射信息,可实现对映像组件分组和布局的全面控制。分散加载通常仅用于具有复杂存储器映射的映像(尽管也可用于简单映像),也就是适合加载和执行时内存映射中的多个区是分散的情况。
要构建映像的存储器映射,链接器必须有:描述节如何分组成区的分组信息、描述映像区在存储器映射中的放置地址的放置信息。
分散加载区域分两类:
加载区:该映像文件开始运行前存放的区域,即当系统启动或加载时应用程序存放的区域。
执行区:映像文件运行时的区域,即系统启动后,应用程序进行执行和数据访问的存储器区域,系统在实时运行时可以有一个或多个执行块。
3.分散加载文件(即scatter file,后缀为.scf)
分散加载文件是一个文本文件,通过编写一个分散加载文件来指定ARM连接器在生成映像文件时如何分配RO,RW,ZI等数据的存放地址。如果不用SCATTER文件指定,那么ARM连接器会按照默认的方式来生成映像文件,一般情况下我们是不需要使用分散加载文件的。
但在某些场合,我们希望把某些数据放在指定的地址处,那么这时候SCATTER文件就发挥了非常大的作用。而且SCATTER文件用起来非常简单好用。
举个例子:比如像LPC2378芯片具有多个不连续的SRAM,通用的RAM是32KB,可是32KB不够用,我想把某个.C中的RW数据放在USB的SRAM中,那么就可以通过SCATTER文件来完成这个功能。
分散加载文件的语法:
复制代码
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 // 恢复默认设置
样例:
简单存储器映射实例
复制代码
LOAD_ROM 0x0000 0x8000 //Name of load region, Start address for load region, Maximum size of load region
{
EXEC_ROM 0x0000 0x8000 //Name of first exec region, Start address for exec region, Maximum size of this region
{
*(+RO) //Place all code and RO data into this exec region
}
RAM 0x10000 0x60000 //Start of second exec region
{
*(+RW, +ZI) //Place all RW and ZI data into this exec region
}
}
复制代码
复杂存储器映射实例:
复制代码
LOAD_ROM_1 0x0000 //Start address for first load region
{
EXEC_ROM_1 0x0000 //Start address for first exec region
{
program1.o (+RO) //Place all code and RO data from program1.o into this exec region
}
DRAM 0x18000 0x8000 //Start address for this exec region Maximum size of this exec region
{
program1.o (+RW, +ZI) //Place all RW and ZI data from program1.o into this exec region
}
}
LOAD_ROM_2 0x4000 //Start address for second load region
{
EXEC_ROM_2 0x4000
{
program2.o (+RO) //Place all code and RO data from program2.o into this exec region
}
SRAM 0x8000 0x8000
{
program2.o (+RW, +ZI) //Place all RW and ZI data from program2.o into this exec region
}
}
复制代码
具体格式描述请参考资料: 分散加载描述文件
一个具体的例子:
复制代码
; *************************************************************
; * Scatter-Loading Description File generated by uVision *
; *************************************************************
LR_IROM1 0x00000000 0x00080000 { ; 第一个加载域,名字是LR_IROM1,起始地址0x00000000 大小是0x00080000
ER_IROM1 0x00000000 0x00080000 { ; 第一个运行时域,名字是ER_IROM1 起始地址0x00000000 大小事0x00080000
*.o (RESET, +First) ; IAP第一阶段在FLASH中运行
*(InRoot$$Sections) ; All library sections that must be in a root region
.ANY (+RO) ; .ANY与*功能相似,用.ANY可以把已经被指定的具有RW,ZI属性的数据排除
}
RW_IRAM1 0x10000000 0x00010000 { ; RW data
.ANY(+RW +ZI)
}
RW_SDRAM1 0xA0000000 0x00800000 { ; RW data
STARTUP_LPC177X_8X.o (HEAP) ;HEAP用来定位堆栈的底
*.LIB(+RW +ZI)
}
}