ECC的全称是Error Checking and Correction,是一种用于Nand的差错检测和修正算法。如果操作时序和电路稳定性不存在问题的话,NAND Flash出错的时候一般不会造成整个Block或是Page不能读取或是全部出错,而是整个Page(例如512Bytes)中只有一个或几个bit出错。ECC能纠正1个比特错误和检测2个比特错误,而且计算速度很快,但对1比特以上的错误无法纠正,对2比特以上的错误不保证能检测。
校验码生成算法的C语言实现 已经知道,异或运算的作用是判断比特位为1的个数,跟比特位为0的个数没有关系。如果有偶数个1则异或的结果为0,如果有奇数个1则异或的结果为1。 图表 1 出错Bit列地址定位的判决树 注意:图中的CP指的是求异或之后的结果中的CP 为什么只用CP4,CP2,CP0呢?其实这里面包含冗余信息,因为CP5=1则必有CP4=0,CP5=0则必有CP4=1,也就是CP5跟CP4一定相反,同理,CP3跟CP2一定相反,CP1跟CP0一定相反。所以只需要用一半就行了。 这样,我们从异或结果中抽取出CP5,CP3,CP1位,便可定位出错Bit位的列地址。比如上面的例子中CP5/CP3/CP1 = 001,表示Bit 1出错。 同理,行校验RP1发生变化,抽取RP1,可知Byte 1发生变化。这样定位出Byte 1的Bit 0出错。 当数据位256字节时,行校验使用RP0 ~ RP15,抽取异或结果的RP15,RP13,RP11,RP9,RP7,RP5,RP3,RP1位便可定位出哪个Byte出错,再用CP5,CP3,CP1定位哪个Bit出错。
|
原文地址 http://linux.chinaunix.net/bbs/viewthread.php?tid=1116253&extra=page%3D1
终于基本看懂了。。。。
下面解释一下,也许可以给和我曾经一样迷茫的人一点帮助:
对于这个,别人总结出来的规则:
RP0只计算行索引的Bit0为0的行,RP1只计算行索引的Bit0为1的行;
RP2只计算行索引的Bit1为0的行,RP3只计算行索引的Bit1为1的行;
RP4只计算行索引的Bit2为0的行,RP5只计算行索引的Bit2为1的行;
RP6只计算行索引的Bit3为0的行,RP7只计算行索引的Bit3为1的行;
RP8只计算行索引的Bit4为0的行,RP9只计算行索引的Bit4为1的行;
RP10只计算行索引的Bit5为0的行,RP11只计算行索引的Bit5为1的行;
RP12只计算行索引的Bit6为0的行,RP13只计算行索引的Bit6为1的行;
RP14只计算行索引的Bit7为0的行,RP15只计算行索引的Bit7为1的行;
在接下来的描述中,称为行与位的对应关系
另注:
1.上述规则中的RP意思是Row Parity,更多的叫法叫做LP(Line Parity)。为了解释更容易看懂,依旧采用RP的说法。
2.对于第几行,采用Line的说法,比如第1行,其实就是行号为0的Line0.
3.对于行的奇偶性,此处采用Line Parity的说法。
当Line5的Line Parity为1的时候,
首先最简单的理解,也是最直接的理解,那就是,要把所有RP0~RP14中,对应包含着此行的那些最后要计算的值找出来,
我们可以先手动地根据下图:
一点点,掰手指头,慢慢地写出来,那就是:
RP1,RP2,RP5,RP6,RP8,RP10,RP12,RP14
换句话说,如果Line5的Line Parity为1的时候,
我们应该要计算RP1,RP2,RP5,RP6,RP8,RP10,RP12,RP14。
关于这点,我想大家没有什么好疑问的吧,因为这就是按照其规则的最简单,最通俗的理解。
所以,不论你用什么复杂的算法,反正是要记录并且计算这些RP的值,以便和后面的值进行计算。
但是,程序在此处,并没有将这些RP找出来,而只是直接对行号进行XOR异或:
reg3 ^= (uint8_t) i;
表面上看,这和我们要找出来,并计算的那些RP,并没啥关系,这也是我开始很困惑的问题。
按理来说,应该是找出那些行号,然后计算对应的RP的值,并保存,这样才对。
而此处之所以可以这么处理,主要是有以下原因:
1. 行与位的有如下对应关系:
RP0只计算行索引的Bit0为0的行,RP1只计算行索引的Bit0为1的行;
RP2只计算行索引的Bit1为0的行,RP3只计算行索引的Bit1为1的行;
RP4只计算行索引的Bit2为0的行,RP5只计算行索引的Bit2为1的行;
RP6只计算行索引的Bit3为0的行,RP7只计算行索引的Bit3为1的行;
RP8只计算行索引的Bit4为0的行,RP9只计算行索引的Bit4为1的行;
RP10只计算行索引的Bit5为0的行,RP11只计算行索引的Bit5为1的行;
RP12只计算行索引的Bit6为0的行,RP13只计算行索引的Bit6为1的行;
RP14只计算行索引的Bit7为0的行,RP15只计算行索引的Bit7为1的行;
2. 某一行号的二进制分解的对应bit,对应了所要计算的RP:
比如是第6行,也就是Line5,5的二进制是:
Bit7 |
Bit6 |
Bit5 |
Bit4 |
Bit3 |
Bit2 |
Bit1 |
Bit0 |
0 |
0 |
0 |
0 |
0 |
1 |
0 |
1 |
5的二进制值
而根据上面别人分析出来的,行与位的对应关系,我们可以找出,此二进制的每一位所对应了哪些RP:
bit为1的位,分别是0,2,对应代表的是RP1,RP5
bit为0的位,分别是1,3,4,5,6,7,对应代表的是RP2,RP6,RP8,RP10,RP12,RP14
用表格表示为:
Bit7 |
Bit6 |
Bit5 |
Bit4 |
Bit3 |
Bit2 |
Bit1 |
Bit0 |
0 |
0 |
0 |
0 |
0 |
1 |
0 |
1 |
RP14 |
RP12 |
RP10 |
RP8 |
RP6 |
RP5 |
RP2 |
RP1 |
5的二进制值和二进制对应的行
上表中,比如bit2是1,而别人说了“RP5只计算行索引的Bit2为1的行”,
所以,此处如果bit2为1,对应着RP5将要被计算,
那么我们可以肯定地得出来的是,
如果此行,Line5,的Line Parity是1的话,RP5是要被计算的。
而仔细观察就会发现,RP5,就包含在我们上面自己手动找出来的那些LP中:
RP1,RP2,RP5,RP6,RP8,RP10,RP12,RP14
而,剩下的bit位,也依次对应着这些LP。比如bit0为1,对应RP1.
这就是我们上面说的“某一行号的二进制分解的对应bit,对应了所要计算的RP”
也是理解如此处理的关键点之一。
同样地,除了bit为1的bit0,bit2,对应的RP1,RP5之外,
剩下的几个bit对应的RP2,RP6,RP8,RP10,RP12,RP14,由于对应位是0,所以,即使拿过来抑或,也还是0,无法记住这些bit的值,所以,采用将其取反,这样,对应这些为0的bit,就变成1了,就可以记住这些对应的bit了:
reg2 ^= ~((uint8_t) i);
这样,当从0到255检测的过程中,如果发现某行的Line Parity是1,
那么就将其行号数值进行抑或,以存储奇数的LP,将行号取反,以保存偶数的LP,
也就是:
Reg3对应的就是RP1,RP3,RP5,。。。,RP15
Reg2对应的就是RP0,RP2,RP4,。。。,RP14
然后再调用函数nand_trans_result(reg2, reg3, ecc_code);去将reg3和reg2中存储的信息,
重新组织到ecc[1]和ecc[2]中去。
最后的感慨是:
此处仅仅是通过对行号的数值抑或,以保存所要求的各个RP的值,之所以让人很难理解:
一是由于我们之前不知道上面的那个规则:“行与位的对应关系”
二是我们不知道,行号按位分解后,对应的bit位对应着所要计算的那些RP,“某一行号的二进制分解的对应bit,对应了所要计算的RP”
最后感谢各位作者和分享其分析过程的朋友。
代码:
Testecc.c
/* * ===================================================================================== * * Filename: TestEcc.c * * Description: * * Version: 1.0 * Created: 2009年06月04日 20时15分54秒 * Revision: none * Compiler: gcc * * Author: Li Hongwang (mn), [email protected] * Company: University of Science and Technology of China * * ===================================================================================== */ #include <stdio.h> typedef unsigned char u_char; typedef unsigned char uint8_t; typedef unsigned int uint32_t; /* * Pre-calculated 256-way 1 byte column parity */ static const u_char nand_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 }; /** * * nand_calculate_ecc - [NAND Interface] Calculate 3-byte ECC for 256-byte block * * @mtd: MTD block structure * * @dat: raw data * * @ecc_code: buffer for ECC * */ int nand_calculate_ecc(const u_char *dat, u_char *ecc_code) { uint8_t idx, reg1, reg2, reg3, tmp1, tmp2; int i; /* Initialize variables */ reg1 = reg2 = reg3 = 0; /* Build up column parity */ for(i = 0; i < 256; i++) { /* Get CP0 - CP5 from table */ idx = nand_ecc_precalc_table[*dat++]; reg1 ^= (idx & 0x3f); /* All bit XOR = 1 ? */ if (idx & 0x40) { reg3 ^= (uint8_t) i; reg2 ^= ~((uint8_t) i); } } /* Create non-inverted ECC code from line parity */ tmp1 = (reg3 & 0x80) >> 0; /* B7 -> B7 */ tmp1 |= (reg2 & 0x80) >> 1; /* B7 -> B6 */ tmp1 |= (reg3 & 0x40) >> 1; /* B6 -> B5 */ tmp1 |= (reg2 & 0x40) >> 2; /* B6 -> B4 */ tmp1 |= (reg3 & 0x20) >> 2; /* B5 -> B3 */ tmp1 |= (reg2 & 0x20) >> 3; /* B5 -> B2 */ tmp1 |= (reg3 & 0x10) >> 3; /* B4 -> B1 */ tmp1 |= (reg2 & 0x10) >> 4; /* B4 -> B0 */ tmp2 = (reg3 & 0x08) << 4; /* B3 -> B7 */ tmp2 |= (reg2 & 0x08) << 3; /* B3 -> B6 */ tmp2 |= (reg3 & 0x04) << 3; /* B2 -> B5 */ tmp2 |= (reg2 & 0x04) << 2; /* B2 -> B4 */ tmp2 |= (reg3 & 0x02) << 2; /* B1 -> B3 */ tmp2 |= (reg2 & 0x02) << 1; /* B1 -> B2 */ tmp2 |= (reg3 & 0x01) << 1; /* B0 -> B1 */ tmp2 |= (reg2 & 0x01) << 0; /* B7 -> B0 */ /* Calculate final ECC code */ #ifdef CONFIG_MTD_NAND_ECC_SMC //ecc_code[0] = ~tmp2; //ecc_code[1] = ~tmp1; #else //ecc_code[0] = ~tmp1; //ecc_code[1] = ~tmp2; #endif ecc_code[0] = tmp2; ecc_code[1] = tmp1; //ecc_code[2] = ((~reg1) << 2) | 0x03; ecc_code[2] = ((reg1) << 2) | 0x03; return 0; } static inline int countbits(uint32_t byte) { int res = 0; for (;byte; byte >>= 1) res += byte & 0x01; return res; } int nand_correct_data( u_char *read_ecc, u_char *calc_ecc) { uint8_t s0, s1, s2; s0 = calc_ecc[0] ^ read_ecc[0]; s1 = calc_ecc[1] ^ read_ecc[1]; s2 = calc_ecc[2] ^ read_ecc[2]; if ((s0 | s1 | s2) == 0) return 0; /* Check for a single bit error */ if( ((s0 ^ (s0 >> 1)) & 0x55) == 0x55 && ((s1 ^ (s1 >> 1)) & 0x55) == 0x55 && ((s2 ^ (s2 >> 1)) & 0x54) == 0x54) { uint32_t byteoffs, bitnum; byteoffs = (s1 << 0) & 0x80; byteoffs |= (s1 << 1) & 0x40; byteoffs |= (s1 << 2) & 0x20; byteoffs |= (s1 << 3) & 0x10; byteoffs |= (s0 >> 4) & 0x08; byteoffs |= (s0 >> 3) & 0x04; byteoffs |= (s0 >> 2) & 0x02; byteoffs |= (s0 >> 1) & 0x01; bitnum = (s2 >> 5) & 0x04; bitnum |= (s2 >> 4) & 0x02; bitnum |= (s2 >> 3) & 0x01; printf("Error Bit at: Byte %d, Bit %d.\n", byteoffs, bitnum); return 1; } if(countbits(s0 | ((uint32_t)s1 << 8) | ((uint32_t)s2 <<16)) == 1) return 1; return -1; } // static const u_char raw_data[] = { 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0A,0x0B,0x0C,0x0D,0x0E,0x0F, 0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1A,0x1B,0x1C,0x1D,0x1E,0x1F, 0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,0x29,0x2A,0x2B,0x2C,0x2D,0x2E,0x2F, 0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x3A,0x3B,0x3C,0x3D,0x3E,0x3F, 0x40,0x41,0x42,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4A,0x4B,0x4C,0x4D,0x4E,0x4F, 0x50,0x51,0x52,0x53,0x54,0x55,0x56,0x57,0x58,0x59,0x5A,0x5B,0x5C,0x5D,0x5E,0x5F, 0x60,0x61,0x62,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6A,0x6B,0x6C,0x6D,0x6E,0x6F, 0x70,0x71,0x72,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7A,0x7B,0x7C,0x7D,0x7E,0x7F, 0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, 0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, 0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF }; // changed data. 0x34==>0x74 static const u_char new_data[] = { 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0A,0x0B,0x0C,0x0D,0x0E,0x0F, 0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1A,0x1B,0x1C,0x1D,0x1E,0x1F, 0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,0x29,0x2A,0x2B,0x2C,0x2D,0x2E,0x2F, 0x30,0x31,0x32,0x33,0x74,0x35,0x36,0x37,0x38,0x39,0x3A,0x3B,0x3C,0x3D,0x3E,0x3F, 0x40,0x41,0x42,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4A,0x4B,0x4C,0x4D,0x4E,0x4F, 0x50,0x51,0x52,0x53,0x54,0x55,0x56,0x57,0x58,0x59,0x5A,0x5B,0x5C,0x5D,0x5E,0x5F, 0x60,0x61,0x62,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6A,0x6B,0x6C,0x6D,0x6E,0x6F, 0x70,0x71,0x72,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7A,0x7B,0x7C,0x7D,0x7E,0x7F, 0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, 0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, 0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF }; static uint8_t ecc_code_raw[3]; static uint8_t ecc_code_new[3]; int main() { int i=0; nand_calculate_ecc( raw_data, ecc_code_raw ); nand_calculate_ecc( new_data, ecc_code_new ); printf("\nRaw ECC Code: "); for( i=0; i< 3; i++) { printf("0x%02X ", ecc_code_raw[i] ); } printf("\nNew ECC Code: "); for( i=0; i< 3; i++) { printf("0x%02X ", ecc_code_new[i] ); } printf("\n"); nand_correct_data( ecc_code_raw, ecc_code_new ); printf("\n"); }