e500启动分析

参考:e500核 上电启动及uboot流程

P1010rdb新单板u-boot移植过程总结

启动条件

关于e500的启动,我们需要仔细阅读RM手册第四章,仔细确认reset的每一个部分。

系统上电到执行第一条指令前,需要经过配置PLL、Local Bus控制器、LAW、MMU等操作,这些配置完全是由Cpu根据芯片的默认值和芯片外部配置引脚的输入状态自动完成的。如:

  1. Cpu根据cfg_sys_pll[0:2]的输入状态决定当前CPU的BBC对SYS_CLK的倍频参数;

  2. Cpu根据cfg_ddr_pll[0:2] 的输入状态决定DDR的时钟频率(DDR2和DDR3不同);

  3. Cpu根据cfg_core0_pll[0:2]/cfg_core1_pll[0:2]的输入状态决定当前CPU的cor0/cor1对CCB的倍频参数;

  4. Cpu根据cfg_cpu0_boot和cfg_cpu1_boot的输入状态决定复位时执行boot代码的Core,如下图所示:image

    我们设置core0启动,core1等待。

    cfg_cpu0_boot=1,core0启动;

    cfg_cpu1_boot=0,core1等待;

    扫描二维码关注公众号,回复: 9421667 查看本文章
  5. Cpu根据cfg_rom_loc[0:3]的输入状态决定Boot ROM的位置。

    对软件来说最重要的配置是Boot ROM位置的配置。Boot ROM位置与cfg_rom_loc[0:3]输入状态的关系如下图所示:image

    我们从nor启动,所以:

    cfg_rom_loc[0:3] =1110,从Local bus GPCM—16-bit ROM启动;

启动及u-boot分析

cpu上电会先读取以上启动条件的参数,根据这些参数来进行不同的初始化。

根据我们的设置从core0启动,从local bus GPCM加载代码。

Cpu复位后执行的第一条指令的地址是0xFFFF,FFFC。因此,Cpu要能正确执行U-boot代码,至少需要下面几个条件:

  1. Local Access Window能正确转换U-boot代码的物理地址空间
  2. Local Bus Control能正确转换U-boot代码的物理地址空间
  3. MMU能正确转换U-boot代码的有效地址空间(e500核的MMU无法关闭)

​ 首先,根据P1020手册,Local Access Window对Boot ROM的地址转换,不通过LAW寄存器配置。在复位时,映射0x0_FF80_0000到启动地址(硬件配置为Local bus GPCM),共8M空间。这样解决了第一个问题。

​ 接着,P1020复位时Local Bus Control寄存器BRn/ORn有默认值为全0,设定了第0个Local Bus Bank的地址空间是从0x0000_0000开始的4G空间,且MSEL=GPCM。这样,所有映射到Local Bus上的地址都会使CS0有效。

​ 然后,MMU在复位时也有默认值。实际上,虽然P1020的MMU有16个TLB1和512个TLB0,复位时仅TLB1的Entry0有效。Entry0将0xFFFF,F000开始的4k有效地址空间映射到0xFFFF,F000开始的4k物理地址空间中,TLB1的Entry0配置如下表所示:

image

有了以上的配置,P1020 Cpu复位时就能够从连接在Local Bus的CS0上的Nor Flash执行第一条指令了。具体过程如下:

Cpu从0xFFFF,FFFC有效地址开始执行,先经过MMU转换得到物理地址0xFFFF,FFFC;然后,Local Access Window的特殊转换直接将0xFFFF,FFFC转换到Cpu的外围器件Local bus GPCM—16-bit ROM上;最后,由于Local Bus的CS0覆盖了4G空间,0xFFFF,FFFC直接被送到了Nor Flash(Boot Flash)的地址总线上。如果外接Boot Flash只有16M BYTE,则地址总线仅24位,0xFFFF,FFFC代表了Flash 0xFF,FFFC的地址。

/cpu/mpc85xx/u-boot.lds文件是连接器脚本文件,其中:

.bootpg RESET_VECTOR_ADDRESS - 0xffc:
{
	cpu/mpc85xx/start.o       (.bootpg)
} :text = 0xffff

规定了/cpu/mpc85xx/start.s的_start_e500代码段放在Flash倒数4K的位置。

.resetvec RESET_VECTOR_ADDRESS:
{
	*(.resetvec)
} :text = 0xffff

规定了复位后从resetvec段开始取指令,这个resetvec段放在FFFFFFFC的位置,在/cpu/mpc85xx/resetvec.s中可以看到e500核从倒数第4个字节处取的是一条跳转指令为:

b _start_e500

跳转到位于倒数4K位置的_start_e500标号处。

_start_e500开始的代码会在空间1里建立一个4M的Entry映射,使用的是Entry15,这样就把所有的uboot代码都可以访问到了。然后再会跳到_start标号开始的地方,_start标号位于倒数768K的位置,在这之后的过程和e300核的start.s类似。

​ E500核的u-boot的启动是从/cpu/mpc85xx/start.s中的_start_e500标号开始的,经历了/cpu/mpc85xx/start.s,/cpu/mpc85xx/cpu_init_early.c,/cpu/mpc85xx/cpu_init.c,/lib_ppc/Board.c等几个文件中的多个汇编和C函数,最后会在/lib_ppc/Board.c中的board_init_r函数中进入命令死循环,等待执行键入的命令。其具体的流程如下图所示:image

start.s代码过程

  • _start_e500函数

    初始化e500核心,清除指令和数据cache,有效指令和数据cache,设置中断向量,禁止中断响应,禁止看门狗,只允许machine check中断,设置为supervisor级别,无效指令和数据cache,设置HID0寄存器等,为系统创建一个干净可靠的初始环境。

  • 使能L1 cache

  • 设置异常向量表

  • 配置MMU单元,即TLB0和TLB1条目,此时虚实地址相同。默认只映射flash的最后4KB空间,所以此空间也叫bootpg。配置MMU之后,可以访问所有flash。

    从前面P1020上电流程可以看出,上电之后,MMU只配置了4k空间,在这4k空间内必须初步配置MMU,使MMU能够映射u-boot真实的有效地址空间。下面这段代码将TLB1的Entry0配置为4M空间。

    	lis     r6,FSL_BOOKE_MAS0(1, 15, 0)@h
    	ori     r6,r6,FSL_BOOKE_MAS0(1, 15, 0)@l
    	
    	/* create a temp mapping in AS=1 to the 4M boot window */
    	lis     r7,FSL_BOOKE_MAS1(1, 1, 0, 1, BOOKE_PAGESZ_4M)@h
    	ori     r7,r7,FSL_BOOKE_MAS1(1, 1, 0, 1, BOOKE_PAGESZ_4M)@l
    
    	lis     r8,FSL_BOOKE_MAS2(TEXT_BASE & 0xffc00000, (MAS2_I|MAS2_G))@h
    	ori     r8,r8,FSL_BOOKE_MAS2(TEXT_BASE & 0xffc00000, (MAS2_I|MAS2_G))@l
    
    	/* The 85xx has the default boot window 0xff800000 - 0xffffffff */
    	lis     r9,FSL_BOOKE_MAS3(0xffc00000, 0, (MAS3_SX|MAS3_SW|MAS3_SR))@h
    	ori     r9,r9,FSL_BOOKE_MAS3(0xffc00000, 0, (MAS3_SX|MAS3_SW|MAS3_SR))@l
    
    	mtspr   MAS0,r6
    	mtspr   MAS1,r7
    	mtspr   MAS2,r8
    	mtspr   MAS3,r9
    	isync
    	msync
    	tlbwe
    

    ​ 经过以上配置,u-boot代码就可以使用TEXT_BASE定义的有效地址空间了。但是,这时的Local Bus Bank的配置还是默认值。后面的代码会重新修改MMU映射,将u-boot映射到LAW分配的空间里。

  • 重定位CCSRBAR,即cpu的寄存器映射区域,control config status registers

  • 配置Local Access Window L1 cache

  • 设置L1 data cache作为INIT_RAM使用

  • 跳转到_start_cont处开始执行

  • 设置堆栈到INIT_RAM中(L1 data cache),此时ddr尚未规划。

    程序跑到这里,就要进入第一个C函数了。C函数的运行至少需要栈空间,但这时,RAM还没有初始化,只能在Dcache中锁定一定的空间,用做C的栈空间。

    switch_as函数在Dcache中锁定16k的空间。

    下面的几行代码将堆栈指针指向刚刚分配好的Dcache空间。

      /* Setup the stack in initial RAM,could be L2-as-SRAM or L1 dcache*/
    	lis      r1,CONFIG_SYS_INIT_RAM_ADDR@h
    	ori     r1,r1,CONFIG_SYS_INIT_SP_OFFSET@l
    
    	li       r0,0
    	stwu r0,-4(r1)
    	stwu r0,-4(r1)              /* Terminate call chain */
    
  • 调用cpu_init_f()函数(arch/powerpc/cpu/mpc85xx/cpu_init_early.c),完成下面的工作:

    1. 为u-boot的全局变量指定地址并清空
    2. 为CCSRBAR建立TBL
    3. 建立nor flash、cpld、nand flash到LAW
    4. 为L2SRAM建立TLB,配置L2RAM
    5. 为NOR FLASH建立TLB,并重新配置eLBC驱动nor flash
    6. 禁止MMU TLB1,这样就导致不能使用启动时的默认TLB去映射nor flash了
    7. 为L1 DCache重新建立TLB,L1 DCache for SRAM用作stack空间
  • 调用board_init_f()板级初始化函数(common/board_f.c)

    该函数为板级初始化的第一个函数,会对板子上很多硬件外设做初始化,其中最重要的为init_sequence数组,该数组里面包含了很多板级的硬件初始化函数,在board_init_f函数中会依次的调用该数组中的函数去初始化各个硬件,该数组如下:

    init_fnc_t *init_sequence[] = {
    
    #if defined(CONFIG_BOARD_EARLY_INIT_F)
             board_early_init_f,
    #endif
    
    #if defined(CONFIG_MPC85xx) || defined(CONFIG_MPC86xx)
             probecpu,
    #endif
    #if !defined(CONFIG_8xx_CPUCLK_DEFAULT)
             get_clocks,          /* get CPU and bus clocks (etc.) */
    #if defined(CONFIG_TQM8xxL) && !defined(CONFIG_TQM866M) \
        && !defined(CONFIG_TQM885D)
             adjust_sdram_tbs_8xx,
    #endif
             init_timebase,
    #endif
    #ifdef CONFIG_SYS_ALLOC_DPRAM
    #if !defined(CONFIG_CPM2)
             dpram_init,
    #endif
    #endif
    #if defined(CONFIG_BOARD_POSTCLK_INIT)
             board_postclk_init,
    #endif
             env_init,
    #if defined(CONFIG_8xx_CPUCLK_DEFAULT)
             get_clocks_866,            /* get CPU and bus clocks according to the environment variable */
             sdram_adjust_866,       /* adjust sdram refresh rate according to the new clock */
             init_timebase,
    #endif
             init_baudrate,
             serial_init,
             console_init_f,
             display_options,
    #if defined(CONFIG_8260)
             prt_8260_rsr,
             prt_8260_clks,
    #endif /* CONFIG_8260 */
    #if defined(CONFIG_MPC83xx)
             prt_83xx_rsr,
    #endif
             checkcpu,
    #if defined(CONFIG_MPC5xxx)
             prt_mpc5xxx_clks,
    #endif /* CONFIG_MPC5xxx */
    #if defined(CONFIG_MPC8220)
             prt_mpc8220_clks,
    #endif
             checkboard,
             INIT_FUNC_WATCHDOG_INIT
    #if defined(CONFIG_MISC_INIT_F)
             misc_init_f,
    #endif
             INIT_FUNC_WATCHDOG_RESET
    #if defined(CONFIG_HARD_I2C) || defined(CONFIG_SOFT_I2C)
             init_func_i2c,
    #endif
    #if defined(CONFIG_HARD_SPI)
             init_func_spi,
    #endif
    #ifdef CONFIG_POST
             post_init_f,
    #endif
             INIT_FUNC_WATCHDOG_RESET
             init_func_ram,
    #if defined(CONFIG_SYS_DRAM_TEST)
             testdram,
    #endif /* CONFIG_SYS_DRAM_TEST */
             INIT_FUNC_WATCHDOG_RESET
    
             NULL,                          /* Terminate this list */
    };
    

    有阴影的部分在本系统内不采用。可以看到时钟,串口,控制台,内存等初始化函数的调用。

    1. 逐个调用init_sequence[]中的板级初始化函数,包括cpu探测,时钟获取、初始化时间基数、环境变量env的初始化,初始化波特率、初始化串口、初始化终端、打印选项、检测cpu、检测board等和最关键的初始化DDR SDRAM(board/freescale/p1020ndae/ddr.c,不使用SPD EEPROM)
    2. 分配0-1GB的RAM空间。初始化bd_t *bd板级数据结构信息。调用hanshrelocate_code(addr_sp,id,addr)把代码从flash中load到RAM中并从RAM中继续运行,其中:addr_sp指向栈顶(最大地址)。id指向gd_t,即全局数据结构。Addr指向代码搬移的目的地址。
  • relocate_code的工作

    relocate_code(addr_sp, id, addr)把代码从flash中load到RAM中并从RAM中继续运行!

    1. 将栈由内部RAM(cache)空间重新定位到外部DDR内

    2. 从flash中拷贝代码到DDR RAM中,在RAM中的高端,具体地址根据RAM大小确定。

    3. 重新定位中断向量到DDR RAM中

    4. 计算in_ram函数在RAM中的地址,并跳转到该函数继续执行。

    5. 调整GOT表中函数的地址,并清零bss段。

    6. 调用board_init_r()函数

      • 更新u-boot命令函数地址,因为代码已拷贝至RAM低地址处

      • 调用函数trap_init() copy exception代码到RAM低地址处

      • 调用flash——init()获取flash校验等,并填写flash信息到bd中。

      • 调用cpu_init_r()初始化L2 cache。

      • 调用mem_malloc_init()初始化u-boot阶段的,可以allocate的memory,我们板卡定义了128KB空间。

      • 调用env_relocate(),如果enviroment包含在编译的镜像中,则修改其地址。否则,,申请空间,并拷贝默认的environment。

      • 获取ethernet mac地址,并保存到bd中

      • 调用getenv_IPaddr()从environment信息中获取ip地址,保存daobd中。

      • device_init(),根据板卡头文件定义,注册必要的设备,如串口设备。

      • jumptable_init()初始化gd中的jump table中的函数,如get_version,malloc,getenv

      • 调用console_init_r()把串口设备设置为标准的输入、输出、错误控制终端。

      • 调用interrupt_init()使能中断。

      • 获取uImage在RAM中的地址,引导uImage,引导失败进行向下执行

      • 调用eth_initialize(bd)初始化以太网接口(net/eth.c _initialize())。

      • 调用main_loop()函数,进入主循环。

        等待标准输入输入命令。

发布了20 篇原创文章 · 获赞 69 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/sinat_36544290/article/details/104518848
今日推荐