要了解RO,RW和ZI需要首先了解以下知識:
(1)
ARM程序的組成
此處所說(shuō)的“ARM程序”是指在A(yíng)RM系統中正在執行的程序,而非保存在ROM中的bin映像(image)文件,這一點(diǎn)清注意區別。
一個(gè)ARM程序包含3部分:RO,RW和ZI
RO是程序中的指令和常量
RW是程序中的已初始化變量
ZI是程序中的未初始化的變量
由以上3點(diǎn)說(shuō)明可以理解為:
RO就是readonly,
RW就是read/write,
ZI就是zero
(2)
ARM映像文件的組成
所謂ARM映像文件就是指燒錄到ROM中的bin文件,也成為image文件。以下用Image文件來(lái)稱(chēng)呼它。
Image文件包含了RO和RW數據。
之所以Image文件不包含ZI數據,是因為ZI數據都是0,沒(méi)必要包含,只要程序運行之前將ZI數據所在的區域一律清零即可。包含進(jìn)去反而浪費存儲空間。
Q:為什么Image中必須包含RO和RW?
A:因為RO中的指令和常量以及RW中初始化過(guò)的變量是不能像ZI那樣“無(wú)中生有”的。
(3)
ARM程序的執行過(guò)程
從以上兩點(diǎn)可以知道,燒錄到ROM中的image文件與實(shí)際運行時(shí)的ARM程序之間并不是完全一樣的。因此就有必要了解ARM程序是如何從ROM中的image到達實(shí)際運行狀態(tài)的。
實(shí)際上,ROM中的指令至少應該有這樣的功能:
1.將RW從ROM中搬到RAM中,因為RW是變量,變量不能存在ROM中。
2.將ZI所在的RAM區域全部清零,因為ZI區域并不在Image中,所以需要程序根據編譯器給出的ZI地址及大小來(lái)將相應得RAM區域清零。ZI中也是變量,同理:變量不能存在ROM中
在程序運行的最初階段,RO中的指令完成了這兩項工作后C程序才能正常訪(fǎng)問(wèn)變量。否則只能運行不含變量的代碼。
下面我將給出幾個(gè)例子,最直觀(guān)的來(lái)說(shuō)明RO,RW,ZI在C中是什么意思。
1; RO
看下面兩段程序,他們之間差了一條語(yǔ)句,這條語(yǔ)句就是聲明一個(gè)字符常量。因此按照我們之前說(shuō)的,他們之間應該只會(huì )在RO數據中相差一個(gè)字節(字符常量為1字節)。
Prog1:
#include <stdio.h>
void main(void)
{
;
}
Prog2:
#include<stdio.h>
const char a = 5;
voidmain(void)
{
;
}
Prog1編譯出來(lái)后的信息如下:
================================================================================
Co
948 60 0 96 0 Grand Totals
================================================================================
Total RO Size(Co
Total RW Size(RW Da
Total ROM Size(Co
================================================================================
Prog2編譯出來(lái)后的信息如下:
================================================================================
Co
948 61 0 96 0 Grand Totals
================================================================================
Total RO Size(Co
Total RW Size(RW Da
Total ROM Size(Co
================================================================================
以上兩個(gè)程序編譯出來(lái)后的信息可以看出:
Prog1和Prog2的RO包含了Co
如果增加的是一條指令而不是一個(gè)常量,則結果應該是Co
2; RW
同樣再看兩個(gè)程序,他們之間只相差一個(gè)“已初始化的變量”,按照之前所講的,已初始化的變量應該是算在RW中的,所以?xún)蓚€(gè)程序之間應該是RW大小有區別。
Prog3:
#include <stdio.h>
void main(void)
{
;
}
Prog4:
#include<stdio.h>
char a = 5;
voidmain(void)
{
;
}
Prog3編譯出來(lái)后的信息如下:
================================================================================
Co
948 60 0 96 0 Grand Totals
================================================================================
Total RO Size(Co
Total RW Size(RW Da
Total ROM Size(Co
================================================================================
Prog4編譯出來(lái)后的信息如下:
================================================================================
Co
948 60 1 96 0 Grand Totals
================================================================================
Total RO Size(Co
Total RW Size(RW Da
Total ROM Size(Co
================================================================================
可以看出Prog3和Prog4之間確實(shí)只有RW Da
3; ZI
再看兩個(gè)程序,他們之間的差別是一個(gè)未初始化的變量“a”,從之前的了解中,應該可以推測,這兩個(gè)程序之間應該只有ZI大小有差別。
Prog3:
#include <stdio.h>
voidmain(void)
{
;
}
Prog4:
#include <stdio.h>
char a;
void main(void)
{
;
}
Prog3編譯出來(lái)后的信息如下:
================================================================================
Co
948 60 0 96 0 Grand Totals
================================================================================
Total RO Size(Co
Total RW Size(RW Da
Total ROM Size(Co
================================================================================
Prog4編譯出來(lái)后的信息如下:
================================================================================
Co
948 60 0 97 0 Grand Totals
================================================================================
Total RO Size(Co
Total RW Size(RW Da
Total ROM Size(Co
================================================================================
編譯的結果完全符合推測,只有ZI數據相差了1個(gè)字節。這個(gè)字節正是未初始化的一個(gè)字符型變量“a”所引起的。
注意:如果一個(gè)變量被初始化為0,則該變量的處理方法與未初始化華變量一樣放在ZI區域。
即:ARM C程序中,所有的未初始化變量都會(huì )被自動(dòng)初始化為0。
總結:
1; C中的指令以及常量被編譯后是RO類(lèi)型數據。
2; C中的未被初始化或初始化為0的變量編譯后是ZI類(lèi)型數據。
3;C中的已被初始化成非0值的變量編譯后市RW類(lèi)型數據。
(4)
ROM主要指:NAND Flash,Nor Flash
RAM主要指:PSRAM,SDRAM,SRAM,DDRAM
(5)
Image$$??$$Limit 的含義
對于剛學(xué)習ARM的人來(lái)說(shuō),如果分析它的啟動(dòng)代碼,往往不明白下面幾個(gè)變量的含義:|Image$$RO$$Limit|、|Image$$RW$$Base|、|Image$$ZI$$Base|。
當把程序編寫(xiě)好以后,就要進(jìn)行編譯和鏈接了,在A(yíng)DS1.2中選擇MAKE按鈕,會(huì )出現一個(gè)Errors and Warnings的對話(huà)框,在該欄中顯示編譯和鏈接的結果,如果沒(méi)有錯誤,在文件的最后應該能看到Image component sizes,后面緊跟的依次是Co
Co
Tatal RO size (Co
Tatal RW size(RW Da
Tatal ROM size(Co
后面的字節數是根據用戶(hù)不同的程序而來(lái)的,下面就以上面的數據為例來(lái)介紹那幾個(gè)變量的計算。
在A(yíng)DS的Debug Settings中有一欄是Linker/ARM Linker,在output選項中有一個(gè)RObase選項,
假如
RO base設置為0x0c100000,后面的RW base設置為0x0c200000,然后在Options選項中有Image entry point,是一個(gè)初始程序的入口地址,設置為0x0c100000 。
有了上面這些信息我們就可以完全知道這幾個(gè)變量是怎么來(lái)的了:
|Image$$RO$$Base| = Image entry point =RO base =0x0c100000 ;表示程序代碼存放的起始地址
|Image$$RO$$Limit|=程序代碼起始地址+代碼長(cháng)度+1=0x0c100000+Tatal RO size+1
= 0x0c100000 + 184571 + 1 = 0x0c100000 +0x2D0FB + 1
= 0x0c12d0fc
|Image$$RW$$Base| = 0x0c200000=RW base地址指定
|Image$$RW$$Limit| =|Image$$RW$$Base|+ RW Da
=0x0c200037
|Image$$ZI$$Base| = |Image$$RW$$Limit| + 1 =0x0c200038
|Image$$ZI$$Limit| = |Image$$ZI$$Base| + ZI Da
=0x0c200038 + 0x4284
=0x0c2042bc
也可以由此計算:
|Image$$ZI$$Limit| = |Image$$RW$$Base| +TatalRWsize(RWData+ZIData)17081
=0x0c200000+0x42b9+3(要滿(mǎn)足4的倍數)
=0x0c2042bc
Part1 簡(jiǎn)介
一 概述
Scatter file(分散加載描述文件)用于armlink的輸入參數,他指定映像文件內部各區域的download與運行時(shí)位置。Armlink將會(huì )根據scatterfile生成一些區域相關(guān)的符號,他們是全局的供用戶(hù)建立運行時(shí)環(huán)境時(shí)使用。(注意:當使用了scatter file 時(shí)將不會(huì )生成以下符號Image$$RW$$Base, Image$$RW$$Limit, Image$$RO$$Base, Image$$RO$$Limit,Image$$ZI$$Base, and Image$$ZI$$Limit)
二 什么時(shí)候使用scatter file
當然首要的條件是你在利用ADS進(jìn)行項目開(kāi)發(fā),下面我們看看更具體的一些情況。
1 存在復雜的地址映射:例如代碼和數據需要分開(kāi)放在在多個(gè)區域。
2 存在多種存儲器類(lèi)型:例如包含Flash,ROM,SDRAM,快速SRAM。我們根據代碼與數據的特性把他們放在不同的存儲器中,比如中斷處理部分放在快速SRAM內部來(lái)提高響應速度,而把不常用到的代碼放到速度比較慢的Flash內。
3 函數的地址固定定位:可以利用Scatter file實(shí)現把某個(gè)函數放在固定地址,而不管其應用程序是否已經(jīng)改變或重新編譯。
4 利用符號確定堆與堆棧:
5 內存映射的IO:采用scatter file可以實(shí)現把某個(gè)數據段放在精確的地指處。
因此對于嵌入式系統來(lái)說(shuō)scatter file是必不可少的,因為嵌入式系統采用了ROM,RAM,和內存映射的IO。
三 scatter file 實(shí)例
1 簡(jiǎn)單的內存映射
LOAD_ROM 0x0000 0x8000
{
EXEC_ROM 0x0000 0x8000
{
*( RO)
}
RAM 0x10000 0x6000
{
*( RW, ZI)
}
}
LOAD_ROM(下載區域名稱(chēng)) 0x0000(下載區域起始地址) 0x8000(下載區域最大字節數)
{
EXEC_ROM(第一執行區域名稱(chēng)) 0x0000(第一執行區域起始地址) 0x8000(第一執行區域最大字節數)
{
*( RO(代碼與只讀數據))
}
RAM(第二執行區域名稱(chēng)) 0x10000(第二執行區域起始地址) 0x6000(第二執行區域最大字節數)
{
*( RW(讀寫(xiě)變量), ZI(未初始化變量))
}
}
2 復雜內存映射
LOAD_ROM_1 0x0000
{
EXEC_ROM_1 0x0000
{
program1.o( RO)
}
DRAM 0x18000 0x8000
{
program1.o ( RW, ZI)
}
}
LOAD_ROM_2 0x4000
{
EXEC_ROM_2 0x4000
{
program2.o( RO)
}
SRAM 0x8000 0x8000
{
program2.o ( RW, ZI)
}
}
LOAD_ROM_1 0x0000(下載區域一起始地址)
{
EXEC_ROM_1 0x0000(第一執行區域開(kāi)始地址)
{
program1.o( RO) (program1.o內的Co
}
DRAM 0x18000(第二執行區域開(kāi)始地址) 0x8000(第二執行區域最大字節數)
{
program1.o ( RW, ZI) (program1.o內的RW da
}
}
LOAD_ROM_2 0x4000(下載區域二起始地址)
{
EXEC_ROM_2 0x4000
{
program2.o( RO) (program2.o內的Co
}
SRAM 0x8000 0x8000
{
program2.o ( RW, ZI) (program2.o內的RW da
}
}
Part2 基本語(yǔ)法
2.1 BNF 符號與語(yǔ)法
" :由引號賴(lài)標示的符號保持其字面原意,如A” ”B標示A B。
A ::= B :定義A為B。
[A] :標示可選部分,如A[B]C用來(lái)標示ABC或AC。
A :用來(lái)標示A可以重復任意次,如A 可標示A,AA,AAA, …
A* :同A 。
A | B :用來(lái)標示選擇其一,不能全選。如A|B用來(lái)標示A或者B。
(A B) :標示一個(gè)整體,當和|符號或復雜符號的多次重復一起使用時(shí)尤其強大,如(AB)(C|D)標示ABC,ABD,ABABC,ABABD, …
2.2 分散加載文件各部分描述
(2.1)
如圖2.1所示為一個(gè)完整的分散加載腳本描述結構圖。下面我們對圖示中各個(gè)部分進(jìn)行講述。
2.2.1 加載區描述
每個(gè)加載區有:
ó名稱(chēng):供連接器確定不同下載區域
ó基地址:相對或絕對地址
ó屬性:可選
ó最大字節數:可選
ó執行區域列:確定執行時(shí)各執行區域的類(lèi)型與位置
load_region_name (base_address | (" " offset)) [attribute_list] [max_size ]
"{"
execution_region_description
"}"
load_region_name:下載區域名稱(chēng),最大有效字符數31。(并不像執行區域段名用于Load$$region_name,而是僅僅用于標示下載區域)。
base_address:本區域內部目標被連接到的地址(按字對齊)。
offset:相對前一個(gè)下載區域的偏移量(4的整數倍,如果為第一個(gè)區域)。
2.2.2 執行區描述
每個(gè)執行區有:
ó名稱(chēng):供連接器確定不同下載區域
ó基地址:相對或絕對地址
ó屬性:確定執行區域的屬性
ó最大字節數:可選
ó輸入段:確定放在該執行區域的模塊
exec_region_name (base_address | " " offset) [attribute_list][max_size]
"{"
input_section_description
"}"
exec_region_name:執行區域名稱(chēng),最大有效字符數31。
base_address:本執行區域目標要被聯(lián)接到的位置,按字對齊。
offset:相對于前一個(gè)執行區域結束地址的偏移量,4的整數倍;如果沒(méi)有前繼之能夠行區域(本執行區域為該下載區域的第一個(gè)執行區域),則該偏移量是相對于該下載區域的基址偏移量。
attribute_list:PI,OVERLAY,ABSOLUTE,FIXED,UNINIT。
PI: 位置獨立。
OVERLAY: 覆蓋。
ABSOLUTE: 絕對地址。
FIXED: 固定地址,下載地址與執行地址具有該地址指示確定。
UNINIT: 未初始化數據。
RELOC:無(wú)法明確指定執行區域具有該屬性,而只能通過(guò)繼承前一個(gè)執行區或父區域獲得。
對于PI,OVERLAY,ABSOLUTE,FIXED,我們只能選擇一個(gè),缺省屬性為ABSOLUTE。一個(gè)執行區域要么直接繼承其前面的執行區域的屬性或者具有屬性為ABSOLUTE。
具有PI,OVERLAY,RELOC屬性的執行區域允許其地址空間重疊,對于BSOLUTE,FIXED屬性執行區域地址空間重疊Armlink會(huì )報錯。
max_size:可選,他用于指使Armlink在實(shí)際分配空間大于指定值時(shí)報錯。
input_section_description:指示輸入段的內容。
2.2.3 輸入段描述
輸入段:
ó模塊名:目標文件名,庫成員名,庫文件名。名稱(chēng)可以使用通配符。
ó輸入段名,或輸入段屬性(READ-ON
module_select_pattern
["("
(" " input_section_attr | input_section_pattern)
([","] " " input_section_attr | "," input_section_pattern))*
")"]
2.2.3.1
module_select_pattern:選擇的模塊名稱(chēng)(目標文件,庫文件成員,庫文件),模塊名可以使用通配符(*匹配任意多個(gè)字符,?匹配任意一個(gè)字符),名稱(chēng)不區分字母大小寫(xiě),它是供選擇的樣本。
例1:*libtx.a ( RO)
libtx.a為threadX庫文件。
例2:tx_ill.o (INIT)
tx_ill.o為threadX中斷向量目標文件。
2.2.3.2
input_section_attr:輸入段屬性選擇子,每個(gè)選擇子以” ”開(kāi)頭,選擇子不區分大小寫(xiě)字符。
選擇子可選RO-CO
以下同義詞可以選擇:CO
還有兩個(gè)偽屬性:FIRST,LAST。如果各段的先后順序比較重要時(shí),可以使用FIRST,LAST標示一個(gè)執行區域的第一個(gè)和最后一個(gè)段。
例1:os_main_init.o (INIT , FIRST)
FIRST表示放于本執行區域的開(kāi)始處。
例2:*libtx.a ( RO)
RO 表示*libtx.a的只讀部分。
2.2.3.3
input_section_pattern:輸入段名。
例1:os_main_init.o (INIT , FIRST)
INIT 為os_main_init.o的一個(gè)段。
例2:os_stackheap.o (heap)
heap 為os_stackheap.o的一個(gè)段。
例3:os_stackheap.o (stack)
stack為os_stackheap.o的一個(gè)段。
//--------------------------------------------------------------------------------------------------------------------------
分散加載文件事例
ADS下的分散加載文件應用實(shí)例
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: 加載區,用來(lái)保存永久性數據(程序和只讀變量)的區域;
execution_region: 執行區,程序執行時(shí),從加載區域將數據復制到相應執行區后才能被正確執行;
load_region_name: 加載區域名,用于“Linker”區別不同的加載區域,最多31個(gè)字符;
start_address: 起始地址,指示區域的首地址;
offset: 前一個(gè)加載區域尾地址+offset 做為當前的起始地址,且“offset”應為“0”或“4”的倍數;
attributes: 區域屬性,可設置如下屬性:
PI 與地址無(wú)關(guān)方式存放;
RELOC 重新部署,保留定位信息,以便重新定位該段到新的執行區;
OVERLAY 覆蓋,允許多個(gè)可執行區域在同一個(gè)地址,ADS不支持;
ABSOLUTE 絕對地址(默認);
max_size: 該區域的大??;
execution_region_name:執行區域名;
start_address: 該執行區的首地址,必須字對齊;
offset: 同上;
attributes: 同上;
PI 與地址無(wú)關(guān),該區域的代碼可任意移動(dòng)后執行;
OVERLAY 覆蓋;
ABSOLUTE 絕對地址(默認);
FIXED 固定地址;
UNINIT 不用初始化該區域的ZI段;
module_select_pattern: 目標文件濾波器,支持通配符“*”和“?”;
*.o匹配所有目標,* (或“.ANY”)匹配所有目標文件和庫。
input_section_attr: 每個(gè)input_section_attr必須跟隨在“+”后;且大小寫(xiě)不敏感;
RO-CO
RO-DA
RO或TEXT, selects both RO-CO
RW-DA
RW-CO
RW 或 DA
ZI 或 BSS
ENTRY, that is a section containing an ENTRYpoint.
FIRST,用于指定存放在一個(gè)執行區域的第一個(gè)或最后一個(gè)區域;
LAST,同上;
input_section_pattern: 段名;
匯編中指定段:
AREA vectors, CO
C中指定段:
#pragma arm section [sort_type[[=]"name"]] [,sort_type="name"]*
sort_type: co
如果“sort_type”指定了但沒(méi)有指定“name”,那么之前的修改的段名將被恢復成默認值。
#pragma arm section // 恢復所有段名為默認設置。
應用:
#pragma arm section rwdata = "SRAM",zidata = "SRAM"
static OS_STK SecondTaskStk[256]; //“rwdata”“zidata”將定位在“sram”段中。
#pragma arm section // 恢復默認設置
分散加載文件中定義如下:
Exec_Sram 0x80000000 0x40000
{
* (sram)
}
“PI” 屬性使用示例:
LR_1 0x010000 PI ; The first load region is at0x010000.
{
ER_RO 0 ; The PI attribute is inherited fromparent.
; The default execution address is0x010000, but the co
{
*( RO) ; All the RO sections go here.
}
ER_RW 0 ABSOLUTE ; PI attribute is overridden byABSOLUTE.
{
*( RW) ; The RW sections are placed next.They cannot be moved.
}
ER_ZI 0 ; ER_ZI region placed after ER_RWregion.
{
*( ZI) ; All the ZI sections are placedconsecutively here.
}
}
LR_1 0x010000 ; The first load region is at0x010000.
{
ER_RO 0 ; Default ABSOLUTE attribute isinherited from parent. The execution address
; is 0x010000. The co
{
*( RO) ; All the RO sections go here.
}
ER_RW 0x018000 PI ; PI attribute overrides ABSOLUTE
{
*( RW) ; The RW sections are placed at0x018000 and they can be moved.
}
ER_ZI 0 ; ER_ZI region placed after ER_RWregion.
{
*( ZI) ; All the ZI sections are placedconsecutively here.
}
}
程序中對某區域地址等的引用方法:
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 theend of the execution region.
Image$$region_name$$ZI$$Base Execution address of the ZIoutput section in this region.
Image$$region_name$$ZI$$Length Length of the ZI output sectionin bytes (multiple of 4).
Image$$region_name$$ZI$$Limit Address of the byte beyond theend of the ZI output sectionin the execution region.
SectionName$$Base Input Address of the start of theconsolidated section called SectionName.
SectionName$$Limit Input Address of the byte beyondthe end of the consolidated section called SectionName.
Load: 加載區,即存放地址;
Image: 執行區,即運行地址;
Base: 區首地址;
Limit: 區尾地址;
Length: 區長(cháng)度;
region_name: RO、RW、ZI、load_region_name、execution_region_name;
例如:
“RAM1”區域的首地址: Image$$RAM1$$Base
上例中“sram”段首地址: sram$$Base
匯編引用示例:
IMP
IMP
IMP
IMP
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 ;
}
}
加載文件示例一:
起始地址 大小
ROM: 0x00000000 256K ;0x1fc 保留為加密字,程序在ROM中運行;
RAM 0x40000000 16K ;用于全局變量及任務(wù)堆棧;
SRAM 0x80000000 512K ;SRAM速度慢,主要用于存放大的數據表;
LOAD_ROM1 0x00000000 0x1f8 ; 指定該加載區域首地址、大小
{
EXEC_ROM1 0 0x1f8 ;沒(méi)有前一加載區域,所以該執行區域首地址為加載去首地址
; 并指定該區域長(cháng)度
{
Startup.o (vectors, FIRST) ;目標文件的“vectors”段放在該執行區域的第一段
irq.o ( RO) ; 目標文件的所有“RO”段放在該執行區域
}
}
LOAD_ROM2 0x00000200 ; 第二個(gè)加載區域
{
EXEC_ROM2 0 0x3e600
{
* ( RO) ; 所有目標文件和庫文件中的“RO”段存放在該區域
}
RAM1 0x40000000 0x4000
{
* ( RW, ZI) ;所有目標文件和庫文件的“RW”和“ZI”段存放在該區域
}
SRAM2 0x80000000 0x80000
{
* (sram) ; 所有目標文件中的“sram”段存放在該區域
}
}
示例二:
“iap.o”定義在“Exec_RAM1”中運行,所以設置“PI”屬性;
在調用“iap.c”中函數之前應該將其從“Load$$Exec_IAP$$Base”復制到指定的“Exec_RAM1”區域;
Load_region1 0x00000000 0x1fc
{
EXEC_ROM1 0
{
Startup.o (vectors, FIRST)
irq.o ( RO)
}
}
Load_region2 0x00000200 0x3e600
{
EXEC_ROM2 0
{
* ( RO)
}
Exec_IAP 0 PI // 可能引起鏈接器未使用該屬性警告,忽略
{
iap.o ( RO)
}
Exec_RAM1 0x40000000 0x4000
{
* ( RW, ZI)
}
Exec_Sram 0x80000000 0x40000
{
* (SRAM)
}
}
// 移動(dòng)“IAP.o”中的所有函數到“ImageExecIAPBase”加載區,并調用其中的函數
extern unsigned char Load$$Exec_IAP$$Base;
extern unsigned char Image$$Exec_IAP$$Length;
#define ImageExecIAPBase (0x40000000 0x1000) // 加載區首址
void MoveIAPRO(void)
{
unsigned char * psrc, *pdst;
unsigned int count;
count = (unsigned int) &Image$$Exec_IAP$$Length;
psrc = (unsigned char *)&Load$$Exec_IAP$$Base;
pdst = (unsigned char *)ImageExecIAPBase;
while (count--) {
*pdst = *psrc ;
}
}
// 調用“IAP.O”中的某函數
{
void (* pfnIAPWrite)(unsigned long, int);
pfnIAPWrite = (void (*)(unsigned long, int))
(ImageExecIAPBase
(unsigned int)IAPWrite - // 被調用函數名
(unsigned int)&Load$$Exec_IAP$$Base);
pfnIAPWrite((int)((CUPDATA *)CODESTARTADDR)->da
((CUPDATA *)CODESTARTADDR)->length);
}
//————————————————————————————————————————————————————————————
ARM編譯程序參考
介紹ARM編譯程序的ARM特有方面,包括:
Pragmas 編譯指示
Function keywords 函數關(guān)鍵字
Variabledeclaration keywords 變量聲明關(guān)鍵字
Pragmas
ARM編譯程序可識別一下格式的編譯指示:
#pragma [no_] feature-name
編譯指示優(yōu)于相關(guān)的命令行選項。
能識別的編譯選項如下:
Pragma name
Default
Reference
armsection
Off
Pragmas controlling co
check_printf_formats
Off
Pragmascontrolling printf and scanf argument checking
check_scanf_formats
Off
Pragmas controlling printf and scanf argumentchecking
check_stack
On
Pragmascontrolling co
debug
On
Pragmascontrolling debugging
imp
co
Ospace
optimization
Otime
optimization
On
optimization
softfp_linkage
Off
co
* check_printf_formats
該編譯指示標記類(lèi)似于printf的函數,如果存在文字格式串,則對照進(jìn)行類(lèi)型檢查。
#pragmacheck_printf_formats
extern void myprintf(const char *format, …);
#pragmano_check_printf_formats
* check_scanf_formats
該編譯指示對聲明為類(lèi)似于scanf的函數做標記,以便對照文字格式串檢查自變量的格式。
#pragmacheck_scanf_formats
extern void myformat(const char *format, …);
#pragmano_check_scanf_formats
* debug 該編譯指示可打開(kāi)或關(guān)閉調試表生成, 如果指定#pragmano_debug,則不會(huì )為隨后的聲明和函數生成調試信息表條目,直到下一個(gè)#pragma debug出現。
* Pragmascontrolling optimization
Ospace
Otime
On
* Pragmas controlling co
ocheck_stack 如果已經(jīng)使用了#pragmano_check_stack和-apcs/swst命令行選項禁止棧檢查,則該編譯指示可使的檢查是否違反了棧限制的函數入口代碼的重新生成。
o on
osoftfp_linkage 該編譯指示指定了至下一個(gè)#pragmano_softfp_linkage之間的所有函數聲明描述了使用軟件浮點(diǎn)鏈接的函數。__softfp關(guān)鍵字與該編譯指示的效果相同
o imp
o arm sectionsection_sort_list This pragma specifies the co
內聯(lián)函數(及其局部靜態(tài)變量)
模板實(shí)例(及其局部靜態(tài)變量)
消除未使用的變量和函數
將定義寫(xiě)入目標文件中的順序
該編譯指示完整語(yǔ)法為:
#pragma arm section[sort_type[[=]“name”]][,sort_type=
“name”]
此處name用于段名稱(chēng),sort_type可為如下之一co
和zidata。若指定sort_type,沒(méi)有指定name,則sort_type的段名被
重新設置為默認值。單獨輸入#pragma arm section,則所以對象段的
恢復為其默認值
int x1 = 5; // in .da
int y1[100]; // in .bss (default)
int constz1[3] = {1,2,3}; // in .constdata (default)
#pragma arm sectionrwdata = "foo", rodata = "bar"
int x2 = 5; // in foo (da
inty2[100]; // in .bss
int const z2[3]={1,2,3}; // in bar
char *s2 = "abc"; // s2 infoo, "abc" in bar
#pragma arm section rodata
int x3 =5; // in foo
int y3[100]; //in .bss
int const z3[3] ={1,2,3}; // in .constdata
char *s3 = "abc"; // s3 in foo, "abc" in .constdata
#pragma arm section co
int add1(intx) // in foo (co
{
return x 1;
}
#pragma arm section co
使用分散加載描述文件和鏈接程序,以控制將命名段放置在存儲器中
的特定地址。
· Function keywords
一些關(guān)鍵字指示編譯程序對其某個(gè)函數進(jìn)行特殊處理。包括函數內的聲明,函數限定符及函數存儲類(lèi)限定符。即Declarations inside function, Function qualifiers and Function storage.
__asm{assembler-co
__irq Thisenables a C or C function to be used as an interrupt routine called bythe IRQ, or FIQ vectors. All corrupted registers except floating-pointregisters are preserved, not on
__pure 指明函數聲明為純的。純函數沒(méi)有了公共子表達式。默認情況下,函數假定是不純的(產(chǎn)生副作用)。純函數需要滿(mǎn)足:其結果僅取決于其自變量的值;沒(méi)有副作用,其不能調用非純函數。不能使用全局變量或廢棄指針,同一參數兩次調用純函數,返回應該相同
一般而言,一個(gè)程序包括只讀的代碼段和可讀寫(xiě)的數據段。在A(yíng)RM的集成開(kāi)發(fā)環(huán)境中,只讀的代碼段和常量被稱(chēng)作RO段(ReadOnly);可讀寫(xiě)的全局變量和靜態(tài)變量被稱(chēng)作RW段(ReadWrite);RW段中要被初始化為零的變量被稱(chēng)為ZI段(ZeroInit)。對于嵌入式系統而言,程序映象都是存儲在Flash存儲器等一些非易失性器件中的,而在運行時(shí),程序中的RW段必須重新裝載到可讀寫(xiě)的RAM中。這就涉及到程序的加載時(shí)域和運行時(shí)域。簡(jiǎn)單來(lái)說(shuō),程序的加載時(shí)域就是指程序燒入Flash中的狀態(tài),運行時(shí)域是指程序執行時(shí)的狀態(tài)。對于比較簡(jiǎn)單的情況,可以在A(yíng)DS集成開(kāi)發(fā)環(huán)境的ARM LINKER選項中指定ROBASE和RWBASE,告知連接器RO和RW的連接基地址。對于復雜情況,如RO段被分成幾部分并映射到存儲空間的多個(gè)地方時(shí),需要創(chuàng )建一個(gè)稱(chēng)為“分布裝載描述文件”的文本文件,通知連接器把程序的某一部分連接在存儲器的某個(gè)地址空間。需要指出的是,分布裝載描述文件中的定義要按照系統重定向后的存儲器分布情況進(jìn)行。在引導程序完成初始化的任務(wù)后,應該把主程序轉移到RAM中去運行,以加快系統的運行速度。
什么是arm的映像文件,arm映像文件其實(shí)就是可執行文件,包括bin或hex兩種格式,可以直接燒到rom里執行。在axd調試過(guò)程中,我們調試的是axf文件,其實(shí)這也是一種映像文件,它只是在bin文件中加了一個(gè)文件頭和一些調試信息。映像文件一般由域組成,域最多由三個(gè)輸出段組成(RO,RW,ZI)組成,輸出段又由輸入段組成。所謂域,指的就是整個(gè)bin映像文件所處在的區域,它又分為加載域和運行域。加載域就是映像文件被靜態(tài)存放的工作區域,一般來(lái)說(shuō)flash里的 整個(gè)bin文件所在的地址空間就是加載域,當然在程序一般都不會(huì )放在flash里執行,一般都會(huì )搬到sdram里運行工作,它們在被搬到sdram里工作所處的地址空間就是運行域。我們輸入的代碼,一般有代碼部分和數據部分,這就是所謂的輸入段,經(jīng)過(guò)編譯后就變成了bin文件中ro段和rw段,還有所謂的zi段,這就是輸出段。對于加載域中的輸出段,一般來(lái)說(shuō)ro段后面緊跟著(zhù)rw段,rw段后面緊跟著(zhù)zi段。在運行域中這些輸出段并不連續,但rw和zi一定是連著(zhù)的。zi段和rw段中的數據其實(shí)可以是rw屬性。
| Image$$RO$$Base| |Image$$RO$$Limit| |Image$$RW$$Base||Image$$ZI$$Base| |Image$$ZI$$Limit|這幾個(gè)變量是編譯器通知的,我們在makefile文件中可以看到它們的值。它們指示了在運行域中各個(gè)輸出段所處的地址空間| Image$$RO$$Base|就是ro段在運行域中的起始地址,|Image$$RO$$Limit|是ro段在運行域中的截止地址。其它依次類(lèi)推。我們可以在linker的output中指定,在 simple模式中,ro base對應的就是|Image$$RO$$Base|,rw base對應的是|Image$$RW$$Base|,由于rw和zi相連,|Image$$ZI$$Base| 就等于|Image$$ZI$$limit|.其它的值都是編譯器自動(dòng)計算出來(lái)的。
下面是2410啟動(dòng)代碼的搬運部分,我給出注釋
BaseOfROM DCD |Image$$RO$$Base|
TopOfROM DCD|Image$$RO$$Limit|
BaseOfBSS DCD |Image$$RW$$Base|
BaseOfZero DCD |Image$$ZI$$Base|
EndOfBSS DCD|Image$$ZI$$Limit|
adr r0, ResetEntry; ResetEntry是復位運行時(shí)域的起始地址,在boot
nand中一般是0
ldr r2, BaseOfROM;
cmp r0, r2
ldreq r0,TopOfROM;TopOfROM=0x30001de0,代碼段地址的結束
beq InitRam
ldr r3, TopOfROM
;part 1,通過(guò)比較,將ro搬到sdram里,搬到的目的地址從 |Image$$RO$$Base| 開(kāi)始,到|Image$$RO$$Limit|結束
0
ldmia r0!, {r4-r7}
stmia r2!, {r4-r7}
cmp r2, r3
bcc %B0;
;part2,搬rw段到sdram,目的地址從|Image$$RW$$Base| 開(kāi)始,到|Image$$ZI$$Base|結束
sub r2, r2, r3;r2=0
sub r0, r0, r2
InitRam ;carry rw to baseofBSS
ldr r2, BaseOfBSS;TopOfROM=0x30001de0,baseofrw
ldr r3, BaseOfZero;BaseOfZero=0x30001de0
0
cmp r2, r3
ldrcc r1, [r0], #4
strcc r1, [r2], #4
bcc%B0
;part 3,將sdramzi初始化為0,地址從|Image$$ZI$$Base|到|Image$$ZI$$Limit|
mov r0,#0;init 0
ldr r3, EndOfBSS;EndOfBSS=30001e40
1
cmp r2, r3
strcc r0, [r2], #4
bcc %B1
聯(lián)系客服