Chinaunix首页 | 论坛 | 博客
  • 博客访问: 451980
  • 博文数量: 72
  • 博客积分: 3186
  • 博客等级: 中校
  • 技术积分: 1039
  • 用 户 组: 普通用户
  • 注册时间: 2009-03-07 16:53
文章分类

全部博文(72)

文章存档

2012年(1)

2011年(5)

2010年(10)

2009年(56)

我的朋友

分类: 嵌入式

2009-09-23 12:08:15

最近在阅读 vivi,顺便将其中的针对 2410 有用的部分精简出来,构成自己的代码。


刚刚完成了汇编部分,head.S 终于顺利的跳入到了 main.c 中。这个过程我是逐个功能添加的。每添加一个功能,直到测试成功再添加下一功能。这样的累积我觉得也是有好处的,可能对每个功能模块、每个步骤都详细了解出了问题也能很快的定位。当然,最重要的是深入细节,其中可以学到很多的东西,掌握很多东西。(“学会”与“掌握”是不同的)


从 head.S 跳入 main.C 中,这个过程我出了一点差错。其实之前对运行域与加载域也有所了解,但在这里还是卡了一下。这里分析一下:

首先,要明确的是这个跳转用 B/BL 指令肯定是不行的。因为 2410 的 sdram 一般都放在 bank6(0x30000000),我们要从 bank0 跳到 bank6,而 B/BL 的范围只有 +/-32M (这在另一篇里分析过)。那么这个跳转只能由向 pc 直接写值来实现。


vivi 中的这个跳转过程采用了“弹簧床”的技巧。让我们来看看在 vivi 中它是如何实现的:

    @ 10 jump to ram
    ldr r1, =on_the_ram // 将 on_the_ram 的地址保存到 r1 中
    add pc, r1, #0      // 将地址加载到pc中,跳入 on_the_ram
    nop
    nop
1:     
    b     .


// 链接之后,on_the_ram 之后的代码都会被链接到 sdram 里的某个位置
on_the_ram:
    mov r1, #GPIO_CTL_BASE
    add r1, r1, #oGPIO_F
    mov r2, #0xff
    str r2, [r1, #oGPIO_DAT]
   
    @ print prompt information
    ldr r1, SerBase
    ldr r0, STR_STACK
    bl     PrintWord
    ldr r0, DW_STACK_START
    bl     PrintHexWord
    mov r0, pc
    bl     PrintHexWord

    ldr sp, DW_STACK_START
    mov fp, #0
    mov a2, #0
// 前面这几句是我测试用的,熄灯、向串口打印堆栈地址和当前pc地址。

    bl     main                      // 这里在main.c中写了一个流水灯,便于看到效果。

    mov pc, #FLASH_BASE

从上面可以看到,add pc, r1, #0 这一句之后程序已经开始运行在 sdram 中了。

其次,要注意一个细节:“位置无关代码”与“位置相关代码”。

位置无关代码,即代码在链接之后无论放在哪里,都能正确的运行。程序的运行是顺序的,但跳转的时候,位置的问题就来了。跳转的目标地址是如何计算来的呢?在指令 b/bl 中,目标地址是通过当前 pc 来计算的,它是目标地址是一个相对量(相对当前pc)。

位置相关代码,链接之后,代码需要放在指定的地方,才能正确的运行。链接的时候,主要是链接程序中的symbol,给其一个地址。ldr r1, =on_the_ram 这条指令中,on_the_ram 的地址值在链接后即是确定的。也就是说cpu在其后,即会跳到指定的地址去,我们必须把我们的代码也放到这个指定的地址,那么cpu才能运行正确的指令。否则就会出错。

具体来看一下这些程序的编译过程:

vivi:head.S nand_read.c
    arm-linux-gcc -c head.S -I ../../include/ -o head.o
    arm-linux-gcc -c nand_read.c -I ../../include/ -o nand_read.o
    arm-linux-gcc -c main.c -o main.o
    arm-linux-ld -Ttext 0x33f00000 head.o nand_read.o main.o -o tmp.o
    arm-linux-objcopy -O binary -S tmp.o vivi
    arm-linux-objdump -D -b binary -m arm vivi > aaa

clean:
    rm *~ -f
    rm *.o -f
    rm vivi -f

Makefile 写的很初级。其中可以看到,程序的链接地址是 0x33f00000 。即链接之后,on_the_ram的地址值一定在0x33f00000之后(bank6中),而不是bank0中的地址。

还可以直接写一个链接脚本,这样可以看得更直观:
SECTIONS {
        first     0x33f00000 : { head.o nand_read.o main.o }
}
这里也写的很简单,直接都链接到了0x33f00000。在ldr r1, =on_the_ram这一句之前的所有操作都是位置无关代码,复制nand的nand_read.c也自然是位置无关代码,这些无论放在0地址开始还是放在0x33f00000 都可以正确的被执行。

再次,这里为什么链接到 0x33f00000 呢?

其实很简单,在 head.S 里我把整个 代码都复制到了 0x33f00000 开始的地方。如果链接到了 0x30000000,那么cpu就是从0x30000000后面开始的相应位置支找 on_the_ram,自然是找不到的。

我开始犯了一个错误,链接到了0x30000000。


最后,来看一下反汇编代码。

dc:    e59f13e8     ldr    r1, [pc, #1000]    ; 0x4cc // 这一句就是 ldr r1, =on_the_ram 把0x4cc处的值赋给r1,即r1 = 0x33f000f0
  e0:    e281f000     add    pc, r1, #0    ; 0x0
  e4:    e1a00000     nop            (mov r0,r0)
  e8:    e1a00000     nop            (mov r0,r0)
  ec:    eafffffe     b    0xec
  f0:    e3a01456     mov    r1, #1442840576    ; 0x56000000 //
此处就到了on_the_ram,从这以下都在sdram里
  f4:    e2811050     add    r1, r1, #80    ; 0x50
  f8:    e3a020ff     mov    r2, #255    ; 0xff
  fc:    e5812004     str    r2, [r1, #4] // 熄灯
 100:    e59f138c     ldr    r1, [pc, #908]    ; 0x494
 104:    e59f03b2     ldr    r0, [pc, #946]    ; 0x4be
 108:    eb0000c0     bl    0x410 // 调用打印信息的子程序
 10c:    e59f038c     ldr    r0, [pc, #908]    ; 0x4a0
 110:    eb0000a3     bl    0x3a4 // 调用打印信息的子程序
 114:    e1a0000f     mov    r0, pc
 118:    eb0000a1     bl    0x3a4 // 调用打印信息的子程序
 11c:    e59fd37c     ldr    sp, [pc, #892]    ; 0x4a0
 120:    e3a0b000     mov    fp, #0    ; 0x0
 124:    e3a01000     mov    r1, #0    ; 0x0
 128:    eb000174     bl    0x700 // 0x700后面就是main里的内容了
 12c:    e3a0f000     mov    pc, #0    ; 0x0


 4cc:    33f000f0     mvnccs    r0, #240    ; 0xf0 //这条指令的机器码是33f000f0,实际上是存储了这么一个值
 4d0:    0000f830     andeq    pc, r0, r0, lsr r8 //这里也是同一个道理,存储了0x0000f830这么一个值,这个值看起来眼熟吧,是配置nand flash用的


最后,贴一下打印出来的信息:

@000000D4      //初始化UART好了之后,第一次打印pc值
MTST-OK        // 内存检测
OK             // nand复制
NAND-OK        // 复制后检测通过
ST
                                                                                
33DEFFFC      // 栈指针
33F0011C      // 当前pc


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