1. 从指令级来看:
请参考书的3.1.2中的第1小节
2. 从C语言来看,如果要使用全局变量、静态变量,那么肯定就是位置相关的。
要理解这点,需要看汇编代码。
比如使用静态变量mem_cfg_val的memsetup函数(这里说是静态变量也许不合适,但是mem_cfg_val是保存在数据段中的):
/*
* 设置存储控制器以使用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];
}
连接地址是0时,它的反汇编代码是:
00000048 :
48: e59f0030 ldr r0, [pc, #48] ; 80 // r0等于内存地址0x80处的数值,往后看“80: 00000160 ”,就是0x160
4c: e5902000 ldr r2, [r0] // 0x160就是mem_cfg_val数组的地址,取值,r2就是mem_cfg_val[i]数组的值
50: e3a03312 mov r3, #1207959552 ; 0x48000000 // 存储控制器的基址
54: e5832000 str r2, [r3] // 把r2的值写入寄存器
58: e2831004 add r1, r3, #4 ; 0x4 // 寄存器地址增4
5c: e0813000 add r3, r1, r0
60: e283332e add r3, r3, #-1207959552 ; 0xb8000000
64: e5932000 ldr r2, [r3]
68: e3a03312 mov r3, #1207959552 ; 0x48000000
6c: e4812004 str r2, [r1], #4
70: e2833034 add r3, r3, #52 ; 0x34
74: e1510003 cmp r1, r3
78: 1afffff7 bne 5c
7c: e12fff1e bx lr
80: 00000160 .word 0x00000160
……
00000160 :
160: 22011110 .word 0x22011110
164: 00000700 .word 0x00000700
168: 00000700 .word 0x00000700
16c: 00000700 .word 0x00000700
170: 00000700 .word 0x00000700
174: 00000700 .word 0x00000700
178: 00000700 .word 0x00000700
17c: 00018005 .word 0x00018005
180: 00018005 .word 0x00018005
184: 008c07a3 .word 0x008c07a3
188: 000000b1 .word 0x000000b1
18c: 00000030 .word 0x00000030
190: 00000030 .word 0x00000030
194: 43434700 .word 0x43434700
198: 4728203a .word 0x4728203a
19c: 2029554e .word 0x2029554e
1a0: 2e322e34 .word 0x2e322e34
1a4: 0f410032 .word 0x0f410032
1a8: 61000000 .word 0x61000000
1ac: 69626165 .word 0x69626165
1b0: 00050100 .word 0x00050100
1b4: 00000000 .word 0x00000000
可以看到,使用“unsigned long const mem_cfg_val”时,这些数值不是在栈中的,而是通过它的“编译地址”去内存中读取。
这就不是位置无头的代码,而是位置相关:如果编译地址是0x30000000,那么就会去SDRAM中取值。
3. 从C语言来看,如果使用“p[0] = 0x22011110;”这样的方式,是位置无关的。
比如:
/*
* 设置存储控制器以使用SDRAM
*/
void memsetup(void)
{
volatile unsigned long *p = (volatile unsigned long *)MEM_CTL_BASE;
/* 这个函数之所以这样赋值,而不是像前面的实验(比如mmu实验)那样将配置值
* 写在数组中,是因为要生成”位置无关的代码”,使得这个函数可以在被复制到
* 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
/* REFRESH,
* HCLK=12MHz: 0x008C07A3,
* HCLK=100MHz: 0x008C04F4
*/
p[9] = 0x008C04F4;
p[10] = 0x000000B1; //BANKSIZE
p[11] = 0x00000030; //MRSRB6
p[12] = 0x00000030; //MRSRB7
}
它的反汇编代码如下:
30000100 :
30000100: e3a03422 mov r3, #570425344 ; 0x22000000 // 直接赋值
30000104: e2833a11 add r3, r3, #69632 ; 0x11000 // 相加,就是0x22011000
30000108: e3a01312 mov r1, #1207959552 ; 0x48000000 // 直接赋值,就是BWSCON的地址
3000010c: e2833e11 add r3, r3, #272 ; 0x110 // 相加,就是0x22011110,这就是“ p[0] = 0x22011110”中右边的值
30000110: e5813000 str r3, [r1] // 相当于: p[0] = 0x22011110
30000114: e3a03723 mov r3, #9175040 ; 0x8c0000
30000118: e3a02c07 mov r2, #1792 ; 0x700
3000011c: e3a00906 mov r0, #98304 ; 0x18000
30000120: e2833e4f add r3, r3, #1264 ; 0x4f0
30000124: e5812004 str r2, [r1, #4]
30000128: e2800005 add r0, r0, #5 ; 0x5
3000012c: e5812008 str r2, [r1, #8]
30000130: e3a0c030 mov ip, #48 ; 0x30
30000134: e581200c str r2, [r1, #12]
30000138: e2833004 add r3, r3, #4 ; 0x4
3000013c: e5812010 str r2, [r1, #16]
30000140: e5812014 str r2, [r1, #20]
30000144: e5812018 str r2, [r1, #24]
30000148: e3a020b1 mov r2, #177 ; 0xb1
3000014c: e581001c str r0, [r1, #28]
30000150: e5810020 str r0, [r1, #32]
30000154: e5813024 str r3, [r1, #36]
30000158: e5812028 str r2, [r1, #40]
3000015c: e581c02c str ip, [r1, #44]
30000160: e581c030 str ip, [r1, #48]
30000164: e12fff1e bx lr
可见,memsetup的操作不涉及编译地址,所以它的位置无关的。
4.
请问,在95页的mem_cfg_val是在代码段中还是栈中?或是其他方式?
而这个程序的链接地址是0x30000000,却可以在Steppingstone中正确执行,这是为什么?
在/work/hardware/timer Head.S 的48行 ldr pc,=on_sdram 执行后就会跳到sdram中执行,而不是继续在Steppingstone中执行,这是为什么?
① 95页中使用汇编代码赋值,都是使用adrl、mov、ldr等与位置无关的指令,所以“链接地址是0x30000000,却可以在Steppingstone中正确执行”
② ldr pc,=on_sdram 的结果是这样,你看看反汇编:
30000034: e59ff03c ldr pc, [pc, #60] ; 30000078 // 当前PC值是0x34+8(流水线,PC值是当前指令+8)=0x3c,pc+60=0x78;放条指令就是从0x78的内存取值,赋给PC
30000078: 30000038 .word 0x30000038 // 0x78的值就是0x30000038,所以,上面的指令执行完后,PC的值就是0x30000038,在SDRAM中了
====