运行地址,链接地址,加载地址,存储地址 位置无关码、位置有关码

搞ARM开发时,在连接目标代码会提到运行地址和加载地址。这两者有什么区别呢?其次,网上也有说链接地址和存储地址,那么这四个地址之间有什么区别?

1、运行地址<--->链接地址:他们两个是等价的,只是两种不同的说法。

2、加载地址<--->存储地址:他们两个是等价的,也是两种不同的说法。

 

运行地址:程序在SRAM、SDRAM中执行时的地址。就是执行这条指令时,PC应该等于这个地址,换句话说,PC等于这个地址时,这条指令应该保存在这个地址内。

加载地址:程序保存在Nand flash中的地址。

 

位置无关码:B、BL、MOV都是位置位置无关码。

位置有关码:LDR PC,=LABEL等类似的代码都是位置有关码。

 

下面我们来看看一个Makefile文件

sdram.bin : head.S  leds.c
    arm-linux-gcc  -c -o head.o head.S
    arm-linux-gcc -c -o leds.o leds.c
    arm-linux-ld -Ttext 0x30000000 head.o leds.o -o sdram_elf
    arm-linux-objcopy -O binary -S sdram_elf sdram.bin
    arm-linux-objdump -D -m arm  sdram_elf > sdram.dis
clean:
 rm -f   sdram.dis sdram.bin sdram_elf *.o

 我们可以看到sdram_elf的代码段是从0x30000000地址开始存放,这个地址我们称之为运行地址。为什么从这个地址开始存放,因为SDRAM的起始地址是0x30000000.

 

下面来看看一个启动代码

 

@*************************************************************************
@ File:head.S
@ 功能:设置SDRAM,将程序复制到SDRAM,然后跳到SDRAM继续执行
@*************************************************************************      

.equ        MEM_CTL_BASE,       0x48000000
.equ        SDRAM_BASE,         0x30000000

.text
.global _start
_start:
    bl  disable_watch_dog               @ 关闭WATCHDOG,否则CPU会不断重启
    bl  memsetup                        @ 设置存储控制器
    bl  copy_steppingstone_to_sdram     @ 复制代码到SDRAM中
    ldr pc, =on_sdram                   @ 跳到SDRAM中继续执行
on_sdram:
    ldr sp, =0x34000000                 @ 设置堆栈
    bl  main
halt_loop:
    b   halt_loop

disable_watch_dog:
    @ 往WATCHDOG寄存器写0即可
    mov r1,     #0x53000000
    mov r2,     #0x0
    str r2,     [r1]
    mov pc,     lr      @ 返回

copy_steppingstone_to_sdram:
    @ 将Steppingstone的4K数据全部复制到SDRAM中去
    @ Steppingstone起始地址为0x00000000,SDRAM中起始地址为0x30000000
    
    mov r1, #0
    ldr r2, =SDRAM_BASE
    mov r3, #4*1024
1:  
    ldr r4, [r1],#4     @ 从Steppingstone读取4字节的数据,并让源地址加4
    str r4, [r2],#4     @ 将此4字节的数据复制到SDRAM中,并让目地地址加4
    cmp r1, r3          @ 判断是否完成:源地址等于Steppingstone的未地址?
    bne 1b              @ 若没有复制完,继续
    mov pc,     lr      @ 返回

memsetup:
    @ 设置存储控制器以便使用SDRAM等外设

    mov r1,     #MEM_CTL_BASE       @ 存储控制器的13个寄存器的开始地址
    adrl    r2, mem_cfg_val         @ 这13个值的起始存储地址
    add r3,     r1, #52             @ 13*4 = 54
1:  
    ldr r4,     [r2], #4            @ 读取设置值,并让r2加4
    str r4,     [r1], #4            @ 将此值写入寄存器,并让r1加4
    cmp r1,     r3                  @ 判断是否设置完所有13个寄存器
    bne 1b                          @ 若没有写成,继续
    mov pc,     lr                  @ 返回


.align 4
mem_cfg_val:
    @ 存储控制器13个寄存器的设置值
    .long   0x22011110      @ BWSCON
    .long   0x00000700      @ BANKCON0
    .long   0x00000700      @ BANKCON1
    .long   0x00000700      @ BANKCON2
    .long   0x00000700      @ BANKCON3  
    .long   0x00000700      @ BANKCON4
    .long   0x00000700      @ BANKCON5
    .long   0x00018005      @ BANKCON6
    .long   0x00018005      @ BANKCON7
    .long   0x008C07A3      @ REFRESH
    .long   0x000000B1      @ BANKSIZE
    .long   0x00000030      @ MRSRB6
    .long   0x00000030      @ MRSRB7


下面来看看反汇编代码


sdram_elf:     file format elf32-littlearm


Disassembly of section .text:

30000000 <_start>:
30000000:    eb000005     bl    3000001c <disable_watch_dog>
30000004:    eb000010     bl    3000004c <memsetup>
30000008:    eb000007     bl    3000002c <copy_steppingstone_to_sdram>
3000000c:    e59ff090     ldr    pc, [pc, #144]    ; 300000a4 <mem_cfg_val+0x34>

30000010 <on_sdram>:
30000010:    e3a0d30d     mov    sp, #872415232    ; 0x34000000
30000014:    eb000033     bl    300000e8 <main>

30000018 <halt_loop>:
30000018:    eafffffe     b    30000018 <halt_loop>

3000001c <disable_watch_dog>:
3000001c:    e3a01453     mov    r1, #1392508928    ; 0x53000000
30000020:    e3a02000     mov    r2, #0
30000024:    e5812000     str    r2, [r1]
30000028:    e1a0f00e     mov    pc, lr

3000002c <copy_steppingstone_to_sdram>:
3000002c:    e3a01000     mov    r1, #0
30000030:    e3a02203     mov    r2, #805306368    ; 0x30000000
30000034:    e3a03a01     mov    r3, #4096    ; 0x1000
30000038:    e4914004     ldr    r4, [r1], #4
3000003c:    e4824004     str    r4, [r2], #4
30000040:    e1510003     cmp    r1, r3
30000044:    1afffffb     bne    30000038 <copy_steppingstone_to_sdram+0xc>
30000048:    e1a0f00e     mov    pc, lr

3000004c <memsetup>:
3000004c:    e3a01312     mov    r1, #1207959552    ; 0x48000000
30000050:    e28f2018     add    r2, pc, #24
30000054:    e1a00000     nop            ; (mov r0, r0)
30000058:    e2813034     add    r3, r1, #52    ; 0x34
3000005c:    e4924004     ldr    r4, [r2], #4
30000060:    e4814004     str    r4, [r1], #4
30000064:    e1510003     cmp    r1, r3
30000068:    1afffffb     bne    3000005c <memsetup+0x10>
3000006c:    e1a0f00e     mov    pc, lr

30000070 <mem_cfg_val>:
30000070:    22011110     andcs    r1, r1, #4
30000074:    00000700     andeq    r0, r0, r0, lsl #14
30000078:    00000700     andeq    r0, r0, r0, lsl #14
3000007c:    00000700     andeq    r0, r0, r0, lsl #14
30000080:    00000700     andeq    r0, r0, r0, lsl #14
30000084:    00000700     andeq    r0, r0, r0, lsl #14
30000088:    00000700     andeq    r0, r0, r0, lsl #14
3000008c:    00018005     andeq    r8, r1, r5
30000090:    00018005     andeq    r8, r1, r5
30000094:    008c07a3     addeq    r0, ip, r3, lsr #15
30000098:    000000b1     strheq    r0, [r0], -r1
3000009c:    00000030     andeq    r0, r0, r0, lsr r0
300000a0:    00000030     andeq    r0, r0, r0, lsr r0
300000a4:    30000010     andcc    r0, r0, r0, lsl r0
300000a8:    e1a00000     nop            ; (mov r0, r0)
300000ac:    e1a00000     nop            ; (mov r0, r0)

300000b0 <wait>:
300000b0:    e52db004     push    {fp}        ; (str fp, [sp, #-4]!)
300000b4:    e28db000     add    fp, sp, #0
300000b8:    e24dd00c     sub    sp, sp, #12
300000bc:    e50b0008     str    r0, [fp, #-8]
300000c0:    ea000002     b    300000d0 <wait+0x20>
300000c4:    e51b3008     ldr    r3, [fp, #-8]
300000c8:    e2433001     sub    r3, r3, #1
300000cc:    e50b3008     str    r3, [fp, #-8]
300000d0:    e51b3008     ldr    r3, [fp, #-8]
300000d4:    e3530000     cmp    r3, #0
300000d8:    1afffff9     bne    300000c4 <wait+0x14>
300000dc:    e28bd000     add    sp, fp, #0
300000e0:    e8bd0800     pop    {fp}
300000e4:    e12fff1e     bx    lr

300000e8 <main>:
300000e8:    e92d4800     push    {fp, lr}
300000ec:    e28db004     add    fp, sp, #4
300000f0:    e24dd008     sub    sp, sp, #8
300000f4:    e3a03000     mov    r3, #0
300000f8:    e50b3008     str    r3, [fp, #-8]
300000fc:    e59f3030     ldr    r3, [pc, #48]    ; 30000134 <main+0x4c>
30000100:    e3a02b55     mov    r2, #87040    ; 0x15400
30000104:    e5832000     str    r2, [r3]
30000108:    e59f0028     ldr    r0, [pc, #40]    ; 30000138 <main+0x50>
3000010c:    ebffffe7     bl    300000b0 <wait>
30000110:    e59f3024     ldr    r3, [pc, #36]    ; 3000013c <main+0x54>
30000114:    e3a02000     mov    r2, #0
30000118:    e5832000     str    r2, [r3]
3000011c:    e59f0014     ldr    r0, [pc, #20]    ; 30000138 <main+0x50>
30000120:    ebffffe2     bl    300000b0 <wait>
30000124:    e59f3010     ldr    r3, [pc, #16]    ; 3000013c <main+0x54>
30000128:    e3a02e1e     mov    r2, #480    ; 0x1e0
3000012c:    e5832000     str    r2, [r3]
30000130:    eafffff4     b    30000108 <main+0x20>
30000134:    56000010     undefined instruction 0x56000010
30000138:    00007530     andeq    r7, r0, r0, lsr r5
3000013c:    56000014     undefined instruction 0x56000014

Disassembly of section .ARM.attributes:

00000000 <.ARM.attributes>:
   0:    00002541     andeq    r2, r0, r1, asr #10
   4:    61656100     cmnvs    r5, r0, lsl #2
   8:    01006962     tsteq    r0, r2, ror #18
   c:    0000001b     andeq    r0, r0, fp, lsl r0
  10:    00543405     subseq    r3, r4, r5, lsl #8
  14:    01080206     tsteq    r8, r6, lsl #4
  18:    04120109     ldreq    r0, [r2], #-265    ; 0x109
  1c:    01150114     tsteq    r5, r4, lsl r1
  20:    01180317     tsteq    r8, r7, lsl r3
  24:    Address 0x00000024 is out of bounds.


Disassembly of section .comment:

00000000 <.comment>:
   0:    3a434347     bcc    10d0d24 <SDRAM_BASE-0x2ef2f2dc>
   4:    74632820     strbtvc    r2, [r3], #-2080    ; 0x820
   8:    312d676e     teqcc    sp, lr, ror #14
   c:    312e362e     teqcc    lr, lr, lsr #12
  10:    2e342029     cdpcs    0, 3, cr2, cr4, cr9, {1}
  14:    00332e34     eorseq    r2, r3, r4, lsr lr

当我们从Nand flash启动时,硬件会自动将Nand flash前4kB代码拷贝到片内SRAM中,然后CPU从SRAM的0x00000000地址处开始执行程序。在这里我想纠正一个错误的观点,网上很多人都说是CPU自动把Nand flash前4kB代码拷贝到片内SRAM中,其实不然,是Nand flash控制器完成的,这个过程中CPU根本就没有参与 。

通过上面的Makefile文件,我们可以知道 bl  disable_watch_dog   这条指令的运行地址是0x30000000,但是它现在保存在SRAM的0x00000000的地址处,那么这条指令能够正确执行吗?of course,why?

因为这条指令 bl  disable_watch_dog   是位置无关码,虽然它的运行地址是在SDRAM中的0x30000000,但是它可以在Steppingstone中执行。这条指令的功能是跳转到标号disable_watch_dog  处执行,它是一个相对跳转,PC=当前PC的值+偏移量OFFSET。其中当前PC的值等于下两条指令的地址,通过反汇编可以看到下两条指令的地址为0x0000 0008,而不是0x3000 0008.因为现在指令是保存在SRAM中。

这条指令的机器码为eb00 0005,将机器码低24位按符号位扩展成32位得到0x0000 00005.然后将0x0000 0005左移2位得到0x0000 0014。这个值就是偏移量OFFSET=0X0000 0014。所以PC=0X0000 0008+0X0000 0014=0X0000 001c.那么CPU就会到SRAM中的0x0000 001c地址处执行程序。


可以发现bl指令依赖当前PC的值,这个特性使得bl指令不依赖指令的运行地址。所以接下来的bl mensetup ,bl cope_steppingstone_to_sdram都能够执行。


接下来的ldr pc,=on_sdram是一条位置有关码,经过反汇编可以看到,它是当前pc的值+偏移量,得到一个地址Addr,然后从内存中的这个地址去取数据Data赋给pc。现在我们计算一下Addr这个地址。

Addr=当前PC的值+144。当前PC的值等于下两条指令的地址0x0000 0014,而不是0x3000 0014,因为现在程序是保存在SRAM中。所以Addr=0x0000 0014+144=0x0000 0014+0x0000 0090=0x0000 00a4.那么这个地址0x0000 00a4中保存了什么数据。通过反汇编可以看到SRAM中的0x0000 00a4这个地址保存的机器码为30000010,所以cpu会跳转到0x30000010这个地址处执行程序。这个地址0x30000010

是在SDRAM中,这样程序就跳到SDRAM中去了。因为我们前面的指令 bl cope_stepping_to_sdram 已经把SRAM中4kB的程序拷贝到了SDRAM中,所以现在SDRAM中有程序了。

 

但是如果在程序的开头放置一条这样的指令:ldr pc,=disable_watch_dog ,那么整个程序就不能够正确执行了。why?


我们先来看看启动代码

@*************************************************************************
@ File:head.S
@ 功能:设置SDRAM,将程序复制到SDRAM,然后跳到SDRAM继续执行
@*************************************************************************       

.equ        MEM_CTL_BASE,       0x48000000
.equ        SDRAM_BASE,         0x30000000

.text
.global _start
_start:
    @bl  disable_watch_dog               @ 关闭WATCHDOG,否则CPU会不断重启
    ldr pc, =disable_watch_dog           
    bl  memsetup                        @ 设置存储控制器
    bl  copy_steppingstone_to_sdram     @ 复制代码到SDRAM中
    ldr pc, =on_sdram                   @ 跳到SDRAM中继续执行
on_sdram:
    ldr sp, =0x34000000                 @ 设置堆栈
    bl  main
halt_loop:
    b   halt_loop

disable_watch_dog:
    @ 往WATCHDOG寄存器写0即可
    mov r1,     #0x53000000
    mov r2,     #0x0
    str r2,     [r1]
    mov pc,     lr      @ 返回

copy_steppingstone_to_sdram:
    @ 将Steppingstone的4K数据全部复制到SDRAM中去
    @ Steppingstone起始地址为0x00000000,SDRAM中起始地址为0x30000000
    
    mov r1, #0
    ldr r2, =SDRAM_BASE
    mov r3, #4*1024
1:  
    ldr r4, [r1],#4     @ 从Steppingstone读取4字节的数据,并让源地址加4
    str r4, [r2],#4     @ 将此4字节的数据复制到SDRAM中,并让目地地址加4
    cmp r1, r3          @ 判断是否完成:源地址等于Steppingstone的未地址?
    bne 1b              @ 若没有复制完,继续
    mov pc,     lr      @ 返回

memsetup:
    @ 设置存储控制器以便使用SDRAM等外设

    mov r1,     #MEM_CTL_BASE       @ 存储控制器的13个寄存器的开始地址
    adrl    r2, mem_cfg_val         @ 这13个值的起始存储地址
    add r3,     r1, #52             @ 13*4 = 54
1:  
    ldr r4,     [r2], #4            @ 读取设置值,并让r2加4
    str r4,     [r1], #4            @ 将此值写入寄存器,并让r1加4
    cmp r1,     r3                  @ 判断是否设置完所有13个寄存器
    bne 1b                          @ 若没有写成,继续
    mov pc,     lr                  @ 返回


.align 4
mem_cfg_val:
    @ 存储控制器13个寄存器的设置值
    .long   0x22011110      @ BWSCON
    .long   0x00000700      @ BANKCON0
    .long   0x00000700      @ BANKCON1
    .long   0x00000700      @ BANKCON2
    .long   0x00000700      @ BANKCON3  
    .long   0x00000700      @ BANKCON4
    .long   0x00000700      @ BANKCON5
    .long   0x00018005      @ BANKCON6
    .long   0x00018005      @ BANKCON7
    .long   0x008C07A3      @ REFRESH
    .long   0x000000B1      @ BANKSIZE
    .long   0x00000030      @ MRSRB6
    .long   0x00000030      @ MRSRB7



对应的反汇编代码


sdram_elf:     file format elf32-littlearm


Disassembly of section .text:

30000000 <_start>:
30000000:    e59ff09c     ldr    pc, [pc, #156]    ; 300000a4 <mem_cfg_val+0x34>
30000004:    eb000010     bl    3000004c <memsetup>
30000008:    eb000007     bl    3000002c <copy_steppingstone_to_sdram>
3000000c:    e59ff094     ldr    pc, [pc, #148]    ; 300000a8 <mem_cfg_val+0x38>

30000010 <on_sdram>:
30000010:    e3a0d30d     mov    sp, #872415232    ; 0x34000000
30000014:    eb000033     bl    300000e8 <main>

30000018 <halt_loop>:
30000018:    eafffffe     b    30000018 <halt_loop>

3000001c <disable_watch_dog>:
3000001c:    e3a01453     mov    r1, #1392508928    ; 0x53000000
30000020:    e3a02000     mov    r2, #0
30000024:    e5812000     str    r2, [r1]
30000028:    e1a0f00e     mov    pc, lr

3000002c <copy_steppingstone_to_sdram>:
3000002c:    e3a01000     mov    r1, #0
30000030:    e3a02203     mov    r2, #805306368    ; 0x30000000
30000034:    e3a03a01     mov    r3, #4096    ; 0x1000
30000038:    e4914004     ldr    r4, [r1], #4
3000003c:    e4824004     str    r4, [r2], #4
30000040:    e1510003     cmp    r1, r3
30000044:    1afffffb     bne    30000038 <copy_steppingstone_to_sdram+0xc>
30000048:    e1a0f00e     mov    pc, lr

3000004c <memsetup>:
3000004c:    e3a01312     mov    r1, #1207959552    ; 0x48000000
30000050:    e28f2018     add    r2, pc, #24
30000054:    e1a00000     nop            ; (mov r0, r0)
30000058:    e2813034     add    r3, r1, #52    ; 0x34
3000005c:    e4924004     ldr    r4, [r2], #4
30000060:    e4814004     str    r4, [r1], #4
30000064:    e1510003     cmp    r1, r3
30000068:    1afffffb     bne    3000005c <memsetup+0x10>
3000006c:    e1a0f00e     mov    pc, lr

30000070 <mem_cfg_val>:
30000070:    22011110     andcs    r1, r1, #4
30000074:    00000700     andeq    r0, r0, r0, lsl #14
30000078:    00000700     andeq    r0, r0, r0, lsl #14
3000007c:    00000700     andeq    r0, r0, r0, lsl #14
30000080:    00000700     andeq    r0, r0, r0, lsl #14
30000084:    00000700     andeq    r0, r0, r0, lsl #14
30000088:    00000700     andeq    r0, r0, r0, lsl #14
3000008c:    00018005     andeq    r8, r1, r5
30000090:    00018005     andeq    r8, r1, r5
30000094:    008c07a3     addeq    r0, ip, r3, lsr #15
30000098:    000000b1     strheq    r0, [r0], -r1
3000009c:    00000030     andeq    r0, r0, r0, lsr r0
300000a0:    00000030     andeq    r0, r0, r0, lsr r0
300000a4:    3000001c     andcc    r0, r0, ip, lsl r0
300000a8:    30000010     andcc    r0, r0, r0, lsl r0
300000ac:    e1a00000     nop            ; (mov r0, r0)

300000b0 <wait>:
300000b0:    e52db004     push    {fp}        ; (str fp, [sp, #-4]!)
300000b4:    e28db000     add    fp, sp, #0
300000b8:    e24dd00c     sub    sp, sp, #12
300000bc:    e50b0008     str    r0, [fp, #-8]
300000c0:    ea000002     b    300000d0 <wait+0x20>
300000c4:    e51b3008     ldr    r3, [fp, #-8]
300000c8:    e2433001     sub    r3, r3, #1
300000cc:    e50b3008     str    r3, [fp, #-8]
300000d0:    e51b3008     ldr    r3, [fp, #-8]
300000d4:    e3530000     cmp    r3, #0
300000d8:    1afffff9     bne    300000c4 <wait+0x14>
300000dc:    e28bd000     add    sp, fp, #0
300000e0:    e8bd0800     pop    {fp}
300000e4:    e12fff1e     bx    lr

300000e8 <main>:
300000e8:    e92d4800     push    {fp, lr}
300000ec:    e28db004     add    fp, sp, #4
300000f0:    e24dd008     sub    sp, sp, #8
300000f4:    e3a03000     mov    r3, #0
300000f8:    e50b3008     str    r3, [fp, #-8]
300000fc:    e59f3030     ldr    r3, [pc, #48]    ; 30000134 <main+0x4c>
30000100:    e3a02b55     mov    r2, #87040    ; 0x15400
30000104:    e5832000     str    r2, [r3]
30000108:    e59f0028     ldr    r0, [pc, #40]    ; 30000138 <main+0x50>
3000010c:    ebffffe7     bl    300000b0 <wait>
30000110:    e59f3024     ldr    r3, [pc, #36]    ; 3000013c <main+0x54>
30000114:    e3a02000     mov    r2, #0
30000118:    e5832000     str    r2, [r3]
3000011c:    e59f0014     ldr    r0, [pc, #20]    ; 30000138 <main+0x50>
30000120:    ebffffe2     bl    300000b0 <wait>
30000124:    e59f3010     ldr    r3, [pc, #16]    ; 3000013c <main+0x54>
30000128:    e3a02e1e     mov    r2, #480    ; 0x1e0
3000012c:    e5832000     str    r2, [r3]
30000130:    eafffff4     b    30000108 <main+0x20>
30000134:    56000010     undefined instruction 0x56000010
30000138:    00007530     andeq    r7, r0, r0, lsr r5
3000013c:    56000014     undefined instruction 0x56000014

Disassembly of section .ARM.attributes:

00000000 <.ARM.attributes>:
   0:    00002541     andeq    r2, r0, r1, asr #10
   4:    61656100     cmnvs    r5, r0, lsl #2
   8:    01006962     tsteq    r0, r2, ror #18
   c:    0000001b     andeq    r0, r0, fp, lsl r0
  10:    00543405     subseq    r3, r4, r5, lsl #8
  14:    01080206     tsteq    r8, r6, lsl #4
  18:    04120109     ldreq    r0, [r2], #-265    ; 0x109
  1c:    01150114     tsteq    r5, r4, lsl r1
  20:    01180317     tsteq    r8, r7, lsl r3
  24:    Address 0x00000024 is out of bounds.


Disassembly of section .comment:

00000000 <.comment>:
   0:    3a434347     bcc    10d0d24 <SDRAM_BASE-0x2ef2f2dc>
   4:    74632820     strbtvc    r2, [r3], #-2080    ; 0x820
   8:    312d676e     teqcc    sp, lr, ror #14
   c:    312e362e     teqcc    lr, lr, lsr #12
  10:    2e342029     cdpcs    0, 3, cr2, cr4, cr9, {1}
  14:    00332e34     eorseq    r2, r3, r4, lsr lr



通过反汇编代码我们可以看到:第一条指令

ldr pc,=disable_watch_dog 对应的反汇编代码为30000000:    e59ff09c     ldr    pc, [pc, #156]    ; 300000a4 <mem_cfg_val+0x34>


其中pc=下两条指令的地址=0x0000 0008,立即数156对应的16进制为为0x0000 009c,0x0000 0008+0x0000 009c=0x0000 00a4.而SRAM中的0x0000 00a4这个地址中保存的机器码为3000001c,所以执行完这条指令后pc=0x3000 001c,0x3000 001c这个地址是SDRAM中的,而现在SDRAM中什么都没有,所以程序不能正确执行。













在嵌入式编程中,我们经常讲程序保存在 nand flash中。但是我们知道,nand flash的接口设计和 RAM 的接口设计是不一样的。
他的 数据线通常都是复用的,所以通常存取都是以块为单位(nor flash带有RAM接口,有足够的地址线来寻址,
所以可以访问内存中每一个字节)  这导致了,nand flash不可以片内执行程序(nor flash可以,因为他能存取内存每一个字节)


对于 s3c2440 来说,当使用 nand flash 启动时,为了解决 nand flash 不能片内执行程序的问题(片内不能执行,那么程序烧进去不是不能运行嘛)
 于是 s3c2440 在内部有一个 叫 Stepping Stone(垫脚石?)的东西,其实他就是一块 4KB 的RAM,当我们以 nand flash启动想运行烧写在上面
 的程序是,s3c2440会自动将 nand flash 前面 4KB 的内容拷贝到 这个叫Stepping Stone的片内内存中。
 然后 pc指针为0 从这个片内内存0地址开始运行。


 但是这个 片内内存只有 4Kb 大小,如果我们烧到 nand falsh中的程序大于 4KB 那么只有一部分被考到了片内内存中去执行。剩下的就不能执行了。
 
 但是我们不是可以接外设吗, s3c2440的BANK6 和 BANK7都可以接最大 128M的SDRAM。SDRAM是一个RAM(内存)
 
 那么当程序大于 4k 的时候,当我们以 nand flash启动后,前面的4Kb 被拷贝到 片内RAM中去执行(自动完成)。
 我们在这前4K的程序中初始化SDRAM(SDRAM 使用前需要初始化) ,然后将剩下的程序拷贝到 SDRAM中(不是只有4kb 被拷贝到片内RAM中执行了嘛)
 然后跳转到 SDRAM中去执行剩下的程序。
 
 那么也就是说 通常当程序大于 4kb的 时候,我们就需要把程序拷贝到SDRAM中去运行。(程序小于4KB 那么也就可以不用拷贝了,以nand flash方式 启动后,程序全被拷贝到 片内4kb的 RAM中去运行。)。
 
 那么,既然程序大于4kb的时候需要从nand flash中拷贝到 SDRAM中去运行。自然可以想到 烧到nand flash中的程序前面一部分代码应该
 是初始化SDRAM(程序最终需要拷贝到SDRAM中去运行)和 将NAND flash中的剩余的程序拷贝到SDRAM中去(全考过去也行,方便点),然后跳转到SDRAM中执行。
 
下面我们就要详细说明,前面这一段跳转到 SDRAM去执行前在片内内存中运行的初始化代码的要点和细节。也就是 关于程序运行地址和加载地址以及位置无关指令的 一些注意点和细节


先来看下程序运行地址和加载地址


看个 随便写的简单的示例,这是一个连接脚本中的一段 first 0x30000000 : AT(0){main.o}


我们只注意 0x30000000 和 AT(4096) 这两处。


如果程序是烧到nand flash中。这句话里面的意思就是  a.o烧到nand flash 中从0地址(当然也可以是其他数)开始的地方,但他的运行地址是在
从0x30000000地址开始的地方。(为什么是0x30000000,应为 s3c2440的 bank6 和bank7 可以接sdram,bank6从地址0x30000000开始,我们的程序最终是要
在SDRAM中运行的)


烧到 nand flash中从地址0 开始的地方应该比较好理解,就是说我程序是存储在nand flash中最开始的地方。那么运行地址呢怎么理解呢
  
 看一下断汇编 
  1 .text
  2 .global _start
  3 _start:
  4         mov     r0, #2
  5 loop:
  6         ldr     pc,=loop




  
 上面这段汇编,我们将 他的运行地址分别设为 0x0 和0x30000000来看看反汇编后的情况
先将运行地址设为0x0
  all: test.c head.s
  2         arm-linux-gcc -c -Wall -o head.o head.s
  3         arm-linux-ld -Ttext 0x0 -o main_elf  head.o
  4         arm-linux-objdump -D -m arm main_elf > main.dis
  5 
  6 clean:
  7         rm -rf *.o main.dis main_elf


反汇编 main.dis如下:
 6 00000000 <_start>:
  7    0:   e3a00002        mov     r0, #2  ; 0x2
  8 
  9 00000004 <loop>:
 10    4:   e51ff004        ldr     pc, [pc, #-4]   ; 8 <.text+0x8>
 11    8:   00000004        andeq   r0, r0, r4








 将     arm-linux-ld -Ttext 0x0 -o main_elf  head.o
 改为    arm-linux-ld -Ttext 0x30000000 -o main_elf  head.o
 也就是将运行地址设定为0x30000000 再看看它的反汇编
  6 30000000 <_start>:
  7 30000000:       e3a00002        mov     r0, #2  ; 0x2
  8 
  9 30000004 <loop>:
 10 30000004:       e51ff004        ldr     pc, [pc, #-4]   ; 30000008 <.text+0x8>
 11 30000008:       30000004        andcc   r0, r0, r4








 
 注意最左边的数字,这就代表程序的运行时地址。也就是说程序的代码的地址是以运行地址为基址来标示的。什么意思呢,
 
 看 head.s中的  ldr     pc,=loop  这段,这是想让 程序调回到 loop处(死循环)
 当运行地址设定为 0x0 时 从汇编代码我们看到 loop标号代表的地址为0000004 
 也就是说  ldr     pc,=loop 反汇编为 ldr     pc, [pc, #-4] 即pc值为pc-4地址里面放的值(4)。就是跳转到 00000004 地址去
 
 那么把运行地址设定为0x30000000时, 从反汇编代码中我们看到 这时 loop表号代表的地址是0x30000000
 那么 ldr     pc,=loop 就是跳转到 地址 0x30000000
 
简单的理解就是 程序运行地址 就是 计算机认为程序运行时应该处于的地址。
所以 运行地址设置为0 时,程序中的所有代码的中的标号都是以0x0为基址的。计算机认为他运行的时候的地址是从 0x0开始的。
运行地址设置为0x30000000 时,程序中的所有代码的中的标号都是以0x30000000为基址的。计算机认为他运行的时候的地址是从0x30000000开始的
 
 
 
明白了 运行地址 的概念后,我们来看看程序的启动过程


假设现在程序的 加载地址(烧到nand flash中的位置)为0 运行地址为0x30000000
前面说过程序是烧写在 nand flash中从 0地址开始的地方,那么以nand flash方式启动后,该程序被拷贝到 片内ram中。这时候pc为0。并开始运行程序,也就是说,计算机现在从 片内内存地址0开始运行程序进行一些初始化操作并将sdram初始化后再跳转到sdram中去运行。
但是我们上面不是说,程序运行地址被设置成了 0x30000000(SDRAM的起始地址)吗。

但是程序在跳转到sdram之前却是在0x0地址开始运行。这就造成了前面这段还未跳转到sdram(从0x30000000开始)的程序的实际程序运行地址和设定的运行地址不符合。


如果程序中没有用到 地址有关代码,和全局变量静态变量之类地址有关变量,那么这段 在初始化SDRAM之前 的程序 其实还是可以运行下去的
并在初始化SDRAM后,跳转到SDRAM中去运行,一切正常
但是如果程序中用了这些代码。就不会运行成功了。


原因很简单,应为现在我们设定的 运行地址为 0x30000000,那么当我们执行地址有关代码 如 ldr pc,=A (A是个标号或函数名)
就是使用了 绝对地址,那么 pc = 0x3000000+x (x为标号A相对于起始也就是前面设定的运行地址的偏移) 。

那么这条指令执行之后,pc 指针将跳转到 0x30000000后的某处(SDRAM中)。但是 sdram 现在 还未初始化! 
这就 造成了 错误。
同样 如果有全局变量 或静态变量也是。


所以我们需要使用 b bl类的 位置无关指令。  如  b  A
b bl跳转是基于 pc的 跳转。即相对跳转 ,比如执行 b A 这条指令时,假设 现在pc =5. A标号相对当前的位置为2(在之后)
那么 b A 后 pc =pc +2    即计算机不管当前pc指针是多少,他执行的是相对于当前位置的跳转。也就避免了 向上面的那样跳转到了 0x30000000之后的未初始化的地址中去


1. 假设PC=0x1000, 现在 PC <--- 0x1200 ,这个0x1200就是绝对地址;如果是 PC + 0x20 这个020就是相对地址。
 
2. 绝对和相对路径是PC中文件目录结构中用的.绝对地址和相对地址在汇编中经常出现.关键是理解什么是绝对,什么是相对.


举个简单的例子:


在一个队伍中,从头开始你排在15位,这就是绝对位置,指从头开始.
如果前9个是女生,第10位开始为男生.那么你就是从男生开始的第5位,这个5就是相对位置.


在汇编中有绝对跳转和相对跳转指令.绝对跳转指令中给出的是绝对地址,也就是从0000H开始算起的地址.而相对跳转的指令中是相对地址,是说相对跳转指令本身所在地址加上一个(偏移)量.


如果转移指令在10的位置,要转移执行15位置的指令:可以使用


绝对转移:JMP 15      =======>这里的15是绝对地址,从0开始算.
相对转移:JNE 5       =======>这里的5时相对地址,相对JNE的位置,加5.而JNE在第10个,加上5为15,同上一句相同都是跳转到15的位置执行了.
 
3. 也就是说绝对地址是相对于地址0x0000的偏移量.而相对地址上相对于当前执行指令地址量来说的.




只要将被编译到0xC0000000地址的代码放到0x00000000地址开始执行,如果它们只
使用顺序执行或者相对跳转执行方式就可以正常运行,但如果使用了绝对寻址,那么程序就跑飞了。

因此我们在看linux内核启动这部分的代码时只能看到顺序执行指令和相对跳转指令,不会存在绝对跳转指令,就是这个原因。如果需要使用全局变量或者函数指针时,则需要将 这个地址减去0xC0000000的偏移量才可以获取到此时运行的地址,因为全局变量和函数指针在编译时都是按照0xC0000000基址编译出的绝对地址,运行时既然将程序段和数据段都偏移了0xC0000000的距离,那么使用时只要减去这个值就可以找到正确的位置了.




链接时指定链接地址为0X30000000的目的是为了让SRAM程序和SDRAM程序无缝连接,通过一条绝对跳转指令ldr,让程序跳转到SDRAM中"继续"运行。让CPU错误的以为程序刚开始就是运行在SDRAM的0x30000000上的。

  • 3
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值