oob =NF_READ_BYTE();
}
NFMECCDATA0_REG =((MECC<<8)&(0xff<<16)) | (MECC&0xff);
NFMECCDATA1_REG =((MECC>>8)&(0xff<<16)) | ((MECC>>16)&0xff);
NF_CE_DISABLE();
// Read ecc status
Status = NFESTAT0_REG;
if ((Status & 0x3) == 1) {
// 1biterror, Correctable
ecc[(Status>>7)&0x7ff]^= (1 << ((Status>>4) & 0x7));
} else if ((Status & 0x3) > 1) {
Debug("ECCuncorrectable error detectedrn");
NF_CE_DISABLE();
return -3;
}
if (data_len == 0) {
return 0;
}
NF_CE_ENABLE(); // 使能片选
NF_CLEAR_RB(); // 清数据传输标志
NF_CMD(NAND_CMD_READ0); // page read cycle 1
NF_ADDR(0); // column address
NF_ADDR(0); // columu address
NF_ADDR(page & 0xff); // 写入3字节的页地址
NF_ADDR((page>>8) & 0xff);
NF_ADDR((page>>16) & 0xff);
NF_CMD(NAND_CMD_READSTART); // page read cycle 2
NF_WAIT_READY(); // 等待命令完成
// read main area
pecc = ecc;
while (data_len) {
pBuffer =data;
NF_INIT_MECC();// main区ECC清空
NF_MECC_UNLOCK();// main区ECC解锁,开始ECC计算
for (i=0;i<512; i++) {
*data++ =NF_READ_BYTE();
data_len--;
if (data_len == 0) {
break;
}
}
NF_MECC_LOCK(); // 锁定main ECC
// SLC: Write ecc to compare
MECC = (pecc[1] << 16) | (pecc[0] << 0);
NFMECCDATA0_REG = MECC;
MECC = (pecc[3] << 16) | (pecc[2] << 0);
NFMECCDATA1_REG =MECC;
pecc += 4;
// Read eccstatus
Status =NFESTAT0_REG;
if ((Status& 0x3) == 1) {
// 1biterror, Correctable
pBuffer[(Status>>7)&0x7ff]^= (1 << ((Status>>4) & 0x7));
} else if((Status & 0x3) > 1) {
Debug("ECCuncorrectable error detectedrn");
NF_CE_DISABLE();
return-4;
}
}
NF_CE_DISABLE();
return 0;
}
2.3. Nand页编程当要写数据到一个页时,要先确保这个块已经被擦除。数据写完后,为确保写入与读取的数据一致,应同时写入数据的ecc值到spare area约定好的位置。
int32_t Nand_WriteWithOob(uint32_t page, const uint8_t*data, uint32_t data_len, const uint8_t *oob, uint32_t oob_len)
{
uint8_t ecc[32]; // 4 byte ecc/512 byte, total 32byte/4096
uint32_t i;
uint8_t *pecc;
uint32_t MECC;
uint32_t data_len_temp;
uint16_t Col;
uint8_t State;
if (!data && (data_len!=0)) {
return -1;
}
if (!oob && (oob_len!=0)) {
return -1;
}
// 保留oob前面38字节作为坏块标记以及数据ecc
if ((data_len>4096) || (oob_len>224-38)) {
return -2;
}
NF_CE_ENABLE(); // 使能片选
NF_CLEAR_RB(); // 清数据传输标志
NF_CMD(NAND_CMD_SEQIN); // page program cycle 1
NF_ADDR(0); // column address
NF_ADDR(0); // columu address
NF_ADDR(page & 0xff); // 写入3字节页地址
NF_ADDR((page>>8) & 0xff);
NF_ADDR((page>>16) & 0xff);
pecc = ecc;
data_len_temp = data_len;
// write main area
while (data_len_temp) {
NF_INIT_MECC();// main区ECC清空
NF_MECC_UNLOCK();// main区ECC解锁,开始ECC计算
// Writebuffer to main area
for (i=0;i<512; i++) {
NF_WRITE_BYTE(*data++);
data_len_temp--;
if(data_len_temp == 0) {
break;
}
}
NF_MECC_LOCK();// 锁定main ECC
MECC =NFMECC0_REG; // 4字节写main区数据的ECC
*pecc++ =MECC & 0xff;
*pecc++ =(MECC>>8) & 0xff;
*pecc++ =(MECC>>16) & 0xff;
*pecc++ =(MECC>>24) & 0xff;
}
if (data_len < 4096) {
// 调整到oob区
Col = 4096;
NF_CMD(NAND_CMD_RNDIN);// 页内随机写命令
NF_ADDR(Col& 0xff); // 2字节页内地址
NF_ADDR((Col>>8)& 0xff);
}
// Reserved for bad block (2 byte)
NF_WRITE_BYTE(0xff);
NF_WRITE_BYTE(0xff);
NF_INIT_MECC();
NF_MECC_UNLOCK(); // 解锁main ECC
// main ecc 32 byte, start spare area offset 2
for (i=0; i<32; i++) {
NF_WRITE_BYTE(ecc);
}
NF_MECC_LOCK(); // 锁定main ECC
MECC = NFMECC0_REG; // 4字节写main区数据的ECC
NF_WRITE_BYTE(MECC & 0xff); // 继续写入MECC
NF_WRITE_BYTE((MECC>>8) & 0xff);
NF_WRITE_BYTE((MECC>>16) & 0xff);
NF_WRITE_BYTE((MECC>>24) & 0xff);
for (i=0; i
NF_WRITE_BYTE(oob);
}
NF_CMD(NAND_CMD_PAGEPROG); // page program cycle 2
NF_WAIT_READY(); // 等待写完
NF_CMD(NAND_CMD_STATUS); // 读取nand状态
do {
State = NF_READ_BYTE();
} while(!(State &(1<<6))); // 等待状态变成Ready
NF_CE_DISABLE();
// 是否写成功,第0位为0则pass,不然fail
if (State & (1<<0)){
return -3; // 写不成功
}
return 0;
}
2.4. Nand坏块检查坏块标记约定用spare area第0~1字节来作标志,坏块这两字节标志为非0xff,好块为0xff。我们读取block中页0,spare area第0~1字节的值即可判断这个block是否坏块。
int32_t Nand_IsBadBlock(uint32_t Block)
{
uint8_toob[2];
// 每个block第一页spare区0, 1字节非0xff标记为好坏
Nand_Read(Block,0, 4096, 2, oob);
if((oob[0]==0xff) && (oob[1]==0xff)) {
return 0; // 好块
}
return 1; // 坏块
}
2.5. 坏块标记如果页编程不成功或者擦除失败,检查出坏块,需要对相应的块在约定位置进行坏块标记,以免再对这个坏块进行读写。其代码实现如下:
int32_t Nand_MarkBadBlock(uint32_t Block)
{
// 每个block第一页spare区第0, 1字节标记非0xff坏块
uint8_t oob[2];
oob[0] = 0;
oob[1] = 0;
returnNand_Write(Block, 0, 4096, 2, oob);
}
2.6. 块区擦除数据写入块区前,对应的块应已擦除好。
int32_t Nand_EraseBlock(uint32_t Block)
{
uint8_t State;
NF_CE_ENABLE();
NF_CLEAR_RB();
NF_CMD(NAND_CMD_ERASE1); // erase block command cycle1
// write 3 cycle block address[BA7:BA17]
NF_ADDR((Block<<7) & 0xff); // [BA7]
NF_ADDR((Block>>1) & 0xff); // [BA15:BA8]
NF_ADDR((Block>>9) & 0xff); // [BA17:BA16]
NF_CMD(NAND_CMD_ERASE2); // erase block command cycle2
NF_WAIT_READY();
NF_CMD(NAND_CMD_STATUS);
do {
State =NF_READ_BYTE();
} while(!(State & (1<<6))); // 等待状态变成Ready
NF_CE_DISABLE();
// 是否擦写成功,第0位为0则pass,不然fail
if (State & (1<<0)) {
return -1; //擦除不成功
}
return 0; // 成功擦除
}
3. Nand启动对于OneNand/Nand启动设备,BL0除了检验BL1的检验和之外,还会采用8bit ecc检验BL1代码的正确性,BL1在烧录进Nand设备时,还应生成相应的8/16位ECC数据,写入到spare area的指定位置,不然ecc失败也将无法从Nand设备启动。Nand启动需要8bit ecc处理,8bit ecc参考Bootloader中相应的NandBoot.c这个Nand驱动。
图3-1 Nand ECC检验
4. 附录Nand.rar,1GB 1bit ecc Nand驱动实现。
Nand.rar
(3.96 KB)
(下载次数: 13, 2015-7-26 11:31 上传)