自己编写JZ2440 bootloader分析
来源:互联网 发布:网络加速app 编辑:程序博客网 时间:2024/05/17 04:00
一.明白bootloader的作用,和要实现的功能
- bootloader最终的目的就是去启动内核,将一些参数传递给内核,使内核可以在内存上运行。
- bootloader要实现的功能就是要将内核从FLASH上读出来给内存。
二.既然bootloader要从FLASH上读内核传给内存,那么bootloader就要完成一些准备工作
针对于JZ2440这款开发板,我们要做以下工作(选择NAND FLASH启动):
1.初始化硬件:关看门狗,初始化时钟,初始化SDRAM,初始化NAND FLASH(这里要知道NAND FLASH和NOR FLASH的区别)
2.如果boot loader比较大,我们要进行代码重定位,重定位到SDRAM(这里要明白什么是代码的重定位,代码重定位需要链接地址,链接地址和存储地址的区别)
3.清BSS段(什么是BSS段)
4.跳转到main函数,从NAND FLASH中将内核读到SDRAM中
5.设置参数(这里的参数设置是用一个TAG结构体实现的,参数的位置放在SDRAM中的一个地址处是和内核约定好的,在启动内核时将地址告诉内核)
5.跳转到SDSRAM内核处,执行内核
三.可以分别在start.S、init.c、boot.c、这几个文件中完成代码,当然不能缺少链接文件boot.lds、Makefile和头文件setup.h
1.start.S 是汇编文件,在这个文件中主要实现上述的一二三步骤。
我们要明白这几条指令的意思:
mov r0 ,#0 //将r0写0
ldr r1, = 0x123456 //r1的值为0x123456
str r0 ,[r1] //实现r1这个值对应的地址处的值为0
初始化SDRAM,将SDRAM的值对应的写入SDRAM的寄存器中
ldr r0, =MEM_CTL_BASE
adr r1, sdram_config
add r3, r0, #(13*4)
1:
ldr r2, [r1], #4
str r2, [r0], #4
cmp r3, r0
bne 1b //1b表示如果比较不相等,则返回前一个1。还有1f是返回下一个1的意思
2.代码重定位,将bootloader本身的代码拷到SDRAM中
bl copy_code_to_sdram() 因为这条指令在汇编中,跳转到.c文件中,所以在这条语句之前需要设置栈
ldr sp , =0x34000000 这个sp指向SDRAM的最大处。
起始地址为0x30000000,64M大小。
3.bl copy_code_to_sdram() 跳转到init.c文件中
在init.c中实现了copy_code_to_sdram(unsigned char *src,unsigned char*dst,unsigned int len)。
这三个参数是在start.S中设置。
第一个参数设置:无论是NAND启动还是NOR启动,他们的源地址都是0。NOR启动CPU直接指向NOR的o地址,NAND启动CPU指向片内内存的0地址。因为上电后,CPU自动将NAND Flash中4K的代码拷到片内内存中。
第二个参数设置:是目的地址,也就是CPU要将boot loader拷到的地方,这里就是第一条指令的标号_start,也就是链接地址
第三个参数设置:是长度。表示整个二进制文件(包括代码段,只读数据段,数据段)的大小,这里不包括BSS段,BSS段是单独的。需要看链接脚本。大小是从链接地址到__bss的起始地址。
在这个函数中首先要判断是NAND、还是NOR启动。判断的依据是根据Nand和Nor Flash的特性。Nand Flash可以直接写但不能读;Nor Flash可以直接读但不能写。我们可以往一个地址写如一个数,如果写成功则是NAND 启动,否则是Nor启动。
如果是Nor启动,则可以直接传递:
for(i=0;i<len ;i++)
{
dst[i] = src[i];
}
如果是NAND启动,则需要命令进行读,在读之前要进行Nand 初始化,这个可以在跳转到copy_code_to_sdram之前进行。
nand_init()需要完成对nand flash控制器的初始化,主要是时序和使能控制器。
nand_read()来完成读
void nand_read(unsigned int addr,unsigned char *buf,int len){int i = 0;int col = addr % 2048; /*因为2440的nand flash 每一页的大小为2048,所以这样可以确定列数*//* 1.片选选中 */nand_select();while(i<len){/* 2.写入00H的命令 */nand_cmd(0x00);/* 3.写地址,需要五个周期,五部来完成 */nand_addr(addr);/* 4.写入命令30H */nand_cmd(0X30);/* 5.判断状态,是处于准备还是忙碌 */nand_wait_ready();/* 6.读数据 */for(;(col<2048) && (i<len);col++){ buf[i] = nand_read_data(); i++; addr++; /*这里如果不写addr++可不可以???????*/}col = 0; }/* 7.取消片选 */nand_deselect();}
4.清BSS段
void clear_bss(void){extern int __bss_start, __bss_end;int *p = &__bss_start;for(;p <(int*) __bss_end;p++){*p = 0;}}
5.跳转到main
首先要读内核,然后设置参数,跳转执行。
读内核用nand_read(0x60000+64,0x30008000;0x200000)
这里要解释的是0x60000+64的原因是因为uImage在Flash中存的地址是0x60000,而uImage是由64字节的头部加上zImage组成的
我们要读的是zImage,所以源地址应该是0x60000+64.
设置参数:是通过一个tag的结构体,包括设置NAND FLASH、还有命令行参数
最后是跳转,跳转涉及到一个函数指针 void (*theKernel)(int zero, int arch, unsigned int params); /*定义的函数指针*/
theKernel = (void (*)(int, int, unsigned int))0x30008000;/*0x30008000内核在SDRAM中存放的地址*/
theKernel(0 ,362, 0x30000100); /*0x30000100是内核和bootloader约定存放参数的地址*/
在这些之前要先进行串口初始化,因为kernel要在串口中打印一些东西,但是kernel不具有初始化串口的功能,所以boot loader要对
串口进行初始化。
这就是bootloader的主要部分。还需要完成的还有Makefile、boot.lds
启动改进:加入ICACHE 可以加快内核启动,在start.S中,初始化时钟后可以启动ICACHE:
/* 启动ICACHE */
mrc p15, 0 ,r0 ,c1 ,c0,0 /*协处理器到ARM寄存器,相当于从协处理器读数据到ARM*/
orr r0 , r0, #(1<<12)
mcr p15, 0 ,r0 ,c1, c0,0/*写入控制寄存器*/
0 0
- 自己编写JZ2440 bootloader分析
- 自己写bootloader笔记1---流程分析
- 自己写bootloader笔记7---Makefile分析
- 自己写bootloader笔记1---流程分析
- 自己写bootloader笔记7---Makefile分析
- (8)ok6410学习之自己的bootloader编写架构
- (嵌入式开发)自己写bootloader之编写第一阶段
- (嵌入式开发)自己写bootloader之编写第二阶段
- 学习嵌入式Linux-JZ2440-初识Bootloader
- JZ2440看门狗分析
- JZ2440 中断分析
- JZ2440 中断分析
- JZ2440 串口分析
- 自己写bootloader笔记2---start.S分析
- 自己写bootloader笔记3---init.c分析
- 自己写bootloader笔记4---uboot.lds分析
- 自己写bootloader笔记2---start.S分析
- 自己写bootloader笔记4---uboot.lds分析
- 51Nod 1106 质数检测
- 提交(Commit)
- Oracle 12c 新特性之 temp undo
- Spring+SpringMVC+MyBatis+easyUI整合优化篇(十四)谈谈写博客的原因和项目优化
- 什么是面向对象思想
- 自己编写JZ2440 bootloader分析
- The Martian Challenge 2017 E. Secret Passage
- 记一个归并排序模板
- ceshi
- 500. Keyboard Row
- #webview简介与遇到的问题及解决方案##
- 51Nod 1011 最大公约数GCD
- Android MediaRecorder 实现录制无声视频 禁止录入声音
- 图像处理之_轮廓匹配