韦东山嵌入式总结——代码重定位002_链接脚本的引入与简单测试(难点:数据段首地址的选取)

韦东山嵌入式总结——代码重定位002_链接脚本的引入与简单测试(难点:数据段首地址的选取)


S3C2440开发板中SDRAM \NOR FLASH\ NAND FLASH地址分配:
韦东山嵌入式总结——代码重定位002_链接脚本的引入与简单测试(难点:数据段首地址的选取)_第1张图片
韦东山嵌入式总结——代码重定位002_链接脚本的引入与简单测试(难点:数据段首地址的选取)_第2张图片
韦东山嵌入式总结——代码重定位002_链接脚本的引入与简单测试(难点:数据段首地址的选取)_第3张图片
韦东山嵌入式总结——代码重定位002_链接脚本的引入与简单测试(难点:数据段首地址的选取)_第4张图片
s3c2440 开发板的NOR FLASH是16bit数据位宽,选择从NOR FLASH启动,所以OM0接VDD,OM1接VSS,NOR FLASH接的是ngcs0,也就是接在BANK0.因为选择的NOR FLASH是2Mbyte,所以NOR FLASH的地址范围是0x0000 0000 — 0x001f ffff。上电时,程序会从Norflash中启动,ARM直接取Norflash中的指令运行。
NAND FLASH 以页为单位读写,要先命令,再给地址,才能读到NAND的数据。NAND FLASH是接在NAND FLASH控制器上而不是系统总线上,所以没有在8个BANK中分配地址。如果S3C2440被配置成从Nand Flash启动, S3C2440的Nand Flash控制器有一个特殊的功能,在S3C2440上电后,Nand Flash控制器会自动的把Nand Flash上的前4K数据搬移到4K内部SRAM中,系统会从起始地址是0x0000 0000的内部SRAM启动。程序员需要完成的工作,是把最核心的启动程序放在Nand Flash的前4K中,也就是说,你需要编写一个长度小于4K的引导程序,作用是将主程序拷贝到SDRAM中运行。
由于Nand Flash控制器从Nand Flash中搬移到内部RAM的代码是有限的,所以在启动代码的前4K里,我们必须完成S3C2440的核心配置以及把启动代码(U-BOOT)剩余部分搬到RAM中运行,至于将2440当做单片机玩裸跑程序的时候,就不要做这样的事情,当代码小于4K的时候,只要下到nand flash中就会被搬运到内部RAM中执行了。
不管是从NOR FLASH启动还是从NAND FLASH启动,ARM都是从0x0000 0000地址开始执行的。

韦东山嵌入式总结——代码重定位002_链接脚本的引入与简单测试(难点:数据段首地址的选取)_第5张图片
韦东山嵌入式总结——代码重定位002_链接脚本的引入与简单测试(难点:数据段首地址的选取)_第6张图片
首先对于只有1个全局变量(占1个字节)来说,操作NOR FLASH,main函数如下图:
韦东山嵌入式总结——代码重定位002_链接脚本的引入与简单测试(难点:数据段首地址的选取)_第7张图片
其结果显而易见,串口只能打印出AAAAA…而不会去执行g_char++,因为nor flash只能读而不能写。
具体查看反汇编代码:
在这里插入图片描述
第298行即表示g_char++
第299行表示把 r3 写入 [r2]的地址,而 r2 指向的地址正是nor flash,所以不可写入。

那么问题来了,如果我在nor 启动也想写入该怎么办呢,这就需要代码重定位,把全局变量重定位,拷贝到sdram(首地址0x30000000)中,这样就可以实现全局变量的可读可写了。

修改Makefile把数据段.data首地址直接指向sdram首地址(0x30000000):
韦东山嵌入式总结——代码重定位002_链接脚本的引入与简单测试(难点:数据段首地址的选取)_第8张图片
这样造成的后果是bin文件竟然有800多兆,而nor flash 只有2M,显然这是不对的,如下图:

在这里插入图片描述
韦东山嵌入式总结——代码重定位002_链接脚本的引入与简单测试(难点:数据段首地址的选取)_第9张图片
那么如何解决这个问题?
解决方法有两种:
第1种:只把数据段(全局变量)重新定位
首先,在bin文件中,让数据段(全局变量)挨在代码段的后面,具体它们之间是多大的距离后面会有详细讲解。
然后,烧写至nor flash中
最后,运行时让前面的代码段将数据段(g_char)复制到SDRAM起始的0x30000000地址空间中,即重定位。
韦东山嵌入式总结——代码重定位002_链接脚本的引入与简单测试(难点:数据段首地址的选取)_第10张图片
第2种:将整个代码包括全局变量重定位

韦东山嵌入式总结——代码重定位002_链接脚本的引入与简单测试(难点:数据段首地址的选取)_第11张图片

如何将位于0x30000000的数据段跟位于0地址的代码段拼在一起呢?

这时需要引入链接脚本,其格式如下:
韦东山嵌入式总结——代码重定位002_链接脚本的引入与简单测试(难点:数据段首地址的选取)_第12张图片
修改Makefile:
韦东山嵌入式总结——代码重定位002_链接脚本的引入与简单测试(难点:数据段首地址的选取)_第13张图片
编辑链接脚本sdram.lds文件:
该链接脚本为通用版本,在知道原地址和重定位地址的情况下,可以从0x7d0复制多个字节到0x30000000。
韦东山嵌入式总结——代码重定位002_链接脚本的引入与简单测试(难点:数据段首地址的选取)_第14张图片
注意:链接脚本中的“.”代表当前地址,这里的当前地址指的是前面各种段地址的顺延。

注释:
韦东山嵌入式总结——代码重定位002_链接脚本的引入与简单测试(难点:数据段首地址的选取)_第15张图片
然后修改start.S文件:
韦东山嵌入式总结——代码重定位002_链接脚本的引入与简单测试(难点:数据段首地址的选取)_第16张图片
在main函数中加入一个全局变量:
韦东山嵌入式总结——代码重定位002_链接脚本的引入与简单测试(难点:数据段首地址的选取)_第17张图片
结果:
在这里插入图片描述
最后说明数据段(.data)的首地址选取:
韦东山嵌入式总结——代码重定位002_链接脚本的引入与简单测试(难点:数据段首地址的选取)_第18张图片
韦东山嵌入式总结——代码重定位002_链接脚本的引入与简单测试(难点:数据段首地址的选取)_第19张图片
图中数据段地址为0x800,其实这个地址不是固定的,只要他和代码段挨着就行。但是我们并不知道代码段究竟有多宽,所以选择0x800(十进制为2048)作为数据段的首地址,其目的是尽量给代码段足够大的空间。

编译过后,我们可以通过查看反汇编来查看代码段的具体地址:
韦东山嵌入式总结——代码重定位002_链接脚本的引入与简单测试(难点:数据段首地址的选取)_第20张图片
如上图可知代码段的最末地址为0x4b0(十进制为1200),所以只要我们把数据段的首地址指定的比0x4b0(十进制为1200)大就好。
比如把数据段的首地址设为0x7d0(十进制为2000)代码依然成功运行
韦东山嵌入式总结——代码重定位002_链接脚本的引入与简单测试(难点:数据段首地址的选取)_第21张图片

你可能感兴趣的:(韦东山嵌入式总结——代码重定位002_链接脚本的引入与简单测试(难点:数据段首地址的选取))