ADS Developer Guid:
Procedure Call Standard
由於ARM不像x86有一個指令支援CALL, RET的自動Save/Restore Register動作,所以Save Restore的動作是由Program來作的。
startup
Linker發現link obj中有main()時,會自動link C initial code,這時如果沒有在指定Entry Lable ( Target Setting -- Linker -- ARM Linker -- Layout : Object/Symbol填入**.o , Section填入AREA Name),ADS會使用自己提供的C Library Code的__main作爲entry point, __main將需要初始化的參數(Data,Code)由ROM Copy到RAM,將需要Reset爲0的變數Reset,呼叫rt_entry( )設定stack base(r13)和heap base,再跳到user的main( )執行。啟動的執行順序是:
__main : copy RW, ZI to RAM
__rt_entry : setup stack and heap base
main : user program
其中__rt_entry設定的stack是執行__main時的CPU Mode的stack。(__main和__rt_entry都沒有更改CPU Mode)
更詳細的:
_move_region
_zero_region
_rt_entry
_rt_stackheap_init
_usr_libspace
_usr_init_stackheap
_rt_lib_init
_fp_init
_rt_fp_status_addr
_usr_libspace
__fplib_config_pureend_doubles
main
ADS Linker的Default Memory Map是:
Program (RO+RW) : 0x008000
RW
ZI
-------Image$$ZI$$Limit Mark
Heap
|
Stack : 0x8000000
其中Heap是由Image$$ZI$$LimitMark開始,和Stack共用到最後0x800_0000
Stack Limit 0x800_0000是ARMulator的default值
如果是自己設計的板子就要自己定義$top_of_memory變數
Scatter Loading
scater loading是用一個loading script (*.scf) 來指定各程式,變數的放置位置,在script中要說明ROM的位置,和執行時的位置,arm linker很聰明,其實只要將執行時的image宣告出來,外面再用ROM { }刮起來,linker就會自動將image中需要放在ROM裡的部分安排好,放在ROM{ }這個image裡(自動將需要initial的RW變數的初值,copy一份到ROM裡,如果有在RAM裡的function,也會將CODE Copy一份到ROM裡)。
範例:
先將執行時的image宣告出來
FLASH 0x01000000 0x01000000
{
__main.o (+RO)
}
RAM 0x02000000
{
ramfun.o
}
HEAP +0
{
heap.o (+RW,+ZI)
}
STACK +0x02000
{
stack.o (+ZI)
}
指定Memory Mapping
Example:
指定Load的Address Region (Start:0x24000000, Size:0x40000000)
Flash 0x24000000 0x40000000
預計需要5個區域:
Flash : Flash的位置
32bitRAM : ARM內建的32bitRAM
HEAP : 配合heap.s的內容,可以決定heap的起始位置
STACK : 配合stack.s的內容,可以決定stack的最高地址
UART0 : memory-mapped I/O
所以先宣告出下面的幾項,在每個區域名稱後的是地址和長度(可省略)位元位址部分若是數位代表絕對位址,+0代表目前地址+0 UNINIT代表不需要作初始化設定。
{
Flash 0x24000000 0x40000000
{
}
32bitRAM 0x0000
{
}
HEAP +0 UNINT
{
}
STACKS 0x40000 UNINIT
{
}
UART0 0x16000000 UNINIT
{
}
}
接下來爲每個區域作細部的宣告(安排每個source code, data region所屬的區域),先看FLASH區段:
說明init.s要先Link,init.s的Init AREA要放在這個區域的開頭(因爲Init AREA是程式的entry point)。接下來是其他的READONLY程式。在init.S的開頭有AREA Init, CODE, READONLY宣告,指明有Init AREA, 是READONLY.
FLASH
{
init.o (Init, +First)
* (+RO)
}
接下來指定32bitRAM:
說明32bitRAM要從0x000開始,並且先放vector.s ,並且將vect.s中的AREA Vect放在最開頭,接下來是所有READWRITE資料
32bitRAM 0x0000
{
vector.o (Vect, +First)
* (+RW, +ZI)
}
說明在放完32bitRAM後接著放HEAP, HEAP中只有heap.s。
HEAP + 0 UNINIT
{
heap.o (+ZI)
}
從heap.s Source Code來看:
只有宣告一個變數bottom_of_heap,這樣其他程式由bottom_of_heap的值就可以知道heap區的起始地址。一般heap和stack會共用一段區域(一個往下長,一個往上長)。所以在relocate script中,HEAP和STACK要連在一起。共用空間則是由STACK +XXXXX來決定的。
heap.S不用宣告出heap的空間。
AREA Heap, DATA, NOINIT
EXPORT bottom_of_heap
bottom_of_heap SPACE 1
END
STACK的部分,loading-script:
STACKS 0x40000 UNINIT
{
stack.o (+ZI)
}
Stack.s和HEAP一樣,只有宣告一個variable
AREA STACK, DATA, NOINIT
EXPORT top_of_stacks
top_of_stacks SPACE 1
END
因爲STACKS由0x40000開始,所以top_of_stack會被放在0x40000,所以利用這個variable就可以得到stack的top address。
最後是memory-mapped I/O
UART0 0x16000000 UNINT
{
uart.o (+ZI)
}
uart.c裏宣告了一個structure變數struct uart uart0;
struct uart的宣告在uart.h裏:
struct uart
{
volatile unsigned dr; // @0x0
volatile unsigned ecr; // @0x4
volatile unsigned lcrh; // @0x8
volatile unsigned lcrm; // @0x0c
volatile unsigned lcrl; // @0x10
volatile unsigned cr; // @0x14
volatile unsigned fr; // @0x18
volatile unsigned iir; // @0x1C
};
*這部分要配合ARM的evaluation board來看,並不是用ATMEL的CPU.
Section-related symbols
Linker會產生一些symbols代表每一個section(Readonly section,Data section,Zero inititialize section),供program取用。以下是symbols列表:
Symbols
|Image$$RO$$Base| READONLY (RO) section的啟動位址
|Image$$RO$$Limit| READONLY (RO) section的結束位址
|Image$$RW$$Base| RW section的開始位址
|Image$$RW$$Limit| RW section的結束位址
|Image$$ZI$$Base| Zero Initialize section的開始位址
|Image$$ZI$$Limit| Zero Initialize section的結束位址
|SectionName$$Base| SectionName Section的開始位址
|SectionName$$Limit| SectionName Section的結束位址
在Assembly code中,可以直接使用這些名稱,就像已經宣告過的一樣。
ADS Assembly:
Register:
有17個, r0 ~ r15+CPSR+SPSR.r0~r15裏三個是特殊用途
r15 : PC (program counter)
r14 : lr (link register)
r13 : sp (stack pointer)
其他都是general purpose register.
CPSR : Current Program Status Register目前Mode的status。
SPSR : Save Program Status Register,當CPU Mode轉換時的CPSR會被save在這裏。
CPU Mode
ARM有七種privilege mode,由CPSR (Current Program Status Register)中的bit[0~4]決定。
User : 一般的執行
FIQ : 當進入Fast Interrupt
IRQ : 當進入一般的Interrupt
SVC : 當進入Software Interrupt (SWI)
Abort : 當發生Memory faults: data access fault或是instruction prefetch fault (進入Prefetch Abort Interrupt)
Undef : 當發生Undefined Instruction (進入Undef Instruction Interrupt_
System : 和User Mode相同,但是有修改CPSR的能力
除了User Mode外,CPU在其他的Mode時,都可以藉由改變CPSR的bit[0-4]來改變目前的Mode。在User Mode要變更爲其他的Mode只有經過Interrupt (Hardware或Software)。
ARM Reset後是在SVC Mode,所以通常在InitCode會切換到各Mode把SP Initial好之後再切到User Mode,最後設定User Mode的SP後才開始執行main。
Mode的狀態和register的access有關,每個mode都有自己的SP(r13), lr(r14)。
在User Mode要改變成其他Mode的方法:先抽換掉SWI的Vector Address,改為一行指令:mov pc,lr (直接返回),之後再把原Vector放回去
mov r0,#8 ; SWI vector address
ldr r1,=mv_pc_lr;
swp r1,r1,[r0]
swi 8 ; The ‘8’ is an dummy operand,
; because our handler won’t use it.
; Because the SWI handler is a “mov pc, lr”
; instruction,
; so it will fall back here.
str r1,[r0] ; Restore the origional vector
; Now it’s in SVC Mode, can modify the CPSR
:
;
mv_pc_lr ; This is our vector used to replace the
; origional SWI vector
mov pc, lr
r14的功能
r14通常用在儲存return address,有兩種狀況會用到return address: call subroutine和Interrupt.
1. Call subroutine : 使用Branch with Link(BL)時,return address會被放在r14裏,然後跳到目的地去執行
2. Interrupt : Interrupt會改變CPU Operation Mode, 在interrupt時, return address會被放在新的mode的r14裏,所以原Mode的r14的值不會被overwrite掉。
Flag
狀態旗標在CPSR裏
N:Negative
Z:Zero
C:Carry
V:oVerflow
Q : Satuation (沒用)
所有的運算與移動指令加上S後會依照運算內容update這四個旗標。
NZCV剛好是CPSR的最高四個bit
N:0x80000000
Z:0x40000000
C:0x20000000
V:0x10000000
條件執行 :
所有的指令都可以依照CPSR旗標的內容決定要不要執行,只要在指令後加上
EQ : Equal (N=1)
NE : Not Equal (N=0)
CS/HS : Carry Set / Unsigned High or Same (C=1)
CC/LO : Carry Clear / Unsigned Lower (C=0)
MI : Minus / Negative (N=1)
PL : Plus / positive or zero (N=0)
VS : Overflow (V=1)
VC : No overflow (V=0)
HI : Unsigned higher (C=1 and Z=0)
LS : Unsigned lower or the same (C=0 or Z=1)
GE : Signed greater than or equal (N=V)
LT : Signed less than (N!=V)
GT : Signed greater than (Z=0 and N=V)
LE : Signed less than or equal (Z=1 or N!=V)
AL : Always
CPSR:
CPSR的內容可分爲四部分(byte):
C : Control Field,就是Mode
S : Status Field
F : Flags Field
有三個Flag:
I : Interrupt Disabled
F: Fast Interrupt Disabled
T: Now Exceuting Thumb Instructions
CPSR和一般的Register不同,所以對CPSR動作時,要用特殊的指令MSR,MRS
MSR將資料搬到CPSR裏:
格式 MSR
例如:
MSR CPSR_f, 0x80000 ; 設定flag field的
MSR CPSR_f, R0 ; 依照r0的內容update CPSR的flag byte的內容
MRS將CPSR的資料般出來
MRS R0, CPSR ; 把CPSR的資料copy到R0
PC (runing time和assembly time)
Runtime Program Counter:ARM7是3 Stage的machine,所以PC的值會是目前執行位置+2 Instruction (8 bytes)
Assemblytime Program Counter:和執行時的PC無關,是一個assemler的reserved mark,用一個逗點( . )代表目前地址。
LDR Rd,=const
‘=’這個符號是pseudo-instruction.因爲ARM instruction只能放8 bit的const.
所以這個pseudo code會依照後面const的大小把這個指令轉爲
MOV Rd,=const (const <=255)
或是
LDR Rd,[pc,#offset_to_literal_pool] (const > 255)
** LDR這個指令後方的target address是relative的(相對於目前pc值),所以不管這段程式碼relocate到哪一個地方,都不會影響到結果。但是由於AXD的disassembler會自動把pc-relative address轉爲實際位址,所以在AXD的disassembly窗口中看到的好象是絕對位址。
LOAD ( LDR,LDM ):
LDR|STR{
對應Binary的格式是
P : pre/post index
U : up/down
B : unsigned byte/ word
W : write-back (update)
L : load/ store
Rn : base register
Rd : source/destination
從格式中可以知道一定要指定base register(Rn), 當不指定Rn時,表示用pc作Rn.
LDR:SingleLoad 右到左
LDR r0,[r1] ; r0=mem32[r1] r1是base address
LDR r0,[r1,#2] ; r0=mem32[r1+2] r1是base address
LDR r0,AddressData ; 沒有寫出base register
; 會用pc作爲base address
; 轉換的動作在compile時會作
LDM:MultiLoad 左到右
LDMIA r1,{r0,r2,r5} ; r0=mem32[r1],
; r2=mem32[r1+4],
; r5=mem32[r1+8]
STORE ( STR,STM )
STR:Single Store左到右
STR r0,[r1] ; mem32[r1]=r0
STM:MultiStore右到左
STMIA r1,{r0,r2,r5} ; mem32[r1]=r0,
; mem32[r1+4]=r2,
; mem32[r1+8]=r5
其他相關事項
1. 實際上LDM, STM屬於multiple register transfer instructions,格式都是Rm,{Reg-list},雖然Rm沒有寫成[Rm],但是在 multiple_register_transfer_instruction裏規定Rm是indirect addressing。
2. Load/Store時,所reference的Register存放的內容是絕對位址(不是相對於pc),和MOV命令,LDR命令不一樣。
Multi Register Transfer instruction
Machine Code的旗標有
P : pre/post index
U : up/down
S : restore PSR and force user bit
W : write back (update index register)
L : load/store
針對是memory block和stack操作,同樣的動作有兩個不同的名稱。
Memory block : Load Store都依照operation的定義
I : Increase
D : Decrease
A : After
B : Before
Stack operation : Load Store依照stack的定義
F : Full SP目前指向有資料的位址
E : Empty SP目前指向空的地址
A : Ascending Stack是向高位指增加
D : Decenging Stack是向低地址增加
對Stack動作時相同的操作定義對SP的動作會不一樣
STMFD sp!,{rs-re} ; sp-4,存一個Register,從re到rs
LDMFD rm!,{rs-re} ; 取出一個Data,SP + 4,從rs到re
所以Stack操作時只要LDMXX和STMXX交換,其他rm, rs, re(可能要換成pc)都不用變就可以完成了。
ARM的基本型別大小
char 1 byte 叫Byte
short 2 byte 叫Halfword
int 4 byte 叫Word
對應這三種型別的load, store指令也不同
LDR Word
LDRH Halfword
LDRB Byte
Branch
跳躍是經由修改pc來達成的,可以用
BX|BL Rm
跳到Rm所指內容來執行
Offset Addressing
[ Rm, #+/-
這是代表以Rm爲Base+/-immed_12的地址內容,
** 因爲12 bit的限制,有時把正數轉爲負數使用,例如要取得pc+FFFFF0E0的內容,由於FFFFF0E0 > FFF,所以用pc-F20來代替
literal_pool
一個在Code區域的資料區段,用來放const.這個節區的位置和使用
pool裏資料的命令,距離(offset)不得超過4k (Thumb=1k).
奇怪的符號
1. #<+/->數字,代表constant。 如#123, #-123,如果要表示16進制,用#0x123,#-0x123或是#&123, #-&123, 也就是說#後的&符號可以用來代表16進制符號。
2. 小逗點‘ . ’和其他的assembly一樣,一個小點‘ . ’代表compile時的“目前地址”,可以用來作運算。
3. ‘ [ ’ 代表IF ,‘ | ’ 代表ELSE, ‘ ] ’ 代表ENDIF。
4. ‘ ! ’ 代表INFO
5. RN是用來將register重新命名用, sqr RN r6 意指 :sqr就是r6
6. A,L,S字尾分別代表arithmetic,logical,string。如SETA,SETL,SETS分別代表設定arighmetic, logical, string變數。
7. ADS的Assembler的Operator都是以‘ : ’作開頭和結尾,像 :MOD:代表Mode,:AND:代表AND,:CC:代表Concatenate,其他Operator說明在3-24。
8. ‘ % ’ 代表SPACE。如data1 % 255代表在data1開始預留255個byte的空間並把資料清爲0。
9. ‘ ^ ’代表MAP
10. ‘ # ’ 代表FIELD
11. ‘ = ’代表DCB 。 c_string DCB “C_string”,0
12. ‘ & ’代表DCD
13. ‘ * ’代表EQU
14. a1-a4 (argument) = r0-r3
15. v1-v8 (variable) = r4-r11
16. sb (static base) = r9
17. sl (stack limit) = r10
18. fp (frame pointer) = r11
19. ip (inter-procedure-call scratch register) = r12
Remap的動作
ARM CPU的ROM/RAM CS都有Remap的功能。這個功能要加上pipeline的特性才能正常動作。作Remap時,先把CS的map設好,接下來一定要接著作branch 的動作。因為有pipeline的關係,所以在把CS設新的值時,不會馬上就access到新的memory region,因為pipeline中還有一些命令還沒執行。做完remap後要馬上作branch,這樣cpu會將pipeline清空,再由新的位址 填入指令pipeline,這時候就會將新的memory region的instruction填入。
Software Stack Check
當Compiler Enable SWST,Compiler會使用r10作為stack limit variable (al),紀錄program的stack limit。並在有需要使用stack的function,在進入時比較r13(sp)和r10(sl)。
程式啟動的狀態
程式有兩種啟動狀況:(i)ROM Start。(ii)ICE RAM Start。差異是在Remap之前或是之後。如果是ROM Start,表示Remap動作還沒作,在Remap之前要把RAM的Interrupt Vector Table準備好。如果是ICE RAM Start,代表已經Remap過,就不需要準備RAM的Interrupt Vector Table。啟動程式(init.s)說明:
Vector_Table
Check Execution Address (ROM or RAM)
ROM : Copy Vector_Table to RAM
Remap
Setup Mode Stacks
Jump __main
l Project Foler:Standalone/StartRemap
型態轉換
int, short,signed, unsigned之間的轉換
ADS在作一切運算時,都是以int運作。因為ARM的特殊結構,function argument的傳遞都是以int為size。所以即使argument是short。在作passing argument時,ADS也會先將var作alrithematical signed extend( lsl 16, asr 16),再傳入。
如果是signed時,會以argment size (不是真正的size)的highest bit為signed。
void saveshort(short);
int vara;
saveshort(vara)
mov r1,vara;
mov r0,r1,lsl,16
mov r0,r0,asr,16 //雖然argument是short,還是以int的方式傳入
bl saveshort //但確保value範圍是short
如果是unsigned時,會將多餘的部分mark為0
void saveunsigned short(unsigned short)
int vara
saveunsignedshort(vara)
mov r1,vara;
mov r0,r1,lsl,16
mov r0,r0,lsr,16 //argumrnt是unsigned short,所以把所有 high
bl saveunsignedshort //word都清除為0再傳傳入`
傳回值
型別轉換
沒有留言:
張貼留言