MPC8560 平台是freescale公司是PowerpcQUICC II的下一代通讯处理器。它为网络和通讯外设提高了强大的计算能力,从而提高了整个系统的吞吐量和性能。它的主要架构是由一个高性能的e500的核和一个 通讯处理器模块(CPM)组成. e500的核实现了powerpc BOOK E的ISA。
如下:
.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 ——用于从表中读出 offsetGET_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, PVR_85xx_REV1@h 90 ori r1, r1, PVR_85xx_REV1@l 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 */ 禁止数据cache135 /* invalidate i-cache */ 141 /* disable i-cache */ 禁止指令cache
146 /* clear registers */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,CFG_CCSRBAR@h 301 ori r7,r7,CFG_CCSRBAR@l 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的死循环中。