Welcome 微信登录

首页 / 操作系统 / Linux / 移植U-Boot 1.3.4到GT2440(第二版2.0)

第二版主要是对于第一版的一些错误改正,之前nand flash的移植失败,特在此处使用另一种nand flash移植方法.另外在这里还介绍一下uboot支持yaffs烧写功能的移植(新版里已经支持了),还介绍一下yaffs2文件系统的制作与使用。 移植U-Boot 1.3.4到GT2440(第一版)下载地址免费下载地址在 http://linux.linuxidc.com/用户名与密码都是www.linuxidc.com具体下载目录在 /2011年资料/嵌入式Linux/移植U-Boot 1.3.4到GT2440/硬件配置1、 GT2440其它开发板2、 其中nand flash为2Kb一页(具体和512byte的有什么区别,参考nand元件手册,或者上网搜一下,对于uboot,主要是读nand时的写地址时序的不一样)3、 串行线4、 J-linkj-tag(笔者用的是jlink,主要用于uboot的调试,因为你移植马上能用的可能性不大,所以需要调试)软件配置:1、u-boot-1.3.42、-   j-link或者H-jtag一、从nandflash 启动uboot的原理       Uboot 源码是不支持从nand中启动的,但是2410 2440是支持的,而且对于闪存,nand比较大容量,比较便宜,所以使用nand启动uboot是比较需要的。那么它启动的原理是什么?       其实从nand flash 控制器有一个特殊功能,会自动把nand flash前4K内容复制到4K SRAM中(注意,是只有4k的SRAM而不是SDRAM,超过怎么办,所以要复制到SDRAM中,如下图)中,并把0x00000000设置成内存起始地址,cpu从这个地址开始运行。这个过程不需要程序干涉。在配置NAND启动模式之后,S3C2440上电会先将NAND中的0x0 - 0x1000共4096字节的数据拷贝到位于Bank0中的Boot Internal SRAM上       这4K的内容,主要是保存的uboot的部分功能(拷贝功能),执行后,再把nand里的内容拷贝到SDRAM中,原因有下:1、 SDRAM运行速度快2、 实际的uboot代码永远大于4Kb的空间,,所以要开辟一个新空间给uboot运行
这就是前4K所在的地方
 
  
二、uboot的运行流程首先先大概分析一下uboot,这样有利于明白,移植的每一步是需要做些什么?在讲uboot启动前,先讲一下arm启动流程,arm启动是先运行芯片厂家固化的boot block,这段程序是引导块,芯片厂家将boot  block地址重映射到片内存储器空间最高处,接近接近2G(0x8000 0000靠MMU映射)。运行完这段程序后,就会运行0x0 地址。看回上面的2440存储映射图,假如是nor的话,就会直接运行Nor 里的内容,如果是nand的话,nand控制器先拷4k到0x0000 0000里的SRAM,然后0x4000000后的内容会被重映射到0x0000 0000的映射,这样就代表前4k直接操作的是bootinternal的SRAM,而不是nand的前4k,可能是因为SRAM速度快.  上个很好的图: 现在看回uboot启动流程:像网上说的,uboot编译时首先编译的是u-boot-1.3.4oard你的开发板文件夹u-boot.lds,看一个2410的例子:ENTRY(_start)SECTIONS{       . = 0x00000000;       . = ALIGN(4);       .text     :       {        cpuarm920tstart.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,事实并不是这样的,.text为代码段,可以看出cpu/arm920t/start.o 在代码段的最前面,所以会先执行start.o 中的代码, 如何设置从0x33f80000开始呢?~这是链接的时候指定的 在根目录下面的config.mk中有下面一句 LDFLAGS +=-Bstatic -T $(LDSCRIPT) -Ttext $(TEXT_BASE) $(PLATFORM_LDFLAGS) 关键就是其中的-Ttext $(TEXT_BASE),这句指明了代码段的起始地址 而TEXT_BASE在 board/smdk2440/config.mk中定义  TEXT_BASE = 0x33F8 0000 为什么是0x33F80000呢?~ 这是将NAND中Uboot拷贝到RAM中的起始地址,所以在代码拷贝到RAM之前不能使用绝对地址(链接后都会加上_TEXT_BASE)来寻址数据,只能用相对地址(相对于当前PC)(adr r0, _start可以确定程序在哪里运行,因为adr是基于pc当前值的,假如在内存中0x33f8000就应该是0x33f8000+_start,假如是在flash中,就应该是0+_start )ARM汇编中,常有两种跳转方法:b跳转指令、ldr指令向PC赋值。 (参考《[NAND]UBOOT从NAND FLASH启动分析》)要特别注意这两条指令的意思: (a)       b step:b跳转指令是相对跳转,依赖当前PC的值,偏移量是通过该指令本身的    bit[23:0]算出来的,这使得使用b指令的程序不依赖于要跳到的代码的位置,只看指令本身。具体是将这24位左移两位加上PC再赋给PC寄存器,得到目标地址 (b)       ldr pc, =step :该指令是一个伪指令编译后会生成以下代码:         ldr pc, 0x30008000         <0x30008000 > step    是从内存中的某个位置(step)读出数据并赋给PC,同样依赖当前PC的值,但是偏移量是step的连接地址(运行时的地址),所以可以用它实现从Flash到RAM的程序跳转。 (c)此外,有必要回味一下adr伪指令,U-boot中那段relocate代码就是通过adr实现当前程序是在RAM中还是flash中:          relocate:                                   /* 把U-Boot重新定位到RAM*/               adr r0, _start                       /* r0是代码的当前位置 *//* adr伪指令,汇编器自动通过当前PC的值算出这条指令中“_start"的值,执行到_start时PC的值放到r0中: 当此段在flash中执行时r0 = _start = 0;当此段在RAM中执行时_start = _TEXT_BASE(在board/smdk2410/config.mk中指定的值为0x33F80000,即u-boot在把代码拷贝到RAM中去 执行的代码段的开始) */     ldr r1, _TEXT_BASE                       /* 测试判断是从Flash启动,还是RAM */ /* 此句执行的结果r1始终是0x33F80000,因为此值是链接指定的 */     cmp r0, r1                                    /* 比较r0和r1,调试的时候不要执行重定位 */(引用《u-boot.lds解析》)所以说实际链接时,都是以_TEXT_BASE为基地址,但是运行时会根据现在代码所处的位置去复制uboot代码到内存(因为在复制到内存之前都是用相对地址的),如果在内存,就直接接着运行即可.具体请参考u-boot根目录下的config.mk. 顺着这个config.lds文件,就开始执行u-boot-1.3.4cpuarm920tstart.S它里面大概执行流程是(1)    CPU为SVC模式(2)    定义中断向量表(3)    关闭中断 (因为uboot不需要使用中断)(4)    底层初始化:两个函数cpu_init_crit和lowlevel_initcpu_init_crit是禁止MMU和CACHE,为什么?因为如果你不禁止,有些数据会残留在cache上,会造成脏数据(《具体也可以参考ARM体系结构与编程》)lowlevel_init主要是定义部分寄存器以及初始化SDRAM(5)   然后把代码拷贝到内存中(没修改时为下面代码,修改后使用自己移植的)#ifndefCONFIG_SKIP_RELOCATE_UBOOT relocate:           /* relocate U-Boot to RAM         */  adr  r0,_start         /* r0 <- currentposition of code   */  ldr r1, _TEXT_BASE  /* test if we run from flash or RAM */  cmp    r0, r1          /* don"t relocduring debug        */  beq    stack_setup   ldr r2, _armboot_start  ldr r3, _bss_start  sub r2, r3, r2     /* r2 <- size of armboot             */  add r2, r0, r2      /* r2 <- source end address         */  copy_loop:  ldmia r0!, {r3-r10}  /* copy from source address [r0]    */  stmia r1!, {r3-r10}  /* copy to  target address [r1]     */  cmp r0, r2   /* until source end addreee [r2]    */  ble copy_loop #endif /*CONFIG_SKIP_RELOCATE_UBOOT */(6)    初始化堆栈(其实就是在内存中开辟一个区域,用于保存数据,其实是栈,经典映射图如下图)(7)    调用C语言函数入口,start_armboot到此第一阶段基本完成注意b 跳转跟 ldr              跳转是不一样的b跳转,依赖当前PC值,偏移量通过指令bit[23:0]算出Ldr  pc,=labelLdr  伪指令,将内存值赋给PC,也是依赖于当前PC值,但是偏移量是label的运行时的地址这就是创建的堆 这就是创建的栈