有关概念
首先arm地址有几个地址概念:编译地址、期望运行物理地址、实际运行物理地址、位置无关、位置相关 编译地址 :编译阶段的生成地址,其是相对地址 ,有些地址还没确定,如调用其他.O文件中的函数或库函数 期望运行物理地址 :链接阶段生成的地址(期望绝对地址 ),会确定编译阶段没有确定的地址,并会根据链接器假设期望的程序烧录运行物理启始地址 ,来分配期望运行物理地址;*1* 实际运行物理地址 :当程序被烧写到系统后,实际加载运行时的地址(实际绝对地址 ),又有一个实际的程序烧录运行物理启始地址,这个时候进行相应的转换得到实际运行物理地址 位置无关 :和实际烧写运行的地址无关 以上转载自http://blog.sina.com.cn/s/blog_76894ccd01014xwm.html
注*1*:这里有点错误:
1)这里如果使用脚本一般会生成运行地址(0xb0004000),不一定是物理地址;因为这个有CPU负责,CPU并不知道是虚拟地址还是物理地址,所以要看开启MMU没有,没有开启就是物理地址,开启之后就要经过MMU经过va->pa的转换,才能正确寻址到正确地址。
2)另外这里会将程序放到指定的位置(AT(2048)),就是烧到nand后的地址,这个地址是相对于nand而言,跟cpu没有关系,,在nand4k的代码会有片内芯片自动复制到sram中进行执行
如下脚本
SECTIONS {
firtst 0x00000000 : { head.o init.o }
second 0xB0004000 : AT(2048) { leds.o }
}3)一般来说在没有启用MMU时,这时候主要考虑位置相关地址和位置无关地址的指令如何执行
4)在2440使用nand启动的代码执行流程:复制nand中前面4k->片内芯片sram执行4k代码->复制4k代码到SDRAM->从sdram执行
这里面就牵涉到位置相关与位置无关地址的问题,因为在4k代码要保证在任何地方执行,所以使用位置无关的指令,而ldr就是位置相关代码
代码分析
在使用sdram之前,肯定要初始化存储控制器,下面的例子就牵涉这些,看下面代码
这里使用的是adr r0, mem_cfg_val ,反汇编代码对比
这时候r0=0x70,正好指向mem_cfg_val所指向的地址,说明使用adr是正确的
在修改为ldr r0, =mem_cfg_val之后,看如下汇编代码
看到此时r0代码指向地址[0xa8]=ela00000并不是所希望的地址0x70,而是是一条空指令。
这里初次调试没有在意!!,看下面
这里进过再次调试,发现原来[0xa8]=0x70,汇编指令里就自动生成了这个地址,如下
但是这里调试之后,并不能执行程序中led的点亮,why??,这是因为在程序运行的时候根本无法定位0x30000070这个地址,因为sdram这时候并没有初始化,就无从淡起这个地址0x30000070,所以应该使用无关地址
这里为什么使用的是位置无关指令adrl/adr??
如何界定哪里需要使用相对寻址(位置无关) 与绝对地址(位置相关)??
当我代码在4k之内就跳到sdram时,这时候也可以使用位置无关代码,还是必须使用相对地址, 如ldr这种指令??
在4k之内,比如3096时,此时已经跳到sdram,这时候sdram假设是从30000000+3096时执行,这个时候是否意味只能使用ldr这种绝对地址的指令??
估计以上的问题有几个已经清楚了,下面继续看
首先从以上代码我就看出来了,adr是基 当前pc+偏移的寻址;而ldr是伪指令,是大范围的地址读取指令,当有“=”时,表示伪指令,就是从那个地址读取数据,否则就是地址读取指令,下面代码会进行阐述
修改为ldr r0, mem_cfg_val,反汇编得到的代码如下
这时候看到是取0x70里面的值,而此时我只是取其地址,所以并不正确
综上所述
1. 4k之内跟地址有关一般使用无关性指令,比如b/bl、adr/adrl...,这是因为要保证任何位置都能执行
2. 使用sdram时(一般都是大于4k),这时候在4k之内有些变量并不一定能执行,因为有些位置相关地址这时候并不一定存在那个地址处, 而adr的寻址空间只有前后4kb,ldr就没有这些要求,当然使用adrl可以>4k(b/bl可以跳转前后32m)