從事了這麼久的ARM開發,對entry point的含義一知半解,今日再次拿出一本寫得還算不錯的ARM教材來翻,書中這一段僅僅是把英文手冊單純翻譯成中文,並沒有講到真正的實質。不是ARM公司手冊寫得不詳細,而是多數中文教材的編寫採用的方式是: english--華文 mapping,但英文手冊是有系列組織的suite,而書本往往翻譯自系列中的一篇,容易使得讀者管中窺豹。
摘要:ARM link的時候-entry 『行標號』 選項用於指定程序的入口地址,其實就是ICE把代碼load完之後自動跳入的地址,最後的程序中這個工作是由bootloader來完成的,當然bootloader也可以跳到別的入口地址去。幾乎所有的成品ARM晶片都有內置的bootloader,因此開發者看到的程序入口點就是entry point。對於FPGA上的ARM系統調試,ARM復位後跳轉到一個固化地址,就需要自己寫bootloader代碼並通過scatter加載到rom中(我們的EAS片上存儲器包括rom在內全部用FPGA的ram實現,tape out的時候bootloader代碼會被固化到chip的rom中)
1.entry point在編譯中的指定
看了make file,entry point的指定是在link的時候用命令行參數的方式告訴編譯器的,
armlink ... -Entry reset_handler
如果採用RVDS或ADS,工程屬性中有entry point選項可供設置,但最終還是作為參數傳遞給armlink。
這樣, 在連接ICE並loadimage之後,ICE會通過JTAG把代碼映像搬移到內存中,然後把當前PC指針指向reset_handler,但引出了一個問題:在最終的產品中不存在ICE和JTAG,ARM上電復位後跳向固化的復位地址,如0xFFFF0000,那麼由誰來讓PC指針指向我們指定的entry point呢?答案是:由bootloader代碼來完成,進一步引出2個問題:1,誰來生成bootloader?2,怎麼把bootloader定位到硬體復位地址?
2. 自己寫bootloader並通過scatter文件將之定位到硬體復位地址。
或許某些開發工具會自動的為我們生成bootloader,這裡介紹的是自力更生的方法。
首先介紹bootloader,這裡僅實現中斷向跳轉:
AREA Vect, CO
ENTRY ;這個entry和本文中的entry point是沒有關係的,它表示的是彙編程序代碼部分的開始
LDR PC, Reset_Addr
LDR PC, Undefined_Addr
LDR PC, SWI_Addr
LDR PC, Prefetch_Addr
LDR PC, Abort_Addr
NOP ; Reserved vector
LDR PC, IRQ_Addr
LDR PC, FIQ_Addr
Reset_Addr DCD Reset_Handler
Undefined_Addr DCD Undefined_Handler
SWI_Addr DCD SWI_Handler
Prefetch_Addr DCD Prefetch_Handler
Abort_Addr DCD Abort_Handler
IRQ_Addr DCD IRQ_Handler
FIQ_Addr DCD FIQ_Handler
Reset_Handler
LDR PC, =0x00000000;
然後是scatter文件:
ROM_LOAD 0x00000000
{
ROM_EXEC 0x00000000
{
* (InRoot$$Sections); 實現搬移scatter的代碼,編譯器自動生成
*(+RO) ; 除了bootloader的所有代碼段放這裡
}
D-TCM 0x00400000 0x003FFFFF
{
* (+RW,+ZI);這裡放所有的RW段(char ch=4;)和ZI段(char ch; ch會被初始化零)。
}
}
ROM2 0xFFFF0000
{
BOOTLOAD 0xFFFF0000
{
bootloader.o(+RO) ;bootloader的程序段放這裡
}
}
scatter也是在link的時候通過參數『-scatter 文件名』傳遞的。
3.總結
在以上條件下,比較使用ICE和最終產品中的啟動順序:當使用ICE調試時,ICE讀入映像文件,找到其中的scatter信息,通過JTAG把代碼段和數據段放到指定的位置,然後把PC設為-entry所指定的標號,這就是為什麼當我們loadimage之後看到的是在entry point中指定的行號所在的原始碼。當脫離ICE運行時,ARM上電後跳轉到硬體復位地址0xFFF0000 (ROM),我們已事先在那裡燒入了我們的bootloader,bootloder從外設中讀入加載映像文件(如片外nand,I2C,UART等)並放入scatter的加載域中(0x00000000),然後跳轉到0x000000,由於我們已在scatter中將InRoot$$Sections定位在那裡,所以它被執行,它負責把映像文件從加載域搬移到執行域中,然後跳轉到__main,__main是arm編譯器自動生成的,它負責清零ZI段和初始化C庫,最後__main跳向C代碼的入口main()