① NOR启动:
② nand 启动:
上次讲到的g_char不能按照程序正确输出的原因是nor启动的时候g_char在nor flash中,其不可写的特性决定了输出结果。现在能否修改Makefile让nor启动时讲代码拷贝到SDRAM中,这样就可以实现全局变量的可读可写了。
现在想在nor flash启动的时候将其中的代码段拷贝至SDRAM中0地址起始的空间,将全局变量g_char拷贝至0x30000000起始的空间。
编译之后发现.bin文件变成了800M,805306369-->0x30000001,符合程序中的g_char所占1字节的设置。但是nor flash一共才2M,不可实现。解决方法有两种:第1种: 只是重定位了全局变量① 在,bin文件中让g_char和代码段靠在一起② 烧写至nor flash中③ 运行时让前面的代码段将g_char复制到SDRAM起始的0x30000000地址空间中,
即重定位。第2种:重定位了整个代码+全局变量让代码段和g_char之间没有那么大的空洞。
我们怎么将位于0x30000000的数据data段跟位于0地址的代码段拼在一起呢?因为这些复杂的操作通过简单的参数设置已经无法实现,因此需要引入链接脚本来完成相应的操作。
链接脚本:通过链接脚本的方式实现:
SECTIONS {
.test 0 : { *(.test) }
.rodata : { *(.rodata) }
.data 0x30000000 : AT(0X800) { *(.data) }
.bss : { *(.bss) *(.COMMON)}
}
编译
烧写至开发板的nor flash之后发现输出是乱码,烧写至nand flash之后依然是乱码。
在链接脚本中,我们将data段定位到了0x800(2048)的位置上,但是在main函数中访问g_char时的地址是0x30000000。
下面查看main函数的反汇编码:
从反汇编码可以看出,寄存器r3确实在0x30000000地址中得到了其中的值,但是我们在程序中并没有将g_char的值放到0x3000000的地址空间去,因此缺少了重定位的一个步骤。
修改代码:现在需要将g_char的值从0x800的地方复制到0x30000000的地址空间去。在start.S文件中加入重定位的代码即可:
/* 重定位data段 */
mov r1, #0x800
mov r0, [r1]
mov r1, #0x30000000
str r0, [r1]
之后编译代码下载至开发板,发现还是乱码。。。“RP值低预警”。。。查找问题后发现,原因竟然是。。。在start.S中没有初始化SDRAM!!
之后再次编译代码下载至开发板(注意设置开发板启动模式为nor启动):
成功!!接下来的问题是,刚刚的重定位代码并不通用。在重定位的时候我们是在知道原地址和重定位地址的情况下从0x800仅仅复制了一个字节到0x30000000地址中去的,如果我们要复制多个字节又该怎么做呢?首先修改链接脚本:
SECTIONS {
.text 0 : { *(.text) }
.rodata : { *(.rodata) }
.data 0x30000000 : AT(0X800)
{
data_load_addr = LOADADDR(.data);
data_start = . ;
*(.data)
data_end = . ;
}
.bss : { *(.bss) *(.COMMON)}
}
注意:链接脚本中的“.”代表当前地址,这里的当前地址指的是前面各种段地址的顺延。然后修改start.S文件:
/* 重定位data段 */
ldr r1, =data_load_addr /* data段在bin文件中的地址,加载地址 */、
ldr r2, =data_start /* data段的重定位(从nor或者nand flash中定位至SDRAM中)开始地址,运行时的地址 */
ldr r3, =data_end /* data段重定位的结束地址 */
/* 接下来将r1所指地址中的值拷贝至r2所指的地址空间 */
cpy:
ldrb r4, [r1] //从r1地址处拷贝一个字节到r4
strb r4, [r2]
add r1, r1, #1
add r2, r2, #1
cmp r2, r3
bne cpy
在main函数中加入一个全局变量:
char g_char = 'A';
char g_char3 = 'a';
const char g_char2 = 'B';
int g_A = 0;
int g_B;
int main(void)
{
uart0_init();
while(1)
{
putchar(g_char);
g_char++; /* nor启动时,此代码无效 */
putchar(g_char3);
g_char3++; /* nor启动时,此代码无效 */
delay(100000);
}
编译烧写:
成功。