我的BL1源代码来源于网路,博客地址是http://blog.csdn.net/xiaojiaohuazi/article/details/8265757。在这里记录下自己的移植心得。
为什么要制作这个BL1呢,对于官方以及很多人说的它是u-boot启动的第二阶段,这里我不做过多说明,我想说的是,这个BL1可以让你对内存操作的理解更加深入,对于后面移植u-boot的理解有帮助。
实际上在BL1阶段只需要做串口和内存的初始化就可以了,初始化串口的目的是调试使用,这个是必须要的;初始化内存那不用说了,你想拷贝代码,不初始化内存怎么能行的。
但是在这里我还加入了一个LED的操作代码,为什么要加入呢,对于学习一个东西,不能说会移植了就算学会了,我想有问题的时候会调试才是更重要的,在开发时也是如此,有了调试手段就有了查错的手段,那么找问题就可以很方便的定位了。所以加入这个LED的控制目的也就是为了调试使用,这也是看到网上有人说过,仔细想想,非常好用,O(∩_∩)O。
首先看看我的BL1的所有文件:
这里只说明移植的时候涉及到的文件,build、main.c、mem_setup.S、s5pv210.h、start.S这几个文件。其中build是我加入的一个文件,目的在操作编译烧写的时候减少输入命令,build文件的内容如下:
#!/bin/sh
make
./mkv210_image bl.bin blSD2.bin
dd iflag=dsync oflag=dsync if=blSD2.bin of=/dev/sdc seek=1
做的工作就是编译,制作BL1,烧写到SD卡。
这个BL1启动的入口也是在start.S文件,看看做的是什么:
.text
.global _start
_start:
bl mem_ctrl_asm_init
ldr sp, =0xD0035400
bl main
loop:
b loop
mem_ctrl_asm_init:
/* DMC0 Drive Strength (Setting 2X) */
ldr r0, =ELFIN_GPIO_BASE
ldr r1, =0x0000AAAA
str r1, [r0, #MP1_0DRV_SR_OFFSET]
ldr r1, =0x0000AAAA
str r1, [r0, #MP1_1DRV_SR_OFFSET]
ldr r1, =0x0000AAAA
str r1, [r0, #MP1_2DRV_SR_OFFSET]
ldr r1, =0x0000AAAA
str r1, [r0, #MP1_3DRV_SR_OFFSET]
ldr r1, =0x0000AAAA
str r1, [r0, #MP1_4DRV_SR_OFFSET]
ldr r1, =0x0000AAAA
str r1, [r0, #MP1_5DRV_SR_OFFSET]
ldr r1, =0x0000AAAA
str r1, [r0, #MP1_6DRV_SR_OFFSET]
ldr r1, =0x0000AAAA
str r1, [r0, #MP1_7DRV_SR_OFFSET]
ldr r1, =0x00002AAA
str r1, [r0, #MP1_8DRV_SR_OFFSET]
/* DMC1 Drive Strength (Setting 2X) */
ldr r0, =ELFIN_GPIO_BASE
ldr r1, =0x0000AAAA
str r1, [r0, #MP2_0DRV_SR_OFFSET]
ldr r1, =0x0000AAAA
str r1, [r0, #MP2_1DRV_SR_OFFSET]
ldr r1, =0x0000AAAA
str r1, [r0, #MP2_2DRV_SR_OFFSET]
ldr r1, =0x0000AAAA
str r1, [r0, #MP2_3DRV_SR_OFFSET]
ldr r1, =0x0000AAAA
str r1, [r0, #MP2_4DRV_SR_OFFSET]
ldr r1, =0x0000AAAA
str r1, [r0, #MP2_5DRV_SR_OFFSET]
ldr r1, =0x0000AAAA
str r1, [r0, #MP2_6DRV_SR_OFFSET]
ldr r1, =0x0000AAAA
str r1, [r0, #MP2_7DRV_SR_OFFSET]
ldr r1, =0x00002AAA
str r1, [r0, #MP2_8DRV_SR_OFFSET]
/* DMC0 initialization at single Type*/
ldr r0, =APB_DMC_0_BASE
ldr r1, =0x00101000 @PhyControl0 DLL parameter setting, manual 0x00101000
str r1, [r0, #DMC_PHYCONTROL0]
ldr r1, =0x00000086 @PhyControl1 DLL parameter setting, LPDDR/LPDDR2 Case
str r1, [r0, #DMC_PHYCONTROL1]
ldr r1, =0x00101002 @PhyControl0 DLL on
str r1, [r0, #DMC_PHYCONTROL0]
ldr r1, =0x00101003 @PhyControl0 DLL start
str r1, [r0, #DMC_PHYCONTROL0]
find_lock_val:
ldr r1, [r0, #DMC_PHYSTATUS] @Load Phystatus register value
and r2, r1, #0x7
cmp r2, #0x7 @Loop until DLL is locked
bne find_lock_val
and r1, #0x3fc0
mov r2, r1, LSL #18
orr r2, r2, #0x100000
orr r2 ,r2, #0x1000
orr r1, r2, #0x3 @Force Value locking
str r1, [r0, #DMC_PHYCONTROL0]
#if 0 /* Memory margin test 10.01.05 */
orr r1, r2, #0x1 @DLL off
str r1, [r0, #DMC_PHYCONTROL0]
#endif
/* setting DDR2 */
ldr r1, =0x0FFF2010 @ConControl auto refresh off
str r1, [r0, #DMC_CONCONTROL]
ldr r1, =DMC0_MEMCONTROL @MemControl BL=4, 2 chip, DDR2 type, dynamic self refresh, force precharge, dynamic power down off
str r1, [r0, #DMC_MEMCONTROL]
ldr r1, =DMC0_MEMCONFIG_0 @MemConfig0 256MB config, 8 banks,Mapping Method[12:15]0:linear, 1:linterleaved, 2:Mixed
str r1, [r0, #DMC_MEMCONFIG0]
ldr r1, =DMC0_MEMCONFIG_1 @MemConfig1
str r1, [r0, #DMC_MEMCONFIG1]
ldr r1, =0xFF000000 @PrechConfig
str r1, [r0, #DMC_PRECHCONFIG]
ldr r1, =DMC0_TIMINGA_REF @TimingAref 7.8us*133MHz=1038(0x40E), 100MHz=780(0x30C), 20MHz=156(0x9C), 10MHz=78(0x4E)
str r1, [r0, #DMC_TIMINGAREF]
ldr r1, =DMC0_TIMING_ROW @TimingRow for @200MHz
str r1, [r0, #DMC_TIMINGROW]
ldr r1, =DMC0_TIMING_DATA @TimingData CL=3
str r1, [r0, #DMC_TIMINGDATA]
ldr r1, =DMC0_TIMING_PWR @TimingPower
str r1, [r0, #DMC_TIMINGPOWER]
ldr r1, =0x07000000 @DirectCmd chip0 Deselect
str r1, [r0, #DMC_DIRECTCMD]
ldr r1, =0x01000000 @DirectCmd chip0 PALL
str r1, [r0, #DMC_DIRECTCMD]
ldr r1, =0x00020000 @DirectCmd chip0 EMRS2
str r1, [r0, #DMC_DIRECTCMD]
ldr r1, =0x00030000 @DirectCmd chip0 EMRS3
str r1, [r0, #DMC_DIRECTCMD]
ldr r1, =0x00010400 @DirectCmd chip0 EMRS1 (MEM DLL on, DQS# disable)
str r1, [r0, #DMC_DIRECTCMD]
ldr r1, =0x00000542 @DirectCmd chip0 MRS (MEM DLL reset) CL=4, BL=4
str r1, [r0, #DMC_DIRECTCMD]
ldr r1, =0x01000000 @DirectCmd chip0 PALL
str r1, [r0, #DMC_DIRECTCMD]
ldr r1, =0x05000000 @DirectCmd chip0 REFA
str r1, [r0, #DMC_DIRECTCMD]
ldr r1, =0x05000000 @DirectCmd chip0 REFA
str r1, [r0, #DMC_DIRECTCMD]
ldr r1, =0x00000442 @DirectCmd chip0 MRS (MEM DLL unreset)
str r1, [r0, #DMC_DIRECTCMD]
ldr r1, =0x00010780 @DirectCmd chip0 EMRS1 (OCD default)
str r1, [r0, #DMC_DIRECTCMD]
ldr r1, =0x00010400 @DirectCmd chip0 EMRS1 (OCD exit)
str r1, [r0, #DMC_DIRECTCMD]
ldr r1, =0x07100000 @DirectCmd chip1 Deselect
str r1, [r0, #DMC_DIRECTCMD]
ldr r1, =0x01100000 @DirectCmd chip1 PALL
str r1, [r0, #DMC_DIRECTCMD]
ldr r1, =0x00120000 @DirectCmd chip1 EMRS2
str r1, [r0, #DMC_DIRECTCMD]
ldr r1, =0x00130000 @DirectCmd chip1 EMRS3
str r1, [r0, #DMC_DIRECTCMD]
ldr r1, =0x00110400 @DirectCmd chip1 EMRS1 (MEM DLL on, DQS# disable)
str r1, [r0, #DMC_DIRECTCMD]
ldr r1, =0x00100542 @DirectCmd chip1 MRS (MEM DLL reset) CL=4, BL=4
str r1, [r0, #DMC_DIRECTCMD]
ldr r1, =0x01100000 @DirectCmd chip1 PALL
str r1, [r0, #DMC_DIRECTCMD]
ldr r1, =0x05100000 @DirectCmd chip1 REFA
str r1, [r0, #DMC_DIRECTCMD]
ldr r1, =0x05100000 @DirectCmd chip1 REFA
str r1, [r0, #DMC_DIRECTCMD]
ldr r1, =0x00100442 @DirectCmd chip1 MRS (MEM DLL unreset)
str r1, [r0, #DMC_DIRECTCMD]
ldr r1, =0x00110780 @DirectCmd chip1 EMRS1 (OCD default)
str r1, [r0, #DMC_DIRECTCMD]
ldr r1, =0x00110400 @DirectCmd chip1 EMRS1 (OCD exit)
str r1, [r0, #DMC_DIRECTCMD]
ldr r1, =0x0FF02030 @ConControl auto refresh on
str r1, [r0, #DMC_CONCONTROL]
ldr r1, =0xFFFF00FF @PwrdnConfig
str r1, [r0, #DMC_PWRDNCONFIG]
ldr r1, =DMC0_MEMCONTROL @MemControl 0x00202400 BL=4, 2 chip, DDR2 type, dynamic self refresh, force precharge, dynamic power down off
str r1, [r0, #DMC_MEMCONTROL]
/* DMC1 initialization */
ldr r0, =APB_DMC_1_BASE
ldr r1, =0x00101000 @Phycontrol0 DLL parameter setting
str r1, [r0, #DMC_PHYCONTROL0]
ldr r1, =0x00000086 @Phycontrol1 DLL parameter setting
str r1, [r0, #DMC_PHYCONTROL1]
ldr r1, =0x00101002 @PhyControl0 DLL on
str r1, [r0, #DMC_PHYCONTROL0]
ldr r1, =0x00101003 @PhyControl0 DLL start
str r1, [r0, #DMC_PHYCONTROL0]
find_lock_val1:
ldr r1, [r0, #DMC_PHYSTATUS] @Load Phystatus register value
and r2, r1, #0x7
cmp r2, #0x7 @Loop until DLL is locked
bne find_lock_val1
and r1, #0x3fc0
mov r2, r1, LSL #18
orr r2, r2, #0x100000
orr r2, r2, #0x1000
orr r1, r2, #0x3 @Force Value locking
str r1, [r0, #DMC_PHYCONTROL0]
#if 0 /* Memory margin test 10.01.05 */
orr r1, r2, #0x1 @DLL off
str r1, [r0, #DMC_PHYCONTROL0]
#endif
/* settinf fot DDR2 */
ldr r0, =APB_DMC_1_BASE
ldr r1, =0x0FFF2010 @auto refresh off
str r1, [r0, #DMC_CONCONTROL]
ldr r1, =DMC1_MEMCONTROL @MemControl BL=4, 2 chip, DDR2 type, dynamic self refresh, force precharge, dynamic power down off
str r1, [r0, #DMC_MEMCONTROL]
ldr r1, =DMC1_MEMCONFIG_0 @MemConfig0 512MB config, 8 banks,Mapping Method[12:15]0:linear, 1:linterleaved, 2:Mixed
str r1, [r0, #DMC_MEMCONFIG0]
ldr r1, =DMC1_MEMCONFIG_1 @MemConfig1
str r1, [r0, #DMC_MEMCONFIG1]
ldr r1, =0xFF000000
str r1, [r0, #DMC_PRECHCONFIG]
ldr r1, =DMC1_TIMINGA_REF @TimingAref 7.8us*133MHz=1038(0x40E), 100MHz=780(0x30C), 20MHz=156(0x9C), 10MHz=78(0x4
str r1, [r0, #DMC_TIMINGAREF]
ldr r1, =DMC1_TIMING_ROW @TimingRow for @200MHz
str r1, [r0, #DMC_TIMINGROW]
ldr r1, =DMC1_TIMING_DATA @TimingData CL=3
str r1, [r0, #DMC_TIMINGDATA]
ldr r1, =DMC1_TIMING_PWR @TimingPower
str r1, [r0, #DMC_TIMINGPOWER]
ldr r1, =0x07000000 @DirectCmd chip0 Deselect
str r1, [r0, #DMC_DIRECTCMD]
ldr r1, =0x01000000 @DirectCmd chip0 PALL
str r1, [r0, #DMC_DIRECTCMD]
ldr r1, =0x00020000 @DirectCmd chip0 EMRS2
str r1, [r0, #DMC_DIRECTCMD]
ldr r1, =0x00030000 @DirectCmd chip0 EMRS3
str r1, [r0, #DMC_DIRECTCMD]
ldr r1, =0x00010400 @DirectCmd chip0 EMRS1 (MEM DLL on, DQS# disable)
str r1, [r0, #DMC_DIRECTCMD]
ldr r1, =0x00000542 @DirectCmd chip0 MRS (MEM DLL reset) CL=4, BL=4
str r1, [r0, #DMC_DIRECTCMD]
ldr r1, =0x01000000 @DirectCmd chip0 PALL
str r1, [r0, #DMC_DIRECTCMD]
ldr r1, =0x05000000 @DirectCmd chip0 REFA
str r1, [r0, #DMC_DIRECTCMD]
ldr r1, =0x05000000 @DirectCmd chip0 REFA
str r1, [r0, #DMC_DIRECTCMD]
ldr r1, =0x00000442 @DirectCmd chip0 MRS (MEM DLL unreset)
str r1, [r0, #DMC_DIRECTCMD]
ldr r1, =0x00010780 @DirectCmd chip0 EMRS1 (OCD default)
str r1, [r0, #DMC_DIRECTCMD]
ldr r1, =0x00010400 @DirectCmd chip0 EMRS1 (OCD exit)
str r1, [r0, #DMC_DIRECTCMD]
ldr r1, =0x07100000 @DirectCmd chip1 Deselect
str r1, [r0, #DMC_DIRECTCMD]
ldr r1, =0x01100000 @DirectCmd chip1 PALL
str r1, [r0, #DMC_DIRECTCMD]
ldr r1, =0x00120000 @DirectCmd chip1 EMRS2
str r1, [r0, #DMC_DIRECTCMD]
ldr r1, =0x00130000 @DirectCmd chip1 EMRS3
str r1, [r0, #DMC_DIRECTCMD]
ldr r1, =0x00110440 @DirectCmd chip1 EMRS1 (MEM DLL on, DQS# disable)
str r1, [r0, #DMC_DIRECTCMD]
ldr r1, =0x00100542 @DirectCmd chip1 MRS (MEM DLL reset) CL=4, BL=4
str r1, [r0, #DMC_DIRECTCMD]
ldr r1, =0x01100000 @DirectCmd chip1 PALL
str r1, [r0, #DMC_DIRECTCMD]
ldr r1, =0x05100000 @DirectCmd chip1 REFA
str r1, [r0, #DMC_DIRECTCMD]
ldr r1, =0x05100000 @DirectCmd chip1 REFA
str r1, [r0, #DMC_DIRECTCMD]
ldr r1, =0x00100442 @DirectCmd chip1 MRS (MEM DLL unreset)
str r1, [r0, #DMC_DIRECTCMD]
ldr r1, =0x00110780 @DirectCmd chip1 EMRS1 (OCD default)
str r1, [r0, #DMC_DIRECTCMD]
ldr r1, =0x00110400 @DirectCmd chip1 EMRS1 (OCD exit)
str r1, [r0, #DMC_DIRECTCMD]
ldr r1, =0x0FF02030 @ConControl auto refresh on
str r1, [r0, #DMC_CONCONTROL]
ldr r1, =0xFFFF00FF @PwrdnConfig
str r1, [r0, #DMC_PWRDNCONFIG]
ldr r1, =DMC1_MEMCONTROL @MemControl BL=4, 2 chip, DDR2 type, dynamic self refresh, force precharge, dynamic power down off
str r1, [r0, #DMC_MEMCONTROL]
//下面为点灯代码,用于调试
ldr r0, =0xE0200C00
ldr r1, =0x11000000
str r1, [r0]
ldr r1, =0xc0
str r1, [r0, #4]
//上面为点灯代码,用于调试
mov pc, lr
好,现在回到该函数的起始部分,对于该函数已经有人做过了分析,这里我就引用之博文地址为http://blog.csdn.net/mutemob/article/details/12974483。在这之外我们需要注意一个问题,那就是下面代码的位置处
有可能你的代码红色部分为0x00202400这种格式的16进制,最好是改成上图所示的请款个,目的是为了方便修改。
从start.S文件中可以知道,第二个执行的函数为main函数,函数位置在main.c。
main.c文件源代码请下载下来观看,这里我就不一次全部贴上来了。我们从main主函数出发,看看都做了些什么:
int main (void)
{
__attribute__((noreturn)) void (*uboot)(void);
uart_init(); //串口初始化
mem_test(); //内存测试
printf ("copy uboot from sd to sdram ddr2\r\n");
copy_uboot_to_ram();
printf ("jump to u-boot image\r\n");
//下面代码是控制班子上的LED,可用于调试
//*(volatile unsigned int *)0xE0200C00 = 0x11000000;
//*(volatile unsigned int *)0xE0200C04 = 0x000000c0;
//上面代码是控制班子上的LED,可用于调试
/* Jump to U-Boot imag
uboot = (void *)0x33e00000;
(*uboot)();
return 1;
}
开始进行了串口的初始化,然后是内存的测试,接着是u-boot的拷贝阶段,最后跳转到u-boot的位置开始执行。我的u-boot的起始地址是0x33e00000,不同的板子地址可能有所不同,请调整为自己的u-boot起始地址即可,也就是把0x33e00000换成自己的板子的地址。
串口的初始化我的板子是Real210板子,调试口使用的是串口2,所以在网络上原来的代码基础上把他的串口初始化从串口0修改到了串口2,在源码的s5pv210.h中可以看到各寄存器的定义。串口的初始化是在文件uart.c中进行,这里我不再说明,具体的请参阅s5pv210 pdf进行对比学习。
内存的测试函数mem_test()代码如下:
void mem_test()
{
#define test_mem1 0x30000000
#define test_mem2 0x33e00004
#define test_mem3 0x3f000f08
#define test_mem4 0x3ffff0fc
#define test_mem5 0x3ffffffc //内存地址是每隔4个字节存放的,所以访问时是4个字节访问
#define test_mem6 0x40000000
#define test_mem7 0x43e00004
#define test_mem8 0x4f00f008
#define test_mem9 0x4ffffff8
#define test_mem10 0x4ffffffc
unsigned int *p1 = (volatile unsigned int *)test_mem1;
unsigned int *p2 = (volatile unsigned int *)test_mem2;
unsigned int *p3 = (volatile unsigned int *)test_mem3;
unsigned int *p4 = (volatile unsigned int *)test_mem4;
unsigned int *p5 = (volatile unsigned int *)test_mem5;
unsigned int *p6 = (volatile unsigned int *)test_mem6;
unsigned int *p7 = (volatile unsigned int *)test_mem7;
unsigned int *p8 = (volatile unsigned int *)test_mem8;
unsigned int *p9 = (volatile unsigned int *)test_mem9;
unsigned int *p10 = (volatile unsigned int *)test_mem10;
printf ("******s5pv210 Test********\r\n");
printf ("********By ZheGao********\r\n");
*p1 = 0x12345678;
if (*p1 == 0x12345678) {
printf ("1: equ\r\n");
} else {
printf ("1: not equ\r\n");
}
*p2 = 0x12345678;
if (*p2 == 0x12345678) {
printf ("2: equ\r\n");
} else {
printf ("2: not equ\r\n");
}
*p3= 0xfffffff0;
if (*p3 == 0xfffffff0) {
printf ("3: equ\r\n");
} else {
printf ("3: not equ\r\n");
}
*p4 = 0x12345678;
if (*p4 == 0x12345678) {
printf ("4: equ\r\n");
} else {
printf ("4: not equ\r\n");
}
*p5 = 0x12345678;
if (*p5 == 0x12345678) {
printf ("5: equ\r\n");
} else {
printf ("5: not equ\r\n");
}
*p6= 0xfffffff0;
if (*p6 == 0xfffffff0) {
printf ("6: equ\r\n");
} else {
printf ("6: not equ\r\n");
}
*p7 = 0x12345678;
if (*p7 == 0x12345678) {
printf ("7: equ\r\n");
} else {
printf ("7: not equ\r\n");
}
*p8 = 0x12345678;
if (*p8 == 0x12345678) {
printf ("8: equ\r\n");
} else {
printf ("8: not equ\r\n");
}
*p9= 0x12345678;
if (*p9 == 0x12345678) {
printf ("9: equ\r\n");
} else {
printf ("9: not equ\r\n");
}
*p10= 0x12345678;
if (*p10 == 0x12345678) {
printf ("10: equ\r\n");
} else {
printf ("10: not equ\r\n");
}
}
从最开始宏定义可以知道测试的内存地址,这里把我板子的全部内存做了隔段测试,我的内存地址是从0x30000000到0x4fffffff总共512MB,注意在测试的时候内存地址需要隔4个字节进行测试,也就是最后一位只能取值0、4、8、c这四个值,原因是,我们的内存是32位的也就是4个字节,所以在测试访问的时候需要隔4个字节,注意不能取最后一位除0、4、8、c以外的值,否者就会死掉。
copy_uboot_to_ram()这个拷贝函数的说也参考一个博文http://blog.csdn.net/shangguobuliuhen/article/details/9844371。
同样在main函数中也有LED灯的代码,不过这次换成了C语言的写法,放在这里也是为了判断代码执行的位置,进而判断代码会死在哪里,这个LED定位的思想要灵活运用,可以为你学习和调试带来极大的帮助。就在u-boot中一样我们使用串口打印信息来进行调试一个道理。
第一阶段告一段落,后面继续更新,下一个就是让u-boot能够启动并能进入控制台,过些天在写上来吧。有问题得朋友可以留言,大家共同探讨。