我的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能够启动并能进入控制台,过些天在写上来吧。有问题得朋友可以留言,大家共同探讨。