Bootloader之Nand Flash驱动
近些年来,相较于SDRAM和NAND flash存储器的经济性,NOR flash存储器价格高,这就促使了很多用户将启动程序放在NAND flash,需要执行的主代码放到SDRAM中。S3C2440A启动代码可以在外部的NAND flash中执行。为了支持NAND flash运行bootloader,S3C2440A内部有一个名为“踏脚石”的SRAM缓冲区。当系统上电启动时,NAND flash中的前4KB数据将会被自动加载到“踏脚石”中,这样加载到“踏脚石”中的启动代码将会被执行。通常,启动代码将会从NAND flash复制到SDRAM。使用了硬件ECC检测机制,NAND flash中的数据将会进行校验检查。复制完成后,主程序将会在SDRAM中执行。
在复位过程中,Nand flash控制器通过引脚状态(NOCON(Adv flash),GPG13(页大小),GPG14(地址周期),GPG15(总线宽度))读取与其相连的NAND flash信息。系统上电或者复位时,NAND Flash控制器将会自动地将4Kb bootloader代码加载。加载完毕后,bootloader代码将会在“踏脚石”中执行。
开发板中用到的Nand Flash型号是K9F2G08U0B。下图是引脚
8bit Nand Flash存储器接口
引脚说明:
|
|
I/O0 ~ I/O7 |
数据输入输出 I/O引脚用于输入命令,地址,数据,或者在读数据的过程中输出数据。当芯片未被选中或者输出被禁用是,I/O 引脚浮动到高阻。 |
CLE |
命令锁存使能(COMMAND LATCH ENABLE) CLE输入引脚可以控制命令发送到命令寄存器的有效通道。当有效信号为高电平,WE是上升沿信号时,命令通过I/O口被锁存到命令寄存器中。 |
ALE |
地址锁存使能(ADDRESS LATCH ENABLE) ALE输入引脚可以控制地址发送到内部地址寄存器的有效通道。当WE是上升沿信号时,地址将会被锁存。 |
CE |
芯片使能(CHIP ENABLE) CE输入引脚用于设备选择的控制信号。当设备处于忙碌状态,CE的高电平信号将被忽略,编程及操作操作时设备不会回到待机模式。 |
RE |
读使能(READ ENABLE) RE输入引脚是串行数据输出控制信号,以及控制数据何时传送到I/O总线上。RE出现下降沿信号后,数据成为有效的tREA,同时内部的列计数值会加1。 |
WE |
写使能(WRITE ENABLE) WE输入引脚用于控制数据写入I/O端。当WE脉冲出现上升沿信号时,命令,地址和数据将会被锁存。 |
WP |
写保护(WRITE PROTECT) 当电源发生变化时,WP引脚提供编程擦除保护。当WP引脚是低电平有效信号时,内部的高压生成器将会复位。 |
R/B |
就绪/忙碌状态输出(READY/BUSY OUTPUT) 该输出引脚显示设备操作的状态。当引脚处于低电平时,表示处理器正在进行编程,擦除或者随机读操作,操作完成后引脚将会变成高电平。当芯片未选中或者输出被禁用时,是开漏输出,不会浮动到高阻上。 |
Vcc |
POWER VCC是设备的供电。 |
Vss |
GROUND |
N.C |
NO CONNECTION 引脚没有内部连线。 |
操作特性:
注意:在就绪状态下,如果复位命令(FFh)被写入,设备将会进入忙碌状态,该状态最多持续5µs。
Nand Flash操作时序图
K9F2G08X0B共有2112Mbit约256M容量,由131072行(页)和2112×8列(页内索引)组成。空闲区(用于校验等非数据区)的 64×8列的列地址编号范围是2048~2111。一个2112字节的数据寄存器和存储器单元矩阵连接对应起来,用于在I/O缓存和存储器之间进行页读取和页写入。存储器矩阵是由32个单元以串行方式组成形成NAND结构。32个单元中的每一个分布在不同的页中。一个块由两个NAND连在一起构成。一个NAND结构由32个单元组成。一个块中共有1081344个NAND单元存在。 编程和读操做以页为基本单位进行操作,擦除操作是以块单位进行操作。存储器矩阵由2048个单独的128KB大小的可擦除块组成。在K9F2G08X0B上禁止按bit进行擦除操作。
K9F2G08X0B的8个IO口为地址复用形式。这种方案可以显著的减少引脚的数量以及在将来系统升级时可以保持系统板设计密度的一致性。当CE是低电平时,可通过设置WE为低电平来向IO口写入命令、地址和数据。WE上升沿时这些内容将会被锁存。命令锁存使能(CLE) 和地址锁存使能(ALE) 用于复用命令、地址来实现单独访问IO口。有些命令需要一个总线周期。例如,复位命令,状态读取命令等只需要一个周期的总线时间。其他的一些命令,比如页读取,块擦除以及页编程则需要两个周期:一个周期用来设置,另外一个周期用来执行。264M字节的物理空间需要29个地址bit,从而需要5个周期用来进行编址:顺序是,2个周期的列地址,3个周期的行地址。页读取和页编程需要相同的5个地址周期进行相应的命令输入。对于块草除操作,仅需要3个地址周期。 对设备的操作是通过向命令寄存器写特殊的命令来实现的。
下表描述了K9F2G08X0B 的一些特殊命令。
提示:1.除了70h/F1h/FFh,所有11h~81h之间的命令都是被禁止的。
2.在一页中可以进行数据的随机读写。
警告:除了上表列出的命令集,其他任何未定义的命令输入都是禁止的。
在一页中随机读取数据的时序图:
本节只讲解页内随机读数据操作,其他操作请自行查阅数据手册。
以下是Nand Flash随机读数据的代码:
1 #define NF_READ_BYTE() (rNFDATA & 0xFF)
2 int nand_read_ll(unsigned char *buf, unsigned long start_addr, int size)
3 {
4 int i, block, page, add;
5 nf_init();
6 for(i=start_addr; i < (start_addr + size); i++) {
7 block = i / 131072;
8 page = (i % 131072) / 2048;
9 add = i % 2048;
10 *buf = nf_read(block, page, add);
11 buf++;
12 }
13 return 0;
14 }
15 void nf_init(void)
16 {
17 rNFCONF=((1<<12)|(1<<8)|(0<<4));
18 rNFCONF&=(~(1<<0));
19 rNFCONT =(0<<13)|(0<<12)|(0<<10)|(0<<9)|(0<<8)|(1<<6)|(1<<5)|(1<<4)|(1<<1)|(1<<0);
20 nf_reset();
21 }
22 void nf_reset(void)
23 {
24 nf_select(1);
25 nf_cmd(0xff);
26 nf_readly();
27 nf_select(0);
28 }
29 void nf_select(int n){
30 if(n==1){
31 NAND_CHIP_ENABLE;
32 }else if(n==0){
33 NAND_CHIP_DISABLE;
34 }else{
35 ;
36 }
37 }
38 void nf_cmd(unsigned char cmd){
39 rNFCMD = cmd
40 }
41 void nf_readly(void){
42 while(!(rNFSTAT &(1<<0)));
43 }
44 void nf_addr(unsigned char addr)
45 {
46 rNFADDR = addr;
47 }
48 unsigned char nf_read(unsigned int block, unsigned int page, unsigned int add)
49 {
50 unsigned char buffer=0;
51 unsigned int page_number =(block<<6) + page;
52 nf_reset();
53 nf_select(1);
54 nf_cmd(0);
55 nf_addr(0x00);
56 nf_addr(0x00);
57 nf_addr((page_number)& 0xff); //取出行地址(页间地址的低8位)
58 nf_addr((page_number>> 8) & 0xff); //取出行地址(页间地址的次低8位)
59 nf_addr((page_number>> 16) & 0xff); //取出行地址(页间地址的次高8位)
60 nf_cmd(0x30);
61 nf_readly();
62 nf_cmd(0x05);
63 nf_addr((char)(add&0xff));
64 nf_addr((char)((add>>8)&0x0f));
65 nf_cmd(0xe0);
66 nf_readly();
67 buffer=NF_READ_BYTE();
68 nf_select(0);
69 return buffer;
70 }
Line2函数nand_read_ll为数据读取主函数。参数buf,是SDRAM数据保存的地址。参数start_addr,是Nand Flash的起始地址,该值的取值范围为0x00~ 0xFFFFFFF(将Nand Flash的地址空间抽象成了256M的线性地址)。参数size是需要连续读取的字节数。本函数仅作为演示,采用是循环随机读取页内数据,效率不高,读者可自行编写程序,采用页批次读进行优化。
Line5对应的函数nf_init用于初始化Nand flash。
Line17 寄存器NFCONF[12~13],用于设置CLE和ALE时间周期,公式为HCLK*X,X此处设置为1。NFCONF[8~10],用于设置TWRPH0的时间周期,公式为HCLK*(X+1),X此处为1。NFCONF[4~6],用于设置TWRPH1的时间周期,公式为HCLK*(X+1),X此处为0。
Line18 寄存器NFCONF[0]用于选择IO总线宽度,此处设置为0:8-bit总线。
Line19 寄存器NFCONT[13]用于配置锁定存储器,如果设置为1,写入和擦除命令都是无效的,且只能在复位或者唤醒的情况下设置为0,此处设置为0:禁用锁定功能。寄存器NFCONT[12]用于配置软件锁,如果设置为1,写入和擦除命令都是无效的,可以在任何时候由软件改成0,此处设置为0:禁用软件锁定。寄存器NFCONT[10]用于设置非法访问中断控制,如果设置为1 ,非法访问将产生中断,此处设置为0:禁用中断。寄存器NFCONT[9]用于设置RnB状态转变中断控制,如果设置为1,使能RnB中断,此处设置为0:禁用RnB中断。寄存器NFCONT[8]用于设置RnB状态转变检测控制,如果设置为1,检测下降沿,此处设置为0:检测上升沿。寄存器NFCONT[6]用于设置spare区ECC生成锁定功能,如果设置为0,不锁定ECC,此处设置为1:锁定ECC。寄存器NFCONT[5]用于设置Main数据区域ECC生成锁定功能,如果设置为0,不锁定ECC,此处设置为1:锁定ECC。寄存器NFCONT[4]用于设置初始化ECC解码器/编码器,如果设置为0,不初始化,此处设置为1:初始化ECC解码器和编码器。寄存器NFCONT[1]用于设置Nand flash存储器nFCE信号控制,如果设置为0,将nFCE信号设置为低电平,即选中芯片,此处设置为1:将nFCE信号设置为高电平,即不选中芯片。寄存器NFCONT[0]用于设置Nand flash控制器操作模式,如果设置为0,Nand flash控制器被禁用,此处设置为1:使能Nand flash控制器的功能。
Line20对应的函数nf_reset用于复位Nand flash。
Line24对应的函数nf_select用于选中/不选中芯片,对应于寄存器NFCONT[1]的功能,此处设置为选中芯片。
Line25对应的函数nf_cmd用于设置命令,即NFCMD寄存器低8位。通过上面的介绍可知,0xFF表示复位命令。
Line26对应的函数nf_readly()用于等待寄存器NFSTAT[0]的值变成1,即RnB输入引脚的值变1:Nand Flash已就绪可以操作。
Line27对应的函数nf_select(0)用于不选中芯片。
Line7~9将flash线性地址转换成块号,行号(页号),列号。
Line10对应的函数用于从指定nand flash特定位置读取数据。
Line51由于每块有64(2^6=64)页得到页的绝对地址。
Line54发送按页读的第一个周期的读命令,0x00
Line55~59先设置3次行地址。
Line60发送按页读的第二个周期的读命令
Line61等待完成要求动作
Line62发送按字节读的第一个周期的读命令
Line63~64先发送页间地址
Line65发送按字节读的第二个周期的读命令
Line66等待完成要求动作
Line67读取值
Line68关闭片选