Nand Flash坏块管理
由于Nand Flash的工艺不能保证Nand的Memory Array在其生命周期中保持性能的可靠,因此,在Nand的生产中及使用过程中会产生坏块。坏块的特性是:当编程/擦除这个块时,不能将某些位拉高,这会造成Page Program和Block Erase操作时的错误,相应地反映到Status Register的相应位。总体上,坏块可以分为两大类:
(1) 固有坏块
这是生产过程中产生的坏块,一般芯片原厂都会在出厂时都会将坏块第一个page的spare area的某个字节标记为非0xff的值。
(2) 使用坏块
这是在NAND Flash使用过程中,如果Block Erase或者Page Program错误,就可以简单地将这个块作为坏块来处理,这个时候需要把坏块标记起来。为了和固有坏块信息保持一致,将新发现的坏块的第一个page的 spare area的某字节标记为非0xff的值。
一般来说,small page的NAND芯片,坏块信息存储在每个block的第一个page的oob的第六个字节中,而big page的NAND芯片,坏块信息存储在每个block的第一个page的oob的第1个字节中。
而K9F2G08U0C每页的大小为2KB,属于大页。所以我们要检查每个块的第一页或者第二页的第一个字节是否非0xFF。若非0xFF,则该块即为坏块。
为此,检测坏块的代码如下所示:
int nand_BadBlockCheck(unsigned char* nandAddr) {
unsigned int blockAddr = (unsigned int)nandAddr / K9F2G08U0C_BLOCK_SIZE;
blockAddr *= K9F2G08U0C_BLOCK_SIZE;
unsigned int page = blockAddr / K9F2G08U0C_PAGE_SIZE;
unsigned int col = 2048;
//nand_selectChip();
nand_cmd(0x00);
nand_col(col);
nand_page(page);
nand_cmd(0x30);
nand_waitReady();
//nand_cmd(0xFF);
//nand_waitReady();
//nand_deSelectChip();
unsigned char data = nand_ReadData();
if(data != 0xFF){
//坏块
return 1;
}
return 0;
}
Nand Flash的擦除和写入
Nand Flash在进行写入操作之前必须先执行擦除操作。
擦除操作
擦除操作是以块为单位的,其时序图如下所示:
对应擦除代码如下所示:
int nand_Erase(unsigned char* nandAddr, unsigned int length) {
unsigned char status = 0;
unsigned int page = (unsigned int)nandAddr / K9F2G08U0C_PAGE_SIZE;
if((unsigned int)nandAddr & (K9F2G08U0C_BLOCK_SIZE - 1)){
printf("nand_Erase Error. Address 0x%x is not aligned by Block size %dKB.\n\r", nandAddr, K9F2G08U0C_BLOCK_SIZE/1024);
return -1;
}
if(length & (K9F2G08U0C_BLOCK_SIZE - 1)){
printf("nand_Erase Error. Erase length %d is not aligned by Block size %dKB.\n\r", length, K9F2G08U0C_BLOCK_SIZE/1024);
return -1;
}
nand_selectChip();
nand_waitReady();
while(length > 0) {
page = (unsigned int)nandAddr / K9F2G08U0C_PAGE_SIZE;
nand_cmd(0x60);
nand_page(page);
nand_cmd(0xD0);
nand_waitReady();
nand_cmd(0x70);
status = nand_ReadData();
if(status & 0x1){
//Erase this block failed
printf("Erase Block(0x%8x) failed.\n\r", nandAddr);
}
nandAddr += K9F2G08U0C_BLOCK_SIZE;
length -= K9F2G08U0C_BLOCK_SIZE;
}
nand_deSelectChip();
}
写入操作
写入操作是以页为单位的,其操作时序如下所示:
对应写入代码如下所示:
void nand_Write(unsigned char* nandAddr, unsigned char* srcData, unsigned int len) {
//从指定的内存源地址srcData处开始写入数据,
//存储到Nand Flash的nand地址nandAddr处,字节长度由len指定
unsigned char status = 0;
unsigned int tmpNandAddr = (unsigned int)nandAddr;
unsigned int pageAddr = tmpNandAddr / K9F2G08U0C_PAGE_SIZE;
//对src取模,得到列地址
unsigned int colAddr = tmpNandAddr & (K9F2G08U0C_PAGE_SIZE - 1);
unsigned idx = 0;
//Nand是按页写入,按块擦除
nand_selectChip();
nand_waitReady();
while(idx < len) {
/* 一个block只判断一次 */
if (!(tmpNandAddr & 0x1FFFF) && nand_BadBlockCheck((unsigned char*)tmpNandAddr)) {
tmpNandAddr += (128*1024); /* 跳过当前block */
pageAddr = tmpNandAddr / K9F2G08U0C_PAGE_SIZE;
colAddr = tmpNandAddr & (K9F2G08U0C_PAGE_SIZE - 1);
continue;
}
nand_cmd(0x80);
//发送12位列地址
nand_addr(colAddr & 0xFF);
nand_addr((colAddr >> 8) & 0x1F);
//发送17位行地址
nand_addr(pageAddr & 0xFF);
nand_addr((pageAddr >> 8) & 0xFF);
nand_addr((pageAddr >> 16) & 0x1);
for( ;(colAddr < 2048) & (idx < len); idx++){
nand_WriteData(srcData[idx]);
colAddr++;
}
nand_cmd(0x10);
nand_waitReady();
//当前页的数据写入完毕,开始下一页,从头列开始写入
colAddr = 0;
pageAddr++;
nand_cmd(0x70);
nand_waitReady();
status = nand_ReadData();
if(status & 0x1) {
printf("Write Page address 0x%8x failed.\n\r", pageAddr);
}
}
//reset nand chip
nand_cmd(0xFF);
nand_waitReady();
nand_deSelectChip();
return;
}
实验结果图
编译并烧写至Nand Flash中,并选择Nand方式启动,上电,观察串口输出,如下所示:
实验完整代码
Nand实验代码总共由19个源文件构成。
.
├── CRT0.S
├── Ctype.c
├── Ctype.h
├── main.c
├── Makefile
├── myprintf.c
├── myprintf.h
├── nand.c
├── nand.h
├── nand.lds
├── relocate.c
├── s3c2440_soc.h
├── sdram.c
├── sdram.h
├── stdarg.h
├── uart.c
├── uart.h
├── util.c
└── util.h
0 directories, 19 files
完整代码下载路径如下所示:
Nand Flash完整驱动