基于JZ2440开发板编写bootloader总结(二)

紧接着上一章,下面进入Bootloader第二阶段


第二阶段需要完成三项工作:

  1. 从NAND FLASH 将内核读取到SDRAM中;
  2. 设置bootloader需要传递给内核的参数;
  3. 跳转执行内核;

 第一步:将内核从NAND FLASH 读取到SDRAM

内核是使用uboot事先就烧写到NAND FLASH的,内核在NAND FLASH中的起始地址是0X60000+64,内核大小小于2M。要完成内核转移工作,需要在bootloader阶段初始化NAND FLASH,使其具备读写功能。在进行初始化NAND FLASH之前,建议先了解下NAND FLASH 原理,参考文档《[嵌入式Linux应用开发完全手册》第八章,在此不再赘述。

(1)初始化 NAND FLASH

         S3C2440有NAND FLASH 控制器,本文所述bootloader 仅仅需要读NAND功能,根据具体的NAND配置相应参数,使NAND具备读功能。初始化NAND需要关注以下参数: 

  TACLS:表示CLT/ALE的建立时间(setup time),由下图2 可得TACLS = tcls - twp 。表示发出CLE 或者 ALE后,需要等待多长时间才可发出WE信号。

  TWRPH0:表示nWE/nRE持续的时间,读写管脚低电平需要持续的时间,对应下图 1 twp。

  TWRPH1:表示写进去的数据起作用的时间(hold time),数据从管脚输入到NAND Flash 阵列数据有效的时间,对应下图1 tCLH  /  tALH。

图1  nand flash手册 时序       

 图2  S3C2440 nand flash 读时序

本文涉及的NAND型号为 K9F2G08U0M ,上述参数的值见下表:

                                                                                                图3 nand flash手册 参数表

由表可知TACLS:CLE AND ALE setup time >= 0ns ; TWRPH0 :R/W time  >=15ns ; TWRPH1 : CLE AND ALE Hold time >=5ns ,这样便可以设置NAND 控制器寄存器相应的参数了。NAND 初始化时需要配置寄存器:NFCONF and NFCONT,下面来算具体位的值:

TACLS :     Duration = HCLK x TACLS >=0                TACLS = 0; 

TWRPH0 : Duration = HCLK x ( TWRPH0 + 1)  >= 15ns    TWRPH0 = 1; 

TWRPH1 : Duration  = HCLK x ( TWRPH1 + 1)  >= 5ns     TWRPH1 = 0;

 图4 寄存器

(2)NAND FLASH 地址机制

下图是flash 示意图,一块nand flash 组成结构:

1 page = [ 2k (main ) + 64 (spare) ] B ; 一页由2 K main区 和 64B spare区组成,main 区用于存放用户数据,spare 区存放ECC校验和坏块标记的信息数据

1 block = 64 pages = ( 2k + 64 )B * 64 = (128K +4K )B

1 device = 2048 blocks = 2112 Mbi

         图5

NandFlash 读取时以 页(page)位单位,以块(block)为最小单位进行擦除。因此在读取NandFlash时,指定首地址后,便开始从首地址读取,直到一页数据全部读完。

现在讲述NandFlash地址寻址方式:

NandFlash地址分为三类:

Column Address:指定在某一页的某一列,简称列地址  

 Page Address   : 页地址    

Block Address   : 块地址 

每一页有(2K+64)=2112Byte,2112byte 需要12bit来表示,对于2112byte系列的NAND,这2112byte被分成1st half Page Register和2nd half Page Register,各自的访问由地址指针命令来选择,A[11:0]就是所谓的column address(列地址),在进行擦除操作时不需要它,因为以块为单位擦除。64个page需要6bit来表示,占用A[17:12],即该page在块内的相对地址,也就是确定位于哪一页。A11这一位地址被用来设置2048byte的1st half page还是2nd half page,0表示1st,1表示2nd。Block的地址是由A[18:28]来表示,也就是确定位于哪一块。用A[0:28]这29位,就可以将K9F2G08U0A这256M的数据存储空间全部访问到。2的29次方是2GBit,256MByte=2GBit。

 图6

(3)NAND FLASH 读操作

如下图7所示为NAND FLASH读操作时序图,程序按时序图设计即可:先发出00H  ->  发地址序列  ->  发30H  ->  判忙 -> 取数据。

          

 图7   读时序图  

(4)内核从NAND 复制到SDRAM

        执行  nand_read(0x60000+64, (unsigned char *)0x30008000, 0x200000); //uImage = 64字节头部 + zImage(内核),本课程烧写的内核是uImage 

实现内核转移工作。内核位于NAND FLAHS  地址 0x60000+64 处, 内核需要放到SDRAM  起始地址 0x30008000  处, 内核大小小于2M,取为2M  即可。                                                                                                                                                   

下面为nand 相关函数:

 1 void nand_init(void)
 2 {
 3 #define TACLS   0
 4 #define TWRPH0  1
 5 #define TWRPH1  0
 6     /* 设置时序 */
 7     NFCONF = (TACLS<<12)|(TWRPH0<<8)|(TWRPH1<<4);
 8     /* 使能NAND Flash控制器, 初始化ECC, 禁止片选 */
 9     NFCONT = (1<<4)|(1<<1)|(1<<0);    
10 }
11 void nand_select(void)
12 {
13     NFCONT &= ~(1<<1);    
14 }
15 
16 void nand_deselect(void)
17 {
18     NFCONT |= (1<<1);     // bit1  0: select  1:deselect
19 }
20 
21 void nand_cmd(unsigned char cmd)
22 {
23     volatile int i;
24     NFCMMD = cmd;
25     for (i = 0; i < 10; i++);
26 }
27 
28 void nand_addr(unsigned int addr)
29 {
30     unsigned int col  = addr % 2048;  //col A0-A10  nand flash 一页2K
31     unsigned int page = addr / 2048;  //page A11-A27
32     volatile int i; 
33 
34     NFADDR = col & 0xff;         // col 表示一页中哪个地址数据
35     for (i = 0; i < 10; i++);    //延时
36     NFADDR = (col >> 8) & 0xff;
37     for (i = 0; i < 10; i++);
38     
39     NFADDR  = page & 0xff;       //page 表示是nand flash 里面的哪一页
40     for (i = 0; i < 10; i++);
41     NFADDR  = (page >> 8) & 0xff;
42     for (i = 0; i < 10; i++);
43     NFADDR  = (page >> 16) & 0xff;
44     for (i = 0; i < 10; i++);    
45 }
46 
47 void nand_wait_ready(void)
48 {
49     while (!(NFSTAT & 1));  //0 :nand busy  1: nand ready
50 }
51 
52 unsigned char nand_data(void)
53 {
54     return NFDATA;
55 }
56 void nand_read(unsigned int addr, unsigned char *buf, unsigned int len)
57 {
58     int col = addr % 2048;
59     int i = 0;
60         
61     /* 1. 选中 */  
62     nand_select();
63 
64     while (i < len)  //读一页走一个循环
65     {
66         /* 2. 发出读命令00h */
67         nand_cmd(0x00);
68 
69         /* 3. 发出地址(分5步发出) */
70         nand_addr(addr);
71 
72         /* 4. 发出读命令30h */
73         nand_cmd(0x30);
74         /* 5. 判断状态 */
75         nand_wait_ready();
76 
77         /* 6. 读数据 */
78         for (; (col < 2048) && (i < len); col++)
79         {
80             buf[i] = nand_data();
81             i++;
82             addr++;
83         }
84         
85         col = 0; //一页读完后,清零col,准备度下一页(col 从0开始)
86     }
87 
88     /* 7. 取消选中 */        
89     nand_deselect();
90 }

 

第二步:设置BootLoader 和 内核需要传递的启动参数

 1 //设置启动参数
 2 static struct tag *params;
 3 
 4 void setup_start_tag(void)
 5 {
 6     params = (struct tag *)0x30000100;  //内核和bootloader 约定存放参数 地址 0x30000100 ~ 0x30008000
 7     
 8     params->hdr.tag = ATAG_CORE;          //表单必须以ATAG_CORE node点开始
 9     params->hdr.size = tag_size (tag_core);
10     
11     params->u.core.flags = 0;
12     params->u.core.pagesize = 0;
13     params->u.core.rootdev = 0;
14 
15     params = tag_next (params);   //当前地址+头部大小
16 }
17 
18 void setup_memory_tags(void)           //内存tag根据内存连续片段的多少,可以设置多个tag
19 {
20     params->hdr.tag = ATAG_MEM;        //该TAG类型存放系统的内存信息(起始地址,内存大小)
21     params->hdr.size = tag_size (tag_mem32);
22     
23     params->u.mem.start = 0x30000000;   //SDRAM 起始地址
24     params->u.mem.size  = 64*1024*1024; //内存大小64M
25     
26     params = tag_next (params);
27 }
28 
29 int strlen(char *str)
30 {
31     int i = 0;
32     while (str[i])
33     {
34         i++;
35     }
36     return i;
37 }
38 
39 void strcpy(char *dest, char *src)
40 {
41     while ((*dest++ = *src++) != '\0');
42 }
43 
44 void setup_commandline_tag(char *cmdline)
45 {
46     int len = strlen(cmdline) + 1; //字符串包括结束字符,所以要+1
47     
48     params->hdr.tag  = ATAG_CMDLINE; //命令行TAG类型,具体命令行由形参带入
49     params->hdr.size = (sizeof (struct tag_header) + len + 3) >> 2; 
50 
51     strcpy (params->u.cmdline.cmdline, cmdline);
52 
53     params = tag_next (params);
54 }
55 
56 void setup_end_tag(void)
57 {
58     params->hdr.tag = ATAG_NONE;
59     params->hdr.size = 0;
60 }
1 /* 2. 设置参数 */
2     puts("Set boot params\n\r");
3     setup_start_tag();
4     setup_memory_tags();
5     setup_commandline_tag("noinitrd root=/dev/mtdblock3 init=/linuxrc console=ttySAC0");
6     setup_end_tag(); 

 

第三步:跳转执行 

1 /* 3. 跳转执行 */
2     puts("Boot kernel\n\r");
3     theKernel = (void (*)(int, int, unsigned int))0x30008000;
4     theKernel(0, 362, 0x30000100);  /*ldr mov pc, #0x30008000*/    //去0x30000100地址处读取设置的tag值,传给内核 ;  362 -> 单板号

 

转载于:https://www.cnblogs.com/HZ-xiaobu/p/7563433.html

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值