#ifndef __TEST_H__ #define __TEST_H__ #include"def.h" #define MAX_NAND_BLOCK 2048 //一共2048块 #define NAND_PAGE_SIZE 2048 //每块main区2k字节=2048 typedef struct nand_id_info //芯片的ID信息 { U8 IDm; //厂商ID U8 IDd; //设备ID U8 ID3rd; U8 ID4th; U8 ID5th; } nand_id_info; typedef struct bad_block_info //登记坏块 { U8 area[MAX_NAND_BLOCK]; //0表示非坏块,1表示坏块 U32 sum; //坏块的总数 } bad_block_info; //NAND 操作指令 #define NAND_CMD_READ_1st 0x00 #define NAND_CMD_READ_2st 0x30 #define NAND_CMD_RANDOM_WRITE 0x85 //随机写 #define NAND_CMD_RANDOM_READ_1st 0x05 #define NAND_CMD_RANDOM_READ_2st 0xe0 #define NAND_CMD_READ_CB_1st 0x00 //将NAND里一块内容写进另一块 #define NAND_CMD_READ_CB_2st 0x35 #define NAND_CMD_READ_ID 0x90 #define NAND_CMD_RES 0xff //复位命令 #define NAND_CMD_WRITE_PAGE_1st 0x80 #define NAND_CMD_WRITE_PAGE_2st 0x10 #define NAND_CMD_BLOCK_ERASE_1st 0x60 //擦除命令 #define NAND_CMD_BLOCK_ERASE_2st 0xd0 #define NAND_CMD_READ_STATUS 0x70 //NAND 中断向量 #define INT_NFCON 24 //NFCONF HCLK=100MHZ #define S3C2440_NFCONF_TACLS_init (1<<12) #define S3C2440_NFCONF_TWRPH0_init (4<<8) #define S3C2440_NFCONF_TWRPH1_init (0<<4) #define S3C2440_NFCONF_BusWidth_init (0) #define S3C2440_NFCONF_init() ( rNFCONF = S3C2440_NFCONF_TACLS_init | / S3C2440_NFCONF_TWRPH0_init | / S3C2440_NFCONF_TWRPH1_init | / S3C2440_NFCONF_BusWidth_init ) //NFCONT #define S3C2440_NFCONT_LockTight_init (0<<13) #define S3C2440_NFCONT_SoftLock_init (0<<12) #define S3C2440_NFCONT_EnbIllegalAccINT_init (1<<10) #define S3C2440_NFCONT_EnbRnBINT_init (0<<9) #define S3C2440_NFCONT_RnB_TransMode_init (0<<8) #define S3C2440_NFCONT_SpareECCLock_init (1<<6) #define S3C2440_NFCONT_MainECCLock_init (1<<5) #define S3C2440_NFCONT_InitECC_init (1<<4) #define S3C2440_NFCONT_Reg_nCE_init (1<<1) //初始配置片选无效 #define S3C2440_NFCONT_MODE_init (0) #define S3C2440_NFCONT_init() ( rNFCONT = S3C2440_NFCONT_LockTight_init | / S3C2440_NFCONT_SoftLock_init | / S3C2440_NFCONT_EnbIllegalAccINT_init | / S3C2440_NFCONT_EnbRnBINT_init | / S3C2440_NFCONT_RnB_TransMode_init | / S3C2440_NFCONT_SpareECCLock_init | / S3C2440_NFCONT_MainECCLock_init | / S3C2440_NFCONT_InitECC_init | / S3C2440_NFCONT_Reg_nCE_init | / S3C2440_NFCONT_MODE_init ) //NFSTAT #define S3C2440_NFSTAT_init() ( rNFSTAT &= 0x3 ) //NFESTAT0 #define S3C2440_NFESTAT0_init() ( rNFESTAT0 = 0 ) //NFESTAT1 #define S3C2440_NFESTAT1_init() ( rNFESTAT1 = 0 ) // #define select_nand() ( rNFCONT &= ~(1<<1) ) #define dis_select_nand() ( rNFCONT |= 1<<1 ) #define controller_enable() ( rNFCONT |= 1 ) #define controller_disable() ( rNFCONT &= ~1 ) // void nand_flash_init(void); //初始化 void nand_read_id(void); extern int nand_block_erase(U32 num); //num要删除的块号 extern int nand_page_write(U32 addr,U8 *buffer,U32 size); //addr要写的起始页地址,buffer要写的缓存,size要写的字节大小最大为4G extern int nand_page_read(U32 addr,U8 *buffer,U32 size); //addr开始页地址,从每页00地址开始读 extern int nand_random_read(U32 paddr,U32 offset,U8 *data); //随机读数据 paddr页地址,offset页内偏移地址 extern int nand_random_write(U32 paddr,U32 offset,U8 data); //随机写,paddr页地址,offset页内区最后一个地偏移地址 extern void nand_test_bad_block(void); //测试坏块函数,并标记在nand_bbi变量里和spare里(如果非0xff则为坏块) #endif #include "2440addr.h" #include "test.h" #include"def.h" #define NAND_DEBUG 1 //打印一些串口调试信息 #define USE_ECC 1 //使用ECC验证 nand_id_info nand_id; //定义登记芯片ID的全局变量 bad_block_info nand_bbi; //定义来登记坏的全局变量 void init_nand_bbi(void) //初始化变量 { U32 i; nand_bbi.sum=0; for (i=0;i<MAX_NAND_BLOCK;i++) nand_bbi.area[i]=0; //全部初始化为0 } void nand_mask_bad_block(U32 n) //标志坏块,n是坏块的块号 { #ifdef NAND_DEBUG Uart_Printf("NAND found and mask a bad block= %d .",n); #endif if (nand_bbi.area[n]!=1) { nand_bbi.area[n]=1; nand_bbi.sum++; nand_random_write(n*64,2048+64-1,0);//每块的第一个spare的最后一个字节,标志本块是否为坏块,非0xff为坏块 } } int detect_nand_busy(void) //检测是否忙 { U32 a; a=0; while(!(rNFSTAT&(1<<2))) { a++; if (a==5000000) //等待超时 { Uart_Printf("/r/n Error: Detect Nand Busy time out!!! /r/n"); rNFSTAT |= (1<<2); //清忙标志 return -1; //错误返回-1 } } rNFSTAT |= (1<<2); //清忙标志 return 1; } void nand_reset(void) //NAND复位 { rNFCMD = NAND_CMD_RES; detect_nand_busy(); //检测忙 } void control_start(void) //芯片开启 { select_nand(); controller_enable(); rNFSTAT |= (1<<2); //清忙标志 nand_reset(); } void control_end(void) //芯片关闭 { dis_select_nand(); controller_disable(); } void ecc_main_init(void) //初始化ECC值 { rNFCONT |= 1<<4; //initEcc } void ecc_main_start(void) //开锁main ECC { rNFCONT &= ~(1<<5); //unlock } void ecc_main_end(void) //锁定main ECC { rNFCONT |= 1<<5; //lock } void ecc_spare_start(void) //开锁spare ECC { rNFCONT &= ~(1<<6); //unlock } void ecc_spare_end(void) //锁定spare ECC { rNFCONT |= 1<<6; //lock } void __irq nandINT(void) //NAND中断函数 { //此处写处理代码 #ifdef NAND_DEBUG Uart_Printf("/r/n Nand Error... In interrupt now!!!");//只有错误才会进入中断 #endif rSRCPND |= 0x1<<INT_NFCON; //清中断标志位 rINTPND |= 0x1<<INT_NFCON; //清中断标志位 } void nand_read_id(void)//读取芯片ID信息 { control_start();//开控制 rNFCMD = NAND_CMD_READ_ID; rNFADDR = 0; //读芯片ID nand_id.IDm=(U8)rNFDATA8; nand_id.IDd=(U8)rNFDATA8; nand_id.ID3rd=(U8)rNFDATA8; nand_id.ID4th=(U8)rNFDATA8; nand_id.ID5th=(U8)rNFDATA8; //打印ID信息 #ifdef NAND_DEBUG Uart_Printf("/r/n Read NAND Flash ID:"); Uart_Printf("/r/n NAND Mark code: 0x%x ",nand_id.IDm); Uart_Printf("/r/n NAND Device code: 0x%x ",nand_id.IDd); Uart_Printf("/r/n NAND 3rdID code: 0x%x ",nand_id.ID3rd); Uart_Printf("/r/n NAND 4thID code: 0x%x ",nand_id.ID4th); Uart_Printf("/r/n NAND 5thID code: 0x%x ",nand_id.ID5th); #endif control_end(); //关控制 } //擦出时只要给定块所在页的地址,就能擦除整个块 int nand_block_erase(U32 num) //num要删除的块号 { num=num*64; //每块的第一页 control_start(); //开控制 nand_reset(); //复位 rNFCMD = NAND_CMD_BLOCK_ERASE_1st; rNFADDR = num&0xff; rNFADDR = (num>>8)&0xff; rNFADDR = (num>>16)&0xff; rNFCMD = NAND_CMD_BLOCK_ERASE_2st; detect_nand_busy(); rNFCMD =NAND_CMD_READ_STATUS; //读状态 if(rNFDATA8 & 1) //最低位可以判断擦除和写是否成功 { #ifdef NAND_DEBUG Uart_Printf("/r/n Error:nand erase error... block=0x%x",num/64); #endif control_end();//关控制 nand_mask_bad_block(num/64);//登记为坏块 return -1;//删除错误返回0 } control_end();//关控制 #ifdef NAND_DEBUG Uart_Printf("/r/n NAND block %d erase completed.",num/64); #endif return 1; //擦除成功 } int nand_page_write(U32 addr,U8 *buffer,U32 size) //addr要写的起始页地址,buffer要写的缓存,size要写的字节大小最大为4G { U32 i,n,p,temp,ecc; U8 *bu; bu=buffer; temp=0; n=size/2048+(((size%2048)==0)?0:1); //计算出要写的页数,小于一页的部分当作一页 for(i=0;i<n;i++) { control_start(); //开控制 nand_reset(); //复位 #ifdef USE_ECC ecc_main_init(); ecc_main_start(); //可以产生main区ECC #endif rNFCMD = NAND_CMD_WRITE_PAGE_1st; rNFADDR = 0; //从每页的0地址开始 rNFADDR = 0; //从每页的0地址开始 rNFADDR = (addr)&0xff; rNFADDR = (addr>>8)&0xff; rNFADDR = (addr>>16)&0xff; for (p=0;p<2048;p++) //写入一页 { temp=temp+1; if (temp>size) { rNFDATA8 = 0xff; //多余的填写0xff } else { rNFDATA8 = *(bu+p); } } //delay_lhg(100,100);// #ifdef USE_ECC ecc_main_end(); //锁定main区ecc ecc=rNFMECC0; ecc_spare_start(); //解锁spare区ECC //main ECC值写入备用区的头0~4个地址内 rNFDATA8 = ecc&0xff; rNFDATA8 = (ecc>>8)&0xff; rNFDATA8 = (ecc>>16)&0xff; rNFDATA8 = (ecc>>24)&0xff; ecc_spare_end(); //锁定spare区ECC // delay_lhg(100,100);// ecc = rNFSECC; //spare ECC值写入备用区的5~6两个地址内 rNFDATA8 = ecc&0xff; rNFDATA8 = (ecc>>8)&0xff; #endif bu=bu+2048; //页增量 addr++; rNFCMD = NAND_CMD_WRITE_PAGE_2st; detect_nand_busy();//检测忙 rNFCMD =NAND_CMD_READ_STATUS; //读状态 if (rNFDATA8&1) { #ifdef NAND_DEBUG Uart_Printf("/r/n nand write page error: page addr=0x%d",addr-1); //写入失败,以后改进 #endif control_end(); //关控制 nand_mask_bad_block((addr-1)/64);//登记为坏块 return -1; //写入错误返回-1 } control_end(); //关控制 } return 1; //成功返回1 } int nand_page_read(U32 addr,U8 *buffer,U32 size) //addr开始页地址,从每页00地址开始读,size为需要读的字节数 { U32 i,n,p,temp,ecc; U8 *bu,no; bu=buffer; temp=0; n=size/2048+(((size%2048)==0)?0:1); //计算出要读的页数,小于一页的部分当作一页 for (i=0;i<n;i++) { control_start(); //开控制 nand_reset(); //复位 #ifdef USE_ECC rNFESTAT0 = 0; //复位错误标志位 ecc_main_init(); ecc_main_start(); //可以产生main区ECC #endif rNFCMD = NAND_CMD_READ_1st; rNFADDR = 0; rNFADDR = 0; rNFADDR = addr&0xff; rNFADDR = (addr>>8)&0xff; rNFADDR = (addr>>16)&0xff; rNFCMD = NAND_CMD_READ_2st; detect_nand_busy(); for (p=0;p<2048;p++) { temp=temp+1; if (temp>size) { no=rNFDATA8; //多余的读出来扔掉 } else { *(bu+p) = rNFDATA8; } } #ifdef USE_ECC rNFESTAT0=0; ecc_main_end(); //锁定main区ECC //delay_lhg(100,100);// ecc_spare_start(); //解锁spare区ecc ecc=rNFDATA8; //从flash读出main区ECC,四个字节 no=rNFDATA8; ecc |= ((U32)no)<<8; no=rNFDATA8; ecc |= ((U32)no)<<16; no=rNFDATA8; ecc |= ((U32)no)<<24; rNFMECCD0 = ((ecc&0xff00)<<8)|(ecc&0xff); //硬件检验main ECC rNFMECCD1 = ((ecc&0xff000000)>>8)|((ecc&0xff0000)>>16); ecc_spare_end();//锁定spare区ecc // delay_lhg(100,100);// ecc=rNFDATA8;//从flash读出spare区ECC的值 no=rNFDATA8; ecc |= ((U32)no)<<8; rNFSECCD = ((ecc&0xff00)<<8)|(ecc&0xff);//硬件检验spare ECC //delay_lhg(100,100);//延时一会 ecc=rNFESTAT0&0xffffff; //ecc只是临时用一下错误状态,并非ecc内容 if (ecc!=0)//有错误 { //以后再优化 #ifdef NAND_DEBUG Uart_Printf("/r/n Nand ecc check error... page addr=0x%x,NFESTAT0=0x%x ",addr,ecc); #endif nand_mask_bad_block((addr+i)/64);//登记为坏块 return -1; } #endif bu=bu+2048; addr++; control_end();//关控制 } return 1; } int nand_random_read(U32 paddr,U32 offset,U8 *data) //随机读数据 paddr页地址,offset页内偏移地址 { control_start(); //开控制 nand_reset(); //复位 rNFCMD = NAND_CMD_READ_1st; rNFADDR = 0; rNFADDR = 0; rNFADDR = paddr&0xff; rNFADDR = (paddr>>8)&0xff; rNFADDR = (paddr>>16)&0xff; rNFCMD = NAND_CMD_READ_2st; detect_nand_busy(); rNFCMD = NAND_CMD_RANDOM_READ_1st; rNFADDR = offset&0xff; //写入页内偏移地址 rNFADDR = (offset>>8)&0xff; rNFCMD = NAND_CMD_RANDOM_READ_2st; *data = rNFDATA8; control_end(); return 1; } int nand_random_write(U32 paddr,U32 offset,U8 data)//随机写,paddr页地址,offset页内偏移地址 { control_start(); //开控制 nand_reset(); //复位 rNFCMD = NAND_CMD_WRITE_PAGE_1st; rNFADDR = 0; rNFADDR = 0; rNFADDR = paddr&0xff; rNFADDR = (paddr>>8)&0xff; rNFADDR = (paddr>>16)&0xff; rNFCMD = NAND_CMD_RANDOM_WRITE; rNFADDR = offset&0xff; //写入页内偏移地址 rNFADDR = (offset>>8)&0xff; rNFDATA8 = data; rNFCMD = NAND_CMD_WRITE_PAGE_2st; detect_nand_busy(); //检测忙 rNFCMD =NAND_CMD_READ_STATUS; //读状态 if (rNFDATA8&1) { #ifdef NAND_DEBUG Uart_Printf("/r/n Error:nand random write error... paddr=0x%x,offset=0x%x ",paddr,offset); #endif return -1;//删除错误返回0 } control_end(); return 1;//成功返回1 } void nand_test_bad_block(void) //测试坏块函数,并标记spare区最后一个地址,如果非0xff则为坏块 { U8 dest[64*2048]; //一个块的main区容量 U8 src [64*2048]; U32 i,k; #ifdef NAND_DEBUG Uart_Printf("/r/n test and mask bad block is begain. /r/n"); #endif //main区检测 for (i=0;i<64*2048;i++) { dest[i]=0xff; //初始化缓冲区 src [i]=0; } //删除所有块 for (i=0;i<MAX_NAND_BLOCK;i++) { nand_block_erase(i); } for (i=0;i<MAX_NAND_BLOCK;i++)//测试所有块的main区是否有坏块 { nand_page_write(i*64,src,64*2048); nand_page_read(i*64,dest,64*2048);//使用了ecc校验读出来即可登记坏块信息 } for (i=0;i<64*2048;i++) { dest[i]=0;//初始化缓冲区 src [i]=0xff; } //删除所有块 for (i=0;i<MAX_NAND_BLOCK;i++) { nand_block_erase(i); } for (i=0;i<MAX_NAND_BLOCK;i++)//测试所有块的main区是否有坏块 { nand_page_write(i*64,src,64*2048); nand_page_read(i*64,dest,64*2048);//使用了ecc校验读出来即可登记坏块信息 } // //spare区检测 for (i=0;i<64;i++) { dest[i]=0xff;//初始化缓冲区 src [i]=0; } //删除所有块 for (i=0;i<MAX_NAND_BLOCK;i++) { nand_block_erase(i); } for (i=0;i<MAX_NAND_BLOCK*64;i++) //测试所有块的spare区是否有坏块 { if ( nand_bbi.area[i/64] ==1 ) //如果是坏块则跳过 continue; for (k=0;k<64;k++) { nand_random_write(i,2048+k,src[k]); nand_random_read(i,2048+k,&dest[k]); if (dest[k]!=src[k])//不相等则登记为坏块 { nand_mask_bad_block(i/64); break; } } } for (i=0;i<64;i++) { dest[i]=0x0;//初始化缓冲区 src [i]=0xff; } //删除所有块 for (i=0;i<MAX_NAND_BLOCK;i++) { nand_block_erase(i); } for (i=0;i<MAX_NAND_BLOCK*64;i++) //测试所有块的spare区是否有坏块 { if ( nand_bbi.area[i/64] ==1 )//如果是坏块则跳过 continue; for (k=0;k<64;k++) { nand_random_write(i,2048+k,src[k]); nand_random_read(i,2048+k,&dest[k]); if (dest[k]!=src[k])//不相等则登记为坏块 { nand_mask_bad_block(i/64); break; } } } #ifdef NAND_DEBUG Uart_Printf("/r/n test and mask bad block is over. /r/n"); #endif } void nand_flash_init(void)//初始化 { #ifdef NAND_DEBUG Uart_Printf("/r/nNAND FLASH init");// #endif //中断入口地址 pISR_NFCON = (U32)nandINT; //配置GPIO rGPGUP |= 0x7<<13; //GPG13~15关闭上位 rGPGCON &= ~((U32)0x3f<<26);//GPG13~15为输入 //初始化各寄存器 S3C2440_NFCONF_init(); S3C2440_NFCONT_init(); S3C2440_NFSTAT_init(); S3C2440_NFESTAT0_init(); S3C2440_NFESTAT1_init(); //关于中断 rINTMSK &= ~(0x1<<INT_NFCON) ; //中断 rINTMOD &= ~(0x1<<INT_NFCON); //IRQ rSRCPND |= 0x1<<INT_NFCON; //清中断标志位 rINTPND |= 0x1<<INT_NFCON; //清中断标志位 // init_nand_bbi();//初始化全局变量 nand_read_id();//读ID // nand_test_bad_block();//测试并登记坏块 }