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的死循环中。