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

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

 

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

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

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

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

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

uboot/Makefile

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

uboot/config.mk

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

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

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

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

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

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

几个指令介绍下:

mrs, msr:

bic, orr:

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

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

151行清除bit0-bit5;

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

cpsr寄存器的结构如下:

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

接下去,就到了237行了。

这里使用了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/arch/arm/cpu/arm1176/ast2500/Makefile

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

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 连接使用的总线。

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

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

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

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

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

类似地:

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

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

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

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

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

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

0x1e785010为WDT10: WDT1 Timeout Status Register。

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

 

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

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

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

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

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

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

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

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

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

基址0x1e78:9xxx:

388行,对寄存器HICR2清零。

基址0x1e62:0xxx:

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

基址0x1e78:5xxx:

517行,reset各种controller.

520行复位整个系统。

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

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

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

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

我们接着看bypass_first_reset.

首先配置定时器。

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

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

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

547行,将定时器enable起来。

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

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

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

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

我们接着看DDR4。

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

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

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

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

ASTMMC_DDR4_MANUAL_RPU没有使能,跳过这段。

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

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

Calibration_End就是check一下DRAM size:

之后init DRAM cache.

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

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

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

uboot/Makefile

uboot/arch/arm/lib/Makefile

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

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

83行配置了sp地址为CONFIG_SYS_INIT_SP_ADDR。

这个CONFIG_SYS_INIT_SP_ADDR在哪定义?

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

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

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

uboot/include/config.h

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

uboot/include/configs/ast2500evb.h

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

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

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

由上面的定义可知,这个板子的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继续分析代码:

设置好栈指针后,我们在栈顶预留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/include/asm-offsets.h

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

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值