nand flash

来源:互联网 发布:微商与淘宝区别 编辑:程序博客网 时间:2024/04/30 21:20

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区指示错误

下面是核心代码:

  1. #include "def.h"  
  2. #include "2440addr.h"  
  3. #include "mynand.h"   
  4.   
  5. //define command  
  6. #define CMD_READ1 0x00   //页读命令1  
  7. #define CMD_READ2 0x30   //页读命令2  
  8. #define CMD_READID 0x90   //读取ID  
  9. #define CMD_RESET 0xff    //复位  
  10. #define CMD_WRITE1 0x80   //页写命令1  
  11. #define CMD_WRITE2 0x10   //页写命令2  
  12. #define CMD_ERASE1 0x60   //块擦除命令1  
  13. #define CMD_ERASE2 0xd0   //块擦除命令2  
  14. #define CMD_STATUS 0x70   //读取状态命令  
  15. #define CMD_RANDOMREAD1 0X05  //随机读取命令1  
  16. #define CMD_RANDOMREAD2 0xe0   //随机读取命令2  
  17. #define CMD_RANDOMWRITE  0x85   //随机写命令  
  18.  
  19. #define NF_CE_L()    {rNFCONT &=~(1<<1);}   //使能片选  
  20. #define NF_CE_H()     {rNFCONT |=(1<<1);}   //关闭片选  
  21. #define NF_MECC_UnLock()    {rNFCONT&=~(1<<5);}   //解锁main去ECC  
  22. #define NF_MECC_Lock()      {rNFCONT|=(1<<5);}    //锁定main去ECC  
  23. #define NF_SECC_UnLock()   {rNFCONT &= ~(1<<6); }   //解锁spare区ECC  
  24. #define NF_SECC_Lock()     {rNFCONT |= (1<<6); }    //锁定spare区ECC  
  25. #define NF_RSTECC()        {rNFCONT |= (1<<4); }        //复位ECC  
  26.  
  27.  
  28.  
  29. #define NF_WAITRB()  {while(!(rNFSTAT &(1<<0))) ;}   //等待nand flash 空闲  
  30. #define NF_CLEAR_RB()           {rNFSTAT |= (1<<2); }   //清除RnB信号  
  31. #define NF_DETECT_RB()      {while(!(rNFSTAT&(1<<2)));}    
  32.  
  33. #define NF_RDDATA8()        ((*(volatile unsigned char*)0x4E000010) )             
  34. #define NF_CMD(cmd)   {rNFCMD = (cmd);}  //命令  
  35. #define NF_ADDR( addr)  {rNFADDR = (addr);}  //地址  
  36. #define NF_RDDATA()     (rNFDATA)    //读取32位数据     
  37. //#define NF_RDDATA8()     (rNFDATA)    //读取8位数据    
  38. #define NF_WRDATA(data)  { rNFDATA = (data);}  //写32位数据  
  39. #define NF_WRDATA8(data)  { rNFDATA8 = (data);}  //写8位数据  
  40.  
  41. #define TACLS  1  
  42. #define TWRPH0 2  
  43. #define TWRPH1 0   
  44.   
  45. extern void Delay(int time);   
  46. void nand_init(void)   
  47. {   
  48.     rGPACON = rGPACON & (~(0x3f<<17)) |(0x3f<<17) ;   
  49.     rNFCONF = (TACLS<<12) |(TWRPH0<<8) |(TWRPH1<<4) |(0<<0) ;   
  50.     //非锁定,屏蔽nandflash中断,初始化ECC及锁定main区和spare区ECC,使能nandflash片选及控制器   
  51.     rNFCONT = (0<<13)|(0<<12)|(0<<10)|(0<<9)|(0<<8)|(1<<6)|(1<<5)|(1<<4)|(1<<1)|(1<<0);   
  52.   
  53.   
  54. }   
  55.   
  56. //复位nand   
  57.  void nand_reset()   
  58. {   
  59.     int i;   
  60.     NF_CE_L();  //cs   
  61.     NF_CLEAR_RB();                      //清除RnB信号   
  62.     for(i=0;i<10;i++)  ;   
  63.     NF_CMD(CMD_RESET);           //写入复位命令   
  64.     NF_DETECT_RB();                    //等待RnB信号变高,即不忙   
  65.     NF_CE_H();                               //关闭nandflash片选   
  66.   
  67. }   
  68.   
  69.   
  70. //读取nand的id号,首先需要写入读ID命令,然后再写入0x00地址,就可以读取到一共五个周期的芯片ID,第一个周期为厂商ID,第二个周期为设备ID,   
  71. //第三个周期至第五个周期包括了一些具体的该芯片信息   
  72.  U8 read_id(void)   
  73. {   
  74.     int i;   
  75.     U8 first, second, third, forth, fifth;  //分别读取1---5个周期的数据   
  76.     NF_CE_L();  //cs   
  77.     //NF_CLEAR_RB();                      //清除RnB信号   
  78.    // for(i=0;i<10;i++)  ;   
  79.     NF_CMD(CMD_READID);         //读ID命令   
  80.     NF_ADDR(0x0);                        //写0x00地址   
  81.        
  82.     first = NF_RDDATA8()  ;  //厂商ID:    0Xec   
  83.     second = NF_RDDATA8()  ; //设备ID,一般从这个参数可以判断出nand的一些参数   
  84.     third = NF_RDDATA8()  ; //0x00   
  85.     forth = NF_RDDATA8()  ; //0x95   
  86.     fifth = NF_RDDATA8()  ; // 0x40   
  87.        
  88.     NF_CE_H();                               //关闭nandflash片选   
  89.     return second;   
  90. }   
  91.   
  92. //带硬件ECC校验的读,page_numer为页号,每页2K   
  93. U8 nand_readpage_ecc(U32 page_number, U8 *buffer)   
  94. {   
  95.     int i;   
  96.     U32 mainecc0, spareecc;  //用于存放ecc的临时值   
  97.     NF_RSTECC();                   //复位ECC   
  98.     NF_MECC_UnLock()              //解锁主区ECC   
  99.     NF_CE_L();  //cs   
  100.     NF_CLEAR_RB();                      //清除RnB信号   
  101.     NF_CMD(CMD_READ1);           //页读命令周期1   
  102.     //写入地址,首先写入列地址(也即相对每一个页开始的地址),再写入行地址(也即页号)   
  103.     NF_ADDR(0x00);       //列地址A0~A7,这里直接从每页的开始读可以使用下面被注释的4行代码优化   
  104.     NF_ADDR(0x00);    //列地址A8~A11   
  105.         
  106.     // page = page_number/4;   
  107.     // data_addr = 512 *(page_number%4);   
  108.     // NF_ADDR(data_addr&0xff);   
  109.     // NF_ADDR((data_addr>>8)&0xff);   
  110.        
  111.     NF_ADDR((page_number) & 0xff);                  //行地址A12~A19   
  112.     NF_ADDR((page_number >> 8) & 0xff);           //行地址A20~A27   
  113.        
  114.     NF_CMD(CMD_READ2);           //页读命令周期2   
  115.     NF_DETECT_RB();                    //等待RnB信号变高,即不忙   
  116.   
  117.     for(i=0;i<2048;i++)   
  118.        buffer[i] = NF_RDDATA8() ;   
  119.     NF_MECC_Lock();                     //锁定main区ECC值   
  120.     NF_SECC_UnLock();                  //解锁spare区ECC   
  121.        
  122.     mainecc0=NF_RDDATA();        //读spare区的前4个地址内容,即第2048~2051地址,这4个字节为main区的ECC   
  123.     //把读取到的main区的ECC校验码放入NFMECCD0/1的相应位置内   
  124.     rNFMECCD0=((mainecc0&0xff00)<<8)|(mainecc0&0xff);   
  125.     rNFMECCD1=((mainecc0&0xff000000)>>8)|((mainecc0&0xff0000)>>16);   
  126.   
  127.     NF_SECC_Lock();               //锁定spare区的ECC值   
  128.     spareecc=NF_RDDATA();           //继续读spare区的4个地址内容,即第2052~2055地址,其中前2个字节为spare区的ECC值   
  129.     //把读取到的spare区的ECC校验码放入NFSECCD的相应位置内   
  130.     rNFSECCD=((spareecc&0xff00)<<8)|(spareecc&0xff);   
  131.      NF_CE_H();             //关闭nandflash片选   
  132.        
  133.     //判断所读取到的数据是否正确   
  134.     if ((rNFESTAT0&0xf) == 0x0)   
  135.           return 0x66;                  //正确   
  136.     else    
  137.        return 0x44;      
  138. }   
  139.   
  140. U8 nand_writepage_ecc(U32 page_number, U8 *buffer)   
  141. {   
  142.     int i,stat;   
  143.     U32 mecc0, secc;  //用于存放ecc的临时值   
  144.     char ECCBuf[10];   
  145.     i = nand_is_badblock(page_number>>6) ;   
  146.     if( i ==0x33)   
  147.         return 0x42 ; //坏块   
  148.        
  149.     NF_RSTECC();                   //复位ECC   
  150.     NF_MECC_UnLock()              //解锁主区ECC   
  151.     NF_CE_L();  //cs   
  152.     NF_CLEAR_RB();                      //清除RnB信号   
  153.     NF_CMD(CMD_WRITE1);           //页读命令周期1   
  154.     //写入地址,首先写入列地址(也即相对每一个页开始的地址),再写入行地址(也即页号)   
  155.     NF_ADDR(0x00);       //列地址A0~A7,这里直接从每页的开始读可以使用下面被注释的4行代码优化   
  156.     NF_ADDR(0x00);    //列地址A8~A11   
  157.         
  158.     // page = page_number/4;   
  159.     // data_addr = 512 *(page_number%4);   
  160.     // NF_ADDR(data_addr&0xff);   
  161.     // NF_ADDR((data_addr>>8)&0xff);   
  162.        
  163.     NF_ADDR((page_number) & 0xff);                  //行地址A12~A19   
  164.     NF_ADDR((page_number >> 8) & 0xff);           //行地址A20~A27   
  165.        
  166.     for(i=0;i<2048;i++)   
  167.        NF_WRDATA8(buffer[i]);   
  168.   
  169.     NF_MECC_Lock();                     //锁定main区ECC值   
  170.        
  171.     mecc0=rNFMECC0;                    //读取main区的ECC校验码   
  172.       //把ECC校验码由字型转换为字节型,并保存到全局变量数组ECCBuf中   
  173.     ECCBuf[0]=(U8)(mecc0&0xff);   
  174.     ECCBuf[1]=(U8)((mecc0>>8) & 0xff);   
  175.     ECCBuf[2]=(U8)((mecc0>>16) & 0xff);   
  176.     ECCBuf[3]=(U8)((mecc0>>24) & 0xff);   
  177.        
  178.     NF_SECC_UnLock();                  //解锁spare区ECC   
  179.     for(i=0;i<4;i++)   
  180.        {   
  181.               NF_WRDATA8(ECCBuf[i]);   
  182.        }    
  183.         
  184.     NF_SECC_Lock();               //锁定spare区的ECC值   
  185.     secc=rNFSECC;                   //读取spare区的ECC校验码   
  186.     //把ECC校验码保存到全局变量数组ECCBuf中   
  187.     ECCBuf[4]=(U8)(secc&0xff);   
  188.     ECCBuf[5]=(U8)((secc>>8) & 0xff);   
  189.     //把spare区的ECC值继续写入到spare区的第2052~2053地址内   
  190.   
  191.      for(i=4;i<6;i++)   
  192.        {   
  193.               NF_WRDATA8(ECCBuf[i]);   
  194.        }   
  195.   
  196.     NF_CMD(CMD_WRITE2);           //页读命令周期2   
  197.     Delay(100);   
  198.   
  199.     NF_CMD(CMD_STATUS);                 //读状态命令   
  200.     //判断状态值的第6位是否为1,即是否在忙,该语句的作用与NF_DETECT_RB();相同   
  201.     do {   
  202.         stat = NF_RDDATA8();   
  203.   
  204.     }while(!(stat&0x40));   
  205.   
  206.      NF_CE_H();             //关闭nandflash片选   
  207.        
  208.     //判断所读取到的数据是否正确   
  209.     if (stat & 0x1)   
  210.     {   
  211.         i = rNF_MarkBadBlock(page_number>>6);         //标注该页所在的块为坏块   
  212.         if (i == 0x21)   
  213.               return 0x43  ;         //标注坏块失败   
  214.         else    
  215.             return 0x44; //写操作失败   
  216.     }   
  217.     else    
  218.         return 0x66;   
  219.   
  220.   
  221. }   
  222.   
  223.   
  224. U8 nand_random_readpage(U32 page_number, U32 add)   
  225. {   
  226.        
  227.     NF_CE_L();  //cs   
  228.     NF_CLEAR_RB();                      //清除RnB信号   
  229.     NF_CMD(CMD_READ1);           //页读命令周期1   
  230.     //写入地址,首先写入列地址(也即相对每一个页开始的地址),再写入行地址(也即页号)   
  231.     NF_ADDR(0x00);       //列地址A0~A7,这里直接从每页的开始读可以使用下面被注释的4行代码优化   
  232.     NF_ADDR(0x00);    //列地址A8~A11   
  233.         
  234.     // page = page_number/4;   
  235.     // data_addr = 512 *(page_number%4);   
  236.     // NF_ADDR(data_addr&0xff);   
  237.     // NF_ADDR((data_addr>>8)&0xff);   
  238.        
  239.     NF_ADDR((page_number) & 0xff);                  //行地址A12~A19   
  240.     NF_ADDR((page_number >> 8) & 0xff);           //行地址A20~A27   
  241.        
  242.     NF_CMD(CMD_READ2);           //页读命令周期2   
  243.     NF_DETECT_RB();                    //等待RnB信号变高,即不忙   
  244.        
  245.     NF_CMD(CMD_RANDOMREAD1);                 //随意读命令周期1   
  246.     //页内地址   
  247.     NF_ADDR((char)(add&0xff));                          //列地址A0~A7   
  248.     NF_ADDR((char)((add>>8)&0x0f));                 //列地址A8~A11   
  249.      NF_CMD(CMD_RANDOMREAD2);                //随意读命令周期2   
  250.      return NF_RDDATA8();               //读取数据   
  251.   
  252.   
  253. }   
  254.   
  255.   
  256.   
  257.   
  258. U8 nand_random_writepage(U32 page_number, U32 add, U8 data)   
  259. {   
  260.     U8 stat;   
  261.     NF_CE_L();  //cs   
  262.     NF_CLEAR_RB();                      //清除RnB信号   
  263.     NF_CMD(CMD_WRITE1);           //页读命令周期1   
  264.     //写入地址,首先写入列地址(也即相对每一个页开始的地址),再写入行地址(也即页号)   
  265.     NF_ADDR(0x00);       //列地址A0~A7,这里直接从每页的开始读可以使用下面被注释的4行代码优化   
  266.     NF_ADDR(0x00);    //列地址A8~A11   
  267.         
  268.     // page = page_number/4;   
  269.     // data_addr = 512 *(page_number%4);   
  270.     // NF_ADDR(data_addr&0xff);   
  271.     // NF_ADDR((data_addr>>8)&0xff);   
  272.        
  273.     NF_ADDR((page_number) & 0xff);                  //行地址A12~A19   
  274.     NF_ADDR((page_number >> 8) & 0xff);           //行地址A20~A27   
  275.        
  276.     NF_CMD(CMD_RANDOMWRITE);           //页读命令周期2   
  277.        
  278.     //页内地址   
  279.     NF_ADDR((char)(add&0xff));                          //列地址A0~A7   
  280.     NF_ADDR((char)((add>>8)&0x0f));                 //列地址A8~A11   
  281.     NF_WRDATA8(data);                          //写入数据   
  282.      NF_CMD(CMD_WRITE2);                //页写命令周期2   
  283.         
  284.      //判断状态值的第6位是否为1,即是否在忙,该语句的作用与NF_DETECT_RB();相同   
  285.     do {   
  286.         stat = NF_RDDATA8();   
  287.   
  288.     }while(!(stat&0x40));   
  289.   
  290.      NF_CE_H();             //关闭nandflash片选   
  291.        
  292.     //判断所读取到的数据是否正确   
  293.     if (stat & 0x1)   
  294.         return 0x44;   
  295.     else    
  296.         return 0x66;   
  297.   
  298. }   
  299.   
  300. U8  nand_is_badblock(U32 block)   
  301. {   
  302.     return nand_random_readpage(block*64, 2054);   
  303.   
  304. }   
  305.   
  306. U8 rNF_MarkBadBlock(U32 block)   
  307. {   
  308.     U8 result;   
  309.     result = nand_random_writepage(block*64, 2054, 0x33);   
  310.     if(result == 0x44)   
  311.         return 0x21;                  //写坏块标注失败   
  312.     else  
  313.         return 0x60 ;   
  314.   
  315. }  

原创粉丝点击