作者:GWD 时间:2019.7.12
一、课程内容:
摘要:本节课讲解了整体的代码重定位与位置无关码,其中重点和难点是位置无关码。
实现第二种方法的代码重定位:在第二节课讲的第一个代码实现的功能,是分段重定位的,本节讲的是整体形式的链接脚本。
1、问:位置无关码怎么理解?(很关键,一开始没理解!!!!)
答:位置无关码说白了就是相对跳转指令与绝对跳转指令的关系。譬如bl 0x30000478的意思并不是跳到0x30000478,实际意义是由当前的PC值跳到一个偏移地址。若当前指令PC值是0,则跳到0x00000478了。在程序中写这个值只是为了方便看代码。
注:在汇编文件中B/BL只是方便查看作用,不是跳到这个地址。
例:
这里bl 300001d0的意思相当于一个“标号”只是方便程序员到0x300001d0这个“标号”位置去查看代码,真实情况中这个代码在哪个位置中运行是与PC指针的位置有关系的。理解这点很重要,我们看下一点就知道这个知识点的重要性了。
2、问、1与2的汇编代码一样但是区别在哪里?
答:首先看实验现象,当烧写1语句时,串口打印的速度明显没有语句2速度快,由此可知代码1的时候程序还是在nor中运行。
其次分析代码:语句1就是相对跳转指令,此时PC指针并不是真正的0x300005b0只相当于标号,代码只是偏移了0x5b0还在nor中运行,执行语句2是绝对跳转指令,是真的把0x30000640给了PC指针,程序在sdram中运行了。
3、问:那么什么时候用相对跳转(位置无关),什么时候用绝对跳转(位置相关)呢?
答:
这张图展示了启动的流程,bin文件包含段和地址的信息,烧写进nor_flash中,在nor中运行,然后重定位到SDRAM,所以可知,在重定位之前的代码应该是位置无关的(相对跳转在nor中执行),重定位之后的代码要位置相关(在SDRAM中运行);
4、问:说了这么多怎么写位置无关的程序呢?
答:
a. 调用程序时使用B/BL相对跳转指令
b. 重定位之前, 不可使用绝对地址,比如:
不可访问全局变量/静态变量
不可访问有初始值的数组(因为初始值放在rodata里,使用绝对地址来访问)
c. 重定位之后, 使用绝对跳转命令跳到Runtime Addr,比如: ldr pc, =main
5、问:相对地址与绝对地址怎么理解区分呢?
答:相对地址就是跳转,所有的跳转都是以当前为基准的。相对于当前的地址;绝对地址是直接去了。绝对唯一的地址,在汇编的过程中,会根据跳转的距离自动形成。相对地址的范围一般比较短。在中国说故宫大家都知道,要是在美国说故宫就要说中国北京故宫。所以要是需要绝对地址的时候为了保险不让编译器自己做主就要用ldr pc,=XXX避免程序没跳出nor。
二、代码编写过程
1、写一个新的链接脚本,首先参考u-boot
这种整体的链接脚本,要比之前的分体的好,用的也多,分体的适合单片机,单片机内部有可以运行程序的flash,这样可以节省内存,但是对于嵌入式系统内存很庞大,不用省这一点内存。还有一个原因是,JTAG一般只支持这种整体的链接脚本
2、在START.S中修改重定位前后的代码,重定位前用位置无关码(相对地址),重定位后用绝对跳转指令(绝对地址)
三、代码
链接脚本
SECTIONS
{
. = 0x30000000;
. = ALIGN(4);
.text :
{
*(.text)
}
. = ALIGN(4);
.rodata : { *(.rodata) }
. = ALIGN(4);
.data :
{
*(.data)
}
. = ALIGN(4);
bss_start = .;
.bss : { *(.bss) *(.COMMON) }
end =.;
}
START.S
.text
.global _start
_start:
/* 关闭看门狗 */
ldr r0, =0x53000000
ldr r1, =0
str r1, [r0]
/* 设置MPLL, FCLK : HCLK : PCLK = 400m : 100m : 50m */
/* LOCKTIME(0x4C000000) = 0xFFFFFFFF */
ldr r0, =0x4C000000
ldr r1, =0xFFFFFFFF
str r1, [r0]
/* CLKDIVN(0x4C000014) = 0X5, tFCLK:tHCLK:tPCLK = 1:4:8 */
ldr r0, =0x4C000014
ldr r1, =0x5
str r1, [r0]
/* 设置CPU工作于异步模式 */
mrc p15,0,r0,c1,c0,0
orr r0,r0,#0xc0000000 //R1_nF:OR:R1_iA
mcr p15,0,r0,c1,c0,0
/* 设置MPLLCON(0x4C000004) = (92<<12)|(1<<4)|(1<<0)
* m = MDIV+8 = 92+8=100
* p = PDIV+2 = 1+2 = 3
* s = SDIV = 1
* FCLK = 2*m*Fin/(p*2^s) = 2*100*12/(3*2^1)=400M
*/
ldr r0, =0x4C000004
ldr r1, =(92<<12)|(1<<4)|(1<<0)
str r1, [r0]
/* 一旦设置PLL, 就会锁定lock time直到PLL输出稳定
* 然后CPU工作于新的频率FCLK
*/
/* 设置内存: sp 栈 */
/* 分辨是nor/nand启动
* 写0到0地址, 再读出来
* 如果得到0, 表示0地址上的内容被修改了, 它对应ram, 这就是nand启动
* 否则就是nor启动
*/
mov r1, #0
ldr r0, [r1] /* 读出原来的值备份 */
str r1, [r1] /* 0->[0] */
ldr r2, [r1] /* r2=[0] */
cmp r1, r2 /* r1==r2? 如果相等表示是NAND启动 */
ldr sp, =0x40000000+4096 /* 先假设是nor启动 */
moveq sp, #4096 /* nand启动 */
streq r0, [r1] /* 恢复原来的值 */
bl sdram_init
/*重定位data段*/
mov r1,#0
ldr r2, = _start
ldr r3, = bss_start
cpy:
ldr r4,[r1]
str r4,[r2]
add r1,r1,#4
add r2,r2,#4
cmp r2,r3
bne cpy
/*清除bss段*/
ldr r1,= bss_start
ldr r2,= end
mov r3,#0
clean:
str r3,[r1]
add r1,r1,#4
cmp r1,r2
bne clean
//bl main
ldr pc,=main
halt:
b halt