一、JZ2440(S3C2440开发板)存储单元说明
JZ2440的基本参数如下:
SDRAM:64MB 0x3000 0000~ 0x3400 0000
Nand Flash:256MB 0x0000 0000 ~ 0x1000 0000
二、最简BootLoader启动流程分析
JZ2440开发板的启动分为Nor Flash启动和Nand Flash启动,这里以NandFlash启动为例进行说明s3c2440的启动流程。
1、启动模式选择
s3c2440支持两种启动模式:NAND和非NAND(这里是nor flash)。
具体采用的方式取决于OM0、OM1两个引脚
OM[1:0所决定的启动方式
OM[1:0]=00时,处理器从NANDFlash启动
OM[1:0]=01时,处理器从16位宽度的ROM启动
OM[1:0]=10时,处理器从32位宽度的ROM启动。
OM[1:0]=11时,处理器从TestMode启动。
2、上电启动
(1)当从NAND启动时
cpu会自动从NAND flash中拷贝前4KB的数据放置在片内SRAM里(s3c2440是soc),同时把这段片内SRAM映射到nGCS0片选的空间(即0x00000000)。cpu是从0x00000000开始执行,也就是NAND flash里的前4KB内容。因为NAND FLASH连地址线都没有,不能直接把NAND映射到0x00000000,只好使用片内SRAM做一个载体。通过这个载体把nand flash中剩余的代码(u-boot、linux-kernel)复制到RAM(一般是SDRAM)中去执行
(2)当从非NAND flash(Nor Flash)启动时
nor flash被映射到0x00000000地址(就是nGCS0,这里就不需要片内SRAM来辅助了,所以片内SRAM的起始地址还是0x40000000). 然后cpu从0x00000000开始执行(也就是在Nor falsh中执行)。
三星的stepstone内置了一小块RAM(stepstone),然后从硬件上实现CPU上电后先读取nand flash最开始的一段代码到stepstone中去(当然,要设置硬件为nandflash启动方式),然后从stepstone起始处(被设置为RAM的0地址)去执行。这个stepstone一般很小(2410,2440是4K),所以它没办法放下一个功能复杂的bootloader(比如eboot),只能放一个功能很简单的,这就是需要nboot的原因了。nboot的功能十分单一,就是从nandflash复制image到RAM中去,然后跳转执行。
当将Nand Flash中的code拷贝到stepstone时,并未根据链接脚本中的链接地址,而是将代码的连接地址重新定位为0x00000000,并从0x00000000开始执行。(未确定)
3、最简boot实现流程
我们实现的最简boot主要功能是将内核代码从Nand Flash拷贝到内存(SDRAM)中,并跳转到相应的地址执行。由于最简boot本身代码较小(小于4KB),没有必须要再拷贝boot本身的代码到SDRAM中,只需要片内SRAM 4KB即可完成将内核代码从Nand Flash拷贝到内存(SDRAM)中
最简boot分为两个阶段:
1、第一阶段启动(start.s/init.c)
(1)start.s
初始化硬件:关看门狗、设置时钟、设置总线模式、设置锁相环频率、启动ICACHE、初始化SDRAM相关寄存器、设置堆栈地址sp,初始化NAND FLASH、清除bss段
(2)init.c
如果bootloader比较大,要把它重定位到SDRAM
2、第二阶段启动(boot.c/setup.h)
A. 初始化串口,能够调试
B. 把内核从NANDFLASH(0x60000+64)读到SDRAM(0x30008000)
C. 设置"要传给Linux/EOS内核的参数",SDRAM存放地址0x30000100
D. 跳转执行内核(SDRAM =0x30008000)
具体实现代码见最后
三、最简BootLoader的编译
交叉编译环境的编译见上一节
与编译相关的文件主要有2个
(1)boot.lds文件
boot.lds是链接脚本,说明编译后的各文件顺序如何在FLASH存储器中存放,即告诉链接器arm-linux-ld如何将*.o文件链接起来
这儿是连接脚本里面的内容:*.lds,里面包含代码段(.text)、只读数据段(.rodata)、读写数据段(.data)、bss段(里面存放未初始化的全局变量、初始化为0的全局变量),这些段可以通过在汇编代码中指定,也可以使用GCC相关的宏指定某个符号(变量或函数)属于哪一个段。
lds描述了输出文件的“映射图”:输出文件中各段、各文件怎么存放,最后连接成一个可执行的文件;
通过*.lds生成可执行的链接脚本(elf)
SECTIONS {
. =0x33f80000; //自己随便找的,表示当前代码存放在SDRAM中地址为0x33f80000,
.text : {*(.text) } // 代码段
. =ALIGN(4); //对齐(当前地址取整)
.rodata :{*(.rodata*)} //只读数据段
. = ALIGN(4);
.data : {*(.data) } //读写数据段
. = ALIGN(4);
__bss_start =.; //二进制文件的大小
.bss : {*(.bss) *(COMMON) } //初始值为0的全局变量
__bss_end = .;
}
(2)Makefile
#编译工具
CC = arm-linux-gcc //实际上是使用Makefile中使用自定义的变量,下面只要是不跳出该Makefile文件,该变量都是有效的
#链接工具
LD = arm-linux-ld
#将多个可重定位的目标模块归档为一个函数库文件
AR = arm-linux-ar
#对象复制选项
OBJCOPY =arm-linux-objcopy
#用来显示二进制文件,查看反汇编代码
OBJDUMP = arm-linux-objdump
#编译选项
CFLAGS := -Wall -O2
#编译选项
CPPFLAGS := -nostdinc -nostdlib -fno-builtin
#要生成的目标文件
objs := start.oinit.o boot.o
#最终要生成的目标文件
#使用boot.lds进行目标文件的连接,生成elf(可执行的连接脚本)
boot.bin:$(objs)
${LD} -Tboot.lds -o boot.elf $^ # $^代表*.o文件,所有的依赖目标的集合。
#-O:使用指定的格式输出文件 -S:不从源文件中复制重定位信息和符号信息到目标文件中去
#整体上代表将ELF格式的可执行文件转换为二进制文件 $@:boot.bin
${OBJCOPY} -O binary -S boot.elf $@
#反汇编所有段-D 指定反汇编时使用的架构-m boot.elf可执行的连接文件 boot.dis最终生成的可以查看的汇编代码
${OBJDUMP} -D -m arm boot.elf > boot.dis
%.o:%.c
${CC} $(CPPFLAGS) $(CFLAGS) -c -o $@ $<
%.o:%.S
${CC} $(CPPFLAGS) $(CFLAGS) -c -o $@ $<
clean:
rm -f *.o *.bin *.elf *.dis
四、编译后bin文件下载到NandFlash
使用JZ2440开发板自带的u-boot通过Nor Flash烧写Nand Flash
三秒——>空格—>qàopenJATG->
tftp 30000000 boot.bin
nand erase bootloader
nand write 30000000 bootloader
五、总结
Arm的启动都是从0地址开始,所不同的是地址的映射不一样。在arm上电的时候,要想让arm知道以某种方式(地址映射方式)运行,不可能通过你写的某段程序控制,因为这时候你的程序还没启动,这时候arm会通过引脚的电平来判断。
1.当引脚OM0跟OM1有一个是高电平时,这时地址0会映射到外部nGCS0片选的空间,也就是Norflash,程序就会从Norflash中启动,arm直接取Norflash中的指令运行。
2.当OM0跟OM1都为低电平,则0地址内部stepstone(一段4k的SRAM)开始。系统上电,arm会自动把NAND flash中的前4K内容拷贝到stepstone(也就是0地址),然后从0地址运行。
这时NANDFlash中的前4K就是启动代码(他的功能就是初始化硬件然后在把NANDFlash中的代码复制到RAM中,再把相应的指针指向该运行的地方)
为什么会有这两种启动方式,关键还是两种flash的不同特点造成,NORFLASH容量小,速度快,稳定性好,输入地址,然后给出读写信号即可从数据口得到数据,适合做程序存储器。NAND FLASH 总容量大,但是读写都需要复杂的时序,更适合做数据存储器。这种不同就造成了NORflash可以直接连接到arm的总线并且可以运行程序,而NANDflash必须搬移到内存(SDRAM)中运行。
在实际的开发中,一般可以把bootloader烧入到Nor flash,程序运行可以通过串口交互,进行一定的操作,比如下载,调试。这样就很可以很方便的调试你的一些代码。Norflash中的Bootloader还可以烧录内核到Norflash等等功能。