经典Uboot4启动流程代码注释.docx
《经典Uboot4启动流程代码注释.docx》由会员分享,可在线阅读,更多相关《经典Uboot4启动流程代码注释.docx(32页珍藏版)》请在冰豆网上搜索。
![经典Uboot4启动流程代码注释.docx](https://file1.bdocx.com/fileroot1/2023-2/25/30d89998-9c35-467f-bc39-acc7cb040a8a/30d89998-9c35-467f-bc39-acc7cb040a8a1.gif)
经典Uboot4启动流程代码注释
U-Boot启动第一阶段代码分析:
(1)设置异常向量(cpu/arm920t/start.S)
.globl_start
_start:
bstart_code/*复位*/
ldrpc,_undefined_instruction/*未定义指令向量*/
ldrpc,_software_interrupt/*软件中断向量*/
ldrpc,_prefetch_abort/*预取指令异常向量*/
ldrpc,_data_abort/*数据操作异常向量*/
ldrpc,_not_used/*未使用*/
ldrpc,_irq/*irq中断向量*/
ldrpc,_fiq/*fiq中断向量*/
/*中断向量表入口地址*/
_undefined_instruction:
.wordundefined_instruction
_software_interrupt:
.wordsoftware_interrupt
_prefetch_abort:
.wordprefetch_abort
_data_abort:
.worddata_abort
_not_used:
.wordnot_used
_irq:
.wordirq
_fiq:
.wordfiq
.balignl16,0xdeadbeef
(2)CPU进入SVC模式(cpu/arm920t/start.S)
start_code:
/*setthecputoSVC32mode*/
mrsr0,cpsr
bicr0,r0,#0x1f/*工作模式位清零*/
orrr0,r0,#0xd3/*工作模式位设置为“10011”(管理模式),并将中断禁止位和快中断禁止位置1*/
msrcpsr,r0
(3)设置控制寄存器地址(cpu/arm920t/start.S)
#ifdefined(CONFIG_S3C2400)
#definepWTCON0x15300000
#defineINTMSK0x14400008
#defineCLKDIVN0x14800014
#else/*s3c2410与s3c2440下面4个寄存器地址相同*/
#definepWTCON0x53000000/*WATCHDOG控制寄存器地址*/
#defineINTMSK0x4A000008/*INTMSK寄存器地址*/
#defineINTSUBMSK0x4A00001C/*INTSUBMSK寄存器地址*/
#defineCLKDIVN0x4C000014/*CLKDIVN寄存器地址*/
#endif
(4)关闭看门狗(cpu/arm920t/start.S)
ldrr0,=pWTCON
movr1,#0x0
strr1,[r0]/*看门狗控制器的最低位为0时,看门狗不输出复位信号*/
(5)屏蔽中断(cpu/arm920t/start.S)
/*maskallIRQsbysettingallbitsintheINTMR–default*/
movr1,#0xffffffff/*某位被置1则对应的中断被屏蔽*/
ldrr0,=INTMSK/*中断源寄存器*/
strr1,[r0]
#ifdefined(CONFIG_S3C2440)
ldrr1,=0x7fff
ldrr0,=INTSUBMSK/*子中断源寄存器*/
strr1,[r0]
#endif
(6)设置MPLLCON、PLLCON、CLKDIVN(cpu/arm920t/start.S)
#ifdefined(CONFIG_S3C2440)【这个变量在哪里定义?
】
#defineMPLLCON0x4C000004
#defineUPLLCON0x4C000008
ldrr0,=CLKDIVN
movr1,#5
strr1,[r0]
ldrr0,=MPLLCON
ldrr1,=0x7F021
strr1,[r0]
ldrr0,=UPLLCON
ldrr1,=0x38022
strr1,[r0]
#else
/*FCLK:
HCLK:
PCLK=1:
2:
4*/
/*defaultFCLKis120MHz!
*/
ldrr0,=CLKDIVN
movr1,#3
strr1,[r0]
#endif
CPU上电几毫秒后,晶振输出稳定,FCLK=Fin(晶振频率),CPU开始执行指令。
但实际上,FCLK可以高于Fin,为了提高系统时钟,需要用软件来启用PLL。
这就需要设置CLKDIVN,MPLLCON,UPLLCON这3个寄存器。
(7)关闭MMU,cache(cpu/arm920t/start.S)
#ifndefCONFIG_SKIP_LOWLEVEL_INIT【是不是跳过低水平初始化?
】
blcpu_init_crit【它不是函数,它是个代码段,在start.S】
#endif
cpu_init_crit这段代码在U-Boot正常启动时才需要执行,若将U-Boot从RAM中启动则应该注释掉这段代码。
下面分析一下cpu_init_crit到底做了什么:
320#ifndefCONFIG_SKIP_LOWLEVEL_INIT
321cpu_init_crit:
322/*
323*使数据cache与指令cache无效*/
324*/
325movr0,#0
326mcrp15,0,r0,c7,c7,0/*向c7写入0将使ICache与DCache无效*/
327mcrp15,0,r0,c8,c7,0/*向c8写入0将使TLB失效*/
328
329/*
330*禁止MMU的功能和高速缓存
331*/
332mrcp15,0,r0,c1,c0,0/*读出控制寄存器到r0中*/
333bicr0,r0,#0x00002300@clearbits13,9:
8(--V---RS)
334bicr0,r0,#0x00000087@clearbits7,2:
0(B----CAM)
335orrr0,r0,#0x00000002@setbit2(A)Align
336orrr0,r0,#0x00001000@setbit12(I)I-Cache
337mcrp15,0,r0,c1,c0,0/*保存r0到控制寄存器*/
339/*
340*在拷贝代码之前,必须设置RAM的时序
341*因为内存的时序是板时效关系,你会发现lowlevel_init.S,在你的板子目录。
343*/
344movip,lr
345
346bllowlevel_init【位置=/board/GT2440/lowlevel_init.S】
347
348movlr,ip
349movpc,lr
350#endif/*CONFIG_SKIP_LOWLEVEL_INIT*/
代码中的c0,c1,c7,c8都是ARM920T的协处理器CP15的寄存器。
其中c7是cache控制寄存器,c8是TLB控制寄存器。
325~327行代码将0写入c7、c8,使Cache,TLB内容无效。
第332~337行代码关闭了MMU。
这是通过修改CP15的c1寄存器来实现的。
(8)初始化RAM控制寄存器(board/samsung/mini2440/lowlevel_init.S)
由于内存初始化是依赖于开发板的,因此lowlevel_init的代码一般放在board下面相应的目录中。
45#defineBWSCON0x48000000/*13个存储控制器的开始地址*/
……
129_TEXT_BASE:
130.wordTEXT_BASE
132.globllowlevel_init
133lowlevel_init:
134/*存储器控制设置*/
135/*maker0relativethecurrentlocationsothatit*/
136/*readsSMRDATAoutofFLASHratherthanmemory!
*/
137ldrr0,=SMRDATA
138ldrr1,_TEXT_BASE
139subr0,r0,r1/*SMRDATA减_TEXT_BASE就是13个寄存器的偏移地址*/
140ldrr1,=BWSCON/*BusWidthStatusController*/
141addr2,r0,#13*4
1420:
143ldrr3,[r0],#4/*将13个寄存器的值逐一赋值给对应的寄存器*/
144strr3,[r1],#4
145cmpr2,r0
146bne0b
147
148/*现在一切都很好*/
149movpc,lr
150
151.ltorg
152/*文字池起源*/
153
154SMRDATA:
/*下面是13个寄存器的值*/
155.word……
156.word……
……
lowlevel_init初始化了13个寄存器来实现RAM时钟的初始化。
lowlevel_init函数对于U-Boot从NANDFlash或NORFlash启动的情况都是有效的。
board/samsung/mini2440/lowlevel_init.o将被链接到cpu/arm920t/start.o后面,因此board/samsung/mini2440/lowlevel_init.o也在U-Boot的前4KB的代码中。
由于NORFlash的开始地址是0,而U-Boot的加载到内存的起始地址是TEXT_BASE,SMRDATA标号在Flash的地址就是SMRDATA-TEXT_BASE。
(9)复制U-Boot第二阶段代码到RAM(cpu/arm920t/start.S)[配置nand启动]
cpu/arm920t/start.S原来的代码是只支持从NORFlash启动的,经过修改现在U-Boot在NORFlash和NANDFlash上都能启动了,实现的思路是这样的:
blbBootFrmNORFlash/*判断U-Boot是在NANDFlash还是NORFlash启动*/
cmpr0,#0/*r0存放bBootFrmNORFlash函数返回值,若返回0表
示NANDFlash启动,否则表示在NORFlash启动*/
beqnand_boot/*跳转到NANDFlash启动代码*/
/*NORFlash启动的代码*/
Bstack_setup/*跳过NANDFlash启动的代码*/
nand_boot:
/*NANDFlash启动的代码*/
stack_setup:
/*其他代码*/
bBootFrmNORFlash函数在board/samsung/mini2440/nand_read.c中定义。
intbBootFrmNORFlash(void)
{
volatileunsignedint*pdw=(volatileunsignedint*)0;
unsignedintdwVal;
dwVal=*pdw;/*先记录下原来的数据*/
*pdw=0x12345678;
if(*pdw!
=0x12345678)/*写入失败,说明是在NORFlash启动*/
{
return1;
}
else/*写入成功,说明是在NANDFlash启动*/
{
*pdw=dwVal;/*恢复原来的数据*/
return0;
}
}
下面来分析NORFlash启动部分代码:
208adrr0,_start/*r0<-currentpositionofcode*/
209ldrr1,_TEXT_BASE/*testifwerunfromflashorRAM*/
/*判断U-Boot是否是下载到RAM中运行,若是,则不用再复制到RAM中了,这种情况通常在调试U-Boot时才发生*/
210cmpr0,r1/*_start等于_TEXT_BASE说明是下载到RAM中运行*/
211beqstack_setup
212/*以下直到nand_boot标号前都是NORFlash启动的代码*/
213ldrr2,_armboot_start
214ldrr3,_bss_start
215subr2,r3,r2/*r2<-sizeofarmboot*/
216addr2,r0,r2/*r2<-sourceendaddress*/
217/*搬运U-Boot自身到RAM中*/
218copy_loop:
219ldmiar0!
{r3-r10}/*从地址为[r0]的NORFlash中读入8个字的数据*/
220stmiar1!
{r3-r10}/*将r3至r10寄存器数据复制给地址为[r1]的内存*/
221cmpr0,r2/*untilsourceendaddreee[r2]*/
222blecopy_loop
223bstack_setup/*跳过NANDFlash启动的代码*/
下面再来分析NANDFlash启动部分代码:
nand_boot:
movr1,#NAND_CTL_BASE
ldrr2,=((7<<12)|(7<<8)|(7<<4)|(0<<0))
strr2,[r1,#oNFCONF]/*设置NFCONF寄存器*/
/*设置NFCONT,初始化ECC编/解码器,禁止NANDFlash片选*/
ldrr2,=((1<<4)|(0<<1)|(1<<0))
strr2,[r1,#oNFCONT]
ldrr2,=(0x6)/*设置NFSTAT*/
strr2,[r1,#oNFSTAT]
/*复位命令,第一次使用NANDFlash前复位*/
movr2,#0xff
strbr2,[r1,#oNFCMD]
movr3,#0
/*为调用C函数nand_read_ll准备堆栈*/
ldrsp,DW_STACK_START
movfp,#0
/*下面先设置r0至r2,然后调用nand_read_ll函数将U-Boot读入RAM*/
ldrr0,=TEXT_BASE/*目的地址:
U-Boot在RAM的开始地址*/
movr1,#0x0/*源地址:
U-Boot在NANDFlash中的开始地址*/
movr2,#0x30000/*复制的大小,必须比u-boot.bin文件大,并且必须是NANDFlash块大小的整数倍,这里设置为0x30000(192KB)*/
blnand_read_ll/*跳转到nand_read_ll函数,开始复制U-Boot到RAM*/
tstr0,#0x0/*检查返回值是否正确*/
beqstack_setup
bad_nand_read:
loop2:
bloop2//infiniteloop
.align2
DW_STACK_START:
.wordSTACK_BASE+STACK_SIZE-4
其中NAND_CTL_BASE,oNFCONF等在include/configs/mini2440.h中定义如下:
#defineNAND_CTL_BASE0x4E000000//NANDFlash控制寄存器基址
#defineSTACK_BASE0x33F00000//栈基地址baseaddressofstack
#defineSTACK_SIZE0x8000//栈长度
#defineoNFCONF0x00/*NFCONF相对于NAND_CTL_BASE偏移地址*/
#defineoNFCONT0x04/*NFCONT相对于NAND_CTL_BASE偏移地址*/
#defineoNFADDR0x0c/*NFADDR相对于NAND_CTL_BASE偏移地址*/
#defineoNFDATA0x10/*NFDATA相对于NAND_CTL_BASE偏移地址*/
#defineoNFCMD0x08/*NFCMD相对于NAND_CTL_BASE偏移地址*/
#defineoNFSTAT0x20/*NFSTAT相对于NAND_CTL_BASE偏移地址*/
#defineoNFECC0x2c/*NFECC相对于NAND_CTL_BASE偏移地址*/
(10)设置堆栈(cpu/arm920t/start.S)
/*设置堆栈*/
stack_setup:
ldrr0,_TEXT_BASE/*upper128KiB:
relocateduboot*/
subr0,r0,#CONFIG_SYS_MALLOC_LEN/*堆区*/
subr0,r0,#CONFIG_SYS_GBL_DATA_SIZE/*跳过全局数据区*/
#ifdefCONFIG_USE_IRQ
subr0,r0,#(CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ)
#endif
subsp,r0,#12/*从中断栈删除3字*/
(11)清除BSS段(cpu/arm920t/start.S)
clear_bss:
ldrr0,_bss_start/*BSS段开始地址,在u-boot.lds中指定*/
ldrr1,_bss_end/*BSS段结束地址,在u-boot.lds中指定*/
movr2,#0x00000000
clbss_l:
strr2,[r0]/*将bss段清零*/
addr0,r0,#4
cmpr0,r1
bleclbss_l
(12)跳转到第二阶段代码入口(cpu/arm920t/start.S)
ldrpc,_start_armboot
_start_armboot:
.wordstart_armboot/*跳转到第二阶段代码入口start_armboot处*/
U-Boot启动第二阶段代码分析:
voidstart_armboot(void);//(lib_arm/board.c)236行,是第二阶段代码的入口;
staticintdisplay_banner(void);//(lib_arm/board.c)
staticintdisplay_dram_config(void);//(lib_arm/board.c)
staticvoiddisplay_flash_config(ulongsize);//(lib_arm/board.c)
voidmain_loop(void);//(common/main.c)
这些函数在start_armboot()函数中被调用,用紫色标记出来了。
===================================================================================
U-Boot使用了一个结构体gd_t来存储全局数据区的数据,这个结构体在include/asm-arm/global_data.h中定义如下:
typedefstructglobal_data{
bd_t*bd;
unsignedlongflags;
unsignedlongbaudrate;
unsignedlonghave_console;/*串口初始化是被调用*/
unsignedlongreloc_off;/*重定位偏移*/
unsignedlongenv_addr;/*环境变量结构体的地址*/
unsignedlongenv_valid;/*环境变量校验有效吗?
*/
unsignedlongfb_base;/*帧缓冲器得基地址*/
#ifdefCONFIG_VFD
unsignedcharvfd_type;/*显示类型*/
#endif
#if0
unsignedlongcpu_clk;/*CPU时钟*/
unsignedlongbus_clk;
unsignedlongram_size;/*RAM尺寸*/
unsignedlongreset_status;/*在启动时,复位状态寄存器*/
#endif
void**jt;/*跳跃表*/
}gd_t;
U-Boot使用了一个存储在寄存器中的指针gd来记录全局数据区的地址:
#defineDECLARE_GLOBAL_DATA_PTRregistervolatilegd_t*gdasm("r8")
DECLARE_GLOBAL_DATA_PTR定义一个gd_t全局数据结构的指针,这个指针存放在指定的寄存器r8中。
这个声明也避免编译器把r8分配给其它的变量。
任何想要访问全局数据区的代码,只要代码开头加入“DECLARE_GLOBAL_DATA_PTR”一行代码,然后就可以使用gd指针来访问全局数据区了。
【貌似移植Uboot时需要使用全局数据区,在使用前添加了DECLARE_GLOBAL_DATA_PTR】
======================================