您的位置:首页 > 编程语言

关于ARM指令中位置无关和位置相关代码的认识

2016-01-17 20:14 399 查看
今天在一个问题上折腾了又是半天。就是在学JZ2440串口通信的时候,在sdram初始化函数中有这么一句话


/*
* 设置存储控制器以使用SDRAM
*/
void memsetup(void)
{

volatile unsigned long *p = (volatile unsigned long *)MEM_CTL_BASE;

/* 这个函数之所以这样赋值,而不是像前面的实验那样将配置值
* 写在数组中,是因为要生成”位置无关的代码”,使得这个函数可以在被复制到
* SDRAM之前就可以在steppingstone中运行
*/
/* 存储控制器13个寄存器的值 */
p[0] = 0x22011110;     //BWSCON
p[1] = 0x00000700;     //BANKCON0
p[2] = 0x00000700;     //BANKCON1
p[3] = 0x00000700;     //BANKCON2
p[4] = 0x00000700;     //BANKCON3
p[5] = 0x00000700;     //BANKCON4
p[6] = 0x00000700;     //BANKCON5
p[7] = 0x00018005;     //BANKCON6
p[8] = 0x00018005;     //BANKCON7
p[9]  = 0x008C04F4;    //REFRESH
p[10] = 0x000000B1;     //BANKSIZE
p[11] = 0x00000030;     //MRSRB6
p[12] = 0x00000030;     //MRSRB7

}


对于前面实验,sdram初始化函数是这么写的:

void memsetup(void)
{
/* SDRAM 13个寄存器的值 */
unsigned long  const    mem_cfg_val[]={ 0x22011110,     //BWSCON
0x00000700,     //BANKCON0
0x00000700,     //BANKCON1
0x00000700,     //BANKCON2
0x00000700,     //BANKCON3
0x00000700,     //BANKCON4
0x00000700,     //BANKCON5
0x00018005,     //BANKCON6
0x00018005,     //BANKCON7
0x008C07A3,     //REFRESH
0x000000B1,     //BANKSIZE
0x00000030,     //MRSRB6
0x00000030,     //MRSRB7
};
int     i = 0;
volatile unsigned long *p = (volatile unsigned long *)MEM_CTL_BASE;
for(; i < 13; i++)
p[i] = mem_cfg_val[i];
}


对于这个串口程序,它的链接文件如下:

SECTIONS {
. = 0x30000000;
.text          :   { *(.text) }
.rodata ALIGN(4) : {*(.rodata)}
.data ALIGN(4) : { *(.data) }
.bss ALIGN(4)  : { *(.bss)  *(COMMON) }
}


表示该程序“应该”放在0x30000000中执行。

在程序运行时,首先执行head.S中的代码。

head.S代码如下:

.extern     main
.text
.global _start
_start:
Reset:
ldr sp, =4096           @ 设置栈指针,以下都是C函数,调用前需要设好栈
bl  disable_watch_dog  @ 关闭WATCHDOG,否则CPU会不断重启
bl  clock_init          @ 设置MPLL,改变FCLK、HCLK、PCLK
bl  memsetup            @ 设置存储控制器以使用SDRAM
bl  copy_steppingstone_to_sdram     @ 复制代码到SDRAM中
ldr pc, =on_sdram                   @ 跳到SDRAM中继续执行
on_sdram:
ldr sp, =0x34000000     @ 设置栈指针
ldr lr, =halt_loop      @ 设置返回地址
ldr pc, =main           @ 调用main函数
halt_loop:
b   halt_loop


程序运行时,对于NAND FLASH启动,首先NAND FLASH前4K代码会被复制到steppingstone片内内存中去执行,这个时候0x30000000(对于JZ2440来说该地址是SDRAM地址)还不能使用,因为SDRAM还没初始化,虽然链接的时候指出了,程序应该位于0x30000000执行,但是程序代码从NAND FLASH拷贝到片内内存之后,CPU默认从0地址开始执行,所以程序会被执行,当SDRAM没被初始化之前,用的都是bl跳转指令,这些指令都是位置无关指令。

对于反汇编文件,先看前面一部分:

30000000 <_start>:
30000000:   e3a0da01    mov sp, #4096   ; 0x1000
30000004:   eb00000a    bl  30000034 <disable_watch_dog>
30000008:   eb00000d    bl  30000044 <clock_init>
3000000c:   eb000026    bl  300000ac <memsetup>
30000010:   eb000040    bl  30000118 <copy_steppingstone_to_sdram>
30000014:   e59ff00c    ldr pc, [pc, #12]   ; 30000028 <.text+0x28>

30000018 <on_sdram>:
30000018:   e3a0d30d    mov sp, #872415232  ; 0x34000000
3000001c:   e59fe008    ldr lr, [pc, #8]    ; 3000002c <.text+0x2c>
30000020:   e59ff008    ldr pc, [pc, #8]    ; 30000030 <.text+0x30>

30000024 <halt_loop>:
30000024:   eafffffe    b   30000024 <halt_loop>
30000028:   30000018    andcc   r0, r0, r8, lsl r0
3000002c:   30000024    andcc   r0, r0, r4, lsr #32
30000030:   30000200    andcc   r0, r0, r0, lsl #4

30000034 <disable_watch_dog>:
30000034:   e3a02000    mov r2, #0  ; 0x0
30000038:   e3a03453    mov r3, #1392508928 ; 0x53000000
3000003c:   e5832000    str r2, [r3]
30000040:   e1a0f00e    mov pc, lr


前面表示了程序链接的地址。但是程序在片内内存中还是会继续执行,当执行到

bl  disable_watch_dog


这个语句时,程序会跳转到disable_watch_dog去执行,由于bl是位置无关,所以跳转的时候只是PC(new) = PC + 偏移。

而对于ldr pc, =on_sdram这些指令来说则是位置有关,即让PC赋值为一个确切值。对于

=on_sdram来说,它则等于 30000018,所以程序会跳转到该位置中去执行,若SDRAM没初始化之前使用了这类位置有关的指令,则会出错。

所以在下列情况下:

void memsetup(void)
{
/* SDRAM 13个寄存器的值 */
unsigned long  const    mem_cfg_val[]={ 0x22011110,     //BWSCON
0x00000700,     //BANKCON0
0x00000700,     //BANKCON1
0x00000700,     //BANKCON2
0x00000700,     //BANKCON3
0x00000700,     //BANKCON4
0x00000700,     //BANKCON5
0x00018005,     //BANKCON6
0x00018005,     //BANKCON7
0x008C07A3,     //REFRESH
0x000000B1,     //BANKSIZE
0x00000030,     //MRSRB6
0x00000030,     //MRSRB7
};
int     i = 0;
volatile unsigned long *p = (volatile unsigned long *)MEM_CTL_BASE;
for(; i < 13; i++)
p[i] = mem_cfg_val[i];
}


mem_cfg_val的链接地址会大于0x30000000,这个时候SDRAM还没被初始化,当执行下列语句时:

p[i] = mem_cfg_val[i];


即程序会去 mem_cfg_val 所在位置取值,所以会出错。因为此时SDRAM没初始化。

但是对于下列这种情况则不会出错:

/*
* 设置存储控制器以使用SDRAM
*/
void memsetup(void)
{

volatile unsigned long *p = (volatile unsigned long *)MEM_CTL_BASE;

/* 这个函数之所以这样赋值,而不是像前面的实验那样将配置值
* 写在数组中,是因为要生成”位置无关的代码”,使得这个函数可以在被复制到
* SDRAM之前就可以在steppingstone中运行
*/
/* 存储控制器13个寄存器的值 */
p[0] = 0x22011110;     //BWSCON
p[1] = 0x00000700;     //BANKCON0
p[2] = 0x00000700;     //BANKCON1
p[3] = 0x00000700;     //BANKCON2
p[4] = 0x00000700;     //BANKCON3
p[5] = 0x00000700;     //BANKCON4
p[6] = 0x00000700;     //BANKCON5
p[7] = 0x00018005;     //BANKCON6
p[8] = 0x00018005;     //BANKCON7
p[9]  = 0x008C04F4;    //REFRESH
p[10] = 0x000000B1;     //BANKSIZE
p[11] = 0x00000030;     //MRSRB6
p[12] = 0x00000030;     //MRSRB7

}


虽然这段代码中p指针变量也是位于0x30000000之后,但是p[0],p[1],这些值会被编译器编译成与MEM_CTL_BASE相关的值,也就是与SDRAM初始化有关的寄存器的地址。所以此时不管p指针位于哪也与它的地址无关。

新手一枚还在进步中,有说错的地方希望各位大神能够为我指出,也欢迎各位和我一起交流讨论。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: