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中。
驱动代码
#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;
}