vivi分析-head.S->main.c (转)

最近在阅读 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

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值