ECC 简介
由于 NAND Flash 的工艺不能保证 NAND 的 Memory Array 在其生命周期中保持性能
的可靠,因此,在 NAND 的生产中及使用过程中会产生坏块。为了检测数据的可靠性,在
应用 NAND Flash 的系统中一般都会采用一定的坏区管理策略,而管理坏区的前提是能比
较可靠的进行坏区检测。
如果操作时序和电路稳定性不存在问题的话,NAND Flash 出错的时候一般不会造成整
个 Block 或是 Page 不能读取或是全部出错,而是整个Page(例如 512Bytes)中只有一个
或几个 bit 出错。
对数据的校验常用的有奇偶校验、CRC 校验等,而在 NAND Flash 处理中,一般使用
一种比较专用的校验——ECC。ECC 能纠正单比特错误和检测双比特错误,而且计算速度
很快,但对 1 比特以上的错误无法纠正,对 2 比特以上的错误不保证能检测。
ECC 原理
ECC 一般每 256 字节原始数据生成 3 字节 ECC 校验数据,这三字节共 24 比特分成两
部分:6 比特的列校验和 16 比特的行校验,多余的两个比特置 1,如下图所示:
ECC 的列校验和生成规则如下图所示:
用数学表达式表示为:
P4=D7(+)D6(+)D5(+)D4 P4`=D3(+)D2(+)D1(+)D0
P2=D7(+)D6(+)D3(+)D2 P2`=D5(+)D4(+)D1(+)D0
P1=D7(+)D5(+)D3(+)D1 P1`=D6(+)D4(+)D2(+)D0
这里(+)表示“位异或”操作
ECC 的行校验和生成规则如下图所示:
用数学表达式表示为:
P8 = bit7(+)bit6(+)bit5(+)bit4(+)bit3(+)bit2(+)bit1(+)bit0(+)P8
……………………………………………………………………………………
这里(+)同样表示“位异或”操作
当往 NAND Flash 的 page 中写入数据的时候,每 256 字节我们生成一个 ECC 校验和,
称之为原 ECC 校验和,保存到 PAGE 的 OOB(out-of-band)数据区中。
当从 NAND Flash 中读取数据的时候,每 256 字节我们生成一个 ECC 校验和,称之为
新 ECC 校验和。
校验的时候,根据上述 ECC 生成原理不难推断:将从OOB 区中读出的原 ECC 校验和
新 ECC 校验和按位异或,若结果为 0,则表示不存在错(或是出现了 ECC 无法检测的错误);
若 3 个字节异或结果中存在 11 个比特位为 1,表示存在一个比特错误,且可纠正;若 3 个
字节异或结果中只存在 1 个比特位为 1,表示 OOB 区出错;其他情况均表示出现了无法纠
正的错误。
ECC 算法的实现
显示代码
打印
001 static const u_char nand_ecc_precalc_table[] =
002 {
003
0x00, 0x55, 0x56, 0x03, 0x59, 0x0c, 0x0f, 0x5a, 0x5a, 0x0f,
0x0c, 0x59, 0x03, 0x56, 0x55, 0x00,
004
0x65, 0x30, 0x33, 0x66, 0x3c, 0x69, 0x6a, 0x3f, 0x3f, 0x6a,
0x69, 0x3c, 0x66, 0x33, 0x30, 0x65,
005
0x66, 0x33, 0x30, 0x65, 0x3f, 0x6a, 0x69, 0x3c, 0x3c, 0x69,
0x6a, 0x3f, 0x65, 0x30, 0x33, 0x66,
006
0x03, 0x56, 0x55, 0x00, 0x5a, 0x0f, 0x0c, 0x59, 0x59, 0x0c,
0x0f, 0x5a, 0x00, 0x55, 0x56, 0x03,
007
0x69, 0x3c, 0x3f, 0x6a, 0x30, 0x65, 0x66, 0x33, 0x33, 0x66,
0x65, 0x30, 0x6a, 0x3f, 0x3c, 0x69,
008
0x0c, 0x59, 0x5a, 0x0f, 0x55, 0x00, 0x03, 0x56, 0x56, 0x03,
0x00, 0x55, 0x0f, 0x5a, 0x59, 0x0c,
009
0x0f, 0x5a, 0x59, 0x0c, 0x56, 0x03, 0x00, 0x55, 0x55, 0x00,
0x03, 0x56, 0x0c, 0x59, 0x5a, 0x0f,
010
0x6a, 0x3f, 0x3c, 0x69, 0x33, 0x66, 0x65, 0x30, 0x30, 0x65,
0x66, 0x33, 0x69, 0x3c, 0x3f, 0x6a,
011
0x6a, 0x3f, 0x3c, 0x69, 0x33, 0x66, 0x65, 0x30, 0x30, 0x65,
0x66, 0x33, 0x69, 0x3c, 0x3f, 0x6a,
012
0x0f, 0x5a, 0x59, 0x0c, 0x56, 0x03, 0x00, 0x55, 0x55, 0x00,
0x03, 0x56, 0x0c, 0x59, 0x5a, 0x0f,
013
0x0c, 0x59, 0x5a, 0x0f, 0x55, 0x00, 0x03, 0x56, 0x56, 0x03,
0x00, 0x55, 0x0f, 0x5a, 0x59, 0x0c,
014
0x69, 0x3c, 0x3f, 0x6a, 0x30, 0x65, 0x66, 0x33, 0x33, 0x66,
0x65, 0x30, 0x6a, 0x3f, 0x3c, 0x69,
015
0x03, 0x56, 0x55, 0x00, 0x5a, 0x0f, 0x0c, 0x59, 0x59, 0x0c,
0x0f, 0x5a, 0x00, 0x55, 0x56, 0x03,
016
0x66, 0x33, 0x30, 0x65, 0x3f, 0x6a, 0x69, 0x3c, 0x3c, 0x69,
0x6a, 0x3f, 0x65, 0x30, 0x33, 0x66,
017
0x65, 0x30, 0x33, 0x66, 0x3c, 0x69, 0x6a, 0x3f, 0x3f, 0x6a,
0x69, 0x3c, 0x66, 0x33, 0x30, 0x65,
018
0x00, 0x55, 0x56, 0x03, 0x59, 0x0c, 0x0f, 0x5a, 0x5a, 0x0f,
0x0c, 0x59, 0x03, 0x56, 0x55, 0x00
019 };
020
021 // Creates non-inverted ECC code from line parity
022
static void nand_trans_result(u_char reg2, u_char reg3,u_char
*ecc_code)
023 {
024 u_char a, b, i, tmp1, tmp2;
025
026 /* Initialize variables */
027 a = b = 0x80;
028 tmp1 = tmp2 = 0;
029
030 /* Calculate first ECC byte */
031 for (i = 0; i < 4; i++)
032 {
033 if (reg3 & a) /* LP15,13,11,9 --> ecc_code[0] */
034 tmp1 |= b;
035 b >>= 1;
036 if (reg2 & a) /* LP14,12,10,8 --> ecc_code[0] */
037 tmp1 |= b;
038 b >>= 1;
039 a >>= 1;
040 }
041
042 /* Calculate second ECC byte */
043 b = 0x80;
044 for (i = 0; i < 4; i++)
045 {
046 if (reg3 & a) /* LP7,5,3,1 --> ecc_code[1] */
047 tmp2 |= b;
048 b >>= 1;
049 if (reg2 & a) /* LP6,4,2,0 --> ecc_code[1] */
050 tmp2 |= b;
051 b >>= 1;
052 a >>= 1;
053 }
054
055 /* Store two of the ECC bytes */
056 ecc_code[0] = tmp1;
057 ecc_code[1] = tmp2;
058 }
059
060 // Calculate 3 byte ECC code for 256 byte block
061 void nand_calculate_ecc (const u_char *dat, u_char *ecc_code)
062 {
063 u_char idx, reg1, reg2, reg3;
064 int j;
065
066 /* Initialize variables */
067 reg1 = reg2 = reg3 = 0;
068 ecc_code[0] = ecc_code[1] = ecc_code[2] = 0;
069
070 /* Build up column parity */
071 for(j = 0; j < 256; j++)
072 {
073
074 /* Get CP0 - CP5 from table */
075 idx = nand_ecc_precalc_table[dat[j]];
076 reg1 ^= (idx & 0x3f);
077
078 /* All bit XOR = 1 ? */
079 if (idx & 0x40) {
080 reg3 ^= (u_char) j;
081 reg2 ^= ~((u_char) j);
082 }
083 }
084
085 /* Create non-inverted ECC code from line parity */
086 nand_trans_result(reg2, reg3, ecc_code);
087
088 /* Calculate final ECC code */
089 ecc_code[0] = ~ecc_code[0];
090 ecc_code[1] = ~ecc_code[1];
091 ecc_code[2] = ((~reg1) << 2) | 0x03;
092 }
093
094 // Detect and correct a 1 bit error for 256 byte block
095
int nand_correct_data (u_char *dat, u_char *read_ecc, u_char
*calc_ecc)
096 {
097 u_char a, b, c, d1, d2, d3, add, bit, i;
098
099 /* Do error detection */
100 d1 = calc_ecc[0] ^ read_ecc[0];
101 d2 = calc_ecc[1] ^ read_ecc[1];
102 d3 = calc_ecc[2] ^ read_ecc[2];
103
104 if ((d1 | d2 | d3) == 0)
105 {
106 /* No errors */
107 return 0;
108 }
109 else
110 {
111 a = (d1 ^ (d1 >> 1)) & 0x55;
112 b = (d2 ^ (d2 >> 1)) & 0x55;
113 c = (d3 ^ (d3 >> 1)) & 0x54;
114
115
/* Found and will correct single bit error in the data
*/
116 if ((a == 0x55) && (b == 0x55) && (c == 0x54))
117 {
118 c = 0x80;
119 add = 0;
120 a = 0x80;
121 for (i=0; i<4; i++)
122 {
123 if (d1 & c)
124 add |= a;
125 c >>= 2;
126 a >>= 1;
127 }
128 c = 0x80;
129 for (i=0; i<4; i++)
130 {
131 if (d2 & c)
132 add |= a;
133 c >>= 2;
134 a >>= 1;
135 }
136 bit = 0;
137 b = 0x04;
138 c = 0x80;
139 for (i=0; i<3; i++)
140 {
141 if (d3 & c)
142 bit |= b;
143 c >>= 2;
144 b >>= 1;
145 }
146 b = 0x01;
147 a = dat[add];
148 a ^= (b << bit);
149 dat[add] = a;
150 return 1;
151 }
152 else
153 {
154 i = 0;
155 while (d1)
156 {
157 if (d1 & 0x01)
158 ++i;
159 d1 >>= 1;
160 }
161 while (d2)
162 {
163 if (d2 & 0x01)
164 ++i;
165 d2 >>= 1;
166 }
167 while (d3)
168 {
169 if (d3 & 0x01)
170 ++i;
171 d3 >>= 1;
172 }
173 if (i == 1)
174 {
175 /* ECC Code Error Correction */
176 read_ecc[0] = calc_ecc[0];
177 read_ecc[1] = calc_ecc[1];
178 read_ecc[2] = calc_ecc[2];
179 return 2;
180 }
181 else
182 {
183 /* Uncorrectable Error */
184 return -1;