前面程序运行,发现从Nand Flash启动和从Nor Flash启动表现是不一样的。
设置成Nand Flash启动没有问题 显示ABCDE…
设置成NOor Flash启动则显示AAA…
这是什么原因呢?
Nor启动
Nor Flash就被认为是0地址,g_Char被放在0x700后面。CPU上电后从0地址开始执行,它能读取Nor Flash上的代码,打印出A,当进行g_Char++的时候,写操作操作无效,下次读取的数据仍然是A。
NAND启动
上电后,Nand Flash前4K代码就被自动的复制到SRAM里面,SRAM是CPU认为的0地址。CPU上电后从0地址开始执行,它读取SRAM上的代码,并g_Char++修改变量,下次读取的数据就依次增加了
为了解决Nor Flash里面的变量不能写的问题,我们把变量所在的数据段放在SDRAM里面,看行不行。
修改Makefile 指定数据段为0x30000000 -Tdata 0x30000000:
arm-linux-ld -Ttext 0 -Tdata 0x30000000 start.o led.o uart.o init.o main.o -o sdram.elf
这样的话编译出来的bin文件 从0地址 到 0x30000000地址 文件大小有700多MB,代码段和数据段直接有间隔,称之为黑洞
解决黑洞有两个办法:
第一个方法:
①把数据段的g_Char和代码段靠在一起
②烧写在Nor Flash上面
③运行时把g_char(全局变量)复制到SDRAM,即0x3000000位置(重定位);
第一种办法如何实现
修改Makefile的代码段地址,使用链接脚本sdram.lds指定。
注释:#arm-linux-ld -Ttext 0 -Tdata 0x30000000 start.o led.o uart.o init.o main.o -o sdram.elf
修改为: arm-linux-ld -T sdram.lds start.o led.o uart.o init.o main.o -o sdram.elf
链接脚本的语法:
SECTIONS {
...
secname start BLOCK(align) (NOLOAD) : AT ( ldadr )
{ contents } >region :phdr =fill
...
}
我们需要依次排列 代码段、只读数据段、数据段、.bss段、.common。
其中数据段放在0x800,但运行时在0x3000000:
SECTIONS {
.text 0 : { *(.text) }//所有文件的.text
.rodata : { *(.rodata) } //只读数据段
.data 0x30000000 : AT(0x800) { *(.data) } //放在0x700,但运行时在0x3000000
.bss : { *(.bss) *(.COMMON) }//所有文件的bss段,所有文件的.COMMON段
}
重新编译后烧写bin文件,发现启动后显示乱码。原因是我们从0x30000000处获取g_Char,但在这之前,并没有在0x30000000处准备好数据。因此需要重定位数据段,将0x700的数据移动到0x30000000处,在start.S加入:
bl sdram_init
/* 重定位data段 */
mov r1, #0x700
ldr r0, [r1]
mov r1, #0x30000000
str r0, [r1]
bl main
上面的这种方法,只能复制0x800处的一位数据,不太通用,下面写一个更加通用的复制方法:
链接脚本修改如下:
SECTIONS {
.text 0 : { *(.text) }
.rodata : { *(.rodata) }
.data 0x30000000 : AT(0x700)
{
data_load_addr = LOADADDR(.data);
data_start = . ;//等于当前位置
*(.data) //等于数据段的大小
data_end = . ;//等于当前位置
}
.bss : { *(.bss) *(.COMMON) }
}
修改start.S
bl sdram_init
/* 重定位data段 */
ldr r1, =data_load_addr /* data段在bin文件中的地址, 加载地址 */
ldr r2, =data_start /* data段在重定位地址, 运行时的地址 */
ldr r3, =data_end /* data段结束地址 */
cpy:
ldrb r4, [r1] //从r1读到r4
strb r4, [r2] //r4存放到r2
add r1, r1, #1 //r1+1
add r2, r2, #1 //r2+1
cmp r2, r3 //r2 r3比较
bne cpy //如果不等则继续拷贝
bl main
第二个方法
①让文件直接从0x30000000开始,全局变量在0x3……;
②烧写Nor Flash上 0地址处;
③运行会把整个代码段数据段(整个程序)从0地址复制到SDRAM的0x30000000(重定位);
这两个方法的区别是前者只重定位了数据段,后者重定位了数据段和代码段。
参考文档
[http://ftp.gnu.org/old-gnu/Manuals/ld-2.9.1/html_mono/ld.html Using LD, the GNU linker]