新手如何設(shè)計和看懂ARM的啟動代碼?
基于ARM的芯片多數(shù)為復(fù)雜的片上系統(tǒng)。這種復(fù)雜系統(tǒng)里的多數(shù)硬件模塊都是可配置的。需要由軟件來設(shè)置其需要的工作狀態(tài)。因此在用戶的應(yīng)用程序之前,需要由專門的一段代碼來完成對系統(tǒng)的初始化。由于這類代碼直接面對處理器內(nèi)核和硬件控制器進(jìn)行編程,一般都是用匯編語言。
啟動代碼是芯片復(fù)位后進(jìn)入C語言的main()函數(shù)前執(zhí)行的一段代碼,主要是為運行C語言程序提供基本運行環(huán)境,如初始化存儲器系統(tǒng)等。ARM公司只設(shè)計內(nèi)核,不自己生產(chǎn)芯片,只是把內(nèi)核授權(quán)給其它廠商,其它廠商購買了授權(quán)且加入自己的外設(shè)后生產(chǎn)出各具特色的芯片。這樣就促進(jìn)了基于ARM處理器核的芯片多元化,但也使得每一種芯片的啟動代碼差別很大,不易編寫出統(tǒng)一的啟動代碼。ADS(針對ARM處理器核的C語言編譯器)的策略是不提供完整的啟動代碼,啟動代碼不足部分或者由廠商提供,或者自己編寫。
ARM的啟動代碼類似于電腦中的BIOS,它從系統(tǒng)上電開始接管CPU,依次需要負(fù)責(zé)初始化 CPU在各種模式下的堆??臻g、設(shè)定CPU的內(nèi)存映射、對系統(tǒng)的各種控制寄存器做初始化、對CPU的外部存儲器進(jìn)行初始化、設(shè)定各外圍設(shè)備的基地址、創(chuàng)建正確的中斷向量表、為C代碼執(zhí)行創(chuàng)建ZI(零創(chuàng)建)區(qū),然后進(jìn)入到C代碼。 在C代碼中繼續(xù)對時鐘、RS232端口進(jìn)行初始化,然后打開系統(tǒng)中斷允許位。最后進(jìn)入到應(yīng)用代碼中執(zhí)行,執(zhí)行期間響應(yīng)各種不同的中斷信號并調(diào)用預(yù)先設(shè)置好的中斷服務(wù)程序處理這些中斷。
一般ARM的啟動代碼中通用的內(nèi)容包括:
中斷向量表
初始化存儲器系統(tǒng)
初始化堆棧
初始化有特殊要求的端口,設(shè)備
初始化用戶程序執(zhí)行環(huán)境
改變處理器模式
呼叫主應(yīng)用程序
以stm32的啟動文件為例,stm32的啟動文件一般都是包含在具體單片機(jī)型號的匯編文件中(.s文件),下圖為啟動文件的簡述(description)
和我們預(yù)想的差不多,該啟動文件主要包含了初始化主堆棧指針(MSP)、初始化程序指針(PC)、初始化中斷向量表、配置系統(tǒng)時鐘和外部Sram(可選)、跳轉(zhuǎn)到main函數(shù)
第一部分配置堆和棧的大小(Stack_size Heap_size)
Stack_Size EQU 0x400
AREA STACK, NOINIT, READWRITE, ALIGN=3
Stack_Mem SPACE Stack_Size
__initial_sp
AREA STACK, NOINIT, READWRITE, ALIGN=3 ;定義棧,可初始為0,8字節(jié)對齊 (堆代碼類似相同功能)
Stack_Mem SPACE Stack_Size ;分配0x400個連續(xù)字節(jié),并初始化為0 (堆代碼類似相同功能)
__initial_sp ;匯編代碼地址標(biāo)號 (堆代碼類似相同功能)
PRESERVE8 ;指定當(dāng)前文件堆棧8字節(jié)對齊
THUMB ;告訴匯編器下面是32為的Thumb指令,如果需要匯編器將插入位以保證對齊
第二部分定義中斷向量表
; Vector Table Mapped to Address 0 at Reset
AREA RESET, DATA, READONLY
EXPORT __Vectors
EXPORT __Vectors_End
EXPORT __Vectors_Size
__Vectors DCD __initial_sp ; Top of Stack
DCD Reset_Handler ; Reset Handler
DCD NMI_Handler ; NMI Handler
DCD HardFault_Handler ; Hard Fault Handler
DCD MemManage_Handler ; MPU Fault Handler
DCD BusFault_Handler ; Bus Fault Handler
DCD UsageFault_Handler ; Usage Fault Handler
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD SVC_Handler ; SVCall Handler
DCD DebugMon_Handler ; Debug Monitor Handler
DCD 0 ; Reserved
DCD PendSV_Handler ; PendSV Handler
DCD SysTick_Handler ; SysTick Handler
AREA RESET, DATA, READONLY ;定義復(fù)位向量段,只讀
EXPORT __Vectors ;定義一個可以在其他文件中使用的全局標(biāo)號。此處表示中斷地址
__Vectors DCD __initial_sp ; 給__initial_sp分配4字節(jié)32位的地址0x0
DCD Reset_Handler ; 給標(biāo)號Reset Handler分配地址為0x00000004
DCD NMI_Handler ; 給標(biāo)號NMI Handler分配地址0x00000008
DCD HardFault_Handler ; Hard Fault Handler
DCD ……
__Vectors_End
第三部分Reset_Handler 及假異常處理程序的定義
AREA |.text|, CODE, READONLY ;代碼段定義為只讀
Reset_Handler PROC
EXPORT Reset_Handler [WEAK]
IMPORT SystemInit
IMPORT __main
LDR R0, =SystemInit //把SystemInit 的地址加載到寄存器R0。
BLX R0 //程序跳轉(zhuǎn)到R0 中的地址執(zhí)行程序
LDR R0, =__main //把_main 的地址加載到寄存器R0
BX R0 //程序跳轉(zhuǎn)到R0 中的地址執(zhí)行程序,執(zhí)行完畢之后就去到我們熟知的C 世界。
ENDP //表示子程序的結(jié)束