在裸板2440中,当我们使用nand启动时,2440会自动将前4k字节复制到内部sram中,如下图所示:
然而此时的SDRAM、nandflash的控制时序等都还没初始化,所以我们就只能使用前0~4095地址,在前4k地址里来初始化SDRAM,nandflash,初始化完成后,才能将nandflash的4096至后面的地址内容存放到SDRAM里去.
而裸板驱动的步骤如下所示:
1.写makefile
2.写lds链接脚本 (供makefile调用)
3.写真正要执行的文件代码,比如初始化nand,sdram,串口等
为什么要写lds链接脚本?
首先lds链接脚本的作用就是将多个*.o文件的各个段链接在一起,告诉链接器这些各个段存放的地址先后顺序,它的好处就是,确保裸板2440的前4k地址里存放的是初始化SDRAM,nandflash的内容
1.写makefile
(参考makefile初步制作:http://www.cnblogs.com/lifexy/p/7065175.html)
在写裸板之前首先要来写Makefile,如下所示:
objs := head.o init.o nand.o main.o
//定义objs变量,表示obj文件,包含生成boot.bin目标文件需要的依赖文件, 使用$(objs)就可以使用这个变量了
//‘:=’:有关位置的等于(比如:”x:=a y:=$(x) x:=b”,那么y的值取决于当时位置的a,而不是b)
//‘=’:无关位置的等于(比如:”x=a y=$(x) x=b”,那么y的值永远等于最后的b ,而不是a)
nand.bin : $(objs) //冒号前面的是表示目标文件, 冒号后面的是依赖文件,这里是将所有*.o文件编译出nand.bin可执行文件
arm-linux-ld -Tnand.lds -o nand_elf $^ //将*.o文件生成nand_elf链接文件
//-T:指向链接脚本, $^:指向所有依赖文件,
arm-linux-objcopy -O binary -S nand_elf $@ //将nand_elf链接文件生成nand.bin文件
//$@:指向目标文件:nand.bin
//-O :选项,其中binary就是表示生成的文件为.bin文件
arm-linux-objdump -D -m arm nand_elf > nand.dis //将nand.bin文件反汇编出nand.dis文件
//-D :反汇编nand.bin里面所有的段, -m arm:指定反汇编文件的架构体系,这里arm架构
%.o:%.c //冒号前面的是目标文件,冒号后面的是依赖文件,%.o表示所有.o文件,
arm-linux-gcc -Wall -c -O2 -o $@ $
//$<:>
//$@:指向目标文件,也就是.o文件
//-Wall:编译若有错,便打印警告信息 -O2:编译优化程度为2级
%.o:%.S
arm-linux-gcc -Wall -c -O2 -o $@ $
clean: //输入make clean,即进入该项,来删除所有生成的文件
rm -f nand.dis nand.bin nand_elf *.o //通过rm命令来删除
2.写lds链接脚本
(参考lds脚本解析: http://www.cnblogs.com/lifexy/p/7089873.html)
SECTIONS {
. = 0x30000000; //指定当前的链接地址=0x30000000
.text : {
head.o(.text) //添加第一个目标文件,里面会调用这些函数
init.o(.text) //添加第二个目标文件,里面存放关看门狗,初始化SDRAM等函数
nand.o(.text) //添加第三个目标文件,里面存放初始化nand函数
*(.text) // *(.text) 表示添加剩下的全部文件的.text代码段
}
.rodata ALIGN(4) : {*(.rodata)} //指定只读数据段
.data ALIGN(4) : { *(.data) } //指定读写数据段, *(data):添加所有文件的数据段
__bss_start = .; //把__bss_start赋值为当前地址位置,即bss段的开始位置
.bss ALIGN(4) : { *(.bss) *(COMMON) } //指定bss段,里面存放未被使用的变量
__bss_end = .; //把_end赋值为当前地址位置,即bss段的结束位置
}
上面的链接地址=0x30000000,表示程序运行的地方应该位于0x30000000处,0x30000000就是我们的SDRAM基地址,而一上电后,nand的前4k地址会被2440自动装载到内部ram中,所以我们初始化了sdram和nand后,就需要把程序所有内容都复制到链接地址0x30000000上才行
2.1为什么要在bss段的前后设置两个符号__bss_start, __bss_end?
定义__bss_start和__bss_end符号,是用来程序开始之前将这些未定义的变量清0,节省内存
且__bss_start -0x30000000就等于该bin文件的字节大小,实现动态复制
2.3为什么链接地址在0x30000000处,为什么在初始化sdram和nand之前,还能运行前4k地址的内容?
我们先来看看head.S第一个目标文件,就知道了:
.text @设置代码段
@函数disable_watch_dog, memsetup, init_nand, nand_read_ll在init.c中定义
ldr sp, =4096 @设置堆栈
bl disable_watch_dog @关WATCH DOG
bl memsetup @初始化SDRAM
bl nand_init @初始化NAND Flash
ldr sp,=0x34000000 @64Msdram,所以设置栈SP=0x34000000,避免堆栈溢出
@nand_read_ll函数需要3个参数:
ldr r0, =0x30000000 @1. 目标地址=0x30000000,这是SDRAM的起始地址
mov r1, #0 @2. 源地址 = 0
ldr r2, =__bss_start
sub r2,r2,r0 @3. 复制长度= __bss_start-0x30000000
bl nand_read @调用C函数nand_read,将nand的内容复制到SDRAM中
ldr lr, =halt_loop @设置返回地址
ldr pc, =main @使用ldr命令 绝对跳转到SDRAM地址上
halt_loop: @若main函数跳出后,便进入死循环,避免程序跑飞
b halt_loop
(参考位置无关码(bl)与绝对位置码(ldr): http://www.cnblogs.com/lifexy/p/7117345.html)
从上面代码来看,可以发现在复制数据到sdram之前,都是使用的相对跳转命令bl,bl是一个位置无关码,也就是说无论该代码放在内存的哪个地址,都能正确运行.
而ldr就是绝对跳转命令,是一个绝对位置码,当一上电时,我们的链接地址0x30000000上是没有程序的,因为程序都存在nand flash上,也就是0地址上,而如果在复制数据到sdram之前,使用ldr去执行的话,就会直接跳转到0x30000000上,就会运行出错.
而且在复制数据到sdram之前,执行的代码里都不能用静态变量、全局变量、以及数组,因为这些初始值量的地址与位置有关的,必须将nand的内容复制到sdram地址中,才能用.
2.4比如,下面memsetup ()函数,就是个会出错的函数
其中的mem_cfg_val[]数组的内存是存在链接地址0x30000000上,就是与位置有关,在未复制内容之前使用将会出错
#define MEM_CTL_BASE 0x48000000 //SDRAM寄存器基地址
void memsetup()
{
int i = 0;
unsigned long *p = (unsigned long *)MEM_CTL_BASE;
/* SDRAM 13个寄存器的值 */
unsigned long const mem_cfg_val[]={ 0x22011110, //BWSCON
0x00000700, //BANKCON0
0x00000700, //BANKCON1
0x00000700, //BANKCON2
0x00000700, //BANKCO
『本文转载自网络,版权归原作者所有,如有侵权请联系删除』