Write_RAM(RAM_BANK_0,K9F1G08U_BAD_BLOCK+i,BadBlockTable[i]);
NandFlashECC
NANDFLASHEC校验原理与实现
ECC简介
由于NANDFIash的工艺不能保证NAN啲MemoryArray在其生命周期中保持性能的可靠,因此,在NAND勺生产中及使用过程中会产生坏块。
为了检测数据的可靠性,在应用NANIFIash的系统中一般都会采用一定的坏区管理策略,而管理坏区的前提是能比较可靠的进行坏区检测。
如果操作时序和电路稳定性不存在问题的话,NANDFlash出错的时候一般不
会造成整个Block或是Page不能读取或是全部出错,而是整个Page(例如512Bytes)中只有一个或几个bit出错。
对数据的校验常用的有奇偶校验、CRC校验等,而在NANDFlash处理中,一般使用一种比较专用的校验一一ECCECC能纠正单比特错误和检测双比特错误,而且计算速度很快,但对1比特以上的错误无法纠正,对2比特以上的错误不保证能检测。
ECC原理
ECC一般每256字节原始数据生成3字节ECC校验数据,这三字节共24比特分成两部分:
6比特的列校验和16比特的行校验,多余的两个比特置1,如下图所示:
I/O7
I-06
I/O5
I/O4
I/O3
I/O2
1/01
l/OO
P64
P64'
rp32
P32'
P16
P16'
P8
P8「
P1024
P1024
P512
P512'
P256
P256'
P128
P128'
P4
P4'
P2
P2“
P1
PT
1
1
P8~P1024:
LineparityP1~P4:
Columnparity
ECC的列校验和生成规则如下图所示:
可而冋网|~FT|而而
用数学表达式表示为:
P4=D7(+)D6(+)D5(+)D4P4'=D3(+)D2(+)D1(+)D0
P2=D7(+)D6(+)D3(+)D2P2'=D5(+)D4(+)D1(+)D0
P1=D7(+)D5(+)D3(+)D1P1'=D6(+)D4(+)D2(+)D0
这里(+)表示“位异或”操作
ECC的行校验和生成规则如下图所示:
1stbyte
bit7
bits
bits
bit4
bit3
bit2
bit1
bitO
pM
P161
--
2ndbyte
bit7
bit6
bit5
bit4
bit3
bit2
bit1
bitO
P8
P32'
■■■
3rdbyte
bit7
bitG
bits
bit4
bit3
bit2
bill
bitO
P8*
ri
P16
fa
P1C
4thbyte
■
bit7
bite
bit5
bit4
bit3
bit2
bit1
bitO~
IPS
J
9
■
■
■
•
■
■
■
■
■
■
•
■
Z
253thbyte
bit7
bite
bit5
bit4
bit3
bit2
bit1
bitO
P8
P16'
254thbyte
bit7
bite
bits
bit4
bit3
bit2
bit1
bitO
P8
P32
■■■
P1C
255thbyte
bit7
bite
bit5
bit4
bit3
bit2
bit1
bitO
P16
256thbyte
bit7
bite
bits
bit4
bit3
bit2
bit1
bitO
LbsJ
匕J
[■pY][pi1p?
|_piJ[HlI1BIflU
P2IIP21I
rP2i
丨巴1
P4
P41
用数学表达式表示为:
P8=bit7(+)bit6(+)bit5(+)bit4(+)bit3(+)bit2(+)bit1(+)bit0(+)P8
JJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJ
这里(+)同样表示“位异或”操作
当往NANDFlash的page中写入数据的时候,每256字节我们生成一个ECC校验和,称之为原ECC校验和,保存到PAGE勺00(out-of-band)数据区中。
当从NANDFlash中读取数据的时候,每256字节我们生成一个ECC校验和,称之为新ECC校验和。
校验的时候,根据上述ECC生成原理不难推断:
将从OOB区中读出的原ECC校验和新ECC校验和按位异或,若结果为0,则表示不存在错(或是出现了ECC无法检测的错误);若3个字节异或结果中存在11个比特位为1,表示存在一个比特错误,且可纠正;若3个字节异或结果中只存在1个比特位为1,表示OOB区出错;其他情况均表示出现了无法纠正的错误。
ECC的英文全称是“ErrorCheckingandCorrecting”(错误检查和纠正),从这个名称就可以看出它的主要功能就是“发现并纠正错误”。
奇偶校验技术一样,ECC纠错技术也需要额外的空间来储存校正码,但其占用的位数跟数据的长度并非成线性关系。
具体来说,它是以8位数据、5位ECC码为基准,随
后每增加一个8位数据只需另增加一位ECC码即可。
通俗地讲就是,一个8位的数据产生的ECC码要占用5位的空间,而一个16位数据ECC码只需在原来基础上再增加一位,也就是6位;而32位的数据则只需再在原来基础增加一位,即7位的ECC码即可,如此类推。
ECC码将信息进行8比特位的编码,米用这种方式可以恢复1比特的错误。
每一次数据写入内存的时候,ECC码使用一种特殊的算法对数据进行计算,其结果称为校验位(checkbits)。
然后将所有校验位加在一起的和是“校验和”(checksum),校验和与数据一起存放。
当这些数据从内存中读出时,采用同一算法再次计算校验和,并和前面的计算结果相比较,如果结果相同,说明数据是正确的,反之说明有错误,ECC可以从逻辑上分离错误并通知系统。
当只出现单比特错误的时候,ECC可以把错误改正过来不影响系统运行。
工作原理见图1。
①竝写入内再单无
401(4}I0C蒐祈计校验和.并与
2坯:
家的昵诵U世讦比较
lift
1!
01
1«-
图1
除了能够检查到并改正单比特错误之外,ECC码还能检查到(但不改正)单DRAM芯片上发生的任意2个随机错误,并最多可以检查到4比特的错误。
当有多比特错误发生的时候,ECC内存会生成一个不可隐藏(non-maskableinterrupt)的中断(NM),会中止系统运行,以避免出现数据恶化。
显然ECC码的长度跟数据的长度是成对数关系,当数据长度在64位以上的时候,ECC码在空间占用上就会凸现优势。
此外,ECC校验最大的优点是如果数据中有一位错误,它不但能发现而且可以对其更正,ECC校验还可以发现
2~4位错误(不能更正),当然这样的情况出现的几率是非常低的。
但ECC码
的校验算法比奇偶校验复杂不少,需要专门的芯片来支持,所以普通的电脑主板不一定支持。
而且因为系统需要时间来等待校验的结果,所以ECC校验会降
低系统速度2%-3%左右,但这小小的代价换来系统稳定性的大大提高可以说事非常值得的。
注意:
ECC不是一种内存类型,只是一种内存技术,不仅以前的EDO内
存可以有、SD内存也可有,现在主流的DDF内存同样可以有,所以在现在服务器配置中我们都可见到“512MBECCDD-R400内存”之类的字样。
那是因为它并不是一种影响内存结构和存储速度的技术,可以应用到不同的内存类型之中,就象我们经常到的“奇遇校正”内存技术一样。
ECC内存技术虽然可以同时检测和纠正单一比特错误,但如果同时检测出两个以上比特的数据有错误,则无能为力。
但随着基于Intel处理器架构服务器的CPU生能呈几何级的倍数提高,而硬盘驱动器的性能同期只提高了5倍。
因此为了获得足够的性能,服务器需要大量的内存来临时保存在CPU上读取的
数据。
这样大的数据访问量就导致单一内存芯片上每次访问时通常要提供4(32
位)或8(64位)比特以上的数据。
一次性读取这么多数据,出现多位数据错误的可能性会大大地提高,而ECC又不能纠正双比特以上的错误,这样就很可能造成全部比特数据的丢失,系统就很快崩溃了。
IBM的Chipkill技术是利用
内存的子结构方法来解决这一难题。
ECC算法的实现
staticconstu_charnand_ecc_precalc_table[]=
{
0x00,0x55,0x56,0x03,0x59,0x0c,0x0f,0x5a,0x5a,0x0f,0x0c,
0x59,0x03,0x56,0x55,0x00,
0x65,0x30,0x33,0x66,0x3c,0x69,0x6a,0x3f,0x3f,0x6a,0x69,
0x3c,0x66,0x33,0x30,0x65,
0x66,0x33,0x30,0x65,0x3f,0x6a,0x69,0x3c,0x3c,0x69,0x6a,
0x3f,0x65,0x30,0x33,0x66,
0x03,0x56,0x55,0x00,0x5a,0x0f,0x0c,0x59,0x59,0x0c,0x0f,0x5a,0x00,0x55,0x56,0x03,
0x69,0x3c,0x3f,0x6a,0x30,0x65,0x66,0x33,0x33,0x66,0x65,0x30,0x6a,0x3f,0x3c,0x69,
0x0c,0x59,0x5a,0x0f,0x55,0x00,0x03,0x56,0x56,0x03,0x00,0x55,0x0f,0x5a,0x59,0x0c,
0x0f,0x5a,0x59,0x0c,0x56,0x03,0x00,0x55,0x55,0x00,0x03,0x56,0x0c,0x59,0x5a,0x0f,
0x6a,0x3f,0x3c,0x69,0x33,0x66,0x65,0x30,0x30,0x65,0x66,0x33,0x69,0x3c,0x3f,0x6a,
0x6a,0x3f,0x3c,0x69,0x33,0x66,0x65,0x30,0x30,0x65,0x66,0x33,0x69,0x3c,0x3f,0x6a,
0x0f,0x5a,0x59,0x0c,0x56,0x03,0x00,0x55,0x55,0x00,0x03,0x56,0x0c,0x59,0x5a,0x0f,
0x0c,0x59,0x5a,0x0f,0x55,0x00,0x03,0x56,0x56,0x03,0x00,0x55,0x0f,0x5a,0x59,0x0c,
0x69,0x3c,0x3f,0x6a,0x30,0x65,0x66,0x33,0x33,0x66,0x65,0x30,0x6a,0x3f,0x3c,0x69,
0x03,0x56,0x55,0x00,0x5a,0x0f,0x0c,0x59,0x59,0x0c,0x0f,0x5a,0x00,0x55,0x56,0x03,
0x66,0x33,0x30,0x65,0x3f,0x6a,0x69,0x3c,0x3c,0x69,0x6a,0x3f,0x65,0x30,0x33,0x66,
0x65,0x30,0x33,0x66,0x3c,0x69,0x6a,0x3f,0x3f,0x6a,0x69,0x3c,0x66,0x33,0x30,0x65,
0x00,0x55,0x56,0x03,0x59,0x0c,0x0f,0x5a,0x5a,0x0f,0x0c,0x59,0x03,0x56,0x55,0x00
};
//Createsnon-invertedECCcodefromlineparitystaticvoidnand_trans_result(u_charreg2,u_charreg3,u_char*ecc_code)
{
u_chara,b,i,tmp1,tmp2;
/*Initializevariables*/
a=b=0x80;
tmp1=tmp2=0;
/*CalculatefirstECCbyte*/
for(i=0;i<4;i++)
{
if(reg3&a)/*LP15,13,11,9-->ecc_code[0]*/tmp1|=b;
b>>=1;
if(reg2&a)/*LP14,12,10,8-->ecc_code[0]*/tmp1|=b;
b>>=1;
a>>=1;
}
/*CalculatesecondECCbyte*/
b=0x80;
for(i=0;i<4;i++)
{
if(reg3&a)/*LP7,5,3,1-->ecc_code[1]*/tmp2|=b;
b>>=1;
if(reg2&a)/*LP6,4,2,0-->ecc_code[1]*/tmp2|=b;
b>>=1;
a>>=1;
}
/*StoretwooftheECCbytes*/ecc_code[0]=tmp1;
ecc_code[1]=tmp2;
}
//Calculate3byteECCcodefor256byteblock
voidnand_calculate_ecc(constu_char*dat,u_char*ecc_code){
u_charidx,reg1,reg2,reg3;
intj;
/*Initializevariables*/reg1=reg2=reg3=0;
ecc_code[0]=ecc_code[1]=ecc_code[2]=0;
/*Buildupcolumnparity*/for(j=0;j<256;j++)
{
/*GetCP0-CP5fromtable*/idx=nand_ecc_precalc_table[dat[j]];
reglA=(idx&0x3f);
/*AllbitXOR=1?
*/
if(idx&0x40){reg3A=(u_char)j;reg2A=~((u_char)j);
}}
/*Createnon-invertedECCcodefromlineparity*/nand_trans_result(reg2,reg3,ecc_code);
/*CalculatefinalECCcode*/ecc_code[0]=~ecc_code[0];ecc_code[1]=~ecc_code[1];ecc_code[2]=((~reg1)<<2)|0x03;
}_
//Detectandcorrecta1biterrorfor256byteblock
intnand_correct_data(u_char*dat,u_char*read_ecc,u_char*calc_ecc)
{_
u_chara,b,c,d1,d2,d3,add,bit,i;
/*Doerrordetection*/
di=calc_ecc[0]Aread_ecc[0];
d2=calc_ecc[1]aread_ecc[1];
d3=calc_ecc[2]aread_ecc[2];
if((di|d2|d3)==0)
{
/*Noerrors无错误*/
return0;
}
else
{
a=(dia(di>>1))&0x55;
b=(d2a(d2>>1))&0x55;
c=(d3a(d3>>i))&0x54;
/*Foundandwillcorrectsinglebiterrorinthedata
正好有ii个i*/
if((a==0x55)&&(b==0x55)&&(c==0x54))
{
I/O7
I-06
I/O5
I/O4
I/O3
I/O2
I/O1
l/OO
P64
P64'
rp32
P32'
P16
P16'
P8
P8J
P1024