NAND FLASH ECC 校验原理与实现
2008-01-25 14:57:27
转自:http://blog.csdn.net/nhczp/archive/2007/07/20/1700031.aspx
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 算法的实现
static const u_char nand_ecc_precalc_table[] =
{
0x00, 0x55, 0x56, 0x03, 0x59, 0x0c, 0x0f, 0x5a, 0x5a, 0x0f, 0x0c, 0x59, 0x03, 0x56, 0x55, 0x
00,
0x65, 0x30, 0x33, 0x66, 0x3c, 0x69, 0x6a, 0x3f, 0x3f, 0x6a, 0x69, 0x3c, 0x66, 0x33, 0x30, 0x
65,
0x66, 0x33, 0x30, 0x65, 0x3f, 0x6a, 0x69, 0x3c, 0x3c, 0x69, 0x6a, 0x3f, 0x65, 0x30, 0x33, 0x
66,
0x03, 0x56, 0x55, 0x00, 0x5a, 0x0f, 0x0c, 0x59, 0x59, 0x0c, 0x0f, 0x5a, 0x00, 0x55, 0x56, 0x
03,
0x69, 0x3c, 0x3f, 0x6a, 0x30, 0x65, 0x66, 0x33, 0x33, 0x66, 0x65, 0x30, 0x6a, 0x3f, 0x3c, 0x
69,
0x0c, 0x59, 0x5a, 0x0f, 0x55, 0x00, 0x03, 0x56, 0x56, 0x03, 0x00, 0x55, 0x0f, 0x5a, 0x59, 0x
0c,
0x0f, 0x5a, 0x59, 0x0c, 0x56, 0x03, 0x00, 0x55, 0x55, 0x00, 0x03, 0x56, 0x0c, 0x59, 0x5a, 0x
0f,
0x6a, 0x3f, 0x3c, 0x69, 0x33, 0x66, 0x65, 0x30, 0x30, 0x65, 0x66, 0x33, 0x69, 0x3c, 0x3f, 0x
6a,
0x6a, 0x3f, 0x3c, 0x69, 0x33, 0x66, 0x65, 0x30, 0x30, 0x65, 0x66, 0x33, 0x69, 0x3c, 0x3f, 0x
6a,
0x0f, 0x5a, 0x59, 0x0c, 0x56, 0x03, 0x00, 0x55, 0x55, 0x00, 0x03, 0x56, 0x0c, 0x59, 0x5a, 0x
0f,
0x0c, 0x59, 0x5a, 0x0f, 0x55, 0x00, 0x03, 0x56, 0x56, 0x03, 0x00, 0x55, 0x0f, 0x5a, 0x59, 0x
0c,
0x69, 0x3c, 0x3f, 0x6a, 0x30, 0x65, 0x66, 0x33, 0x33, 0x66, 0x65, 0x30, 0x6a, 0x3f, 0x3c, 0x
69,
0x03, 0x56, 0x55, 0x00, 0x5a, 0x0f, 0x0c, 0x59, 0x59, 0x0c, 0x0f, 0x5a, 0x00, 0x55, 0x56, 0x
03,
0x66, 0x33, 0x30, 0x65, 0x3f, 0x6a, 0x69, 0x3c, 0x3c, 0x69, 0x6a, 0x3f, 0x65, 0x30, 0x33, 0x
66,
0x65, 0x30, 0x33, 0x66, 0x3c, 0x69, 0x6a, 0x3f, 0x3f, 0x6a, 0x69, 0x3c, 0x66, 0x33, 0x30, 0x
65,
0x00, 0x55, 0x56, 0x03, 0x59, 0x0c, 0x0f, 0x5a, 0x5a, 0x0f, 0x0c, 0x59, 0x03, 0x56, 0x55, 0x
00
};
// Creates non-inverted ECC code from line parity
static void nand_trans_result(u_char reg2, u_char reg3,u_char *ecc_code)
{
u_char a, b, i, tmp1, tmp2;
/* Initialize variables */
a = b = 0x80;
tmp1 = tmp2 = 0;
/* Calculate first ECC byte */
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;
}
/* Calculate second ECC byte */
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;
}
/* Store two of the ECC bytes */
ecc_code[0] = tmp1;
ecc_code[1] = tmp2;
}
// Calculate 3 byte ECC code for 256 byte block
void nand_calculate_ecc (const u_char *dat, u_char *ecc_code)
{
u_char idx, reg1, reg2, reg3;
int j;
/* Initialize variables */
reg1 = reg2 = reg3 = 0;
ecc_code[0] = ecc_code[1] = ecc_code[2] = 0;
/* Build up column parity */
for(j = 0; j < 256; j++)
{
/* Get CP0 - CP5 from table */
idx = nand_ecc_precalc_table[dat[j]];
reg1 ^= (idx & 0x3f);
/* All bit XOR = 1 ? */
if (idx & 0x40) {
reg3 ^= (u_char) j;
reg2 ^= ~((u_char) j);
}
}
/* Create non-inverted ECC code from line parity */
nand_trans_result(reg2, reg3, ecc_code);
/* Calculate final ECC code */
ecc_code[0] = ~ecc_code[0];
ecc_code[1] = ~ecc_code[1];
ecc_code[2] = ((~reg1) << 2) | 0x03;
}
// Detect and correct a 1 bit error for 256 byte block
int nand_correct_data (u_char *dat, u_char *read_ecc, u_char *calc_ecc)
{
u_char a, b, c, d1, d2, d3, add, bit, i;
评论1