星期四, 2月 17, 2005

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代表每一個sectionReadonly sectionData sectionZero 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 _, Rm

可以是CPSR或是SPSR

可以決定是要mov哪幾個byte(C,S,F)

例如:

MSR CPSR_f, 0x80000 ; 設定flag field

MSR CPSR_f, R0 ; 依照r0的內容update CPSRflag byte的內容

MRS將CPSR的資料般出來

MRS R0, CPSR ; CPSR的資料copyR0

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{}{B} Rd, [Rn, ]{!}

對應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] r1base address

LDR r0,[r1,#2] ; r0=mem32[r1+2] r1base 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,從rers

LDMFD rm!,{rs-re} ; 取出一個DataSP + 4,從rsre

所以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的地址內容, 指Range = 000~FFF的數值

** 因爲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 SWSTCompiler會使用r10作為stack limit variable (al),紀錄programstack limit。並在有需要使用stackfunction,在進入時比較r13(sp)r10(sl)

程式啟動的狀態

程式有兩種啟動狀況:(i)ROM Start(ii)ICE RAM Start。差異是在Remap之前或是之後。如果是ROM Start,表示Remap動作還沒作,在Remap之前要把RAMInterrupt Vector Table準備好。如果是ICE RAM Start,代表已經Remap過,就不需要準備RAMInterrupt 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 FolerStandalone/StartRemap

型態轉換

int, short,signed, unsigned之間的轉換

ADS在作一切運算時,都是以int運作。因為ARM的特殊結構,function argument的傳遞都是以intsize。所以即使argumentshort。在作passing argument時,ADS也會先將varalrithematical signed extend( lsl 16, asr 16),再傳入。

如果是signed時,會以argment size (不是真正的size)highest bitsigned

void saveshort(short);

int vara;

saveshort(vara)

mov r1,vara;

mov r0,r1,lsl,16

mov r0,r0,asr,16 //雖然argumentshort,還是以int的方式傳入

bl saveshort //但確保value範圍是short

如果是unsigned時,會將多餘的部分mark0

void saveunsigned short(unsigned short)

int vara

saveunsignedshort(vara)

mov r1,vara;

mov r0,r1,lsl,16

mov r0,r0,lsr,16 //argumrntunsigned short,所以把所有 high

bl saveunsignedshort //word都清除為0再傳傳入`

傳回值

型別轉換

沒有留言:

網誌存檔