下面以startup_LPC17xx.s為例,簡單看一下CMSIS中的啟動代碼完成了哪些工作。
主要完成了三項工作:
- 堆棧以及堆的初始化;
- 定位中斷向量表;
- 調用 Reset Handler。
1.1 堆棧以及堆的初始化
1.1.1 堆棧的初始化
程序清單 1.1 startup_LPC17xx.s 中的堆棧初始化代碼
Stack_Size EQU 0x00000200
AREA STACK, NOINIT, READWRITE, ALIGN=3
Stack_Mem SPACE Stack_Size
__initial_sp
Stack_Size EQU 0x00000200,這個語句相當于 Stack_Size 這個标号(标号:鍊接器的術語,下文中提到的所有“标号”,指的都是鍊接器中的标号)等于 0x00000200,相當于 C 語言中的#define Stack_Size 0x00000200,也就是說此語句隻是一個聲明,并未分配地址。
AREA STACK, NOINIT, READWRITE, ALIGN=3,此語句定義了一個名叫 STACK 的代碼段,并指明 8 字節對齊(ALIGN=3)。
Stack_Mem SPACE Stack_Size,為 Stack_Mem 分配 Stack_Size 大小的一塊内存區域,注意這裡分配的是 RAM。
__initial_sp此标号有一層隐含的意思那就是在 M3 中堆棧是滿遞減堆棧,因為它指定了堆棧指針位于堆棧的高地址(在 Stack_Mem 之後),具體如下圖所示。
圖 1.1 堆棧指針 SP 位置
上圖來自一個 LPC1700 工程的.map 文件。可以看出棧的起始地址為 0x10001f70,大小為512 字節(即 0x00000200 = Stack_Size)。而堆棧指針的位置在 0x10002170,其等于棧的起始地址 0x10001f70 0x00000200,說明 LPC1700 系列的 Cortex-M3 微控制器的堆棧為滿遞減堆棧。
1.1.2 堆的初始化
程序清單 1.2 startup_LPC17xx.s 中的堆初始化代碼
Heap_Size EQU 0x00000200
AREA HEAP, NOINIT, READWRITE, ALIGN=3
__heap_base
Heap_Mem SPACE Heap_Size
__heap_limit
具體過程與棧的初始化相同。
1.2 中斷向量表的初始化
程序清單 1.3 中斷向量表的初始化代碼
PRESERVE8
THUMB
; Vector Table Mapped to Address 0 at Reset
AREA RESET, DATA, READONLY
EXPORT __Vectors
__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
PRESERVE8 指定了以下的代碼位 8 字節對齊,這是 keil 編譯器的一個編程要求,對齊情況如下圖所示:
圖 1.2 .list 文件中的 8 字節對齊示意圖
THUMB 指定了接下來的代碼為 THUMB 指令集。
AREA RESET, DATA, READONLY,此語句聲明 RESET 數據段。
EXPORT __Vectors,導出向量表标号,EXPORT 作用類似于 C 語言中的 extern。之後的代碼就是為向量表分配存儲區域。
1.3 調用 Reset Handler
程序清單 1.4 調用 Reset Handler 的代碼
; Reset Handler
Reset_Handler PROC
EXPORT Reset_Handler [WEAK]
IMPORT SystemInit ;
IMPORT __main
LDR R0, =SystemInit;
BLX R0 ;
LDR R0, =__main
BX R0
ENDP
此段代碼隻完成了一個功能,引導程序進入__main。 __main 的具體行為在第三章中有具體的描述。
1.4 其他的代碼
程序清單 1.5 CRP 加密級别
IF :LNOT::DEF:NO_CRP
AREA |.ARM.__at_0x02FC|, CODE, READONLY
CRP_Key DCD 0xFFFFFFFF
ENDIF
此段代碼指定了接下來的代碼存儲與 0x02FC 的地址,具體情況如下圖所示。這段代碼是 NXP 公司的 LPC1700 系列的 MCU 特有的一段代碼,其他公司的 Cortex-M3 MCU 的啟動程序是沒有這段代碼的。
這段代碼是指定 LPC1700 的 CRP 加密級别的代碼段,芯片上電後會自動讀取這一地址的值以确定加密方式,其中 CRP_Key = 0xffffffff 為不加密(0 級加密), CRP_Key = 0x12345678為 1 級加密, CRP_Key = 0x87654321 為 2 級加密, CRP_Key = 0x43218765 為 3 級加密(最高級加密), 3 級加密将會禁止所有的 ISP 指令,也就是說,芯片将不能讀寫、不能擦除。
程序清單1.6 具體的堆棧以及堆的初始化行為
; User Initial Stack & Heap
IF :DEF:__MICROLIB
EXPORT __initial_sp
EXPORT __heap_base
EXPORT __heap_limit
ELSE
IMPORT __use_two_region_memory
EXPORT __user_initial_stackheap
__user_initial_stackheap
LDR R0, = Heap_Mem
LDR R1, =(Stack_Mem Stack_Size)
LDR R2, = (Heap_Mem Heap_Size)
LDR R3, = Stack_Mem
BX LR
ALIGN
ENDIF