UBOOT移植详细 很全面.docx
《UBOOT移植详细 很全面.docx》由会员分享,可在线阅读,更多相关《UBOOT移植详细 很全面.docx(36页珍藏版)》请在冰豆网上搜索。
UBOOT移植详细很全面
UBOOT移植详细很全面(2010-11-1913:
46)
分类:
ARM汇编
u-boot-1.3.4移植到S3C2440(带有某些解析)
移植u-boot-1.3.4到S3C2440
一.预备知识:
1.首先,U-Boot1.3.4还没有支持s3c2440,移植仍是用2410的文件稍作修改而成的。
2.2440和2410的区别:
2440和2410的区别主要是2440的主频更高,增加了摄像头接口和AC‘97音频接口;寄存器方面,除了新增模块的寄存器外,移植所要注意的是NANDFlASH控制器的寄存器有较大的变化、芯片的时钟频率控制寄存器(芯片PLL的寄存器)有一定的变化。
其他寄存器基本是兼容的。
3.你开发板的boot方式是什么,开发板上电以后是怎么执行的。
一般来说三星的开发板有三种启动方式:
nand、nor、ram。
具体用那一种方式来启动决定于CPU的0M[0:
1]这两个引脚,具体请参考S3C2440的datasheet
nand:
对于2440来说,CPU是不给nand-flash分配地址空间的,nand-flash只相当于CPU的一个外设,S3C2440做了一个从nand-flash启动的机制。
开发板一上电,CPU就自动复制
nand-flash里面的前4K-Bytes内容到S3C2440内部集成的SDRAM,然后把4K内容所在的RAM映射到S3C2440的0地址,从0地址开始执行。
这4K的内容主要负责下面这些工作:
初始化中断矢量、设定CPU的工作模式为SVC32模式、屏蔽看门狗、屏蔽中断、初始化时钟、把整个u-boot重定向到外部SDRAM、跳到主要的C函数入口。
nor:
早期的时候利用nor-flash启动的方式比较多,就是把u-boot烧写到nor-flash里面,直接把nor-flash映射到S3C2440的0地址,上电从0地址开始执行。
ram:
直接把u-boot放到外部SDRAM上跑,这一般debug时候用到。
4.u-boot程序的入口地址问题
要理解程序的入口地址,自然想到的是连接文件,首先看看开发板相对于某个开发板的连接文件"/board/你的开发板/u-boot.lds",看一个2410的例子:
ENTRY(_start)
SECTIONS
{
.=0x00000000;
.=ALIGN(4);
.text:
{
cpu/arm920t/start.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=.;
}
(1)从ENTRY(_start)可以看出u-boot的入口函数是_start,这个没错
(2)从.=0x00000000也许可以看出_start的地址是0x00000000,事实并不是这样的,这里的0x00000000没效,在连接的时候最终会被TETX_BASE所代替的,具体请参考u-boot根目录下的config.mk.
(3)网上很多说法是_start=TEXT_BASE,我想这种说法也是正确的,但没有说具体原因。
本人的理解是这样的,TEXT_BASE表示text段的起始地址,而从
.text:
{
cpu/arm920t/start.o(.text)
*(.text)
}
看,放在text段的第一个文件就是start.c编译后的内容,而start.c中的第一个函数就是
_start,所以_start应该是放在text段的起始位置,因此说_start=TEXT_BASE也不为过。
5.一直不明白的U-BOOT是怎样从4Ksteppingstone跳到RAM中执行的,现在终于明白了。
关键在于:
ldrpc,_start_armboot
_start_armboot:
.wordstart_armboot
这两条语句,ldrpc,_start_armboot指令把_start_armboot这个标签的地方存放的内容(也即是start_armboot)移到PC寄存器里面,start_armboot是一个函数地址,在编译的时候给分配了一个绝对地址,所以上面语句实际上是完成了一个绝对地址的跳转。
而我一直不明白的为什么在start.S里面有很多BL,B跳转语句都没有跳出4Ksteppingstone,原因是他们都是相对于PC的便宜的跳转,而不是绝对地址的跳转。
还有要补充一下LDR,MOV,LDR伪指令的区别。
LDRR0,0x12345678//把地址0x12345678存放的内容放到R0里面
MOVR0,#x//把立即数x放到R0里面,x必须是一个8bits的数移到偶数次得到的数。
LDRR0,=0x12345678//把立即数0x12345678放到R0里面
6.在移植u-boot-1.3.3以上版本的时候要注意:
在u-boot1.3.3及以上版本Makefile有一定的变化,使得对于24x0处理器从nand启动的遇到问题。
也就是网上有人说的:
无法运行过lowlevel_init。
其实这个问题是由于编译器将我们自己添加的用于nandboot的子函数nand_read_ll放到了4K之后造成的(到这不理解的话,请仔细看看24x0处理器nandboot原理)。
我是在运行失败后,利用mini2440的4个LED调试发现u-boot根本没有完成自我拷贝,然后看了uboot根目录下的System.map文件就可知道原因。
解决办法其实很简单:
将__LIBS:
=$(subst$(obj),,$(LIBS))$(subst$(obj),,$(LIBBOARD))
改为__LIBS:
=$(subst$(obj),,$(LIBBOARD))$(subst$(obj),,$(LIBS))
7.然后说一下跳转指令。
ARM有两种跳转方式。
(1)movpc<跳转地址〉
这种向程序计数器PC直接写跳转地址,能在4GB连续空间内任意跳转。
(2)通过BBLBLXBX可以完成在当前指令向前或者向后32MB的地址空间的跳转(为什么是32MB呢?
寄存器是32位的,此时的值是24位有符号数,所以32MB)。
B是最简单的跳转指令。
要注意的是,跳转指令的实际值不是绝对地址,而是相对地址——是相对当前PC值的一个偏移量,它的值由汇编器计算得出。
BL非常常用。
它在跳转之前会在寄存器LR(R14)中保存PC的当前内容。
BL的经典用法如下:
blNEXT;跳转到NEXT
……
NEXT
……
movpc,lr;从子程序返回。
二.开始上机移植:
(红色字体为添加的内容,蓝色字体为修改的内容,下同)
给自己的开发板取名为qljt2440。
1.随便找个目录解压u-boot,
$tar–xjvfu-boot-1.3.4.tar.gz2
2.进入u-boot目录修改Makefile(你要编译u-boot那当然少不了配置啦)
$cdu-boot-1.3.4
[uboot@localhostu-boot-1.3.4]$vimMakefile修改内容如下:
__LIBS:
=$(subst$(obj),,$(LIBS))$(subst$(obj),,$(LIBBOARD))
改为
__LIBS:
=$(subst$(obj),,$(LIBBOARD))$(subst$(obj),,$(LIBS))
sbc2410x_config:
unconfig@$(MKCONFIG)$(@:
_config=)armarm920tsbc2410xNULLs3c24x0
qljt2440_config:
unconfig@$(MKCONFIG)$(@:
_config=)armarm920tqljt2440qljts3c24x0
/*
各项的意思如下:
qljt2440_config:
这个名字是将来你配置板子时候用到的名字,参见makeqljt2440_config命令。
arm:
CPU的架构(ARCH)
arm920t:
CPU的类型(CPU),其对应于cpu/arm920t子目录。
qljt2440:
开发板的型号(BOARD),对应于board/qljt/qljt2440目录。
qljt:
开发者/或经销商(vender)。
s3c24x0:
片上系统(SOC)。
*/
4.在/board子目录中建立自己的开发板qljt2440目录
由于我在上一步板子的开发者/或经销商(vender)中填了qljt,所以开发板qljt2440目录一定要建在/board子目录中的qljt目录下,否则编译会出错。
[uboot@localhostu-boot-1.3.4]$cdboard
[uboot@localhostboard]$mkdirqljtqljt/qljt2440
[uboot@localhostboard]$cp-arfsbc2410x/*qljt/qljt2440/
[uboot@localhostboard]$cdqljt/qljt2440/
[uboot@localhostqljt2440]$mvsbc2410x.cqljt2440.c
[uboot@localhostqljt2440]$ls可以看到下面这些文件:
config.mkflash.clowlevel_init.sMakefileqljt2440.cu-boot.lds
[uboot@localhostqljt2440]$vimMakefile
COBJS:
=qljt2440.oflash.o
5.在include/configs/中建立开发板所需要的配置头文件
[uboot@localhostqljt2440]$cd../../..
[uboot@localhostu-boot-1.3.4]$cpinclude/configs/sbc2410x.hinclude/configs/qljt2440.h
6.测试交叉编译能否成功
(1)配置
[uboot@localhostu-boot-1.3.4]$makeqljt2440_config
Configureforqljt2440board…
(2)测试编译
[uboot@localhostu-boot-1.3.4]$make
详细信息如下:
编译信息最后两行:
arm-linux-objcopy--gap-fill=0xff-Osrecu-bootu-boot.srec
arm-linux-objcopy--gap-fill=0xff-Obinaryu-bootu-boot.bin
到此交叉编译成功。
三.开始针对自己的开发板移植
1.修改/cpu/arm920t/start.S
1.1修改寄存器地址定义
#ifdefined(CONFIG_S3C2400)||defined(CONFIG_S3C2410)||defined(CONFIG_S3C2440)
/*turnoffthewatchdog*/
#ifdefined(CONFIG_S3C2400)
#definepWTCON0x15300000
#defineINTMSK0x14400008/*Interupt-Controllerbaseaddresses*/
#defineCLKDIVN0x14800014/*clockdivisorregister*/
#else
#definepWTCON0x53000000/*该地址用来屏蔽看门狗*/
#defineINTMSK0x4A000008/*Interupt-Controllerbaseaddresses该地址用来屏蔽中断*/
#defineINTSUBMSK0x4A00001C/*该地址用来屏蔽子中断*/
#defineCLKDIVN0x4C000014/*clockdivisorregister该地址用来决定FCLK、HCLK、PCLK的比例*/
#defineCLK_CTL_BASE0x4c000000/*qljt从S3C2440A.pdf中可以看出该寄存器是存放Mpll和Upll的P254*/
#ifdefined(CONFIG_S3C2440)
#defineMDIV_4050x7f<<12/*qljt参见P255表,同时要知道本开发板的Fin是12MHz,需要的Fclk(也就
是Mpll)是405MHz*/
#definePSDIV_4050x21/*qljt同上,同时设定PDIV和SDIV的值,PDIV和SDIV参见S3C2440A.pdf*/
#endif
#endif
1.2修改中断禁止部分
#ifdefined(CONFIG_S3C2410)
ldrr1,=0x7ff//根据2410芯片手册,INTSUBMSK有11位可用,
//vivi也是0x7ff,不知为什么U-Boot一直没改过来。
但是由于芯片复位默认
//所有的终端都是被屏蔽的,所以这个不影响工作
ldrr0,=INTSUBMSK
strr1,[r0]
#endif
#ifdefined(CONFIG_S3C2440)
ldrr1,=0x7fff//根据2440芯片手册,INTSUBMSK有15位可用
ldrr0,=INTSUBMSK
strr1,[r0]
#endif
1.3修改时钟设置
/*时钟控制逻辑单元能够产生s3c2440需要的时钟信号,包括CPU使用的主频FCLK,AHB总线使用的HCLK,APB总线设备使用的PCLK,2440里面的两个锁相环(PLL),其中一个对应FCLK、HCLK、PCLK,另外一个对应UCLK(48MHz)*/
/*注意:
AHP、APB总线的简介参见“AHB与APB总线.doc”*/
/*FCLK:
HCLK:
PCLK=1:
4:
8*/
ldrr0,=CLKDIVN
movr1,#5
strr1,[r0]
/*这三条协处理器命令确实不知道什么意思,在ATXJGYBC_ql.pdf中搜p15和c1,只知道它们执行以后会把协处理器p15的寄存器c1的最高两位置1,但c1的最高两位是没有意义啊,弄不懂它的真正意思
不过我却知道这三条语句是从哪里出来的,详细请参考s3c2440的datasheet和s3c2440datasheet中的R1_nF和R1_iA.doc*/
mrcp15,0,r1,c1,c0,0/*readctrlregisterqljt*/
orrr1,r1,#0xc0000000/*Asynchronousqljt改变总线模式为异步模式网上某位朋友说不知到在哪里看到过
如果FCLK与HCLK不同的话就要选择这种模式的*/
mcrp15,0,r1,c1,c0,0/*writectrlregisterqljt*/
#ifdefined(CONFIG_S3C2440)//(2440的主频可达533MHz,但听说设到533MHz时系统
//很不稳定,不知是不是SDRAM和总线配置的影响,所以现在先设到//405MHz,以后在改进。
)
/*now,CPUclockis405.00Mhzqljt*/
movr1,#CLK_CTL_BASE/*qljt*/
movr2,#MDIV_405/*mpll_405mhzqljt*/
addr2,r2,#PSDIV_405/*mpll_405mhzqljt*/
strr2,[r1,#0x04]/*MPLLCONqljt实际上是设置寄存器CLK_CTL_BASE+0x04=0x4c000004的值*/
#endif
#endif/*CONFIG_S3C2400||CONFIG_S3C2410||CONFIG_S3C2440*/
1.4将从Flash启动改成从NANDFlash启动。
(特别注意:
这和2410的程序有不同,不可混用!
!
!
是拷贝vivi的代码。
)
将以下U-Boot的重定向语句段:
@#ifndefCONFIG_AT91RM9200
#if0
#ifndefCONFIG_SKIP_RELOCATE_UBOOT
relocate:
/*relocateU-BoottoRAM*/
adrr0,_start/*r0<-currentpositionofcode*/
ldrr1,_TEXT_BASE/*testifwerunfromflashorRAM*/
cmpr0,r1/*don'trelocduringdebug*/
beqstack_setup
ldrr2,_armboot_start
ldrr3,_bss_start
subr2,r3,r2/*r2<-sizeofarmboot*/
addr2,r0,r2/*r2<-sourceendaddress*/
copy_loop:
ldmiar0!
{r3-r10}/*copyfromsourceaddress[r0]*/
stmiar1!
{r3-r10}/*copytotargetaddress[r1]*/
cmpr0,r2/*untilsourceendaddreee[r2]*/
blecopy_loop
#endif/*CONFIG_SKIP_RELOCATE_UBOOT*/
#endif/*CONFIG_AT91RM9200*/
然后添加:
/*下载了一个vivi源代码看了一下,还真的有下面哪一段代码*/
#ifdefCONFIG_S3C2440_NAND_BOOT@qljt@@@@@@@@@@@@@@@@SSSSSSSSSSSSS
@resetNAND
/*往下四段内容都是针对S3C2440的关于NAND-FLASH的寄存器的设置,具体有什么作用,看了datasheet,有些明白有些不明白*/
movr1,#NAND_CTL_BASE
ldrr2,=((7<<12)|(7<<8)|(7<<4)|(0<<0))
strr2,[r1,#oNFCONF]/*这些宏是在include/configs/qljt2440.h中被定义的*/
ldrr2,[r1,#oNFCONF]/*还是弄不懂为什么上面一句str以后还要有这句的ldr命令?
why?
难道是多余的?
*/
ldrr2,=((1<<4)|(0<<1)|(1<<0))@ActivelowCEControl
strr2,[r1,#oNFCONT]
ldrr2,[r1,#oNFCONT]
ldrr2,=(0x6)@RnBClear
strr2,[r1,#oNFSTAT]
ldrr2,[r1,#oNFSTAT]
movr2,#0xff@RESETcommand
strbr2,[r1,#oNFCMD]
/*delay一段时间*/
movr3,#0@wait
nand1:
addr3,r3,#0x1
cmpr3,#0xa
bltnand1
/*等待nand-flash的复位完毕信号*/
nand2:
ldrr2,[r1,#oNFSTAT]@waitready
tstr2,#0x4
beqnand2
ldrr2,[r1,#oNFCONT]
orrr2,r2,#0x2@FlashMemoryChipDisable/*在这里先DisplayfanshCE先,在C函数中对falsh进行*/
strr2,[r1,#oNFCONT]/*操作的时候才enable,为什么这样操作不太清楚*/
/*下面这段用来初始化栈指针sp和帧指针fp,至于它们的定义和作用参考文件夹”栈指针sp和帧指针fp”里面的内容
记住它们都是与函数调用时候相关的。
简单来讲就是子函数被调用以后是通过指针的相对位置来查找调用参数和局部变量的,但是由于sp经常变化,所以需要fp来协助。
*/
@getreadytocallCfunctions(fornand_read())
ldrsp,DW_STACK_START@setupstackpointer/*sp是指堆栈指针*/
movfp,#0@nopreviousframe,sofp=0
@copyU-BoottoRAM/*vivi里面应该是有一段是针对gpio的程序,也许使用来debug用的信号灯,这里省略了*/
/*TEXT_BASE是uboot自己的入口地址,在u-boot-1.3.4-board/qljt/qljt2440的config.mk中定义
有趣的是外国人的逆向思维很厉害,它们很灵活地把它放在SDRAM的最后0x80000地方,也就是0x33F80000
*/
ldrr0,=TEXT_BASE/*r0:
把u-boot复制到ram的那个位置*/
movr1,#0x0/*r1:
从falsh的那个位置开始复制*/
movr2,#0x20000/*r2:
复制多大的内容*/
blnand_read_ll/*跳到执行uboot复制的程序入口,这个函数从哪里来?
也是来自vivi的,没办法*/
tstr0,#0x0/*这里特别注意r0的值是指nand_read_ll执行完以后的返回值,而不是上面
ldrr0,=TEXT_BASE的值,初学者往往在这里想不通*/
beqok_nand_read
bad_nand_read:
/*如果读nand_read失败的话,那么sorry,重来,或者检查硬件*/
loop2:
bloop2@infiniteloop
ok_nand_read:
@verify
/*计算机就是好,很容易就可以检测我们放在SDRAM中的u-boot是不是flash中的uboot。
本开发板使用的是nand-falsh的启动方式,板子一上电并不是马上进入SDRAM执行程序的。
是这样的:
板子一上电,S3C2440自动把