这几天正好在研究NAND Flash,手头有一块FS2410开发板。从NAND Flash启动,对于S3C2410而言,由于片内具有4K的称作"SteppingStone"的SRAM,NAND FLASH的最低4K代码可以自动复制到"SteppingStone",因此可以将初始化等代码放在NAND FLASH的低4K区域内,其他的代码放置在4K以外,在初始化代码内将这些代码复制到外部SDRAM,从而这些代码可以在外部SDRAM内运行。ADS的分散加载功能正好可以实现这一目的。下面结合实例来说明分散加载文件。
一.ADS分散加载
分散加载能够将加载和运行时存储器中的代码和数据描述在被称为分散加载描述文件的一个文本描述文件中,以供连接时使用。
(1)分散加载区
分散加载区域分为两类:
? 加载区,包含应用程序复位和加载时的代码和数据。
? 执行区,包含应用程序执行时的代码和数据。应用程序启动过程中,从每个加载区可创建一个或多个执行区。
映象中所有的代码和数据准确地分为一个加载区和一个执行区。
(2)分散加载文件示例
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$$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|
0
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++;
}
}
二.分散加载应用
前面提到过,从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开始的以后的区域中,完成后复位即可。
参考资料:
1. ads下分散加载文件,http://hxdlj.spaces.live.com/blog/cns!1083C4D05B5E5E09!152.entry
2. S3C2410数据手册
3. ARM DUI 0203BSC,RealView 编译工具 2.0 版 开发者指南,http://infocenter.arm.com/help/index.jsp
4. FS2410光盘
[From :] http://blog.csdn.net/denlee/archive/2008/05/28/2491332.aspx