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;
}