uboot启动流程之上电启动到第一次准备好C语言运行环境

本文以ast2500evb板子为例来说明uboot的启动过程:

uboot启动流程之上电启动到第一次准备好C语言运行环境

uboot启动流程之上电启动到第一次准备好C语言运行环境

 

在uboot/Makefile中,我们知道uboot.bin依赖uboot, uboot是由各种*.o链接而成,使用的链接脚本为uboot/board/ast2500evb/u-boot.lds.

在uboot/board/ast2500evb/u-boot.lds定义了程序的入口_start, 且其地址为0x00000000.

uboot启动流程之上电启动到第一次准备好C语言运行环境

Arm上电后,从reset vector address处取指令来run。

uboot启动流程之上电启动到第一次准备好C语言运行环境

uboot启动流程之上电启动到第一次准备好C语言运行环境

所以,当我们编出u-boot.bin文件时,需要将其烧写到flash的0地址处,且falsh要映射到arm的0地址处。从u-boot.lds的描述看,在flash 0地址处是符号_start.

这个_start定义可以由uboot/Makefile中的$(OBJS)来确定。

uboot/Makefile

uboot启动流程之上电启动到第一次准备好C语言运行环境

其中$(CPUDIR)定义在uboot/config.mk中。

uboot/config.mk

uboot启动流程之上电启动到第一次准备好C语言运行环境

$(ARCH), $(CPU)定义在由make xxx_confi*生的uboot/include/config.mk中。

uboot启动流程之上电启动到第一次准备好C语言运行环境

所以,对于ast2500evb板子来说,u-boot.bin的入口_start定义在uboot/arch/arm/cpu/arm1176/start.S

Start.S的入口符号_start,直接来个跳转指令.

uboot启动流程之上电启动到第一次准备好C语言运行环境

程序直接跳转到reset处执行。(跳转指令后面的ldr指令是cpu要求的,加载异常处理程序的指令,如软中断,快速中断,中断等)

我们来看一下reset做什么了。

‘reset’ located the same file with ‘_start’(uboot/arch/arm/cpu/arm1176/start.S)

uboot启动流程之上电启动到第一次准备好C语言运行环境

几个指令介绍下:

mrs, msr:

uboot启动流程之上电启动到第一次准备好C语言运行环境

bic, orr:

uboot启动流程之上电启动到第一次准备好C语言运行环境

‘reset’ 操作主要是修改cpsr的值。

150行加载cpsr寄存器的值到寄存器r0;

151行清除bit0-bit5;

152行设置M域为0b10011,即supervisor模式,设置I,F,T位,即disable中断,disable快速中断,设置ARM指令模式;

cpsr寄存器的结构如下:

uboot启动流程之上电启动到第一次准备好C语言运行环境

uboot启动流程之上电启动到第一次准备好C语言运行环境

uboot启动流程之上电启动到第一次准备好C语言运行环境

uboot启动流程之上电启动到第一次准备好C语言运行环境

配置完cpsr寄存器后,到cpu_init_crit:

uboot启动流程之上电启动到第一次准备好C语言运行环境

从上述代码看,主要是配置协处理器,arm1176协处理器的作用如下:

uboot启动流程之上电启动到第一次准备好C语言运行环境

178行,设置r0寄存器为0;

179行,使得I-cache和D-cache失效(也叫刷新I-cache和D-cache);

uboot启动流程之上电启动到第一次准备好C语言运行环境

180行,使得I-TLB和D-TLB失效(也叫刷新指令TLB和数据TLB),TLB全称translation lookaside buffer.

uboot启动流程之上电启动到第一次准备好C语言运行环境

uboot启动流程之上电启动到第一次准备好C语言运行环境

uboot启动流程之上电启动到第一次准备好C语言运行环境

uboot启动流程之上电启动到第一次准备好C语言运行环境

uboot启动流程之上电启动到第一次准备好C语言运行环境

处理完cache和TLB后,处理MMU:

uboot启动流程之上电启动到第一次准备好C语言运行环境

185行,读取协处理器p15的控制寄存器c1。

p15协处理器c1寄存器结构:

uboot启动流程之上电启动到第一次准备好C语言运行环境

186行,清除V,R,S位。

uboot启动流程之上电启动到第一次准备好C语言运行环境

uboot启动流程之上电启动到第一次准备好C语言运行环境

uboot启动流程之上电启动到第一次准备好C语言运行环境

清除V位,表明异常向量基地址使用c12 secure or non-sercure vector base address register配置的基地址。

uboot启动流程之上电启动到第一次准备好C语言运行环境

uboot启动流程之上电启动到第一次准备好C语言运行环境

uboot启动流程之上电启动到第一次准备好C语言运行环境

uboot启动流程之上电启动到第一次准备好C语言运行环境

187行,清除C,A,M位,即,disable L1-D-cache, disable MMU。

188行,设置A位,即,使能alignment fault checking.

uboot启动流程之上电启动到第一次准备好C语言运行环境

189行,设置I位,即使能L1 I-cache.

uboot启动流程之上电启动到第一次准备好C语言运行环境

配置好协处理器p15的c1寄存器(控制寄存器域)数据后,跳转到mmu_disable。

uboot启动流程之上电启动到第一次准备好C语言运行环境

201行,将前面的协处理器p15的c1寄存器(控制寄存器域)配置过的数据重新导入到协处理器p15的c1寄存器(控制寄存器域),此时上面的对协处理器p15的c1寄存器(控制寄存器域)的设置就生效了,即disable了MMU, enable了L1 I-cache。

接下去,就到了237行了。

uboot启动流程之上电启动到第一次准备好C语言运行环境

这里使用了bl指令来跳转到lowlevel_init处执行,bl和b指令的区别是,b指令是无条件跳转,即跳转后,不会回到b指令的下一条指令处;而bl指令使用了link register(r14)保存了bl指令下一条指令的地址,即当bl指令跳转后返回时,会执行bl指令的下一条指令,即 ‘bl     _main’。

我们接着看lowlevel_init处代码:

结合uboot/Makefile我们知道lowlevel_init所在的文件:

uboot/Makefile:

uboot启动流程之上电启动到第一次准备好C语言运行环境

uboot/arch/arm/cpu/arm1176/ast2500/Makefile

uboot启动流程之上电启动到第一次准备好C语言运行环境

lowlevel_init在uboot/arch/arm/cpu/arm1176/ast2500/platform.S

uboot启动流程之上电启动到第一次准备好C语言运行环境

294行,保存lr,即保存’b _main’指令地址到r4寄存器。

300-301行,伪指令,加载0x1e600000, 0xaeed1a03到寄存器r0和r1。

302行,将0x aeed1a03写到地址0x1e600000处。

这个0x1e600000地址是个什么东西?它是AHBC protection key register:

AHB(high performance bus)为系统总线,即ARM 连接使用的总线。

uboot启动流程之上电启动到第一次准备好C语言运行环境

uboot启动流程之上电启动到第一次准备好C语言运行环境

如上图AHBC00的描述,向AHBC00(0x1e600000, AHBC protection key register)写入0x aeed1a03就是设置unlocked.

305行,是向0x1e600084地址处(AHBC84: Interrupt Control/Status Register)写入0x00010000。

uboot启动流程之上电启动到第一次准备好C语言运行环境

向AHBC84写入0x00010000,即向bit16 写入1,相当于清除该位,即清除总线锁中断状态。

308行,向0x1e600088地址处(AHBC88: AHB Bus Target Disable Control Register)写入0.

uboot启动流程之上电启动到第一次准备好C语言运行环境

uboot启动流程之上电启动到第一次准备好C语言运行环境

向AHBC88写入0,相当于disable上图显示的所有功能,比如,target SPI2/SPI3, VIC。即,此时,cpu不可通过总线访问这些。

类似地:

uboot启动流程之上电启动到第一次准备好C语言运行环境

312行通过写0x1688a8a8到SCU(system control unit)寄存器SCU00(protection key register)

uboot启动流程之上电启动到第一次准备好C语言运行环境

uboot启动流程之上电启动到第一次准备好C语言运行环境

这里,就相当于配置unlock SCU registers.

uboot启动流程之上电启动到第一次准备好C语言运行环境

318行,通过SCU70寄存器来检查eSPI(enhanced SPI)是否由flash attach.

319行,如果有flash attach, 即SCU70 bit26为1,则跳转到bypass_first_reset.

uboot启动流程之上电启动到第一次准备好C语言运行环境

uboot启动流程之上电启动到第一次准备好C语言运行环境

我们假定没有flash attach,接下去看代码:

uboot启动流程之上电启动到第一次准备好C语言运行环境

0x1e785xxx为看门狗定时器寄存器基址。

uboot启动流程之上电启动到第一次准备好C语言运行环境

0x1e785010为WDT10: WDT1 Timeout Status Register。

uboot启动流程之上电启动到第一次准备好C语言运行环境

324行,判断到当前为止,是否发生过看门狗定时器发生过超时事件。没有,则跳转到start_first_reset.

 

uboot启动流程之上电启动到第一次准备好C语言运行环境

start_first_reset第一个宏ASTMMC_INIT_RESET_MODE_FULL是没有定义的,我们直接看358开始的代码:

uboot启动流程之上电启动到第一次准备好C语言运行环境

从357行开始到364行,都是在配置下面相关的寄存器:

基址0x1e78:7xxx和0x1e78:8xxx:

uboot启动流程之上电启动到第一次准备好C语言运行环境

程序先配置virtual UART, virtual UART的功能描述如下:

uboot启动流程之上电启动到第一次准备好C语言运行环境

我们来看一下代码怎么配置的:

uboot启动流程之上电启动到第一次准备好C语言运行环境

uboot启动流程之上电启动到第一次准备好C语言运行环境

361行配置FIFO Control Register为0b111, 即,reset VUART的FIFO.

uboot启动流程之上电启动到第一次准备好C语言运行环境

uboot启动流程之上电启动到第一次准备好C语言运行环境

uboot启动流程之上电启动到第一次准备好C语言运行环境

接下去,配置VUART其他寄存器,直到配置PUART0x1e788000.

uboot启动流程之上电启动到第一次准备好C语言运行环境

R2为0,这里就是disable UART的pass through.

uboot启动流程之上电启动到第一次准备好C语言运行环境

uboot启动流程之上电启动到第一次准备好C语言运行环境

接下去,配置PUART其他寄存器,直到LPC controller: 0x1e789000.

基址0x1e78:9xxx:

uboot启动流程之上电启动到第一次准备好C语言运行环境

uboot启动流程之上电启动到第一次准备好C语言运行环境

388行,对寄存器HICR2清零。

uboot启动流程之上电启动到第一次准备好C语言运行环境

基址0x1e62:0xxx:

uboot启动流程之上电启动到第一次准备好C语言运行环境

这个是配置firmwars SPI flash的,即ast2500evb存放uboot的flash。

uboot启动流程之上电启动到第一次准备好C语言运行环境

uboot启动流程之上电启动到第一次准备好C语言运行环境

uboot启动流程之上电启动到第一次准备好C语言运行环境

基址0x1e78:5xxx:

uboot启动流程之上电启动到第一次准备好C语言运行环境

uboot启动流程之上电启动到第一次准备好C语言运行环境

517行,reset各种controller.

uboot启动流程之上电启动到第一次准备好C语言运行环境

520行复位整个系统。

uboot启动流程之上电启动到第一次准备好C语言运行环境

uboot启动流程之上电启动到第一次准备好C语言运行环境

uboot启动流程之上电启动到第一次准备好C语言运行环境

523行就是一直等待看门狗定时器超时后系统复位reset。复位之后,代码又从_start开始执行。

系统复位后,再次运行到lowlevel_init时:

uboot启动流程之上电启动到第一次准备好C语言运行环境

这时,324行判断时,发现系统看门狗定时器发生过超时事件,故不跳转。程序就执行325行了。

325行之后,接着做一些配置,最终跳转到bypass_first_reset处执行。

uboot启动流程之上电启动到第一次准备好C语言运行环境

uboot启动流程之上电启动到第一次准备好C语言运行环境

uboot启动流程之上电启动到第一次准备好C语言运行环境

uboot启动流程之上电启动到第一次准备好C语言运行环境

uboot启动流程之上电启动到第一次准备好C语言运行环境

我们接着看bypass_first_reset.

uboot启动流程之上电启动到第一次准备好C语言运行环境

首先配置定时器。

uboot启动流程之上电启动到第一次准备好C语言运行环境

这里写0XAE,即配置寄存器TMC38的bit0为1,即separate mode.

uboot启动流程之上电启动到第一次准备好C语言运行环境

538行这里写TMC3C寄存器’1’。

uboot启动流程之上电启动到第一次准备好C语言运行环境

结合533和538行可知,这里是将定时器的计数清零,并且disable掉。

uboot启动流程之上电启动到第一次准备好C语言运行环境

uboot启动流程之上电启动到第一次准备好C语言运行环境

547行,将定时器enable起来。

uboot启动流程之上电启动到第一次准备好C语言运行环境

uboot启动流程之上电启动到第一次准备好C语言运行环境

558行,设置dram初始化为SOC Firmware.

uboot启动流程之上电启动到第一次准备好C语言运行环境

接下去,配置USB等,以及配置DDR内存。

uboot启动流程之上电启动到第一次准备好C语言运行环境

uboot启动流程之上电启动到第一次准备好C语言运行环境

uboot启动流程之上电启动到第一次准备好C语言运行环境

Bypass_USB_init会向debug的UART口输出打印:DRAM Init-V

uboot启动流程之上电启动到第一次准备好C语言运行环境

对于ddr4,再输出字符”4”

uboot启动流程之上电启动到第一次准备好C语言运行环境

我们接着看DDR4。

uboot启动流程之上电启动到第一次准备好C语言运行环境

0x1e6e0004为sdram的配置寄存器MCR04.

uboot启动流程之上电启动到第一次准备好C语言运行环境

1168行设置MCR04为0x313, 即设置一次最大可写入的memory size为1G.

uboot启动流程之上电启动到第一次准备好C语言运行环境

uboot启动流程之上电启动到第一次准备好C语言运行环境

uboot启动流程之上电启动到第一次准备好C语言运行环境

uboot启动流程之上电启动到第一次准备好C语言运行环境

接下去配置内存的时序等信息。

uboot启动流程之上电启动到第一次准备好C语言运行环境

配置完后,输出一个字符”0”

uboot启动流程之上电启动到第一次准备好C语言运行环境

ASTMMC_DDR4_MANUAL_RPU没有使能,跳过这段。

uboot启动流程之上电启动到第一次准备好C语言运行环境

如1315行的注释描述的程序后面最的事情,我们不详细看了,跳过。

程序运行轨迹: ddr_phy_init_process  ->  ddr_phy_init_success  ->  ddr4_phyinit_done  ->  Calibration_End

Calibration_End就是check一下DRAM size:

uboot启动流程之上电启动到第一次准备好C语言运行环境

之后init DRAM cache.

uboot启动流程之上电启动到第一次准备好C语言运行环境

Init DRAM cache后做一堆的check,然后跳转到set_scratch.

uboot启动流程之上电启动到第一次准备好C语言运行环境

Set_scratch做一堆配置,比如MAC配置等,最后将之前保存的lr传给pc,这样pc就跑到了_main处了。

uboot启动流程之上电启动到第一次准备好C语言运行环境

这个_main定义在哪里?我们来看uboot/Makefile

uboot/Makefile

uboot启动流程之上电启动到第一次准备好C语言运行环境

uboot/arch/arm/lib/Makefile

uboot启动流程之上电启动到第一次准备好C语言运行环境

uboot启动流程之上电启动到第一次准备好C语言运行环境

uboot启动流程之上电启动到第一次准备好C语言运行环境

uboot启动流程之上电启动到第一次准备好C语言运行环境

uboot启动流程之上电启动到第一次准备好C语言运行环境

故,_main为定义在uboot/arch/arm/lib/crt0.S中。

uboot启动流程之上电启动到第一次准备好C语言运行环境

从注释来看,_main是要准备C语言运行环境的,即要设置好栈指针(SP).

83行配置了sp地址为CONFIG_SYS_INIT_SP_ADDR。

这个CONFIG_SYS_INIT_SP_ADDR在哪定义?

uboot启动流程之上电启动到第一次准备好C语言运行环境

在crt0S的25行include了config.h, 这个config.h为uboot/include/config.h.

我们从Makefile中可知,头文件的搜索路径:

uboot/config.mk(被uboot/arch/arm/lib/Makefile包含)

uboot启动流程之上电启动到第一次准备好C语言运行环境

uboot/include/config.h

uboot启动流程之上电启动到第一次准备好C语言运行环境

在uboot/include/config.h的9行include了uboot/include/configs/ast2500evb.h.

uboot/include/configs/ast2500evb.h

uboot启动流程之上电启动到第一次准备好C语言运行环境

在uboot/include/configs/ast2500evb的行9 include了configs/common.cfg.

uboot启动流程之上电启动到第一次准备好C语言运行环境

在这个uboot/include/configs/common.cfg中定义了CONFIG_SYS_INIT_SP_ADDR。

uboot启动流程之上电启动到第一次准备好C语言运行环境

上述定义中的CONFIG_SYS_SDRAM_BASE定义在uboot/include/configs/ast2500evb.h中,如下:

uboot启动流程之上电启动到第一次准备好C语言运行环境

由上面的定义可知,这个板子的SDRAM在CPU的地址空间为基址0x80000000(2G), 当前阶段的栈指针为0x80000000 + 16K, 即此时栈的大小为16K. 2G之前是排给flash的(注意,这个CONFIG_SYS_SDRAM_BASE在uboot/include/configs/ast.cfg中也定义多了,但是被uboot/include/configs/ast2500evb.h覆盖)。

我们回到uboot/arch/arm/lib/crt0.S继续分析代码:

uboot启动流程之上电启动到第一次准备好C语言运行环境

设置好栈指针后,我们在栈顶预留GD_SIZE的内存给struct global_data.

GD_SIZE定义在uboot/include/generated/generic-asm-offsets.h

Crt0.S在26行包含了头文件uboot/include/asm-offsets.h, 而asm-offsets.h在3行包含了头文件generated/generic-asm-offsets.h

uboot/arch/arm/lib/crt0.S

uboot启动流程之上电启动到第一次准备好C语言运行环境

uboot/include/asm-offsets.h

uboot启动流程之上电启动到第一次准备好C语言运行环境

uboot/include/generated/generic-asm-offsets.h

uboot启动流程之上电启动到第一次准备好C语言运行环境

crt0.S在预留了struct global_data结构后,将这个结构的地址给r8保存,并设置r0为0(board_init_f的参数),之后跳转到board_init_f这个C函数处理(C语言使用的堆栈环境构建好了,虽然栈只有12K – GD_SIZE这么大小)。