由于在学I2C裸板程序中在分析head.S时用到了这个位置无关码这个概念,并且之前一直不明白位置无关码,故此次通过查阅其他大神的博客了解可以得到下面的教程,希望对学ARM的朋友有一点帮助。
参考:http://www.cnblogs.com/mylinux/p/5577472.html
位置无关代码:即该段代码无论放在内存的哪个地址,都能正确运行。究其原因,是因为代码里没有使用绝对地址,都是相对地址。
位置相关码:即它的地址与代码处于的位置相关,是绝对地址,如:mov PC ,#0xff;ldr pc,=0xffff等。
为什么需要位置无关码?
通常情况下,将bootloader程序下载到ROM的0x0地址进行启动(比如固化到NorFlash中)。然而在很多的设计中,比如将bootloader固化在NAND中,在系统复位后S3C2440A中NAND控制器自动读取NAND中存储的前4K的代码到s3c2440a中称之为steppingstone(垫脚石)的SRAM中,steppingstone中的代码用进行一些非核心的硬件初始化,再将NAND中剩下的bootloader代码拷贝到SDRAM中运行。一般境况下两者的地址并不相同,程序在SDRAM中的地址重定位过程必须由程序员来完成。这样就有了位置无关代码的概念,指代码不在连接时制定的运行地址空间,也可以执行,它一段加载到任意地址空间都能执行的特殊代码。这样在steppingstone设计的代码要用位置无关设计。一些裸板程序中也可是需要。如果你的这段代码需要实现位置无关,那么你就不能使用绝对寻址指令,否则的话就是位置有关了。
位置无关码的使用场合
1. 程序在运行期间动态加载到内存;
2. 程序在不同场合与不同程序组合后加载到内存(共享的动态链接库);
3. 在运行期间不同地址相互之间的映射(如bootloader)
怎么实现位置无关码?
{
1. 位置无关的函数跳转
2. 位置无关的常量访问
}
位置无关的汇编写法
1.B指令
跳转指令,B指令接受一个相对地址,因此在汇编里用B跳转到一个标号时,实际编译的结果是一个相对跳转。相对地址有个范围限制,即目标不能太远,一般目标放在同一个文件里是肯定可以的。 Offset must IN 32Mbit
_start:
b Reset
Reset:
2.BL指令
同样也是跳转指令,一般用于跳转函数
bl disable_watch_dog
3.ADR指令
获取标号的地址,在编译时会使用PC+偏移的方式得到该位置的地址。例如,当TEXT_BASE是0时
SMRDATA可能被放在0x100的位置,当TEXT_BASE为0x30000000时放在0x30000100的位置。
使用ADR总能获取正确的位置,与程序的加载地址无关
ADR R0, SMRDATA
SMRDATA:
.word 0x22111120
.word 0x00002F50
.word 0x00000700
4.LDR指令
当加标号时,LDR可以用于伪指令,也可以真指令。
真指令: (标号前不加=号,表示取标号处的值)
LDR R0, SDRDATA
实际被编译为LDR R0, [PC, #NN],其中NN是目标的相对距离
伪指令:(标号前加=号,取标号的地址)
LDR R0, =SDRDATA
实际编译的时候的时候,会在某位置存处SDRDATA的值,然后用一个LDR取出来。
可以看出,使用=和不使用=号是有很大区别的。
无=号:取该标号处的值,位置无关
有=号:取该标号的地址,位置相关
5.bl/b调用的c语言函数里面也不要使用全局变量, 因为c里面的全局变量的地址是根据链接地址生成的
in head.s
Reset:
ldr sp, =4096 @ 设置栈指针,以下都是C函数,调用前需要设好栈
bl disable_watch_dog @ 关闭WATCHDOG,否则CPU会不断重启
bl clock_init @ 设置MPLL,改变FCLK、HCLK、PCLK
bl memsetup @ 设置存储控制器以使用SDRAM
bl nand_init @ 初始化NAND Flash
nand.c :
void nand_init(void)
{
S3C2410_NAND * s3c2410nand = (S3C2410_NAND *)0x4e000000;
S3C2440_NAND * s3c2440nand = (S3C2440_NAND *)0x4e000000;
#define TACLS 0
#define TWRPH0 3
#define TWRPH1 0
/* 判断是S3C2410还是S3C2440 */
if ((GSTATUS1 == 0x32410000) || (GSTATUS1 == 0x32410002))
{
/* 使能NAND Flash控制器, 初始化ECC, 禁止片选, 设置时序 */
s3c2410nand->NFCONF = (1<<15)|(1<<12)|(1<<11)|(TACLS<<8)|(TWRPH0<<4)|(TWRPH1<<0);
/* 复位NAND Flash */
s3c2410_nand_reset();
}
else
{
/* 设置时序 */
s3c2440nand->NFCONF = (TACLS<<12)|(TWRPH0<<8)|(TWRPH1<<4);
/* 使能NAND Flash控制器, 初始化ECC, 禁止片选 */
s3c2440nand->NFCONT = (1<<4)|(1<<1)|(1<<0);
/* 复位NAND Flash */
s3c2440_nand_reset();
}
一开始程序位于Nand中,在上电时,Nand中的前4k内容会被拷贝到片内内存SRAM中,所以得用位置无关码,调用的c语言函数中不允许有全局变量,故上述代码中没有全局变量。
——作者:Edison Gao(小白要学arm)