【C语言常识】Keil MDK的分散加载文件.sct

出处:http://blog.csdn.net/tracing/article/details/9720157


面对这样一个新东西,先去官网看看,或者看看IDE的帮助,基本上你想要的东西都有了,

BAIDU来的都不全面,这是一种学习方法。

    http://www.keil.com/support/man/docs/armlink/armlink_BABDDHBF.htm

    这个链接是我在官网上找到的关于分散加载文件的资料。讲的比较详细了。这里通过一

个例子记录下我学习的过程,通过分散加载文件把代码从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
    VECTOR
    Base;  
  2. extern unsigned char Image
    VECTOR
    Length;  
  3.   
  4. extern unsigned char Load
    ERIRAM1
    Base;  
  5. extern unsigned char Image
    ERIRAM1
    Base;  
  6. extern unsigned char Image
    ERIRAM1
    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
    VECTOR
    Base;  
  17.     count = 0xE4;  
  18.       
  19.     while (count--)  
  20.     {  
  21.         *pDes++ = *pSrc++;  
  22.     }  
  23.       
  24.       
  25.     count = (unsigned int)&Image
    ERIRAM1
    Length;  
  26.     pDes = (unsigned char*)&Image
    ERIRAM1
    Base;  
  27.     pSrc = (unsigned char*)(&Load
    ERIRAM1
    Base + 0xE4);  
  28.       
  29.     while (count--)  
  30.     {  
  31.         *pDes++ = *pSrc++;  
  32.     }  
  33. }  

其中拷贝中断向量的时候要指定中断向量的偏移地址。Load
ERIRAM1
Base表示执行域ER_IRAM1的加载地址;Image
ERIRAM1
Base表示执行域ER_IRAM1的执行地址;Image
ERIRAM1
Length表示执行域ER_IRAM1的实际长度,VECTOR区域因为是EMPTY,所以实际长度是0,
而中断向量的长度是固定的,所以程序里就写了个常数。


出处:http://blog.163.com/yaochen_good/blog/static/5673636320131523020888/

分散加载能够将加载和运行时存储器中的代码和数据描述在被称为分散加载描述文

件的一个文本描述文件中,以供连接时使用。
(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开始的以后的区域中,

完成后复位即可。


下面是找了一篇值得参考的文章:

原文地址:

http://hi.baidu.com/pengjj0807/blog/item/ef73e287a212453cc65cc3be.html

KEIL下分散加载文件的使用

*************************************************************
; *** Scatter-Loading Description File generated by uVision ***
; *************************************************************

LR_IROM1 0x08000000 0x00004000 ; load region size_region 第一个加载域,起始地址0x08000000{ 大小0x00004000

ER_IROM1 0x08000000 0x00004000 ; load address = execution address第一个运行时域,

{ 起始0x08000000,大小0x00004000

*.o (RESET, +First) IAP第一阶段还是在FLASH中运行
*(InRoot$$Sections)
startup_stm32f10x_md.o
}
ER_IROM2 0x20008000 0x00004000 ; load address = execution address第二个运行时域,

{ 起始0x20008000,大小0x00004000

.ANY (+RO) IAP第二阶段加载到SDRAM中运行
}
RW_IRAM1 0x20000000 0x00008000 ; RW data
 把可读写的数据和初始化为0的数据放在内存SDRAM的开头

{

.ANY (+RW +ZI)
}
}

让MDK自己分配--选linker-usexxx


 

对于分散加载的概念,在《ARM体系结构与编程》书中第11章有明确介绍。

分散加载文件(即scatter file 后缀为.scf)是一个文本文件,通过编写一个分散加载文件来指定
ARM连接器在生成映像文件时如何分配RO,RW,ZI等数据的存放地址。如果不用SCATTER文件指定,那么
ARM连接器会按照默认的方式来生成映像文件,一般情况下我们是不需要使用分散加载文件的。

但在某些场合,我们希望把某些数据放在指定的地址处,那么这时候SCATTER文件就发挥了非常大的作用
而且SCATTER文件用起来非常简单好用。

举个例子:比如像LPC2378芯片具有多个不连续的SRAM,通用的RAM是32KB,可是32KB不够用,我想把
某个.C中的RW数据放在USB的SRAM中,那么就可以通过SCATTER文件来完成这个功能。
下面是就这个例子作的说明:


这是一个标准的常用的分散加载文件,现在加注释于后,方便以后查阅:
;******************************************************************************
;
; SCATTER LOADING DEION
; ARM
; KEIL's uVision3
; (RealView Microprocessor Developer Kit)
;
; Filename : LPC2378_Flash.scat
;******************************************************************************

LR_IROM1 0x00000000 0x00080000 ;; 第一个加载域,名字为LR_IROM1,起始
{                  ;;地址为0x0,大小为0x80000
ER_IROM1 0x00000000 0x00080000 ;;加载域中的运行时域,名字为ER_IROM1
{ ;; 起始地址为0x0,大小为0x80000
vectors.o (VECT, +First) ;;将vectors.c编译后生成的文件vectors.o中的代码
init.o (INIT) ;;以及init.o中的代码
* (+RO) ;;以及所有编译生成的RO属性的代码全部存放在
} ;;运行时域ER_IROM1指定的地址范围内,存放方式:顺序存放

RW_IRAM1 0x40000000 0x0000e800  ;;这是第二个运行时域,功能同上
{ ;;其中 *是代表具有()里面指定的属性的全部数据
*(+RW,+ZI) ;;与*功能相似的有.ANY,后面说明
} ;; The following declarations select the "two region model" ;

;; A default __user_initial_stackheap() will be used ;
ARM_LIB_HEAP 0x40007000 EMPTY 0x00000100 {} ;;指定堆栈地址
ARM_LIB_STACK 0x40008000 EMPTY -0x00000E00 {}
}


下面是针对LPC2378的USB SRAM作数据RAM使用的配置:

;******************************************************************************
;
; SCATTER LOADING DEION
; ARM
; KEIL's uVision3
; (RealView Microprocessor Developer Kit)
;
; Filename : LPC2378_Flash.scat
;******************************************************************************

LR_IROM1 0x00000000 0x00080000 ;; 第一个加载域,名字为LR_IROM1,起始
{                  ;;地址为0x0,大小为0x80000
ER_IROM1 0x00000000 0x00080000 ;;加载域中的运行时域,名字为ER_IROM1
{ ;; 起始地址为0x0,大小为0x80000
vectors.o (VECT, +First)
init.o (INIT)
* (+RO)
}

RW_IRAM1 0x40000000 0x0000e800
{
.ANY(+RW,+ZI)     ;; 此处.ANY替换原来的*,是因为下面的一个执行域对指定的模块中的RW,ZI数据指定了存放地址
;;用.ANY就可以把已经被指定的具有RW,ZI属性的数据排除
} ;; The following declarations select the "two region model" ;

找了3个分散加载文件来分析:

1、7x256的flash.sct分散加载文件:

Load_region 0x100000 0x40000 {//ro起始地址为0x100000,大小为0x40000

Fixed_region 0x100000 0x40000 {
*(cstartup +First)
.ANY (+RO)
}

Relocate_region 0x200000 {//rw和zi段的地址为0x200000

*.o (VECTOR, +First)
.ANY (+RW +ZI)
}

ARM_LIB_HEAP 0x20E000 EMPTY 0x1000 {
}

ARM_LIB_STACK 0x210000 EMPTY -0x1000 {
}
}

2、sram.sct文件

Load_region 0x200000 0x10000 {

Fixed_region 0x200000 {
*.o (VECTOR, +First)
.ANY (+RO)
}

Relocate_region +0 {
*(cstartup +First)
.ANY (+RW +ZI)
}

ScatterAssert((ImageLength(Fixed_region) + ImageLength(Relocate_region)) < 0xE000)

ARM_LIB_HEAP 0x20E000 EMPTY 0x1000 {
}

ARM_LIB_STACK 0x210000 EMPTY -0x1000 {
}
}

3、自定义的sram.sct

LR_IROM1 0x00200000 0x00008000 { ; load region size_region
ER_IROM1 0x00200000 0x00008000 { ; load address = execution address//加载域等于运行域
*.o (RESET, +First)
*(InRoot$$Sections)
.ANY (+RO)
}
RW_IRAM1 0x00208000 UNINIT 0x00008000 { ; RW data//rw和zi段
.ANY (+RW +ZI)
}
}

什么是分散加载文件这里就不赘述了。

前面两个分散加载文件是从别的地方拷过来的,用在自己的程序中可能会有问题,因为如果不修改它的话它就固定了加载地址和运行地址,如果程序简单又比较小的话可能不会有问题,但是如果程序代码比较大,超出了那两个加载文件的定义大小可能就会出问题,解决办法也很简单,直接修改.sct文件直到适合你的代码。

更好的办法是自己定义一个分散加载文件,在keil中勾选Use Memory Layout from Target Dialog,那么加载文件就是从你定义irom和iram等地址得到的,如果不勾选的话就是通过你自己指定的加载文件来加载。

如果分散加载文件不对的话,可能出现的问题就是明明是在sram中调试程序,但是却能神奇的通过flash downloader下载到flash中去,刚开始也是不解,后来才发现是分散加载文件有错误,我使用了一个指定的flash.sct分散加载文件,这样的话我设置的irom和iram都无效了,编译器直接根据我指定的flash.sct来分布代码和加载代码,又查看了一下flash.sct文件是加代码加载到flash地址空间的,这就是为什么在jlink-sram工程中也能通过flash downloader工具烧写代码到flash中去的原因


  • 23
    点赞
  • 142
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值