MPC8560 平台是freescale公司是PowerpcQUICC II的下一代通訊處理器。它為網絡和通訊外設提高了強大的計算能力,進而提高了整個系統的吞吐量和性能。它的主要架構是由一個高性能的e500的核和一個 通訊處理器子產品(CPM)組成. e500的核實作了powerpc BOOK E的ISA。
u-boot代碼分析
按照e500的架構描述,MPC8560啟動時,會執行0xFFFF_FFFC位址的指令,這裡存放的是u-boot代碼中的cpu/mpc85xx/resetvec.S一條跳轉指令,
如下:
.section .resetvec,"ax" //定義一個段resetvec
b _start_e500 //跳轉到_start_e500
而在連結腳本 board/pq37pc/pq37pc_8560/u-boot.lds中,将resetvec這個段放到了0xffff_fffc這個位址
28 SECTIONS
29 {
30 .resetvec 0xFFFFFFFC :
31 {
32 *(.resetvec)
33 } = 0xffff
34
35 .bootpg 0xFFFFF000 :
36 {
37 cpu/mpc85xx/start.o (.bootpg)
38 board/pq37pc/pq37pc_8560/init.o (.bootpg)
39 } = 0xffff
40
41 /* Read-only sections, merged into text segment: */
42 . = + SIZEOF_HEADERS;
43 .interp : { *(.interp) }
44 .hash : { *(.hash) }
45 .dynsym : { *(.dynsym) }
46 .dynstr : { *(.dynstr) }
47 .rel.text : { *(.rel.text) }
48 .rela.text : { *(.rela.text) }
49 .rel.data : { *(.rel.data) }
50 .rela.data : { *(.rela.data) }
51 .rel.rodata : { *(.rel.rodata) }
52 .rela.rodata : { *(.rela.rodata) }
53 .rel.got : { *(.rel.got) }
54 .rela.got : { *(.rela.got) }
55 .rel.ctors : { *(.rel.ctors) }
56 .rela.ctors : { *(.rela.ctors) }
57 .rel.dtors : { *(.rel.dtors) }
................
................
}
是以,CPU上電複位後,将跳轉到_start_e500執行。
在cpu/mpc85xx/start.S中:
33 #include <....>
...............
51
52 /*
53 * Set up GOT: Global Offset Table
54 *
55 * Use r14 to access the GOT
56 */
57 START_GOT
58 GOT_ENTRY(_GOT2_TABLE_)
59 GOT_ENTRY(_FIXUP_TABLE_)
60
61 GOT_ENTRY(_start)
62 GOT_ENTRY(_start_of_vectors)
63 GOT_ENTRY(_end_of_vectors)
64 GOT_ENTRY(transfer_to_handler)
65
66 GOT_ENTRY(__init_end)
67 GOT_ENTRY(_end)
68 GOT_ENTRY(__bss_start)
69 END_GOT
70
**********************************************************************************************************************************************************************************
附注:關于START_GOT, GOT_ENTRY, END_GOT宏 **********************************************************************************************************************************************************************************
相關的宏在./include/ppc_asm.tmpl中定義: #define START_GOT /
.section ".got2","aw"; /
.LCTOC1 = .+32768
#define END_GOT /
.text
#define GET_GOT /
bl 1f ; /
.text 2 ; /
0: .long .LCTOC1-1f ; /
.text ; /
1: mflr r14 ; /
lwz r0,0b-1b(r14) ; /
add r14,r0,r14 ;
#define GOT_ENTRY(NAME) .L_ ## NAME = . - .LCTOC1 ; .long NAME
#define GOT(NAME) .L_ ## NAME (r14)
總體來說: START_GOT ——用于定義表的開始 END_GOT ——用于定義表的結束 GOT_ENTRY——用于将offset寫入表中 GOT ——用于從表中讀出 offset GET_GOT ——用于将表進行初始化 下面詳細解釋之: START_GOT定義了段“got2”,aw屬性為“allocatable and writable”,并定義了變量.LCTOC1,.LCTOC1的值是表的最高位址。如果設表的起始位址為TABLE_START,則.LCTOC1的 值為TABLE_START+0x8000。
END_GOT定義為子段text 0的開始。
GOT_ENTRY定義了變量.L_NAME,其值為目前表項的位址(.)-.LCTOC1。如果設NAME的表項偏移位址為 NAME_OFFSET,那麼.L_NAME = . - .LCTOC1 = TABLE_START + NAME_OFFSET - ( TABLE_START + 0x8000 ) = NAME_OFFSET - 0x8000。之後将名字為NAME的offset值寫入目前表項,這些offset值是在編譯的時候确定的。
GOT(NAME)的值定義為.L_NAME(r14),這裡面r14的值為表的最高位址,也就是.LCTOC1的值(參見下面關于 GET_GOT的說明)。這樣GOT(NAME) = .L_NAME + r14 = .L_NAME + .LCTOC1 = NAME_OFFSET - 0x8000 + TABLE_START + 0x8000 = NAME_OFFSET + TABLE_START,也就是NAME所在表項的位址。這樣,通過查表,就可以找到當初存儲在表中的名字為NAME的offset值。
GET_GOT用于初始化GOT表。首先程式跳轉到标号為“1”的位址處(bl 1f),然後将lr的值指派給r14(此時lr的值為“1”的位址值)。然後另r0 = 0b - 1b(r14),0b為“0”處的位址值,1b為“1”處的位址值。這樣r0就等于“0”處的值,也就是.LCTOC1-1f。最後r14 = r0 + r14 = .LCTOC1 - 1f + 1f = .LCTOC1,也就是等于GOT表的最高位址。
**************************************************************************************************************************************************************************************
繼續看start.S中_start_e500,這個函數比較長,我們撿取主要部分分析:
上電之後,MMU隻配置了4k空間,在這4k空間内必須初步配置MMU,使MMU能夠映射u-boot真實的有效位址空間。
71 /*
72 * e500 Startup -- after reset only the last 4KB of the effective
73 * address space is mapped in the MMU L2 TLB1 Entry0. The .bootpg
74 * section is located at THIS LAST page and basically does three
75 * things: clear some registers, set up exception tables and
76 * add more TLB entries for 'larger spaces'(e.g. the boot rom) to
77 * continue the boot procedure.
78
79 * Once the boot rom is mapped by TLB entries we can proceed
80 * with normal startup.
81 *
82 */
83
84 .section .bootpg,"ax"
85 .globl _start_e500
86
87 _start_e500:
88 mfspr r0, PVR
89 lis r1, [email protected]
90 ori r1, r1, [email protected]
91 cmpw r0, r1
92 bne 1f //條件向下跳到标号1處執行
93
94 /* Semi-bogus errata fixup for Rev 1 */
95 li r0,0x2000
96 mtspr 977,r0
97
98 /*
99 * Before invalidating MMU L1/L2, read TLB1 Entry 0 and then
100 * write it back immediately to fixup a Rev 1 bug (Errata CPU4)
101 * for this initial TLB1 entry 0, otherwise the TLB1 entry 0
102 * will be invalidated (incorrectly).
103 */
104 lis r2,0x1000
105 mtspr MAS0,r2
106 tlbre
107 tlbwe
108 isync
109
110 1:
111 /*
112 * Clear and set up some registers.
113 * Note: Some registers need strict synchronization by
114 * sync/mbar/msync/isync when being "mtspr".
115 * BookE: isync before PID,tlbivax,tlbwe
116 * BookE: isync after MSR,PID; msync_isync after tlbivax & tlbwe
117 * E500: msync,isync before L1CSR0
118 * E500: isync after BBEAR,BBTAR,BUCSR,DBCR0,DBCR1,HID0,HID1,
119 * L1CSR0, L1CSR1, MAS[0,1,2,3,4,6],MMUCSR0, PID[0,1,2],
120 * SPEFCSR
121 */
122
123 /* invalidate d-cache */
131 /* disable d-cache */ 禁止資料cache
135 141 禁止指令cache
146 185 /* Setup interrupt vectors */ 建立中斷向量表
222 /* Invalidate MMU L1/L2*/ 禁止MMU
231 /* Invalidate all TLB0 entries.*/ 禁止TLB0
237 /*
238 * To avoid REV1 Errata CPU6 issues, make sure
239 * the instruction following tlbivax is not a store.
240 */
241
242 /*
243 * After reset, CCSRBAR is located at CFG_CCSRBAR_DEFAULT, i.e.
244 * 0xff700000-0xff800000. We need add a TLB1 entry for this 1MB
245 * region before we can access any CCSR registers such as L2
246 * registers, Local Access Registers,etc. We will also re-allocate
247 * CFG_CCSRBAR_DEFAULT to CFG_CCSRBAR immediately after TLB1 setup.
248 *
249 * Please refer to board-specif directory for TLB1 entry configuration.
250 * (e.g. board/<yourboard>/init.S)
251 *
252 */
299 /* set up local access windows, defined at board/<boardname>/init.S */
300 lis r7,[email protected]
301 ori r7,r7,[email protected]
302
303 bl law_entry
326 b _start //最後跳轉到_start
_start函數将做一些基本寄存器的配置,初始化堆棧等,最後會調到以下函數:
bl cpu_init_f //對CPU做一些初始化,對BR和BO做一些預初始化
bl icache_enable // 使能指令cache
bl board_init_f
cpu_init_f函數
該函數是系統執行的第一個C語言的函數,主要是做一些CPU 寄存器的初始化,其中最重要的部分是初始化Local Access Windows的值、Local Bus上的片選BR,OR的值和配置MMU的LTB1、LTB0。這些值需要在/include/configs/PQ37PC_8560.h中配置好。
board_init_f函數
該函數為闆級初始化的第一個函數,會對闆子上很多硬體外設做初始化,其中最重要的為init_sequence數組,該數組裡面包含了很多闆級的硬體初始化函數,在board_init_f函數中會依次的調用該數組中的函數去初始化各個硬體,可以看到時鐘,序列槽,控制台,記憶體等初始化函數的調用。此時代碼仍在ROM中運作。
void board_init_f (ulong bootflag)
{
/* Pointer is writable since we allocated a register for it */
gd = (gd_t *) (CFG_INIT_RAM_ADDR + CFG_GBL_DATA_OFFSET);
/* compiler optimization barrier needed for GCC >= 3.4 */
__asm__ __volatile__("": : :"memory");
//初始化gd結構體
//調用init_sequence數組中的函數
for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr)
{
if ((*init_fnc_ptr) () != 0)
{
hang ();
}
}
len = (ulong)&_end - CFG_MONITOR_BASE;
addr = CFG_SDRAM_BASE + gd->ram_size;//top of ram
/* round down to next 4 kB limit ,對齊準則*/
addr &= ~(4096 - 1);
debug ("Top of RAM usable for U-Boot at: %08lx\n", addr);
/* 在RAM的末端為核心日志緩沖區、保護RAM、LCD 幀緩沖區、monitor code和bd結構體預留白間,注意資料對齊*/
addr -= len;
addr &= ~(4096 - 1);
debug ("Reserving %ldk for U-Boot at: %08lx\n", len >> 10, addr);
/*為malloc預留白間 */
addr_sp = addr - TOTAL_MALLOC_LEN;
debug ("Reserving %dk for malloc() at: %08lx\n",TOTAL_MALLOC_LEN >> 10, addr_sp);
/*為bd_t和gd_t預留白間*/
addr_sp -= sizeof (bd_t);
bd = (bd_t *) addr_sp;
gd->bd = bd;
debug ("Reserving %d Bytes for Board Info at: %08lx\n", sizeof (bd_t), addr_sp);
addr_sp -= sizeof (gd_t);
id = (gd_t *) addr_sp;
debug ("Reserving %d Bytes for Global Data at: %08lx\n",sizeof (gd_t), addr_sp);
/*增大棧空間:與SP之間預留16bytes的空間,注意資料對齊, Clear initial stack frame */
addr_sp -= 16;
addr_sp &= ~0xF;
s = (ulong *)addr_sp;
*s-- = 0;
*s-- = 0;
addr_sp = (ulong)s;
debug ("Stack Pointer at: %08lx\n", addr_sp);
/*将局部變量儲存到bd_t和gd_t中*/
bd->*=....;
debug ("New Stack Pointer is: %08lx\n", addr_sp);
memcpy (id, (void *)gd, sizeof (gd_t));
//将flash中的uboot代碼,資料及bss搬運到ram
relocate_code (addr_sp, id, addr);
/* NOTREACHED - relocate_code() does not return */
}
relocate_code函數
到目前為止,boot代碼都是在Flash中運作,但是代碼最終是要到RAM中運作的,在上面的board_init_f函數中已經将RAM初始化好了,具備了在RAM中運作程式的能力,現在relocate_code函數需要做兩個事情:
1)從Flash中拷貝u-boot的代碼到RAM;
2)記下現在執行代碼的偏移,跳轉到RAM中相應的位置執行。
relocate_code重新調回到彙編代碼中執行一些操作後,調用board_init_r
board_init_r函數
該函數為闆級初始化的第二階段,主要是初始化PCI,PCIE,網口,Flash等裝置,關閉看門狗,把前面配置設定給dcache做堆棧的空間解鎖,還給cache。在一切裝置都初始化好後,便會進main_loop的死循環中。