自己编写JZ2440 bootloader分析

一.明白bootloader的作用,和要实现的功能

  1. bootloader最终的目的就是去启动内核,将一些参数传递给内核,使内核可以在内存上运行。
  2. 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
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值