Chinaunix首页 | 论坛 | 博客
  • 博客访问: 233126
  • 博文数量: 63
  • 博客积分: 2465
  • 博客等级: 大尉
  • 技术积分: 471
  • 用 户 组: 普通用户
  • 注册时间: 2008-10-08 17:25
文章分类

全部博文(63)

文章存档

2011年(5)

2010年(41)

2009年(17)

我的朋友

分类: LINUX

2010-09-15 15:17:49

     本文详细分析start.s文件--->

由于一个可执行的Image必须有一个入口点,并且只能有一个全局入口,通常这个入口放在ROM0x0地址,因此,必须通知编译器以使其知道这个入口,该工作可通过修改连接器脚本来完成,这在board/prochip/UB4020/u-boot.lds可以找到,u-boot的程序入口为_start,在cpu/sep4020/start.S中。

1.设置异常向量表

.globl _start                   /* 声明标量是全局函数,CPU加电启动后,就从这里执行代码*/

_start: b reset                                   /* 跳到复位中断开始位置*/

ldr      pc, _undefined_instruction    /* 未定义的指令异常向量 */

ldr      pc, _software_interrupt       /* 软件中断异常向量 */

ldr      pc, _prefetch_abort          /* 预取指令操作异常向量 */

ldr      pc, _data_abort             /* 数据操作异常向量 */

ldr      pc, _not_used              /* 未使用异常向量 */

ldr      pc, _irq                   /* 中断异常向量 */

ldr      pc, _fiq                   /* 快速中断异常向量 */

_undefined_instruction: .word undefined_instruction

_software_interrupt:     .word software_interrupt

_prefetch_abort:  .word prefetch_abort

_data_abort:                  .word data_abort

_not_used:           .word not_used

_irq:                     .word irq

_fiq:                     .word fiq

 

         .balignl 16,oxdeadbeef

.word {,} …              插入一个32位的数据队列。可以使用.word把标识符作为常量使用。

ldr pc,_irq                                                 从内存的某个位置读取数据并且赋值给pc,但是偏移量是那个位置的链接(运行)地址。

.balignl 16,0xdeadbeef                              这条指令把deadbeef字符串填充进去,一共填到地址为16对齐的地方为止。

下面定义了几个全局变量,在后面的代码搬运,申请空间中将会用到:

/*

 *************************************************************************

 *

 * Startup Code (reset vector)

 *

 * do important init only if we don't start from memory!如果不是从内存启动,做一些重要的初始化工作

 * relocate u-boot to ram 搬运u-bootram

 * setup stack 设置堆栈

 * jump to second stage 跳转到第二阶段

 *

 *************************************************************************

 */

 

_TEXT_BASE:

         .word         TEXT_BASE                       //程序在SDRAM运行的起始地址,即链接基地址,在/board/prochip/UB4020/config.mk中定义为0x30700000

.globl _armboot_start

_armboot_start:

         .word _start                                     //程序运行起始地址,其实是FLASH中程序的起始地址

/*

 * These are defined in the board-specific linker script.这些变量定义在板级的链接脚本中

 */

.globl _bss_start

_bss_start:

         .word __bss_start                            //bss段的链接起始地址

.globl _bss_end

_bss_end:

         .word _end                                      //bss段的连接结束地址

        

2.复位时切换到SVC32模式,并设置各个模式下的堆栈

/*

 * the actual reset code 真正的复位代码

 */

 

reset:

         /*stack setup for each mode 设置各个模式下的堆栈*/

        

         /* SVC32 mode*/        

         mrs   r0,cpsr

         bic     r0,r0,#0x1f

         orr     r0,r0,#0x13

         msr   cpsr,r0

//以下这段代码对不是从NAND FLASH启动的代码才有作用,因为从NAND FLASH启动,已经把uboot
//
搬运到了SDRAM中。

         ldr     r0, _TEXT_BASE

         sub    r0, r0, #CFG_MALLOC_LEN                //动态缓冲区和全局环境变量大小

         sub    r0, r0, #CFG_GBL_DATA_SIZE            //uboot唯一的全局变量区大小

#ifdef CONFIG_USE_IRQ

         sub    r0, r0, #(CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ)

#endif

         sub    sp, r0, #12 

 

 

#ifdef CONFIG_USE_IRQ

         /* IRQ mode*/

         mov  R4, #0xD2

    msr   cpsr, R4  

         ldr     r0, _TEXT_BASE

         sub    r0, r0, #CFG_MALLOC_LEN

         sub    r0, r0, #CFG_GBL_DATA_SIZE

         sub    r0, r0, #(CONFIG_STACKSIZE_FIQ)

         sub    sp, r0, #12 

        

 

         /* FIQ mode*/

         mov  R4, #0xD1

    msr   cpsr, R4  

         ldr     r0, _TEXT_BASE

         sub    r0, r0, #CFG_MALLOC_LEN

         sub    r0, r0, #CFG_GBL_DATA_SIZE

         sub    sp, r0, #12 

#endif

        

         /* ABORT mode*/

         mov  R4, #0xD7

    msr   cpsr, R4  

         ldr     r0, _TEXT_BASE

         sub    r0, r0, #CFG_MALLOC_LEN

         sub    r0, r0, #CFG_GBL_DATA_SIZE

         sub    sp, r0, #8

        

         /* UNDEFINE mode*/

         mov  R4, #0xDB

    msr   cpsr, R4  

         ldr     r0, _TEXT_BASE

         sub    r0, r0, #CFG_MALLOC_LEN

         sub    r0, r0, #CFG_GBL_DATA_SIZE

         sub    sp, r0, #4

        

         /* SYSTEM mode*/

         mov  R4, #0xDF

    msr   cpsr, R4  

         ldr     r0, _TEXT_BASE

         sub    r0, r0, #CFG_MALLOC_LEN

         sub    sp, r0, #CFG_GBL_DATA_SIZE

 

         /*Return to SVC mode*/      

         mov  R4, #0xD3

    msr   cpsr, R4

         各模式下堆栈设置完毕之后的内存分布图如下所示:

         注意:ARM是满递减堆栈。。。

 

_TEXT_BASE

CFG_GBL_DATASIZE

CFG_MALLOC_LEN

SP_USR&SP_SYSTEM

4字节

SP_UNDEFINE

4字节

4字节

SP_ABORT

CONFIG_STACKSIZE_FIQ

SP_FIQ

CONFIG_STACKSIZE_IRQ

SP_IRQ

低地址

高地址

_TEXT_BASE

CFG_GBL_DATASIZE

CFG_MALLOC_LEN

SP_USR&SP_SYSTEM

4字节

SP_UNDEFINE

4字节

4字节

SP_ABORT

CONFIG_STACKSIZE_FIQ

SP_FIQ

CONFIG_STACKSIZE_IRQ

SP_IRQ

低地址

高地址

_TEXT_BASE

CFG_GBL_DATASIZE

CFG_MALLOC_LEN

SP_USR&SP_SYSTEM

4字节

SP_UNDEFINE

4字节

4字节

SP_ABORT

CONFIG_STACKSIZE_FIQ

SP_FIQ

CONFIG_STACKSIZE_IRQ

SP_IRQ

低地址

高地址

3.进行CPU初始化(频率、正常模式、打开所有模块、串口、SDRAM时序参数)

         /*

          * we do sys-critical inits only at reboot, 我们只在重新启动的时候才进行系统重要部分初始化

          * not when booting from ram!                ram中启动的时候不执行

          */

 

#ifndef CONFIG_SKIP_LOWLEVEL_INIT   //这个很关键

         bl      cpu_init_crit        //跳转到cpu初始化部分

         /*

          * before relocating, we have to setup RAM timing 在加载之前,先设置RAM的时序

          * because memory timing is board-dependend, you will 因为RAM因板子的不同而不同

          * find a lowlevel_init.S in your board directory. 在你板子目录里找到lowlevel_init.S这个文件

          */

         bl      lowlevel_init        //跳转到配置EMISDRAM时序参数部分

#endif

         cpu_init_crit代码如下:

/*

 *************************************************************************

 *

 * CPU_init_critical registers

 *

 * setup important registers   设置重要的寄存器

 * setup memory timing        设置存储器的时序

 *

 *************************************************************************

 */

 

cpu_init_crit:

         /* PLLCON */

         ldr    r0, =0x10001004          /*88M*/

         ldr     r1, =0x400B                

         str     r1, [r0]

 

         ldr    r0, =0x10001014          /*Normal*/

         ldr     r1, =0x1

         str     r1, [r0]

        

         ldr    r0, =0x10001004          /*88M*/              //配置系统频率为88MHZ

         ldr     r1, =0xC00B               

         str     r1, [r0]

 

         ldr    r0, =0x1000100C         /*打开系统所有模块*/

         ldr     r1, =0xFFFFFFFF

         str     r1, [r0]

 

         /*UARTCON*/                     //进行串口的配置

#if 1

         ldr    r0, =0x1000500C         /*databit:8*/

         ldr     r1, =0x83

         str     r1, [r0]

 

         ldr    r0, =0x10005004                   /*baud=9600*/

         ldr     r1, =0x0

         str     r1, [r0]

 

         ldr    r0, =0x10005000

         ldr     r1, =0x2F

         str     r1, [r0]

 

         ldr    r0, =0x1000500C

         ldr     r1, =0x3

         str     r1, [r0]

#endif

         mov  pc, lr                             //程序返回

         以下是low_level_init.S中的lowlevel_init代码

.globl lowlevel_init

lowlevel_init:

    ldr    r4,    =EMI_CSECONF

    ldr    r5,    =0x8ca6a6a1                 //配置SDRAM参数

    str    r5,    [ r4 ]

 

    ldr    r4,    =EMI_SDCONF1

    ldr    r5,    =0x1E184177

    str    r5,    [ r4 ]

 

    ldr    r4,    =EMI_SDCONF2

    ldr    r5,    =0x80001860

    str    r5,    [ r4 ]

        

    mov pc, lr                                //程序返回

4. 地址重映射以及BSS段初始化,拷贝u-bootRAM中,拷贝中断向量到RAM首地址

remap:

         mov  r0,    pc

         add    r0,     r0,     #0x20000000

         add    r0,     r0,     #0x08

         mov  pc,     r0                                  //到这条结束,其实就是跳到下一条去执行了

 

         mov  r0,     r0                         //这四个很关键,用来填充流水线

         mov  r0,     r0

         mov  r0,     r0

         mov  r0,     r0

/*关于上面这段代码,我考虑了很久,我是这么认为的:系统启动时,硬件选择从norflash启动,这时的0x2000 0000就是0x0pc的值都是0x0空间的,下面马上要进行地址重映射了,重映射完了后0x3000 0000就是0x0,而重映射完了之后仍然有代码在norflash中执行,这样就必须人为地将PC指针指向0x2000 0000这个地址段,而不是继续在0x0这个空间执行代码,而0x3000 00000x0已经绑定了,在这个地址段里暂时还没有可以执行的代码,因为代码还没有搬运过去。

Shixq说:

BootLoadr里实现Remap是一个技巧。
  首先假设Remap操作前不对PC进行处理,我们来看一下有什么问题。系统上电后,根据外部跳线,将NorFlash映射为零地址(此时NorFlash拥有两个地址:0x000000000x20000000),内核从零地址开始取指令,即开始执行BootLoader里的代码。
  在BootLoader里有一个重要的步骤,就是地址重映射,即将SDRAM0x30000000)映射到零地址,为将来建立中断向量表做准备。如果不对PC处理,那么一旦执行了地址重映射,零地址开始的一段地址将落在SDRAM上,而不再落在NorFlash上,由于SDRAM开始的一段地址还没有任何内容,这样将导致系统崩溃!
  我们希望执行了Remap操作后,CPU能够继续从NorFlash取指,即继续执行BootLoader里的指令。这样在执行Remap前就必须对PC进行处理。由于Remap后,NorFlash只有0x20000000开始的地址,因此,需要将PC值加上0x20000000。假设执行RemapPC0x88,同时这个地址也是0x20000088,我们对PC0x20000000不会影响系统的运行,而且可以保证Remap后,CPU可以继续从NorFlash取指。
  ldr pc,=0x,就是做了上述处理。至于这个值是多少,我们可以看到在这一条指令下面是mov r0,r0,相当于NOP空指令。只要让PC落在其中任意一*/

    ldr     r4,     =0x11000020               //对这个地址即寄存器写0xb完成地址重映射,这时0x30000000=0x0

     ldr    r5,     =0xb

         str     r5,     [ r4 ]

 

/*

         init BSS section bss段清零

*/

         ldr r0, = 0

         ldr r1, _bss_start                    //BSS段起始地址

         ldr r2, _bss_end                     //BSS段结束地址

bss_init:

         str r0, [r1]

         add r1,r1,#4

         cmp r1,r2

         blt bss_init

 

#ifndef CONFIG_SKIP_RELOCATE_UBOOT

relocate:                                  /* relocate U-Boot to RAM   拷贝u-bootRAM   */

         adr    r0, _start              /* r0 <- current position of code 根据现在的PC值计算出的_start地址赋给r0 */

         ldr     r1, _TEXT_BASE                 /* test if we run from flash or RAM 判断是从flash还是从RAM 启动*/

         cmp     r0, r1                  /* don't reloc during debug 在调试的时候不需要拷贝*/

         beq    vector_copy                                //调试的时候全部自动加载到指定的地址了

 

         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

 

/*

         now copy to sram the interrupt vector

*/

vector_copy:

         ldr     r0, _TEXT_BASE

         add    r2, r0, #128                                     //拷贝128个字节到0x3000 0000,里面存放着中断向量

         ldr     r1, =0x30000000 /*modified by shixq from 0x0c000000 to 0x30000000*/

/*      add    r1, r1, #0x08 *//*deleted by shixq*/

vector_copy_loop:

         ldmia r0!, {r3-r10}

         stmia r1!, {r3-r10}

         cmp   r0, r2

         ble     vector_copy_loop

#endif         /* CONFIG_SKIP_RELOCATE_UBOOT */

 

6.使能IRQ中断,进入C代码部分

         /*enable the irq*/

         mrs   R4, cpsr

     bic     R4, R4, #0x80

    msr   cpsr, R4    

 

         ldr     pc, _start_armboot       

/*这条语句不但跳转到C语言处开始执行,而且是从NORFLASH空间跳转到了SDRAM空间*/

/*该指令是取得标号(_start_armboot)绝对地址并且赋值给pc,而_start_armboot中存储的又是start_armboot的链接时候的绝对地址,它们都是0x3070 0000开始的空间的地址。所以可以利用它实现FlashRAM的跳转。*/

_start_armboot:   .word  start_armboot

 

ldradr的区别

ldr     r0, _start
        adr     r0, _start
        ldr     r0, =_start
        nop
        mov     pc, lr

_start:
        nop
        
编译的时候设置 RO 0x0c008000

↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓

0c008000 <_start-0x14>:
c008000:       e59f000c        ldr     r0, [pc, #12]   ; c008014 <_start>
c008004:       e28f0008        add     r0, pc, #8      ; 0x8
c008008:       e59f0008        ldr     r0, [pc, #8]    ; c008018 <_start+0x4>
c00800c:       e1a00000        nop                     (mov r0,r0)
c008010:       e1a0f00e        mov     pc, lr

0c008014 <_start>:
c008014:       e1a00000        nop                     (mov r0,r0)
c008018:       0c008014        stceq   0, cr8, [r0], -#80

分析:

adr     r0, _start

取得 _start 的地址到 r0,但是请看反编译的结果,它是与位置无关的。其实取得的是相对于当时pc的位置。例如这段代码在 0x0c008000 运行,那么 adr r0, _start 得到 r0 = 0x0c008014;如果在地址 0 运行,就是 0x00000014 了。

ldr     r0, =_start

这个取得标号 _start 绝对地址。这个绝对地址是在 link 的时候确定的。看上去这只是一个指令,但是它要占用 2 32bit 的空间,一条是指令,另一条是 _start 的数据(因为在编译的时候不能确定 _start 的值,而且也不能用 mov 指令来给 r0 赋一个 32bit 的常量,所以需要多出一个空间存放 _start 的真正数据,在这里就是 0x0c008014)。
因此可以看出,这个是绝对的寻址,不管这段代码在什么地方运行,它的结果都是 r0 = 0x0c008014

阅读(1224) | 评论(1) | 转发(0) |
给主人留下些什么吧!~~

chinaunix网友2011-04-02 23:04:16

写的真不错,受益匪浅啊,解了我两处迷惑的地方,非常感谢