cortex m0启动代码详解
轉(zhuǎn)自:http://www.cnblogs.com/mddblog/p/4920063.html
?
閱讀目錄
- 概述
- 1.堆棧空間定義
- 2.存放中斷向量表
- 3. 復(fù)位中斷函數(shù)(Reset_Handler)
- 4.其它中斷異常服務(wù)函數(shù),以及弱[WEAK]聲明
- 5.將堆棧地址傳遞給庫函數(shù)
原文在此:http://www.cnblogs.com/mddblog/p/4920063.html
概述
在嵌入式系統(tǒng)中,啟動文件是整個系統(tǒng)非常關(guān)鍵的部分,它會進行一些底層的初始化,構(gòu)建程序運行必要的環(huán)境,比如堆棧初始化,變量初始化等。如果啟動文件出現(xiàn)錯誤,則整個系統(tǒng)就跑不起來,因此研究啟動文件非常必要。
在keil中,啟動文件由匯編代碼編寫,一般命名為startup_xxx.s,xxx為支持的某種芯片,比如可以是lpc15xx(NXP的LPC15xx系列)、MK60D10(飛思卡爾)、stm32f10x(意法半導(dǎo)體stm32f10x系列)等Cortext-M0/M3/M4內(nèi)核芯片。它們的代碼格式非常相近,根據(jù)啟動文件代碼由上到下的編寫順序.
可以將其分為以下5個典型部分:
1.堆棧空間定義;
2.存放中斷向量表;
3.復(fù)位中斷函數(shù)(Reset_Handler);
4.其它中斷異常服務(wù)函數(shù),以及弱[WEAK]聲明;
5.將堆棧地址傳遞給庫函數(shù),利用庫函數(shù)初始化堆棧,和庫函數(shù)自身初始化。
5個部分具體說明如下:
1.堆棧空間定義
如下圖所示,定義了棧大小Stack_Size = 0X200,即512字節(jié);堆大小Heap_Size = 0X100,256字節(jié)。還定義了三個標號:__initial_sp(棧頂)、__heap_base(堆起始地址)和__heap_limit(堆終止地址),它們的空間由SPACE關(guān)鍵字來申請,并記作Stack_Mem和Heap_Mem。
通過這些我們可以很容易的知道堆棧的大小,但是它們的絕對地址或者說基地址僅僅從這里是得不到的。編譯器編譯完工程后,根據(jù)生成.bss段(比如未初始化的全局變量)和.data段(比如初始化的全局變量)的大小以及RAM的起始地址,來計算堆棧的基地址。
舉個例子:
一個芯片的RAM起始地址為0x0200_0000,RAM大小為0x500字節(jié),程序編譯后.bss段為0x100個字節(jié),.data段為0x100個字節(jié)。堆棧大小定義如上圖。則:
A:堆起始地址??__heap_base==Heap_Mem==0x0200_0200;
B:堆終止地址即棧底 __heap_limit==Stack_Mem==0x0200_0300;
C:棧頂?shù)刂?__initial_sp==0x0200_0500(棧是向下生長,棧頂處于RAM最大地址處)。
? 其實,我可以在.map文件中查看堆棧的大小和基地址,如下圖所示:
2.存放中斷向量表
在啟動代碼中,會見到許多由DCD申請空間存放的一個個函數(shù)入口,即中斷向量表,如下圖所示,只列出了部分。
關(guān)鍵字DCD代表申請一個字的空間,后面的函數(shù)名即為中斷服務(wù)函數(shù)入口地址。另外中斷向量表一般存放在Flash 0地址。
?
? 另外,對于NXP微控制器,均實現(xiàn)了芯片的加密,加密的設(shè)置在向量表的結(jié)尾處,具體地址為0x02FC處。通過在此地址存放不同的值實現(xiàn)是否加密或者加密的等級。加密分為三個等級,CRP1:0x12345678;CRP2:0x87654321;CRP3:0x43218765。至于每個等級的具體說明請參考芯片用戶手冊。下面說一下加密步驟,以CRP1為例:
首先將下圖中0xFFFFFFFF,修改為0x12345678。
其次,圖中IF? ? :LNOT::DEF:NOCRP表示如果沒有定義宏NOCRP則執(zhí)行下面的代碼,那么必須保證匯編中沒有定義NOCRP宏。即保證下圖中Define:一欄中沒有定義NOCRP即可。
3. 復(fù)位中斷函數(shù)(Reset_Handler)
程序上電后,首先加載SP和PC,ARM規(guī)定從0地址處加載SP,從偏移為4的地址(0x00000004)處加載PC。然后將程序控制權(quán)交給程序。我們知道0地址處存放__initial_sp,0x00000004地址處存放Reset_Handler,加載PC后,程序跳轉(zhuǎn)到Reset_Handler開始運行。Reset_Handler函數(shù)體如下圖所示:
首先調(diào)用SystemInit函數(shù)來初始化系統(tǒng)的各種時鐘,然后調(diào)用__main函數(shù)(由KEIL微庫或者C庫實現(xiàn)),在__main函數(shù)中:.data段數(shù)據(jù)的初始化->.bss段變量清零->設(shè)置堆棧指針->庫函數(shù)初始化(比如常用的malloc函數(shù))->如果必要會設(shè)置main函數(shù)的argc和argv兩個參數(shù)->調(diào)用用戶main函數(shù)->退出。
4.其它中斷異常服務(wù)函數(shù),以及弱[WEAK]聲明
如上圖所示,這里的中斷服務(wù)函數(shù)是弱聲明的(由[WEAK]關(guān)鍵字標注)。所謂弱聲明,即:如果用戶定義了相同的函數(shù)則此處函數(shù)失效而使用用戶定義的中斷服務(wù)函數(shù)。這樣是為了防止用戶使能了中斷而沒有中斷服務(wù)函數(shù),從而造成程序崩潰。假設(shè)使能了中斷,而用戶又沒有定義中斷服務(wù)函數(shù)則會進入默認中斷,如下圖所示,默認中斷為死循環(huán)(死循環(huán)與程序崩潰不是一個概念)。
5.將堆棧地址傳遞給庫函數(shù)
第三步驟中,調(diào)用__main函數(shù),然后__main調(diào)用庫函數(shù)初始化堆棧,但庫函數(shù)并不知道堆棧的大小,因此我們需要告訴它,具體做法就是傳遞參數(shù)或聲明標號。
? 下圖為具體做法,可以看到第一行為:
IF? ?? ?:DEF:__MICROLIB
是條件編譯選項,如果定義__MICROLIB,則編譯圖中紅線上面部分,否則編譯紅線下面部分。那么就分2種情況。
2種情況的選擇可以如下實現(xiàn):
? ?? ? 如果勾選【Options for Target】->【Target】->【Use MicroLIB】,如下圖所示。即使用微庫,則__MICROLIB會被定義,編譯器編譯紅線以上代碼。用EXPORT聲明 __initial_sp、__heap_base和__heap_limit。
?如果不勾選【Use MicroLIB】,則缺省使用KEIL C庫,上圖紅線以下會參與編譯,KEIL C庫函數(shù)會調(diào)用__user_initial_stackheap,通過R0~R3將堆棧以參數(shù)形式傳遞給KEIL C庫。
轉(zhuǎn)載于:https://www.cnblogs.com/dwj411024/p/7679455.html
總結(jié)
以上是生活随笔為你收集整理的cortex m0启动代码详解的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 一件很好笑的事情
- 下一篇: eventBus 与fragment