UBOOT从NAND FLASH启动分析.docx
《UBOOT从NAND FLASH启动分析.docx》由会员分享,可在线阅读,更多相关《UBOOT从NAND FLASH启动分析.docx(23页珍藏版)》请在冰豆网上搜索。
![UBOOT从NAND FLASH启动分析.docx](https://file1.bdocx.com/fileroot1/2023-2/4/01825994-5845-4748-bfc1-1aeacd8471b6/01825994-5845-4748-bfc1-1aeacd8471b61.gif)
UBOOT从NANDFLASH启动分析
UBOOT从NANDFLASH启动分析
UBOOT从NANDFLASH启动分析
在分析启动代码之前先看一下S3C2440的NAND启动:
在配置NAND启动模式之后,S3C2440上电会先将NAND中的0x0-0x1000共4096字节的数据拷贝到位于Bank0中的BootInternalSRAM上
Bank0如下图:
可以看出BootInternalSRAM为4KB大小,也正是因为BootInternalSRAM只有4KB大小,所以只能从NAND中拷贝4K的内容=3=这个BootInternalSRAM是配置为NANDFLASH启动模式才有的
这4K内容是什么呢?
~这就要看Uboot的镜像文件中是如何进行连接的了~
连接脚本在board/smdk2440/u-boot.lds中,如下
SECTIONS
{
.=0x00000000;
.=ALIGN(4);
.text:
{
cpu/arm920t/start.o(.text)
cpu/arm920t/s3c24x0/nand_read.o(.text)
*(.text)
}
.=ALIGN(4);
.rodata:
{*(.rodata)}
.=ALIGN(4);
.data:
{*(.data)}
.=ALIGN(4);
.got:
{*(.got)}
.=.;
__u_boot_cmd_start=.;
.u_boot_cmd:
{*(.u_boot_cmd)}
__u_boot_cmd_end=.;
.=ALIGN(4);
__bss_start=.;
.bss:
{*(.bss)}
_end=.;
}
.text为代码段,可以看出cpu/arm920t/start.o在代码段的最前面,所以会先执行start.o中的代码
连接完成后的镜像文件的前4K如下
cpu/arm920t/start.o(.text)
.text0x33f800000x4e0cpu/arm920t/start.o
0x33f80050IRQ_STACK_START
0x33f80048_bss_start
0x33f8004c_bss_end
0x33f80044_armboot_start
0x33f80000_start
0x33f80054FIQ_STACK_START
cpu/arm920t/s3c24x0/nand_read.o(.text)
.text0x33f804e00x1b8cpu/arm920t/s3c24x0/nand_read.o
0x33f804e0nand_read_ll
*(.text)
.text0x33f806980x64board/smdk2440/libsmdk2440.a(lowlevel_init.o)
0x33f8069clowlevel_init
.text0x33f806fc0x280cpu/arm920t/libarm920t.a(interrupts.o)
0x33f80934do_fiq
0x33f80880do_undefined_instruction
0x33f80744show_regs
0x33f80958do_irq
0x33f80728bad_mode
0x33f808c8do_prefetch_abort
0x33f8070cdisable_interrupts
0x33f80910do_not_used
0x33f808ecdo_data_abort
0x33f808a4do_software_interrupt
0x33f806fcenable_interrupts
.text0x33f8097c0x250cpu/arm920t/s3c24x0/libs3c24x0.a(interrupts.o)
0x33f80aa4set_timer
0x33f80a20reset_timer
0x33f8097cinterrupt_init
0x33f80ba0get_tbclk
0x33f80a90get_timer
0x33f809f0reset_timer_masked
0x33f80a24get_timer_masked
0x33f80ab4udelay
0x33f80b10udelay_masked
0x33f80bacreset_cpu
0x33f80b8cget_ticks
.text0x33f80bcc0x150cpu/arm920t/s3c24x0/libs3c24x0.a(speed.o)
0x33f80c4cget_HCLK
0x33f80cecget_PCLK
0x33f80c44get_FCLK
0x33f80d14get_UCLK
.text0x33f80d1c0x1e8cpu/arm920t/s3c24x0/libs3c24x0.a(cmd_s3c24xx.o)
0x33f80d8cdo_s3c24xx
.text0x33f80f040xdccpu/arm920t/s3c24x0/libs3c24x0.a(serial.o)
0x33f80f04serial_setbrg
0x33f80fa8serial_tstc
0x33f80f80serial_putc
0x33f80f58serial_init
0x33f80fb8serial_puts
0x33f80f68serial_getc
.text0x33f80fe00x140lib_arm/libarm.a(_divsi3.o)
0x33f80fe0__divsi3
如何设置从0x33f80000开始呢?
~这是链接的时候指定的
在根目录下面的config.mk中有下面一句
LDFLAGS+=-Bstatic-T$(LDSCRIPT)-Ttext$(TEXT_BASE)$(PLATFORM_LDFLAGS)
关键就是其中的-Ttext$(TEXT_BASE),这句指明了代码段的起始地址
而TEXT_BASE在board/smdk2440/config.mk中定义TEXT_BASE=0x33F80000
为什么是0x33F80000呢?
~
这是将NAND中Uboot拷贝到RAM中的起始地址,所以在代码拷贝到RAM之前不能使用绝对地址来寻址数据,只能用相对地址
在以下将用虚拟地址来指Uboot在RAM中的地址,也就是0x33F80000
现在来看代码cpu/arm920t/start.S
_start:
;异常处理向量表
bstart_code
ldrpc,_undefined_instruction;未定义指令异常:
0x00000004
ldrpc,_software_interrupt;软中断异常:
0x00000008
ldrpc,_prefetch_abort;预取异常:
0x0000000C
ldrpc,_data_abort;数据异常:
0x00000010
ldrpc,_not_used;未使用:
0x00000014
ldrpc,_irq;外部中断请求IRQ:
0x00000018
ldrpc,_fiq;快束中断请求FIQ:
0x0000001C
bstart_code在虚拟地址0x33F80000处,拷贝到BootInternalSRAM后则位于0x0处,所以bstart_code是第一条执行的指令,
start_code在cpu/arm920t/start.S中
代码如下:
//读取CPSR寄存器的内容到R0
mrsr0,cpsr
//清除R0中的0-4这5个位后保存到R0中
//也就是清除用户模式位
bicr0,r0,#0x1f
//置R0的01467位为真
//也就是选择SVC模式,同时IRQ和FIQ被禁止,处理器处于ARM状态
//关闭中断和快速中断
orrr0,r0,#0xd3
//将R0中的值保存到CPSR上
msrcpsr,r0
#definepWTCON0x53000000;看门狗控制寄存器WTCON
#defineINTMSK0x4A000008;中断屏蔽寄存器INTMSK
#defineINTSUBMSK0x4A00001C;辅助中断屏蔽寄存器,由于外设中断源太多,要用此寄存器屏蔽剩余的中断源
#defineLOCKTIME0x4c000000;PLL锁定时间计数寄存器
#defineMPLLCON0x4c000004;主时钟锁相环控制寄存器
#defineUPLLCON0x4c000008
#defineCLKDIVN0x4C000014;时钟分频寄存器/*clockdivisorregister*/
#defineINTSUBMSK_val0xffff
#defineMPLLCON_val((18412)+(24)+2)/*406M*/
#defineUPLLCON_val((6012)+(44)+2)/*47M*/
#defineCLKDIVN_val7/*FCLK:
HCLK:
PCLK=1:
3:
6*/
#defineCAMDIVN0x4C000018
//取得看门狗寄存器的地址
ldrr0,=pWTCON
//将R1寄存器清0
movr1,#0x0
//将看门狗寄存器清0,即将看门狗禁止,包括定时器定时,溢出中断及溢出复位等
strr1,[r0]
/*
*maskallIRQsbysettingallbitsintheINTMR-default
*/
//设R1寄存器为0xFFFFFFFF
movr1,#0xffffffff
//读取中断屏蔽寄存器的地址
ldrr0,=INTMSK
//将中断屏蔽寄存器中的位全设1,屏蔽所有中断
strr1,[r0]
//#defineINTSUBMSK_val0xffff
//设R1寄存器为0xFFFF
ldrr1,=INTSUBMSK_val
//读取辅助中断屏蔽寄存器的地址
ldrr0,=INTSUBMSK
//将辅助中断屏蔽寄中的11个中断信号屏蔽掉,本人觉得INTSUBMS_val应设成7ff
strr1,[r0]
//#defineLOCKTIME0x4c000000
//读取PLL锁频计数器寄存器地址到R0中
ldrr0,=LOCKTIME
//将R1设为0x00FFFFFF
ldrr1,=0xffffff
//M_LTIME为最大的0xFFF
//U_LTIME为最大的0xFFF
strr1,[r0];0xfff=4096>1800,远远满足锁定要求
/*FCLK:
HCLK:
PCLK=1:
2:
4*/
/*defaultFCLKis120MHz!
*/
//#defineCLKDIVN0x4C000014/*clockdivisorregister*/
//读取时钟分频寄存器的地址
ldrr0,=CLKDIVN
//#defineCLKDIVN_val7/*FCLK:
HCLK:
PCLK=1:
3:
6*/
//将R1设为0x7
movr1,#CLKDIVN_va
//PDIVN-1:
PCLKhastheclocksameastheHCLK/2.
//HDIVN-11:
HCLK=FCLK/3whenCAMDIVN[8]=0.
//HCLK=FCLK/6whenCAMDIVN[8]=1.
strr1,[r0]
/*MakesurewegetFCLK:
HCLK:
PCLK=1:
3:
6*/
//#defineCAMDIVN0x4C000018
//读取摄像头时钟分频寄存器的地址
ldrr0,=CAMDIVN
//将R1设为0
movr1,#0
//将摄像头时钟分频寄存器清0
strr1,[r0]
/*Clockasynchronousmode*/
//MRCp15,0,Rd,c1,c0,0;readcontrolregister
//读取控制寄存器中的值到R1中
mrcp15,0,r1,c1,c0,0;将协处理器p15的寄存器c1和c0的值传到arm处理器的R1寄存器中
//31iAbitAsynchronousclockselect
//30nFbitnotFastBusselect
orrr1,r1,#0xc0000000;将最高两位置1
//MCRp15,0,Rd,c1,c0,0;writecontrolregister
//将R1中的值写到控制寄存器中
mcrp15,0,r1,c1,c0,0将arm的寄存器R1的32位数据传到协处理器p15的两个16位寄存器c1和c0
//#defineUPLLCON0x4c000008
//读取UPLL设置寄存器的地址到R0中
ldrr0,=UPLLCON
//#defineUPLLCON_val((60
ldrr1,=UPLLCON_val
//将R1中的值写入UPLL设置寄存器中
strr1,[r0]
//ARM920T为5级流水线,需要至少5个周期来让指令生效
nop
nop
nop
nop
nop
nop
nop
nop
//读取MPLL设置寄存器的地址到R0中
ldrr0,=MPLLCON
//#defineMPLLCON_val((184
ldrr1,=MPLLCON_val
//将R1中的值写入MPLL设置寄存器中
strr1,[r0]
#defineGPJCON0x560000D0
#defineGPJDAT0x560000D4
#defineGPJUP0x560000D8
//跳转到cpu_init_crit处执行
//并将下一条指令的地址写入LR寄存器中
blcpu_init_crit
cpu_init_crit在cpu/arm920t/start.S中
代码如下:
cpu_init_crit:
/*
*flushv4I/Dcaches
*/
//将R0寄存器置0
movr0,#0
//InvalidateICacheandDCacheSBZMCRp15,0,Rd,c7,c7,0
//禁止指令和数据cache
mcrp15,0,r0,c7,c7,0/*flushv3/v4cache*/
//InvalidateTLB(s)SBZMCRp15,0,Rd,c8,c7,0
mcrp15,0,r0,c8,c7,0/*flushv4TLB*/
/*
*disableMMUstuffandcaches
*/
//MRCp15,0,Rd,c1,c0,0;readcontrolregister
mrcp15,0,r0,c1,c0,0
//清除[8][9][13]这3个位
//8-Systemprotection
//9-ROMprotection
//13-Baselocationofexceptionregisters-0=Lowaddresses=0x00000000.
bicr0,r0,#0x00002300//clearbits13,9:
8(--V---RS)
//清除[0][1][2][7]这4个位
//0-MMUenable-0=MMUdisabled.
//1-Alignmentfaultenable-0=Faultcheckingdisabled.
//2-DCacheenable-0=DCachedisabled.
//7-Endianness-0=Little-endianoperation.
bicr0,r0,#0x00000087//clearbits7,2:
0(B----CAM)
//设置位[1]为真
//1-Alignmentfaultenable-1=Faultcheckingenabled.
orrr0,r0,#0x00000002//setbit2(A)Align
//设置位[12]为真
//12-ICacheenable-1=ICacheenabled.
orrr0,r0,#0x00001000//setbit12(I)I-Cache
//MCRp15,0,Rd,c1,c0,0;writecontrolregister
mcrp15,0,r0,c1,c0,0
//将返回地址保存到IP中
movip,lr
//跳转到lowlevel_init中执行
bllowlevel_init
cpu_init_crit在cpu/arm920t/start.S中
代码如下:
.globllowlevel_init
//读取下面标号为SMRDATA处的地址到R0中
ldrr0,=SMRDATA
//读取上面标号为_TEXT_BASE处的地址内容到R1中
//也就是取得TEXT_BASE的值到R1中
ldrr1,_TEXT_BASE
//计算SMRDATA的相对地址保存到R0中
//SMRDATA为虚拟地址,而TEXT_BASE为虚拟地址的起始地址
//而现在Uboot的起始地址并不为虚拟地址
//TEXT_BASE为0x33F80000,SMRDATA为0x33F806C8
//而现在程序运行在起始地址为0x00000000的地方
//所以需要计算以0x00000000为标准的相对地址
subr0,r0,r1
//取得带宽与等待状态控制寄存器地址到R1中
ldrr1,=BWSCON/*BusWidthStatusController*/
//一共需要设置13个寄存器,每个寄存器4字节
addr2,r0,#13*4
0:
//读取R0所指的项的值到R3中后R0自加4字节
ldrr3,[r0],#4
//将R3中的值保存到R1所指的地址中后R1自加4字节
strr3,[r1],#4
//比较R0和R2是否相等,相等则说明13个寄存器全部设置完毕
cmpr2,r0
//不等则跳转到上面标号为0处的地址继续执行
bne0b
//跳回到返回地址中继续执行
movpc,lr
.ltorg
/*theliteralpoolsorigin*/
SMRDATA:
.word(0+(B1_BWSCON4)+(B2_BWSCON8)+(B3_BWSCON12)+(B4_BWSCON16)+(B5_BWSCON20)+(B6_BWSCON24)+(B7_BWSCON28))
.word((B0_Tacs13)+(B0_Tcos11)+(B0_Tacc8)+(B0_Tcoh6)+(B0_Tah4)+(B0_Tacp2)+(B0_PMC))
.word((B1_Tacs13)+(B1_Tcos11)+(B1_Tacc8)+(B1_Tcoh6)+(B1_Tah4)+(B1_Tacp2)+(B1_PMC))
.word((B2_Tacs13)+(B2_Tcos11)+(B2_Tacc8)+(B2_Tcoh6)+(B2_Tah4)+(B2_Tacp2)+(B2_PMC))
.word((B3_Tacs13)+(B3_Tcos11)+(B3_Tacc8)+(B3_Tcoh6)+(B3_Tah4)+(B3_Tacp2)+(B3_PMC))
.word((B4_Tacs13)+(B4_Tcos11)+(B4_Tacc8)+(B4_Tcoh6)+(B4_Tah4)+(B4_Tacp2)+(B4_PMC))
.word((B5_Tacs13)+(B5_Tcos11)+(B5_Tacc8)+(B5_Tcoh6)+(B5_Tah4)+(B5_Tacp2)+(B5_PMC))
.word((B6_MT15)+(B6_Trcd2)+(B6_SCAN))
.word((B7_MT15)+(B7_Trcd2)+(B7_SCAN))
.word((REFEN23)+(TREFMD22)+(Trp20)+(Trc18)+(Tchr16)+REFCNT)
.word0x32
.word0x30
.word0x30
执行movpc,lr后将返回到cpu_init_crit中
剩下来还有2条指令
//恢复返回地址到LR
movlr,ip
//跳转到返回地址
movpc,lr
执行完毕之后将返回到start_code中执行接下来的代码
代码如下:
//#defineGPJCON0x560000D0
//取得J端口控制寄存器的地址到R0中
LDRR0,=GPJCON
//将R1设置为0x15555
LDRR1,=0x15555
//将R1中的值保存到J端口控制寄存器
//GPJ0-01-Output
//GPJ1-01-Output
//GPJ2-01-Output
//GPJ3-01-Output
//GPJ4-01-Output
STRR1,[R0]
//#defineGPJUP0x560000D8
//取得J端口上拉功能寄存器的地址到R0中
LDRR0,=GPJUP
//将R1设置为0x1F
LDRR1,=0x1f
//将R1中的值保存到J端口上拉功能寄存器
//禁止GPJ0-GPJ4的上拉功能
STRR1,[R0]
//#defineGPJDAT0x560000D4
//取得J端口数据寄存器的地址到R0中
LDRR0,=GPJDAT
//将R1设为0x0
LDRR1,=0x00
//将R1中的值保存到J端口数据寄存器
//将J端口数据寄存器清0
STRR1,[R0]
//下面是NAND数据拷贝过程
//relocate:
copy_myself:
//#defineS3C2440_NAND_BASE0x4E000000
//取得NandFlash设置寄存器的地址
movr1,#S3C2440_NAND_BASE
//将R2设为0xFFF0
ldr