keil分散加载文件

分散加载文件mem_a.scf,mem_b.scf,mem_c.scf,区别是加载地址不一样
具体加载哪个,在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文件:

[cpp]  view plain  copy
  1. LR_IROM1 0x00000000 0x00002000    
  2. {   
  3.     ER_IROM1 0x00000000 0x00020000    
  4.     {  
  5.         *.o (RESET, +First)  
  6.         *(InRoot$$Sections)  
  7.         startup_lpc177x_8x.o (+RO)  
  8.         system_LPC177x_8x.o (+RO)  
  9.     }  
  10.       
  11.     RW_IRAM1 0x20000000 0x00004000    
  12.     {  
  13.         .ANY (+RW +ZI)  
  14.     }  
  15. }  
  16.   
  17. LR_IROM2 0x00002000 0x0007E000  
  18. {  
  19.     VECTOR 0x10000000 EMPTY 0xE4  
  20.     {  
  21.     }  
  22.       
  23.     ER_IRAM1 +0  
  24.     {  
  25.         .ANY (+RO)  
  26.     }  
  27. }  


这里有两个加载域(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这块区域是不用空出来的,主要是运行域要空出来,用来拷贝中断向量,看个人喜好了,

我觉得空出来方便引用这块区域的执行域地址。


    这样框架就比较清楚了,拷贝的程序清单如下:

[cpp]  view plain  copy
  1. extern unsigned char Image Base;  
  2. extern unsigned char Image Length;  
  3.   
  4. extern unsigned char Load Base;  
  5. extern unsigned char Image Base;  
  6. extern unsigned char Image Length;  
  7.   
  8. void CopyCode2Ram ()  
  9. {  
  10.     unsigned char *pSrc, *pDes;  
  11.     unsigned int count;  
  12.       
  13.     SCB->VTOR = 0x10000000;  
  14.       
  15.     pSrc = 0;  
  16.     pDes = (unsigned char*)&Image Base;  
  17.     count = 0xE4;  
  18.       
  19.     while (count--)  
  20.     {  
  21.         *pDes++ = *pSrc++;  
  22.     }  
  23.       
  24.       
  25.     count = (unsigned int)&Image Length;  
  26.     pDes = (unsigned char*)&Image Base;  
  27.     pSrc = (unsigned char*)(&Load Base + 0xE4);  
  28.       
  29.     while (count--)  
  30.     {  
  31.         *pDes++ = *pSrc++;  
  32.     }  
  33. }  

其中拷贝中断向量的时候要指定中断向量的偏移地址。Load  Base表示执行域ER_IRAM1的加载地址;Image  Base表示执行域ER_IRAM1的执行地址;Image  Length表示执行域ER_IRAM1的实际长度,VECTOR区域因为是EMPTY,所以实际长度是0,
而中断向量的长度是固定的,所以程序里就写了个常数。
分散加载能够将加载和运行时存储器中的代码和数据描述在被称为分散加载描述文
件的一个文本描述文件中,以供连接时使用。
(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

Base             Load address of the region.
Image
Base            Execution address of the region.
Image
Length          Execution region length in bytes (multiple of 4).
Image
Limit           Address of the byte beyond the end of the execution region. 
Image
ZI
region_name
Length      Length of the ZI output section in bytes (multiple of 4).
Image
ZI
Base                   Input Address of the start of the consolidated section called SectionName.
SectionName
RAM1
Base 
汇编引用示例:
  IMPORT |Load
Base|              // Exec_RAM1 为“RW”段
  IMPORT |Image
ExecRAM1
Base|
  IMPORT |Image
ExecRAM1
Length|
  IMPORT |Image
ExecRAM1
Limit| 
  LDR  R0, =|Load
ExecRAM1
Base|
  LDR  R1, =|Image
ExecRAM1
Base|
  LDR  R2, =|Image
ExecRAM1
Limit|
0
  CMP  R1,   R2
  LDRCC R3,   [R0], #4
  STRCC R3,   [R1], #4
  BCC  %b0
C 引用:
extern unsigned char Load
ExecRAM1
Base;
extern unsigned char Image
ExecRAM1
Base;
extern unsigned char Image
ExecRAM1
Length; 
void MoveRO(void)
{
 unsigned char * psrc, *pdst;
 unsigned int  count; 
 count = (unsigned int)   &Image
ExecRAM1
Length;
 psrc  = (unsigned char *)&Load
ExecRAM1
Base;
 pdst  = (unsigned char *)&Image
ExecRAM1
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.ARM映像文件
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)
    }
}







  • 2
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值