移植U-Boot 1.3.4到GT2440(第二版2.0)


第二版主要是对于第一版的一些错误改正,之前nand flash的移植失败,特在此处使用另一种nand flash移植方法.另外在这里还介绍一下uboot支持yaffs烧写功能的移植(新版里已经支持了),还介绍一下yaffs2文件系统的制作与使用。

移植U-Boot 1.3.4到GT2440(第一版)下载地址

免费下载地址在 http://linux.bkjia.com/

用户名与密码都是www.bkjia.com

具体下载目录在 /2011年资料/嵌入式Linux/移植U-Boot 1.3.4到GT2440/

硬件配置

1、 GT2440\其它开发板

2、 其中nand flash为2Kb一页(具体和512byte的有什么区别,参考nand元件手册,或者上网搜一下,对于uboot,主要是读nand时的写地址时序的不一样)

3、 串行线

4、 J-link\j-tag(笔者用的是jlink,主要用于uboot的调试,因为你移植马上能用的可能性不大,所以需要调试)

软件配置:

1、u-boot-1.3.4

2、-   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.4\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,事实并不是这样的,

.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_B

ASE(在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.4\cpu\arm920t\start.S

它里面大概执行流程是

(1)    CPU为SVC模式

(2)    定义中断向量表

(3)    关闭中断 (因为uboot不需要使用中断)

(4)    底层初始化:两个函数cpu_init_crit和lowlevel_init

cpu_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,=label

Ldr  伪指令,将内存值赋给PC,也是依赖于当前PC值,但是偏移量是label的运行时的地址

这就是创建的堆

这就是创建的栈

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 下一页

相关内容