IAR下的匯編/單片機啟動代碼匯編
1、
IAR匯編指令SFB和SFE
SFB Segment begin 段開始
語法格式
SFB(segment [{+|-} offset])
參數(shù)
segment: 可重定位段的段名, 必須在SFB使用前已定義
offset : 從開始地址的偏移, 是一個可選參數(shù), 當(dāng)偏移量省略時, 可以不添加小括號
描述
SFB 右邊可以接受一個操作數(shù), 而且這個操作數(shù)必須是一個可重位段的段名.
這個操作符計算段的首字節(jié)地址. 這個操作發(fā)生在連接時.
例
NAME demo
RSEG CODE
start: DC16 SFB(CODE)
即使上面的代碼和多個其他的模塊進(jìn)行連接, start標(biāo)號處仍被置為段的首字節(jié)地址
語法格式
SFE (segment [{+|-} offset])
參數(shù)
segment: 可重定位段的段名, 必須在SFB使用前已定義
offset : 從開始地址的偏移, 是一個可選參數(shù), 當(dāng)偏移量省略時, 可以不添加小括號
描述
SFE在其右邊接收一個操作數(shù). 操作數(shù)必須是一個可重定位段的段名. SFE操作符將段起始地址和段大小相加. 這個操作在連接時發(fā)生.
SFE accepts a single operand to its right. The operand must be the name of a relocatable segment. The operator evaluates to the segment start address plus the segment size. This evaluation takes place at linking time.
例
NAME demo
RSEG CODE
end: DC16 SFE(CODE)
即使當(dāng)上面的代碼被和多個模塊想連接時, end標(biāo)號仍然會被置為段最后一個字節(jié)的地址. Even if the above code is linked with many other modules, end will still be set to the address of the last byte of the segment.
段MY_SEGMENT的大小可以通過以下方式計算而得:
SFE(MY_SEGMENT)-SFB(MY_SEGMENT)
--------------------------------------------------------------------------------
arm中的幾種跳轉(zhuǎn)
arm匯編的跳轉(zhuǎn)指令無非是b和ldr。但是如果沒有足夠理解,別人靈活的用一下你就犯暈了。
首先我們要知道兩者的兩個本質(zhì)區(qū)別:
1、b是位置無關(guān)的,ldr不是位置無關(guān)的。
2、b的范圍只能是+—32MB,而ldr是4GB。
在arm的啟動匯編的中斷向量表是必然用跳轉(zhuǎn)指令的,但是就是這里也有很多實現(xiàn)形式:
方式1:
B InitReset ; 0x00 Reset handler
undefvec:
B undefvec ; 0x04 Undefined Instruction
swivec:
B swivec ; 0x08 Software Interrupt
pabtvec:
B pabtvec ; 0x0C Prefetch Abort
dabtvec:
B dabtvec ; 0x10 Data Abort
rsvdvec:
B rsvdvec ; 0x14 reserved
irqvec:
B IRQ_Handler_Entry ; 0x18 IRQ
這個我很容易理解,實現(xiàn)的標(biāo)號油InitReset和IRQ_Handler_Entry,其他沒有實現(xiàn)的在原地跳轉(zhuǎn)。
方式二:
LDR pc, =resetHandler ; Reset
LDR pc, Undefined_Addr ; Undefined instructions
LDR pc, SWI_Addr ; Software interrupt (SWI/SYS)
LDR pc, Prefetch_Addr ; Prefetch abort
LDR pc, Abort_Addr ; Data abort
B . ; RESERVED
LDR pc, =irqHandler ; IRQ
LDR pc, FIQ_Addr ; FIQ
LDR PC,[PC,#0x18]
Undefined_Addr: DCD Undefined_Handler
SWI_Addr: DCD SWI_Handler
Prefetch_Addr: DCD Prefetch_Handler
Abort_Addr: DCD Abort_Handler
FIQ_Addr: DCD FIQ_Handler
我們注意到兩種ldr
一種LDR PC,=label,這時把LDR當(dāng)做偽指令,他要被翻譯成:
LDR PC,[PC,offset_to_label2]
label2:DCD resetHandler
這種label是在程序中標(biāo)記的了,如resetHandler和irqHandler
還有一種LDR PC,label,這時直接把label地址內(nèi)存內(nèi)容copy到PC中
這種label都是 label:DCD label2 ,這些label2可以在任何地方實現(xiàn)
我們來理解b和ldr兩者的不同
1,b是位置無關(guān),因為他的跳轉(zhuǎn)都是相對PC來的,而ldr不是位置無關(guān)的,因為他的跳轉(zhuǎn)是根據(jù)DCD里面的值,這個值是連接的時候確定的,他是跟連接地址有關(guān)的
2,b的范圍只能是32M,是因為指令里面給偏移的空間只有24bit,而ldr是一個32bit的DCD,所以他是32bit的
注意:像上面方式二
LDR pc, =resetHandler ; Reset
。
。
。
resetHandler:
....
....
如果運行地址是0,那么LDR pc, =resetHandler還在以基址為0的空間運行
但是執(zhí)行完了,假設(shè)裝載地址是0x3000000,所以resetHandler的基址不是0,而是0x30000000
所以resetHandler標(biāo)號以后的指令應(yīng)該存在以0x3000000為基址的空間,pc跳過去了
這種技巧在at91的remap模式經(jīng)常用到
--------------------------------------------------------------------------------
理解啟動代碼(ADS) 所謂啟動代碼,就是處理器在啟動的時候執(zhí)行的一段代碼,主要任務(wù)是初始化處理器模式,設(shè)置堆棧,初始化變量等等.由于以上的操作均與處理器體系結(jié)構(gòu)和系統(tǒng)配置密切相關(guān),所以一般由匯編來編寫. 具體到S64,啟動代碼分成兩部分,一是與ARM7TDMI內(nèi)核相關(guān)的部分,包括處理器各異常向量的配置,各處理器模式的堆棧設(shè)置,如有必要,復(fù)制向量到RAM,以便remap之后處理器正確處理異常,初始化數(shù)據(jù)(包括RW與ZI),最后跳轉(zhuǎn)到Main.二是與處理器外部設(shè)備相關(guān)的部分,這和廠商的聯(lián)系比較大.雖然都采用了ARM7TDMI的內(nèi)核,但是不同的廠家整合了不同的片上外設(shè),需要不同的初始化,其中比較重要的是初始化WDT,初始化各子系統(tǒng)時鐘,有必要的話,進(jìn)行remap.這一部分與一般控制器的初始化類似,因此,本文不作重點描述. 在進(jìn)行分析之前,請確認(rèn)如下相關(guān)概念: S64片上FLASH起始于0x100000,共64kB,片上RAM起始于0x200000,共16kB. S64復(fù)位之后,程序會從0開始執(zhí)行,此時FLASH被映射到0地址,因此,S64可以取得指令并執(zhí)行.顯然,此時還是駐留在0x100000地址.如果使用remap命令,將會把RAM映射到0地址,同樣的這時0地址的內(nèi)容也只是RAM的鏡像. S64的FLASH可以保證在最差情況時以30MHz進(jìn)行單周期訪問,而RAM可以保證在最大速度時的單周期訪問. OK,以下開始分析啟動代碼. 一,處理器異常 S64將異常向量至于0地址開始的幾個直接,這些是必需要處理的.由于復(fù)位向量位于0,也需要一條跳轉(zhuǎn)指令.具體代碼如下: RESET B SYSINIT ; Reset B UDFHANDLER ; UNDEFINED B SWIHANDLER ; SWI B PABTHANDLER ; PREFETCH ABORT B DABTHANDLER ; DATA ABORT B . ; RESERVED B VECTORED_IRQ_HANDLER B . ; ADD FIQ CODE HERE UDFHANDLER B . SWIHANDLER B . PABTHANDLER B . DABTHANDLER B . 請注意,B指令經(jīng)匯編后會替換為當(dāng)前PC值加上一個修正值(+/-),所以這條指令是代碼位置無關(guān)的,也就是不管這條指令是在0地址還是在0x100000執(zhí)行,都能跳轉(zhuǎn)到指定的位置,而LDR PC,=???將向PC直接裝載一個標(biāo)號的值,請注意,標(biāo)號在編譯過后將被替換為一個與RO相對應(yīng)的值,也就是說,這樣的指令無論在哪里執(zhí)行,都只會跳轉(zhuǎn)到一個指定的位置.下面舉一個具體的例子來說明兩者的區(qū)別: 假定有如下程序: RESET B INIT 或者 LDR PC,=INIT … INIT … 其中RESET為起始時的代碼,也就是這條代碼的偏移為0,設(shè)INIT的偏移量為offset.如果將這段程序按照RO=0x1000000編譯, 那么B INIT可理解為ADD PC, PC, #offset,而LDR PC,=INIT可被理解為 MOV PC,#(RO+offset) .顯然當(dāng)系統(tǒng)復(fù)位時,程序從0開始運行,而0地址有FLASH的副本,執(zhí)行B INIT將把PC指向位于0地址處的鏡像代碼位置,也即INIT;如果執(zhí)行LDR PC,=INIT將會將PC直接指向位于FLASH中的原始代碼.因此以上兩者都能正確運行.下面將RO設(shè)置為0x200000,編譯后生成代碼,還是得燒寫到FLASH中,也就是還是0x100000,系統(tǒng)復(fù)位后從0地址執(zhí)行,還是FLASH的副本,此時執(zhí)行B INIT,將跳到副本中的INIT位置執(zhí)行,此處有對應(yīng)的代碼;但是如果執(zhí)行LDR PC,=INIT,將向PC加載0x200000+offset,這將使得PC跳到RAM中,而此時由于代碼沒有復(fù)制,RAM中的指定位置并沒有代碼,程序無法運行. 二,處理器模式 ARM的處理器可工作于多種模式,不同模式有不同的堆棧 ,以下設(shè)置各模式及其堆棧. 預(yù)定義一些參數(shù): MODUSR EQU 0x10 MODSYS EQU 0x1F MODSVC EQU 0x13 MODABT EQU 0x17 MODUDF EQU 0x1B MODIRQ EQU 0x12 MODFIQ EQU 0x11 IRQBIT EQU 0x80 FIQBIT EQU 0x40 RAMEND EQU 0x00204000 ; S64 : 16KB RAM VECTSIZE EQU 0x100 ; UsrStkSz EQU 8 ; size of USR stack SysStkSz EQU 128 ; size of SYS stack SvcStkSz EQU 8 ; size of SVC stack UdfStkSz EQU 8 ; size of UDF stack AbtStkSz EQU 8 ; size of ABT stack IrqStkSz EQU 128 ; size of IRQ stack FiqStkSz EQU 16 ; size of FIQ stack 修改這些值即可修改相應(yīng)模式堆棧的尺寸. 以下為各模式代碼: SYSINIT ; MRS R0,CPSR BIC R0,R0,#0x1F MOV R2,#RAMEND ORR R1,R0,#(MODSVC :OR: IRQBIT :OR: FIQBIT) MSR cpsr_cxsf,R1 ; ENTER SVC MODE MOV sp,R2 SUB R2,R2,#SvcStkSz ORR R1,R0,#(MODFIQ :OR: IRQBIT :OR: FIQBIT) MSR CPSR_cxsf,R1 ; ENTER FIQ MODE MOV sp,R2 SUB R2,R2,#FiqStkSz ORR R1,R0,#(MODIRQ :OR: IRQBIT :OR: FIQBIT) MSR CPSR_cxsf,R1 ; ENTER IRQ MODE MOV sp,R2 SUB R2,R2,#IrqStkSz ORR R1,R0,#(MODUDF :OR: IRQBIT :OR: FIQBIT) MSR CPSR_cxsf,R1 ; ENTER UDF MODE MOV sp,R2 SUB R2,R2,#UdfStkSz ORR R1,R0,#(MODABT :OR: IRQBIT :OR: FIQBIT) MSR CPSR_cxsf,R1 ; ENTER ABT MODE MOV sp,R2 SUB R2,R2,#AbtStkSz ;ORR R1,R0,#(MODUSR :OR: IRQBIT :OR: FIQBIT) ;MSR CPSR_cxsf,R1 ; ENTER USR MODE ;MOV sp,R2 ;SUB R2,R2,#UsrStkSz ORR R1,R0,#(MODSYS :OR: IRQBIT :OR: FIQBIT) MSR CPSR_cxsf,R1 ; ENTER SYS MODE MOV sp,R2 ; 三,初始化變量 編譯完成之后,連接器會生成三個基本的段,分別是RO,RW,ZI,并會在image中順序擺放.顯然,RW,ZI在運行開始時并不位于指定的RW位置,因此必須初始化 LDR R0,=|Image$$RO$$Limit| LDR R1,=|Image$$RW$$Base| LDR R2,=|Image$$ZI$$Base| 1 CMP R1,R2 LDRLO R3,[R0],#4 STRLO R3,[R1],#4 BLO ? MOV R3,#0 LDR R1,=|Image$$ZI$$Limit| 2 CMP R2,R1 STRLO R3,[R2],#4 BLO ? 四,復(fù)制異常向量 由于代碼于RAM運行時,有明顯的速度優(yōu)勢,而且變量可以動態(tài)配置,因此可以通過remap將RAM映射到0,使得出現(xiàn)異常時ARM從RAM中取得向量. IMPORT |Image$$RO$$Base| IMPORT |Image$$RO$$Limit| IMPORT |Image$$RW$$Base| IMPORT |Image$$RW$$Limit| IMPORT |Image$$ZI$$Base| IMPORT |Image$$ZI$$Limit| COPY_VECT_TO_RAM LDR R0,=|Image$$RO$$Base| LDR R1,=SYSINIT LDR R2,=0x200000 ; RAM START 0 CMP R0,R1 LDRLO R3,[R0],#4 STRLO R3,[R2],#4 BLO ? 這段程序?qū)YSINIT之前的代碼,也就是異常處理函數(shù),全部復(fù)制到RAM中, 這就意味著不能將RW設(shè)置為0x200000,這樣會使得向量被沖掉. 四,在RAM中運行 如果有必要,且代碼足夠小,可以將代碼置于RAM中運行,由于RAM中本身沒有代碼,就需要將代碼復(fù)制到RAM中: COPY_BEGIN LDR R0,=0x200000 LDR R1,=RESET ; =|Image$$RO$$Base| CMP R1,R0 ; BLO COPY_END ; ADR R0,RESET ADR R2,COPY_END SUB R0,R2,R0 ADD R1,R1,R0 LDR R3,=|Image$$RO$$Limit| 3 CMP R1,R3 LDRLO R4,[R2],#4 STRLO R4,[R1],#4 BLO ? LDR PC,=COPY_END COPY_END 程序首先取得RESET的連接地址,判斷程序是否時是在RAM中運行,方法是與RAM起始地址比較,如果小于,那么就跳過代碼復(fù)制. 在復(fù)制代碼的時候需要注意,在這段程序結(jié)束之前的代碼沒有必要復(fù)制,因為這些代碼都已經(jīng)執(zhí)行過了,所以,先取得COPY_END,作為復(fù)制起始地址,然后計算其相對RESET的偏移,然后以RO的值加上這個偏移,就是復(fù)制目的地的起始地址,然后開始復(fù)制. 五,開始主程序 以上步驟完成,就可以跳轉(zhuǎn)到main運行 IMPORT Main LDR PC,=Main B . 六,器件初始化 主程序首先要進(jìn)行器件的初始化,對S64而言,應(yīng)該先初始化WDT,因為默認(rèn)情況下,WDT是打開的,然后是各設(shè)備的時鐘分配,最后應(yīng)該remap
--------------------------------------------------------------------------------
RemapSRAM:
MOV R0, #0x40000000 //RAM 區(qū)首地址
LDR R1, =Vectors //向量表首地址
#下面一段程序是把從0x00000000 開始的64 個字節(jié)(FLASH 中的中斷向量表和地址表)搬移到以
#0x40000000 為首地址的RAM 區(qū)中
LDMIA R1!, {R2-R9} //把以[R1]為首地址的32 個字節(jié)數(shù)據(jù)裝載到R2-R9 中
STMIA R0!, {R2-R9} //把R2-R9 中的數(shù)據(jù)存入以[R0]為首地址的單元中
LDMIA R1!, {R2-R9} //把以[R1]為首地址的32 個字節(jié)數(shù)據(jù)裝載到R2-R9 中
STMIA R0!, {R2-R9} ////把R2-R9 中的數(shù)據(jù)存入以[R0]為首地址的單元中
--------------------------------------------------------------------------------
ARM的啟動代碼分析
相信使用過MDK的朋友都會發(fā)現(xiàn),在新建任意一個ARM芯片的工程時,MDK開發(fā)環(huán)境都會自動的在代碼中加入一個Startup.s的文件。
大家不能小看這個代碼的功能了,這個代碼中就是對應(yīng)芯片的啟動代碼了。
本文就以在MDK開發(fā)環(huán)境下對于LPC2103的啟動代碼分析了。
在MDK開發(fā)環(huán)境下,系統(tǒng)復(fù)位后,都是會將ARM芯片切換到系統(tǒng)模式下工作。啟動代碼會在系統(tǒng)復(fù)位后立即執(zhí)行,其功能如下:
1、定義中斷和異常向量。
2、配置CPU時鐘源(對于一些設(shè)備來說)。
3、使用內(nèi)存重映射命令拷貝ROM中的異常向量到RAM中(例如:Atmel)。
4、初始化外部總線控制器和執(zhí)行內(nèi)存重映射(如果有必要)。
5、如果有必要,初始化其它外設(shè)。
6、為所有的模式預(yù)留和初始化堆棧。
7、清零數(shù)據(jù)初始化(僅針對GNU)。
8、跳轉(zhuǎn)到C語言函數(shù)執(zhí)行。
本文只是在探討第一個步驟(定義中斷和異常向量)。因為這個將會在之后我們要講到的在uC/OS-II中進(jìn)行中斷函數(shù)的編寫有關(guān)系,其他步驟,如果大家有興趣,可以到網(wǎng)上去查閱相關(guān)資料。
下面代碼就是我在MDK中,拷貝出來的一段初始化異常向量的代碼了,詳細(xì)代碼如下:
AREA
RESET, CODE, READONLY
ARM
Vectors
LDRPC, Reset_Addr
LDRPC, Undef_Addr
LDRPC, SWI_Addr
LDRPC, PAbt_Addr
LDRPC, DAbt_Addr
NOP
;LDRPC, IRQ_Addr
LDRPC, [PC, #-0x0FF0]
LDRPC, FIQ_Addr
Reset_Addr DCD Reset_Handler
Undef_Addr DCD Undef_Handler
SWI_Addr DCD SWI_Handler
PAbt_Addr DCD PAbt_Handler
DAbt_Addr DCD DAbt_Handler
DCD 0
IRQ_Addr DCD IRQ_Handler
FIQ_Addr DCD FIQ_Handler
Undef_Handler B Undef_Handler
SWI_Handler B SWI_Handler
PAbt_Handler B PAbt_Handler
DAbt_Handler B DAbt_Handler
IRQ_Handler B IRQ_Handler
FIQ_Handler B FIQ_Handler
下面就進(jìn)行一步一步代碼分析:
1、
AREA
RESET, CODE, READONLY
ARM
這兩段話表達(dá)的意思是:在代碼段中定義一個段名為RESET的只讀段,該代碼段中的代碼都在ARM指令集下進(jìn)行。
2、Vectors
LDR PC, Reset_Addr
LDR PC, Undef_Addr
LDR PC, SWI_Addr
LDR PC, PAbt_Addr
LDR PC, DAbt_Addr
NOP
;LDR PC, IRQ_Addr
LDR PC, [PC, #-0x0FF0]
LDR PC, FIQ_Addr
上面的代碼是ARM內(nèi)核定義的異常像量表
異常類型
異常向量地址
該地址中的內(nèi)容
復(fù)位
0x00000000(或0xFFFF0000)
LDR PC, Reset_Addr
未定義異常
0x00000004(或0xFFFF0004)
LDR PC, Undef_Addr
軟件中斷
0x00000008(或0xFFFF0008)
LDR PC, SWI_Addr
指令預(yù)取異常
0x0000000C(或0xFFFF000C)
LDR PC, PAbt_Addr
數(shù)據(jù)預(yù)取異常
0x00000010(或0xFFFF0010)
LDR PC, DAbt_Addr
預(yù)留
0x00000014(或0xFFFF0014)
NOP
IRQ中斷
0x00000018(或0xFFFF0018)
LDR PC, [PC, #-0x0FF0]
FIQ中斷
0x0000001C(或0xFFFF001C)
LDR PC, FIQ_Addr
大家注意一下,在中斷向量地址中必須保存一條ARM指令集。在對應(yīng)的異常向量地址中將跳轉(zhuǎn)到對應(yīng)的異常處理程序。
當(dāng)然大家在這里也可以使用B指令,但是大家要注意一下,使用LDR指令可以實現(xiàn)±4GB地址空間實現(xiàn)。但是使用B執(zhí)行跳轉(zhuǎn)的話,就只能在±32M地址空間。所以建議大家使用LDR指令實現(xiàn)跳轉(zhuǎn)。
關(guān)鍵詞:
IAR匯編單片機啟動代
相關(guān)推薦
技術(shù)專區(qū)
- FPGA
- DSP
- MCU
- 示波器
- 步進(jìn)電機
- Zigbee
- LabVIEW
- Arduino
- RFID
- NFC
- STM32
- Protel
- GPS
- MSP430
- Multisim
- 濾波器
- CAN總線
- 開關(guān)電源
- 單片機
- PCB
- USB
- ARM
- CPLD
- 連接器
- MEMS
- CMOS
- MIPS
- EMC
- EDA
- ROM
- 陀螺儀
- VHDL
- 比較器
- Verilog
- 穩(wěn)壓電源
- RAM
- AVR
- 傳感器
- 可控硅
- IGBT
- 嵌入式開發(fā)
- 逆變器
- Quartus
- RS-232
- Cyclone
- 電位器
- 電機控制
- 藍(lán)牙
- PLC
- PWM
- 汽車電子
- 轉(zhuǎn)換器
- 電源管理
- 信號放大器
評論