一、重定向
对于程序而言,我们需要理解两个概念,一是程序当前所处的地址,即程序在运行时,所处的当前地址;二是程序的链接地址,即程序运行时应该位于的运行地址。编译程序时,可以指定程序的链接地址。对于Tiny4412而言,启动时只会从MMC/sd等启动设备中拷贝前16K的代码到IRAM中,那么当我们的程序超过16K怎么办?那就需要我们在前16K的代码中将整个程序完完整整地拷贝到DRAM等其他更大存储空间,然后再跳转到DRAM中继续运行我们的代码,这个拷贝然后跳转的过程就叫重定位。
本章中我们主要学习如何重定位,但是并不会涉如何使用到DRAM,而是简单地将代码从IRAM的0x02020010处拷贝到IRAM的0x02028000处,然后跳转到0x02028000处继续运行我们的代码。
二、程序说明
完整代码见目录5_link_0x8000,该目录下的代码与上一章的代码的差别在于start.S和使用了
链接脚本link.lds,我们首先分析link.lds。
1. link.lds
什么是链接脚本?链接脚本就是程序链接时的参考文件,其主要目的是描述如何把输入文件中的段(SECTION)映射到输出文件中,并控制输出文件的存储布局。链接脚本的基本命令式SECTIONS命令,一个SECTIONS命令内部包含一个或多个段,段(SECTION)是链接脚本的基本单元,它表示输入文件中的某个段是如何放置的。
链接脚本的标准格式如下:
SECTIONS
{
sections-command
sections-command
}
下面我们配合link.lds进行具体讲解:
SECTIONS
{
. = 0x02028000;
.text : {
start.o
* (.text)
}
.data : {
* (.data)
}
bss_start = .;
.bss : {
* (.bss)
}
bss_end = .;
}
1) 在链接脚本中,单独的点号(.)代表了当前位置,. =0x02028000;表示程序的链接地址是00x02028000;
2) link.lds中的.text 、 .data 、 .bss分别是text段、data段、bss段的段名(这些段名并不是固定的,是可以随便起的)。.text 段包含的内容是start.o 和其余代码中所有的text段;.data段包含的内容是代码中所有的data段;.bss段包含的内容是代码中所有的bss段。
3) bss_start和bss_end保存的是bss段的起始地址和结束地址,在start.S中会被用到。
下面解释一下什么是data、text、bss段:
1) data段:数据段(datasegment)通常是指用来存放程序中已初始化的全局变量的一块内存区域。数据段属于静态内存分配。
2) text段:代码段通常是指用来存放程序执行代码的一块内存区域。这部分区域的大小在程序运行前就已经确定,并且内存区域通常属于只读,某些架构也允许代码段为可写,即允许修改程序。在代码段中,也有可能包含一些只读的常数变量,例如字符串常量等。
3) bss段:指用来存放程序中未初始化的全局变量的一块内存区域。BSS是英文BlockStarted by Symbol的简称。当我们的程序有全局变量是,它是放在bss段的,由于全局变量默认初始值都是0,所有我们需要手动清bss段。
2. start.S
在start.S中,我们初始化时钟后,增加了3个步骤:
第一步重定位,代码如下:
// _start当前所位于的地址
adr r0, _start
// _start的链接地址
ldr r1, =_start
ldr r2, =bss_start
cmp r0, r1
beq clean_bss
copy_loop:
ldr r3, [r0], #4
str r3, [r1], #4
cmp r1, r2
bne copy_loop
首先需要知道的是,adr指令获取的值是代码当前位于的地址,而ldr指令获取的值是代码的链接地址。再来看代码,代码里首先获得_start标号的当前地址(即0x02020010),然后获取_start标号的链接地址(即0x02028000),因为bin文件中不需要保存bss段,所有拷贝的代码长度为bss_start的运行地址-_start的运行地址,使用copy_loop进行拷贝。
第二步清bss,代码如下:
ldr r0, =bss_start
ldr r1, =bss_end
cmp r0, r1
beq run_on_dram
mov r2, #0
clear_loop:
str r2, [r0], #4
cmp r0, r1
bne clear_loop
首先获得bss段的起始地址(即bss_start),然后获得bss段的结束地址(即bss_end),最后使用clear_loop将bss段所位于的内存清0,bss_start和bss_end的定义位于link.lds。
第三步跳转,代码如下:
run_on_dram:
ldr pc, =main
由于ldr 指令获取的是main函数的链接地址,所以执行ldrpc, =main 后,程序就跳转到
0x02020000+main函数的offset的地址处了。
三、完整的烧写过程
已将SD卡插入电脑,假设linux识别了SD卡,其识别号为sdb。执行下面命令:
# chmod 777 –R 5_link_0x8000
# cd 5_link_0x8000
# make
# cd sd_fuse
# make
# ./ fast_fuse /dev/sdb
四、上电实验
将sd卡插入Tiny4412中,选择sd卡启动,然后上电,可以看到以下现象:
LED正常闪烁,该现象与前面章节的代码的运行效果一模一样,但是程序的运行过程却有了很大的区别。通过本章的学习,我们已经知道了如何对代码进行重定位,这为我们下一章节将代码重定位到DRAM奠定了基础。
备注:说明一下,代码已上传到我的资源里,我要了两个下载分,不好意思,我的资源分太少了,我得赚点,我相信我的程序是绝对能运行的,觉得值就去下载。