加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 百科 > 正文

MPC8560 uboot笔记

发布时间:2020-12-15 06:14:54 所属栏目:百科 来源:网络整理
导读:MPC8560 平台是freescale公司是PowerpcQUICC II的下一代通讯处理器。它为网络和通讯外设提高了强大的计算能力,从而提高了整个系统的吞吐量和性能。它的主要架构是由一个高性能的e500的核和一个 通讯处理器模块(CPM)组成. e500的核实现了powerpc BOOK E的ISA


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,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,2],? ? 120 ? ? ?* ? ?SPEFCSR
? ? 121 ? ? ?*/
    122
? ? 123 ? ? /* invalidate d-cache */
? ? 131 ? ? /* disable d-cache */  禁止数据cache
 	
? ? 135 ? ? /* 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: %08lxn",addr);

 	/* 在RAM的末端为内核日志缓冲区、保护RAM、LCD 帧缓冲区、monitor code和bd结构体预留空间,注意数据对齐*/
 	addr -= len;
 	addr &= ~(4096 - 1);

 	debug ("Reserving %ldk for U-Boot at: %08lxn",len >> 10,addr);

	 /*为malloc预留空间 */
 	addr_sp = addr - TOTAL_MALLOC_LEN;
 	debug ("Reserving %dk for malloc() at: %08lxn",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: %08lxn",sizeof (bd_t),addr_sp);
 	addr_sp -= sizeof (gd_t);
 	id = (gd_t *) addr_sp;
 	debug ("Reserving %d Bytes for Global Data at: %08lxn",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: %08lxn",addr_sp);

	/*将局部变量保存到bd_t和gd_t中*/

 	bd->*=....;
 	debug ("New Stack Pointer is: %08lxn",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的死循环中。

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读