先看圖如下:這是arm的啟動過程。
先對啟動前的硬體描述一下,這樣在分析startup.s的時候就輕鬆多了。我使用的是smartarm2200開發板,其中CPU晶片使用的LPC2210,沒有內部flash(周公太摳門了)。因此說到設定也就是兩點:
一,BOOT[1:0]的設定。決定是從內部Memory還是外部Memory啟動。面對摳門的周公,咱只能從外部儲存空間啟動了。無奈啊!這也就引發了第二個擴充儲存空間的設定。
二,bank0、bank1的設定。要是用於調試的話就把ram設成bank0,可以在ram啟動,這樣可以很容易進行儲存空間重新對應。要是燒flash的話就把flash設成bank0。
因此,我們在流程圖中的路線是始終在最右邊走。哦,原來是從0x80000000啟動啊!
接下來就開始進入代碼了!首先看是整體幾個檔案的關係圖。
剛才看到運行首地址是0x80000000,那麼怎麼安排我們的flash或者ram?記得在單片機中經常實用org 0x800方式就能搞定,我們在ads中也可以設定RO、RW、ZI和進入點地址一額可以辦到。但這隻能應用一些很簡單的情況,現在面臨的要複雜一點,因而“分散載入方式”閃亮登場。這裡不妨總結一下ARM連結方式:
(1)simple方式:(三點設定)分別是,Output下:RO(代碼基址) RW(資料基址包括ZI)。Layout下:Object/Symble填入startup.o Section填入start(根據實際情況定)Option下:Image Entry Pointer填入地址。
(2)scattered方式:(也要三步)分別是,編寫.scf檔案;在output下填入該檔案;Option下填入入口地址。
下面分析.scf檔案,以mem_b.scf為例。
/************** mem_b.scf檔案 *************/
ROM_LOAD 0x80000000 //名 地址 範圍限制(本例子預設)
{
ROM_EXEC 0x80000000 //片外儲存空間 地址 範圍大小(本例預設)
{
Startup.o (vectors, +First) //startup檔案中vector段放於開頭
* (+RO) //其他模組的代碼與唯讀資料放於此
}
IRAM 0x40000000 //片內RAM 地址 範圍大小(預設)
{
Startup.o (MyStacks) //startup檔案中MyStacks段放於開頭
}
STACKS_BOTTOM +0 UNINIT //棧底 地址 不用初始化 範圍大小限制(預設)
{
Startup.o (StackBottom) //startup檔案中MyStackBottom段放於開頭
}
STACKS 0x40004000 UNINIT
{
Startup.o (Stacks)
}
ERAM 0x80040000
{
* (+RW,+ZI) //所有的RW與ZI資料置於此,也就是未初始化和初始化的全域變數
}
HEAP +0 UNINIT
{
Startup.o (Heap)
}
HEAP_BOTTOM 0x80080000 UNINIT
{
Startup.o (HeapTop)
}
}
可以用檔案的方式觀察記憶體的實際格局,在ARM Linker的Listings選項卡下,選中Image Map複選框。這樣編譯的時候就會顯示記憶體格局了。
Memory Map of the image
Image Entry point : 0x80000000
Load Region ROM_LOAD (Base: 0x80000000, Size: 0x000005b4, Max: 0xffffffff, ABSOLUTE)
Execution Region ROM_EXEC (Base: 0x80000000, Size: 0x000005b4, Max: 0xffffffff, ABSOLUTE)
Base Addr Size Type Attr Idx E Section Name Object
0x80000000 0x00000110 Code RO 1 * vectors Startup.o
0x80000110 0x000000a8 Code RO 53 * !!! __main.o(c_a__un.l)
0x800001b8 0x000000dc Code RO 10 .text target.o
0x80000294 0x00000078 Code RO 42 .text main.o
0x8000030c 0x00000008 Code RO 55 .text _no_redirect.o(c_a__un.l)
0x80000314 0x000000a4 Code RO 57 .text stkheap2.o(c_a__un.l)
0x800003b8 0x00000004 Code RO 59 .text use_no_semi.o(c_a__un.l)
0x800003bc 0x00000028 Code RO 61 .text kernel.o(c_a__un.l)
0x800003e4 0x0000000c Code RO 63 .text libspace.o(c_a__un.l)
0x800003f0 0x00000018 Code RO 66 .text exit.o(c_a__un.l)
0x80000408 0x000000fc Code RO 68 .text lib_init.o(c_a__un.l)
0x80000504 0x00000010 Code RO 72 .text rt_fp_status_addr.o(c_a__un.l)
0x80000514 0x00000014 Code RO 70 x$fpl$fpinit fpinit.o(f_a_p.l)
0x80000528 0x00000020 Data RO 43 .constdata main.o
0x80000548 0x00000054 Data RO 74 Region$$Table anon$$obj.o
0x8000059c 0x00000018 Data RO 75 ZISection$$Table anon$$obj.o
Execution Region IRAM (Base: 0x40000000, Size: 0x00000400, Max: 0xffffffff, ABSOLUTE)
Base Addr Size Type Attr Idx E Section Name Object
0x40000000 0x00000400 Zero RW 2 MyStacks Startup.o
Execution Region STACKS_BOTTOM (Base: 0x40000400, Size: 0x00000004, Max: 0xffffffff, ABSOLUTE, UNINIT)
Base Addr Size Type Attr Idx E Section Name Object
0x40000400 0x00000004 Zero RW 4 StackBottom Startup.o
Execution Region STACKS (Base: 0x40004000, Size: 0x00000000, Max: 0xffffffff, ABSOLUTE, UNINIT)
Base Addr Size Type Attr Idx E Section Name Object
0x40004000 0x00000000 Zero RW 6 Stacks Startup.o
Execution Region ERAM (Base: 0x80040000, Size: 0x00000060, Max: 0xffffffff, ABSOLUTE)
Base Addr Size Type Attr Idx E Section Name Object
0x80040000 0x00000060 Zero RW 64 .bss libspace.o(c_a__un.l)
Execution Region HEAP (Base: 0x80040060, Size: 0x00000004, Max: 0xffffffff, ABSOLUTE, UNINIT)
Base Addr Size Type Attr Idx E Section Name Object
0x80040060 0x00000004 Zero RW 3 Heap Startup.o
Execution Region HEAP_BOTTOM (Base: 0x80080000, Size: 0x00000000, Max: 0xffffffff, ABSOLUTE, UNINIT)
Base Addr Size Type Attr Idx E Section Name Object
0x80080000 0x00000000 Zero RW 5 HeapTop Startup.o
Load Region LR$$Debug (Base: 0x00000000, Size: 0x00000000, Max: 0xffffffff, ABSOLUTE)
Execution Region ER$$Debug (Base: 0x00000000, Size: 0x00000000, Max: 0xffffffff, ABSOLUTE)
Base Addr Size Type Attr Idx E Section Name Object
0x00000000 0x00000010 Dbug RW 9 .debug_abbrev Startup.o
0x00000010 0x000003ec Dbug RW 19 .debug_abbrev target.o
0x00000000 0x0000014c Dbug RW 18 .debug_frame target.o
0x0000014c 0x00000058 Dbug RW 52 .debug_frame main.o
0x000001a4 0x0000003c Dbug RW 54 .debug_frame __main.o(c_a__un.l)
0x000001e0 0x0000004c Dbug RW 56 .debug_frame _no_redirect.o(c_a__un.l)
0x0000022c 0x00000094 Dbug RW 58 .debug_frame stkheap2.o(c_a__un.l)
0x000002c0 0x00000044 Dbug RW 60 .debug_frame use_no_semi.o(c_a__un.l)
0x00000304 0x00000058 Dbug RW 62 .debug_frame kernel.o(c_a__un.l)
0x0000035c 0x00000044 Dbug RW 65 .debug_frame libspace.o(c_a__un.l)
0x000003a0 0x0000004c Dbug RW 67 .debug_frame exit.o(c_a__un.l)
0x000003ec 0x0000007c Dbug RW 69 .debug_frame lib_init.o(c_a__un.l)
0x00000468 0x0000004c Dbug RW 71 .debug_frame fpinit.o(f_a_p.l)
0x000004b4 0x0000004c Dbug RW 73 .debug_frame rt_fp_status_addr.o(c_a__un.l)
0x00000000 0x00000074 Dbug RW 7 .debug_info Startup.o
0x00000074 0x0000007c Dbug RW 12 .debug_info target.o
0x000000f0 0x00000108 Dbug RW 35 .debug_info target.o
0x000001f8 0x000000e4 Dbug RW 31 .debug_info target.o
0x000002dc 0x00000540 Dbug RW 15 .debug_info target.o
0x0000081c 0x00000110 Dbug RW 27 .debug_info target.o
0x0000092c 0x00000090 Dbug RW 45 .debug_info main.o
0x000009bc 0x00000134 Dbug RW 49 .debug_info main.o
0x00000000 0x0000009c Dbug RW 8 .debug_line Startup.o
0x0000009c 0x000000b8 Dbug RW 11 .debug_line target.o
0x00000154 0x000000f8 Dbug RW 14 .debug_line target.o
0x0000024c 0x00000064 Dbug RW 26 .debug_line target.o
0x000002b0 0x00000050 Dbug RW 30 .debug_line target.o
0x00000300 0x00000050 Dbug RW 34 .debug_line target.o
0x00000350 0x00000078 Dbug RW 44 .debug_line main.o
0x000003c8 0x000000a4 Dbug RW 48 .debug_line main.o
0x00000000 0x000002c0 Dbug RW 17 .debug_loc target.o
0x000002c0 0x00000064 Dbug RW 51 .debug_loc main.o
0x00000000 0x00000190 Dbug RW 13 .debug_macinfo target.o
0x00000190 0x00000078 Dbug RW 29 .debug_macinfo target.o
0x00000208 0x0000004c Dbug RW 33 .debug_macinfo target.o
0x00000254 0x000001f0 Dbug RW 37 .debug_macinfo target.o
0x00000444 0x00000190 Dbug RW 47 .debug_macinfo main.o
0x00000000 0x00000128 Dbug RW 16 .debug_pubnames target.o
0x00000128 0x00000064 Dbug RW 28 .debug_pubnames target.o
0x0000018c 0x00000058 Dbug RW 32 .debug_pubnames target.o
0x000001e4 0x00000084 Dbug RW 36 .debug_pubnames target.o
0x00000268 0x00000020 Dbug RW 46 .debug_pubnames main.o
0x00000288 0x00000028 Dbug RW 50 .debug_pubnames main.o
================================================================================
Image component sizes
Code RO Data RW Data ZI Data Debug
612 140 0 1032 8356 Object Totals
708 0 0 96 860 Library Totals
================================================================================
Code RO Data RW Data ZI Data Debug
1320 140 0 1128 9216 Grand Totals
================================================================================
Total RO Size(Code + RO Data) 1460 ( 1.43kB)
Total RW Size(RW Data + ZI Data) 1128 ( 1.10kB)
Total ROM Size(Code + RO Data + RW Data) 1460 ( 1.43kB)
================================================================================
****************************************************************************
一般的可執行程式都包括程式碼片段、資料區段。也可以簡單的看作由兩部分組成:RO段和RW段。RO段一般包括程式碼片段和一些常量,在啟動並執行時候是唯讀。而RW段包括一些全域變數和靜態變數,在啟動並執行時候是可以改變的(讀寫)。如果有部分全域變數被初始化為零,則RW段裡還包括了ZI段。
RO: Read Only 程式碼片段
RW: Read Write 已初始化的全域變數
ZI: Zero Init 未初始化的全域變數
因為RO段是唯讀,在啟動並執行時候不可以改變,所以,在啟動並執行時候,RO段可以駐留在Flash裡(當然也可以在SDRAM或者SRAM裡了)。而RW段是可以讀寫的,所以,在啟動並執行時候必須被裝載到SDRAM或者SRAM裡。
在用ADS編譯的時候,是需要設定RO BASE 和RW BASE的,用過ADS的應該都清楚這點。通過RO BASE 和RW BASE的設定,告訴連結器(linker)該程式的起始運行地址(RO BASE)和 RW段的地址 (RW BASE)。如果一個程式只有RO段,沒有RW段,那麼這個程式可以完全在Flash裡運行,不需要用到SDRAM 或者 SRAM。如果包括RW段和RO段,那麼該程式的RW段必須在被訪問以前被拷貝到SDRAM 或者SRAM裡去,以保證程式可以正確運行。下面這個圖說明了一個程式執行前(load view)和執行時(execute view)的狀態。可以看到,整個程式在執行前始放在ROM裡的,在執行的時候,RW段被拷貝到了RAM裡的合適位置去。
程式一開始總是儲存在ROM/Flash裡面的,其RO部分既可以在ROM/Flash裡面執行,也可以轉移到速度更快的RAM中去;而RW和ZI這兩部分是必須轉移到可寫的RAM裡去。所謂應用程式執行環境的初始化,就是完成必要的從ROM到RAM的資料轉送和內容清零。
不同的工具鏈會提供一些不同的機制和方法協助使用者完成這一步操作,主要是跟連結器(Linker)相關。下面是在ARM開發工具環境ADS下,一種常用儲存空間模型的直接實現:
LDR r0, = |Image$$RO$$Limit| ;得到RO段末的下一位元組的地址 ,ROM中的RW的開始地址
LDR r1, = |Image$$RW$$Base| ;得到RAM中的RW段的初始地址
LDR r3, = |Image$$ZI$$Base| ;全域變數的初始地址
CMP r0, r1 ;
BEQ LOOP1
LOOP0
CMP r1, r3 ; 是否到RAM中的RW段的末地址,如果沒到,則一直將ROM(FLASH變 量與資料區段拷貝到RAM中
LDRCC r2, [r0], #4 ;[R0]=[R1]
STRCC r2, [r1], #4 ;
BCC LOOP0
LOOP1
LDR r1, = |Image$$ZI$$Limit| ; LOOP1與LOOP2執行將ZI初始化為0
MOV r2, #0
LOOP2
CMP r3, r1
STRCC r2, [r3], #4 ;
BCC LOOP2
在ADS裡,有一些預先定義了的變數可以用(linker defined symbol)。在下面的實現裡,用到了幾個預定義的變數:
Image$$RO$$Base 該變數指定了RO段的 BASE
Image$$RO$$Limit 該變數指定了RO段的 Limit
Image$$RW$$Base 該變數指定了RW段的 BASE
Image$$RW$$Limit 該變數指定了RW段的 Limit
Image$$ZI$$Base 該變數指定了ZI段的 BASE
Image$$ZI$$Limit 該變數指定了ZI段的 Limit
註:具體可以參考ADS Linker Guide
Image$$RO$$Limit 減 Image$$RO$$Base 等於RO段的大小
Image$$RW$$Limit 減 Image$$RW$$Base 等於RW段的大小
Image$$ZI$$Limit 減 Image$$ZI$$Base 等於ZI段的大小
(Image$$RO$$Limit 減 Image$$RO$$Base)
+ (Image$$RW$$Limit 減 Image$$RW$$Base)
= 等於整個程式的大小
註:ZI段始包括在RW段裡面的。