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)
    }
}







發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章