1.Nand??flash以page为单位进行读写,以block为单位进行擦除,没页分为main区和spare区,main区用于存放正常的数据,spare区用于存放一些附加信息
2.S3c2440?支持从Nand?启动是因为内部有一个叫做Steppingstone的SRAM buffer,当启动的时候,nand?的前4k的将会代码将被拷贝到steppingstone中执行,注意前4k代码是不会经过ECC校验的,所以必须确保这些代码的准确
3.对nand的操作都是通过使用命令来实现,有的操作只要一个命令就可以完成,而有的需要两个命令才能完成,下面是K9F1G08U0B的命令表:
??

?

4.关于TACLS,TWRPH0,TWRH1几个参数的数值问题:
从下面的时序图不难看出这几个参数的意思,在此就不赘述。
?
???????????????????????????????????????????????????????? ????图1 (s3c2440)

????????????????????????????????????????????????????????????????????????????????????????图2(K9F1G08U0B)
比较上面两个时序图,我们发现,TWRPH0?即为K9F1G08U0B?中的twp,?TWRH1?为tCLH
TACLS为tcls – twp,K9F1G08U0B的手册给出了这些参数的最小时间,如图
?

?
都是以ns为单位的。Nand flash?控制器使用的是HCLK?,此时为100MHZ。一个周期为10ns。给本例中我们设置:TACLS为1,TWRPH0为2,TWRH1为0;只要注意设置大于上面的最小值就ok了。
?
6.ECC校验编程:由于使用软件的方法进行ECC校验比较复杂,S3C2440中自带了硬件产生ECC校验,可以通过NFCONT的[5]和[6]位来分别开启硬件ECC产生器,如果是8bit的nand flash接口将产生4个字节的main?区?ECC??校验码和2个字节的spare区?校验码,分别存在NFMECCD和NFSECCD中,当然前提是开启了对应区的ECC发生器。具体可以参考2440的手册P220.下面是编程ECC的过程:
(1)写NFCONT[4]为1初始化ECC?编解码器,写NFCONT[5]为0解锁main区ECC发生器
(2)当读写玩数据时写NFCONT[5]为1来锁定ECC防止被改变
(3)通常用户会将main区产生的ECC校验码写到spare区,这些数据和NFMECC0/1中的数据是相同的。本例中我们约定每一页的spare区的第0个地址到第3个地址存储main区ECC,第4个地址和第5个地址存储spare区ECC
(4)硬件自动产生的ECC会自动保存在NFMECC中,而NFMECCD0/1中的数据需要用户自己写入,当放入数据后,系统会自动比较NFMECC0/1和NFMECCD0/1的内容,这样就实现了ECC码的校验
(5)?最后我们就可以通过读取NFESTAT0/1(因为K9F1G08U0B是8位IO口,因此这里只用到了NFESTAT0)中的低4位来判断读取的数据是否正确,其中第0位和第1位为main区指示错误,第2位和第3位为spare区指示错误
下面是核心代码:
-
#include?"def.h"??
-
#include?"2440addr.h"??
-
#include?"mynand.h"???
- ??
-
//define?command??
-
#define?CMD_READ1?0x00???//页读命令1??
-
#define?CMD_READ2?0x30???//页读命令2??
-
#define?CMD_READID?0x90???//读取ID??
-
#define?CMD_RESET?0xff????//复位??
-
#define?CMD_WRITE1?0x80???//页写命令1??
-
#define?CMD_WRITE2?0x10???//页写命令2??
-
#define?CMD_ERASE1?0x60???//块擦除命令1??
-
#define?CMD_ERASE2?0xd0???//块擦除命令2??
-
#define?CMD_STATUS?0x70???//读取状态命令??
-
#define?CMD_RANDOMREAD1?0X05??//随机读取命令1??
-
#define?CMD_RANDOMREAD2?0xe0???//随机读取命令2??
-
#define?CMD_RANDOMWRITE??0x85???//随机写命令??
- ?
-
#define?NF_CE_L()????{rNFCONT?&=~(1<<1);}???//使能片选??
-
#define?NF_CE_H()?????{rNFCONT?|=(1<<1);}???//关闭片选??
-
#define?NF_MECC_UnLock()????{rNFCONT&=~(1<<5);}???//解锁main去ECC??
-
#define?NF_MECC_Lock()??????{rNFCONT|=(1<<5);}????//锁定main去ECC??
-
#define?NF_SECC_UnLock()???{rNFCONT?&=?~(1<<6);?}???//解锁spare区ECC??
-
#define?NF_SECC_Lock()?????{rNFCONT?|=?(1<<6);?}????//锁定spare区ECC??
-
#define?NF_RSTECC()????????{rNFCONT?|=?(1<<4);?}????????//复位ECC??
- ?
- ?
- ?
-
#define?NF_WAITRB()??{while(!(rNFSTAT?&(1<<0)))?;}???//等待nand?flash?空闲??
-
#define?NF_CLEAR_RB()???????????{rNFSTAT?|=?(1<<2);?}???//清除RnB信号??
-
#define?NF_DETECT_RB()??????{while(!(rNFSTAT&(1<<2)));}????
- ?
-
#define?NF_RDDATA8()????????((*(volatile?unsigned?char*)0x4E000010)?)?????????????
-
#define?NF_CMD(cmd)???{rNFCMD?=?(cmd);}??//命令??
-
#define?NF_ADDR(?addr)??{rNFADDR?=?(addr);}??//地址??
-
#define?NF_RDDATA()?????(rNFDATA)????//读取32位数据?????
-
//#define?NF_RDDATA8()?????(rNFDATA)????//读取8位数据????
-
#define?NF_WRDATA(data)??{?rNFDATA?=?(data);}??//写32位数据??
-
#define?NF_WRDATA8(data)??{?rNFDATA8?=?(data);}??//写8位数据??
- ?
-
#define?TACLS??1??
-
#define?TWRPH0?2??
-
#define?TWRPH1?0???
- ??
-
extern?void?Delay(int?time); ??
-
void?nand_init(void) ??
- { ??
- ????rGPACON?=?rGPACON?&?(~(0x3f<<17))?|(0x3f<<17)?; ??
- ????rNFCONF?=?(TACLS<<12)?|(TWRPH0<<8)?|(TWRPH1<<4)?|(0<<0)?; ??
- ????//非锁定,屏蔽nandflash中断,初始化ECC及锁定main区和spare区ECC,使能nandflash片选及控制器???
- ????rNFCONT?=?(0<<13)|(0<<12)|(0<<10)|(0<<9)|(0<<8)|(1<<6)|(1<<5)|(1<<4)|(1<<1)|(1<<0); ??
- ??
- ??
- } ??
- ??
-
//复位nand???
- ?void?nand_reset() ??
- { ??
- ????int?i; ??
- ????NF_CE_L();??//cs???
- ????NF_CLEAR_RB();??????????????????????//清除RnB信号???
- ????for(i=0;i<10;i++)??; ??
- ????NF_CMD(CMD_RESET);???????????//写入复位命令???
- ????NF_DETECT_RB();????????????????????//等待RnB信号变高,即不忙???
- ????NF_CE_H();???????????????????????????????//关闭nandflash片选???
- ??
- } ??
- ??
- ??
-
//读取nand的id号,首先需要写入读ID命令,然后再写入0x00地址,就可以读取到一共五个周期的芯片ID,第一个周期为厂商ID,第二个周期为设备ID,???
-
//第三个周期至第五个周期包括了一些具体的该芯片信息???
- ?U8?read_id(void) ??
- { ??
- ????int?i; ??
- ????U8?first,?second,?third,?forth,?fifth;??//分别读取1---5个周期的数据???
- ????NF_CE_L();??//cs???
- ????//NF_CLEAR_RB();??????????????????????//清除RnB信号???
- ???//?for(i=0;i<10;i++)??;???
- ????NF_CMD(CMD_READID);?????????//读ID命令???
- ????NF_ADDR(0x0);????????????????????????//写0x00地址???
- ???? ??
- ????first?=?NF_RDDATA8()??;??//厂商ID:????0Xec???
- ????second?=?NF_RDDATA8()??;?//设备ID,一般从这个参数可以判断出nand的一些参数???
- ????third?=?NF_RDDATA8()??;?//0x00???
- ????forth?=?NF_RDDATA8()??;?//0x95???
- ????fifth?=?NF_RDDATA8()??;?//?0x40???
- ???? ??
- ????NF_CE_H();???????????????????????????????//关闭nandflash片选???
- ????return?second; ??
- } ??
- ??
-
//带硬件ECC校验的读,page_numer为页号,每页2K???
- U8?nand_readpage_ecc(U32?page_number,?U8?*buffer) ??
- { ??
- ????int?i; ??
- ????U32?mainecc0,?spareecc;??//用于存放ecc的临时值???
- ????NF_RSTECC();???????????????????//复位ECC???
- ????NF_MECC_UnLock()??????????????//解锁主区ECC???
- ????NF_CE_L();??//清除RnB信号???
- ????NF_CMD(CMD_READ1);???????????//页读命令周期1???
- ????//写入地址,首先写入列地址(也即相对每一个页开始的地址),再写入行地址(也即页号)???
- ????NF_ADDR(0x00);???????//列地址A0~A7,这里直接从每页的开始读可以使用下面被注释的4行代码优化???
- ????NF_ADDR(0x00);????//列地址A8~A11???
- ????? ??
- ????//?page?=?page_number/4;???
- ????//?data_addr?=?512?*(page_number%4);???
- ????//?NF_ADDR(data_addr&0xff);???
- ????//?NF_ADDR((data_addr>>8)&0xff);???
- ???? ??
- ????NF_ADDR((page_number)?&?0xff);??????????????????//行地址A12~A19???
- ????NF_ADDR((page_number?>>?8)?&?0xff);???????????//行地址A20~A27???
- ???? ??
- ????NF_CMD(CMD_READ2);???????????//页读命令周期2???
- ????NF_DETECT_RB();????????????????????//等待RnB信号变高,即不忙???
- ??
- ????for(i=0;i<2048;i++) ??
- ???????buffer[i]?=?NF_RDDATA8()?; ??
- ????NF_MECC_Lock();?????????????????????//锁定main区ECC值???
- ????NF_SECC_UnLock();??????????????????//解锁spare区ECC???
- ???? ??
- ????mainecc0=NF_RDDATA();????????//读spare区的前4个地址内容,即第2048~2051地址,这4个字节为main区的ECC???
- ????//把读取到的main区的ECC校验码放入NFMECCD0/1的相应位置内???
- ????rNFMECCD0=((mainecc0&0xff00)<<8)|(mainecc0&0xff); ??
- ????rNFMECCD1=((mainecc0&0xff000000)>>8)|((mainecc0&0xff0000)>>16); ??
- ??
- ????NF_SECC_Lock();???????????????//锁定spare区的ECC值???
- ????spareecc=NF_RDDATA();???????????//继续读spare区的4个地址内容,即第2052~2055地址,其中前2个字节为spare区的ECC值???
- ????//把读取到的spare区的ECC校验码放入NFSECCD的相应位置内???
- ????rNFSECCD=((spareecc&0xff00)<<8)|(spareecc&0xff); ??
- ?????NF_CE_H();?????????????//关闭nandflash片选???
- ???? ??
- ????//判断所读取到的数据是否正确???
- ????if?((rNFESTAT0&0xf)?==?0x0) ??
- ??????????return?0x66;??????????????????//正确???
- ????else? ??
- ???????return?0x44;??? ??
- } ??
- ??
- U8?nand_writepage_ecc(U32?page_number,153)">int?i,stat; ??
- ????U32?mecc0,?secc;??//用于存放ecc的临时值???
- ????char?ECCBuf[10]; ??
- ????i?=?nand_is_badblock(page_number>>6)?; ??
- ????if(?i?==0x33) ??
- ????????return?0x42?;?//坏块???
- ???? ??
- ????NF_RSTECC();???????????????????//清除RnB信号???
- ????NF_CMD(CMD_WRITE1);???????????//行地址A20~A27???
- ???? ??
- ????for(i=0;i<2048;i++) ??
- ???????NF_WRDATA8(buffer[i]); ??
- ??
- ????NF_MECC_Lock();?????????????????????//锁定main区ECC值???
- ???? ??
- ????mecc0=rNFMECC0;????????????????????//读取main区的ECC校验码???
- ??????//把ECC校验码由字型转换为字节型,并保存到全局变量数组ECCBuf中???
- ????ECCBuf[0]=(U8)(mecc0&0xff); ??
- ????ECCBuf[1]=(U8)((mecc0>>8)?&?0xff); ??
- ????ECCBuf[2]=(U8)((mecc0>>16)?&?0xff); ??
- ????ECCBuf[3]=(U8)((mecc0>>24)?&?0xff); ??
- ???? ??
- ????NF_SECC_UnLock();??????????????????//解锁spare区ECC???
- ????for(i=0;i<4;i++) ??
- ???????{ ??
- ??????????????NF_WRDATA8(ECCBuf[i]); ??
- ???????}? ??
- ????? ??
- ????NF_SECC_Lock();???????????????//锁定spare区的ECC值???
- ????secc=rNFSECC;???????????????????//读取spare区的ECC校验码???
- ????//把ECC校验码保存到全局变量数组ECCBuf中???
- ????ECCBuf[4]=(U8)(secc&0xff); ??
- ????ECCBuf[5]=(U8)((secc>>8)?&?0xff); ??
- ????//把spare区的ECC值继续写入到spare区的第2052~2053地址内???
- ??
- ?????for(i=4;i<6;i++) ??
- ???????{ ??
- ??????????????NF_WRDATA8(ECCBuf[i]); ??
- ???????} ??
- ??
- ????NF_CMD(CMD_WRITE2);???????????//页读命令周期2???
- ????Delay(100); ??
- ??
- ????NF_CMD(CMD_STATUS);?????????????????//读状态命令???
- ????//判断状态值的第6位是否为1,即是否在忙,该语句的作用与NF_DETECT_RB();相同???
- ????do?{ ??
- ????????stat?=?NF_RDDATA8(); ??
- ??
- ????}while(!(stat&0x40)); ??
- ??
- ?????NF_CE_H();?????????????if?(stat?&?0x1) ??
- ????{ ??
- ????????i?=?rNF_MarkBadBlock(page_number>>6);?????????//标注该页所在的块为坏块???
- ????????if?(i?==?0x21) ??
- ??????????????return?0x43??;?????????//标注坏块失败???
- ????????else? ??
- ????????????return?0x44;?//写操作失败???
- ????} ??
- ????else? ??
- ????????return?0x66; ??
- ??
- ??
- } ??
- ??
- ??
- U8?nand_random_readpage(U32?page_number,?U32?add) ??
- { ??
- ???? ??
- ????NF_CE_L();??//等待RnB信号变高,即不忙???
- ???? ??
- ????NF_CMD(CMD_RANDOMREAD1);?????????????????//随意读命令周期1???
- ????//页内地址???
- ????NF_ADDR((char)(add&0xff));??????????????????????????//列地址A0~A7???
- ????NF_ADDR((char)((add>>8)&0x0f));?????????????????//列地址A8~A11???
- ?????NF_CMD(CMD_RANDOMREAD2);????????????????//随意读命令周期2???
- ?????return?NF_RDDATA8();???????????????//读取数据???
- ??
- ??
- } ??
- ??
- ??
- ??
- ??
- U8?nand_random_writepage(U32?page_number,?U32?add,?U8?data) ??
- { ??
- ????U8?stat; ??
- ????NF_CE_L();??//行地址A20~A27???
- ???? ??
- ????NF_CMD(CMD_RANDOMWRITE);???????????//页读命令周期2???
- ???? ??
- ????//列地址A8~A11???
- ????NF_WRDATA8(data);??????????????????????????//写入数据???
- ?????NF_CMD(CMD_WRITE2);????????????????//页写命令周期2???
- ????? ??
- ?????if?(stat?&?0x1) ??
- ????????return?0x44; ??
- ????return?0x66; ??
- ??
- } ??
- ??
- U8??nand_is_badblock(U32?block) ??
- { ??
- ????return?nand_random_readpage(block*64,?2054); ??
- ??
- } ??
- ??
- U8?rNF_MarkBadBlock(U32?block) ??
- { ??
- ????U8?result; ??
- ????result?=?nand_random_writepage(block*64,?2054,?0x33); ??
- ????if(result?==?0x44) ??
- ????????return?0x21;??????????????????//写坏块标注失败???
- ????else??
- ????????return?0x60?; ??
- ??
- }??