版权声明:本文为小斑马学习文章,技术来源于韦东山著作,转载请注明出处!
一、段的概念__重定位的引入
2440开发版:芯片内部有CPU、内存控制器、4K的配对内存SRAM。还有NandFlash控制器。NandFlash控制器可以直接连接NandFlash。
CPU 能直接到达SDRAM Norflash SRAM Nand Flash控制器 但不能直接到达NandFlash。如果直接把程序烧写到NandFlash CPU不能直接运行该程序。那为什么我们还能设置NANDFlash启动呢? 这里有个机制 1.NandFalsh启动时,硬件将前4K复制到SRAM中。2.CPU从0运行,0对应的就是SRAM。
问题:如果在NandFlash程序超过4K怎么办? 前4K的代码 需要把整个程序读出来 放到SDRAM。这就是重定位,即重新确定程序的地址。
.使用NorFlash启动时,CPU看到的0地址在NorFlash上面,SRAM 配对内存的基地址就是0x40000000 NandFlash地址0, NandFlash不可以访问的。
NorFlash的特点:像内存一样读,但不能像内存可以直接写。
比如 mov R0,#0
LRR R1,[R0]
STR R1,[R0]该指令无效的。
问题:如果程序中含有要写的变量==全局变量/静态变量
他们在bin中,写在NORFlash中,直接修改变量会无效。所以需要重定位放到SDRAM中。
#include "s3c2440_soc.h"
#include "uart.h"
#include "init.h"
char g_Char = '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启动时, 此代码无效 */
delay(1000000);
}
return 0;
}
对于NORFlash不能直接的写。
程序包含: 代码段 数据段(全局变量) data rodate(const全局变量) bss(初值为0 无初值的全局变量)commen(注释)后面的两个不保存在bin的文件中。
二、链接脚本的引入与简单测试
因为NORFlash不能写入数据,在设置修改MakeFile将数据段写入到SDRAM中,但出现bin文件超大的问题有800多M。问题的根源在于代码段和数据段出现巨大的间隔。成为HOLO黑洞。
怎么解决数据段和代码段出现巨大空洞问题?
- 1 将bin文件里面的代码段和数据段拼接在一起,烧写在NORFlash里面去,在运行时,将数据段重定位到SDRAM中 地址:0x30000000。
- 2 将bin文件里面的代码段写在一个地址中,数据段写到另外一个地址中,烧写在NORFlash里面去,在运行时,将数据段和数据段重定位到SDRAM中 地址:0x30000000。
如何将数据段和代码段拼接在一起呢?
这要使用到链接脚步。
链接脚本的格式:
SECTIONS {
.text 0 : { *(.text) } //先发所有的.text代码段
.rodata : { *(.rodata) } //紧接着放 所有的.rodata只读的数据段
.data 0x30000000 : AT(0x800) { *(.data) } // 依次是所有文件的数据段 数据放在0x30000000 要加AT
.bss : { *(.bss) *(.COMMON) }
}
设置链接脚本后,运行时!要将数据段重定位到0x30000000中去。
bl sdram_init //初始化SDRAM
/* 重定位data段 */
mov r1, #0x800
ldr r0, [r1]
mov r1, #0x30000000
str r0, [r1]
bl main
通用的重定位代码:解决多个全局变量和未知数据段存储地址的情况下
SECTIONS {
.text 0 : { *(.text) }
.rodata : { *(.rodata) }
.data 0x30000000 : AT(0x800)
{
data_load_addr = LOADADDR(.data); //获取数据段的存储地址
data_start = . ; //.即是当前位置 0x30000000
*(.data)
data_end = . ; //data_end的值 有data的大小来决定
}
.bss : { *(.bss) *(.COMMON) }
}
/* 重定位data段 */
ldr r1, =data_load_addr /* data段在bin文件中的地址, 加载地址 */
ldr r2, =data_start /* data段在重定位地址, 运行时的地址 */
ldr r3, =data_end /* data段结束地址 */
cpy:
ldrb r4, [r1]
strb r4, [r2]
add r1, r1, #1
add r2, r2, #1
cmp r2, r3
bne cpy
三、 链接脚本的解析
SECTIONS {
...
secname start BLOCK(align)(NOLOAD) : AT (ldadr)
{ contents } >region:phdr =fill
...
}
secname:段名:
start:起始地址:运行时地址runtime addr,重定位地址 relocate addr
AT(ldadr) Load Addr:加载地址 不写时,LoadAddr = runtime addr 如果没有加AT 它的的加载地址就等于链接时的起始地址。
使用链接脚本时
arm-linux-ld -T sdram.lds start.o led.o uart.o init.o main.o -o sdram.elf
把.o文件生成了.elf格式的程序。elf格式程序含有地址信息。再将elf格式的文件生成bin文件 bin文件里面不含有地址信息。
arm-linux-objcopy -O binary -- sdram.elf sdram.bin
elf格式的程序:1.链接得到elf文件,含有地址信息(LoadAddr)2.使用加载器将elf程序读入内存(读到LoadAddr)调试工具。对于APP 加载器也是APP。
3.运行程序 4.如果LoadAddr != RuntimeAddr 程序本身要重定位。
核心:程序运行时,应该位于RuntimeAddr 也可以称为relocate Addr 或者链接地址
对于裸版 1.elf--> 2.硬件机制启动 3.如果bin所在地址 != runtime Addr 程序本身实现重定位
SECTIONS {
.text 0 : { *(.text) } //LoadAddr = Runtime Addr =0 不需要重定位
.rodata : { *(.rodata) }
.data 0x30000000 : AT(0x800) LoadAddr = 0x800 RuntimeAddr = 0x30000000
{
data_load_addr = LOADADDR(.data); //获取数据段的存储地址
data_start = . ; //.即是当前位置 0x30000000
*(.data)
data_end = . ; //data_end的值 有data的大小来决定
}
.bss : { *(.bss) *(.COMMON) } runtime = 0x300000000
}
bin文件 left文件都不存bss段 bss段(未初始化的变量和注释) 程序运行时 把bss段对应的空间清零。
SECTIONS {
.text 0 : { *(.text) }
.rodata : { *(.rodata) }
.data 0x30000000 : AT(0x800)
{
data_load_addr = LOADADDR(.data);
data_start = . ;
*(.data)
data_end = . ;
}
bss_start = .;
.bss : { *(.bss) *(.COMMON) }
bss_end = .;
}
/* 清除BSS段 */
ldr r1, = bss_start
ldr r2, = bss_end
mov r3, #0
clean:
strb r3, [r1]
add r1, r1, #1
cmp r1, r2
bne clean
四、拷贝代码和链接脚本的改进
拷贝代码:把数据和代码从NorFlash或者NandFlash中复制到SDRAM去。
cpy:
ldrb r4, [r1] //从Flash中读取出一个字节
strb r4, [r2] //写到SDRaAM中
add r1, r1, #1
add r2, r2, #1
cmp r2, r3
bne cpy
bl main
这种效率非常的低。
原因:在2440芯片中有CPU和内存控制器。外接有32bit的SDRAM 和 16bit的NORFlash。如果使用ldrb从NORFalsh中读到数据,使用strb写数据到SDRAM。如果要复制16字节,ldrb要执行16次,访问NORFlash要16次。strb要执行16次,访问SDRAM16次。硬件访问要32次,效率非常的低。
CPU写数据给SDRAM中,先将地址和数据发送到内存控制器,内存控制把32位的数据和数据屏蔽信号DQM 发送给SDRAM。
改进的方法:使用ldr从NORFlash读取 用str写数据到SDRAM去。读取和写入各执行四次。其从NORFlash读取数据时访问硬件为8次,写数据到SDRAM中访问的硬件为4次。总共12次。效率提高。
cpy:
ldr r4, [r1]
str r4, [r2]
add r1, r1, #4
add r2, r2, #4
cmp r2, r3
ble cpy
/* 清除BSS段 */
ldr r1, =bss_start
ldr r2, =bss_end
mov r3, #0
clean:
str r3, [r1]
add r1, r1, #4
cmp r1, r2
ble clean
bss 清零的时候,要向四取整。否则会出现地址相同,把其他变量的值也清零了。
SECTIONS {
.text 0 : { *(.text) }
.rodata : { *(.rodata) }
.data 0x30000000 : AT(0x800)
{
data_load_addr = LOADADDR(.data);
. = ALIGN(4);
data_start = . ;
*(.data)
data_end = . ;
}
. = ALIGN(4);
bss_start = .;
.bss : { *(.bss) *(.COMMON) }
bss_end = .;
}
网友评论