NAND Flash 结构
直接拿人家数据手册里的图,Flash的存储结构分三个层次,从低到高依次是Page,Block和Plane。图中一个Page是2048个字节,然后旁边还有64个字节是用来存放校验信息的。64个Page组成一个Block,也就是图里的一层;1024层Block组合成一个Plane。
我用的主控是TI的C6678,它的EMIF支持NAND Flash读写,但是最多只支持512字节左右的ECC校验,而这个NAND Flash的一个Page已经超过了512字节,所以我在用的时候就没有ECC校验,虽然很有可能出问题,但是目前还没遇到。
接口
我用的这个NAND Flash只有一个8bit的接口。地址,数据,指令复用。需要按照要求的指令格式来完成读,写,擦除等操作。ALE和CLE引脚可以用来把8个IO的数据锁存到地址寄存器或者指令寄存器中。
读写Page
数据的读写是以Page为单位的,连续读一个Page里的内容的话,每次读一个数据,Flash内部的列地址(Col Addr)会自增,所以可以不需要额外的命令就能连续读一个Page的数据,就像一个FIFO一样。
发起读指令后,Flash会把数据从Cell搬到data register里,全部搬完之后才可以读。这个时候可以通过Busy信号来判断数据是不是已经准备好,但是后来才发现这个Busy信号是开漏输出,我在电路上没有上拉,所以一直检测不到Busy释放。
除了用Busy信号来判断,还可以通过寄存器查询的方式来判断数据是不是准备好了。当查到数据已经准备好了的时候,命令寄存器中保存的还是查询状态的命令,需要重新写一个读数据的命令再读数据,不然读到的都会是状态寄存器的值。
写Page之前一定要先擦除!
查看状态
主要通过查询IO6来判断是不是可以对Flash读写,IO0的状态会在IO5变成1的时候更新,IO1的状态会在IO6变成1的时候更新。所以在每次Flash退出Busy状态之后可以再去确认一下是不是有错误产生。
快速读写(通过Cache)
通过Cache读,发起一般的读指令后,再发起一个Cache读指令。这时候原先读出来的在data register中的数据会搬到Cache中。用户可以读Cache中的数据,同时Flash会从Cell中将下一个Page的数据搬到data register中,类似流水线处理。这样每次读Page的间隔时间就很小。
Cache写也是类似,数据先从Cache写入data register,当Cache准备好了之后又可以接收下一个Page的数据,在接收下一个Page的数据的同时,上一个Page的数据从data register搬到Cell中。
驱动代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213
| #include <nandflash.h>
static inline void WriteCmd(Uint8 cmd) { *(Uint8 *)NAND_CLE_ADDR = cmd; return; }
static inline void WriteAddr(Uint8 addr) { *(Uint8 *)NAND_ALE_ADDR = addr; return; }
static inline Bool isAnyError() { WriteCmd(0x70); return (Bool)(*(Uint8 *)NAND_BASE_ADDR & 0x03); }
static inline Bool isCacheReady() { WriteCmd(0x70); return (Bool)(*(Uint8 *)NAND_BASE_ADDR & 0x40); }
static inline Bool isProgramFinished() { WriteCmd(0x70); return (*(Uint8 *)NAND_BASE_ADDR & 0x60) == 0x60 ; }
Bool CheckS34MS01G2() { int i; Uint8 tmp[4]; WriteCmd(0x90); WriteAddr(0x00); for(i = 0; i<4; i++){ tmp[i] = *(Uint8 *)NAND_BASE_ADDR; } if(tmp[0] == NAND_ID1 && tmp[1] == NAND_ID2 && tmp[2] == NAND_ID3 && tmp[3] == NAND_ID4){ return TRUE; } else{ return FALSE; } }
Bool BlockErase(Uint16 blockAddr) { if(blockAddr > PLANE_SIZE) return FALSE;
Uint8 rowAddr1, rowAddr2; rowAddr1 = (Uint8)((blockAddr & 0x0003)<<6); rowAddr2 = (Uint8)(blockAddr>>2 & 0x00FF);
WriteCmd(0x60); WriteAddr(rowAddr1); WriteAddr(rowAddr2); WriteCmd(0xD0);
while(!isCacheReady()); if(isAnyError()) return FALSE; else return TRUE; }
Bool ProgramPage( Uint8 pageAddr, Uint16 blockAddr, const Uint8 *src, Uint16 len ){ int i; Uint8 colAddr1, colAddr2, rowAddr1, rowAddr2;
if(len > PAGE_SIZE || pageAddr > BLOCK_SIZE || blockAddr > PLANE_SIZE) return FALSE;
colAddr1 = 0x00; colAddr2 = 0x00; rowAddr1 = pageAddr&(0x3F) | (Uint8)((blockAddr & 0x0003)<<6); rowAddr2 = (Uint8)(blockAddr>>2 & 0x00FF);
WriteCmd(0x80); WriteAddr(colAddr1); WriteAddr(colAddr2); WriteAddr(rowAddr1); WriteAddr(rowAddr2);
for(i = 0; i<len; i++){ *(Uint8 *)NAND_BASE_ADDR = *src++; }
WriteCmd(0x10);
while(!isCacheReady());
if(isAnyError()) return FALSE; else return TRUE; }
Bool ProgramBlock( Uint16 blockAddr, const Uint8 *src, Uint32 pageNum ){ int i; Uint8 pageAddr, rowAddr1, rowAddr2;
if(blockAddr > PLANE_SIZE) return FALSE;
rowAddr2 = (Uint8)(blockAddr>>2 & 0x00FF);
for(pageAddr = 0; pageAddr < pageNum; pageAddr++){ rowAddr1 = pageAddr&(0x3F) | (Uint8)((blockAddr & 0x0003)<<6);
WriteCmd(0x80); WriteAddr(0x00); WriteAddr(0x00); WriteAddr(rowAddr1); WriteAddr(rowAddr2);
for(i = 0; i<PAGE_SIZE; i++){ *(Uint8 *)NAND_BASE_ADDR = *src++; } WriteCmd(0x15);
while(!isCacheReady()); if(isAnyError()) return FALSE; else continue; }
while(!isProgramFinished()); if(isAnyError()) return FALSE; else return TRUE; }
Bool ReadPage( Uint8 pageAddr, Uint16 blockAddr, Uint8 *dst, Uint16 len ){ int i; Uint8 colAddr1, colAddr2, rowAddr1, rowAddr2;
if(len > PAGE_SIZE || pageAddr > BLOCK_SIZE || blockAddr > PLANE_SIZE) return FALSE;
colAddr1 = 0x00; colAddr2 = 0x00; rowAddr1 = pageAddr&(0x3F) | (Uint8)((blockAddr & 0x0003)<<6); rowAddr2 = (Uint8)(blockAddr>>2 & 0x00FF);
WriteCmd(0x00); WriteAddr(colAddr1); WriteAddr(colAddr2); WriteAddr(rowAddr1); WriteAddr(rowAddr2); WriteCmd(0x30);
while(!isCacheReady());
WriteCmd(0x00); for(i = 0; i<PAGE_SIZE; i++){ *(dst+i) = *(Uint8 *)NAND_BASE_ADDR; } return TRUE; }
Bool ReadBlock( Uint16 blockAddr, Uint8 *dst, Uint16 pageNum ){ int i; Uint8 colAddr1, colAddr2, rowAddr1, rowAddr2, pageAddr;
if(blockAddr > PLANE_SIZE || pageNum < 2 || pageNum > BLOCK_SIZE) return FALSE;
colAddr1 = 0x00; colAddr2 = 0x00; rowAddr1 = (Uint8)((blockAddr & 0x0003)<<6); rowAddr2 = (Uint8)(blockAddr>>2 & 0x00FF);
WriteCmd(0x00); WriteAddr(colAddr1); WriteAddr(colAddr2); WriteAddr(rowAddr1); WriteAddr(rowAddr2); WriteCmd(0x30); while(!isCacheReady());
for(pageAddr = 0; pageAddr < pageNum - 1; pageAddr++){ WriteCmd(0x31); while(!isCacheReady()); WriteCmd(0x00); for(i = 0; i<PAGE_SIZE; i++){ *dst++ = *(Uint8 *)NAND_BASE_ADDR; } } WriteCmd(0x3F); while(!isCacheReady()); WriteCmd(0x00); for(i = 0; i<PAGE_SIZE; i++){ *dst++ = *(Uint8 *)NAND_BASE_ADDR; } return TRUE; }
|