ECC校验原理
ECC的全称是Error Checking and Correction,是一种用于Nand的差错检测和修正算法。如果操作时序和电路稳定性不存在问题的话,NAND Flash出错的时候一般不会造成整个Block或是Page不能读取或是全部出错,而是整个Page(例如512Bytes)中只有一个或几个bit出错。ECC能纠正1个bit错误和检测2个bit错误,而且计算速度很快,但对1bit以上的错误无法纠正,对2比特以上的错误不保证能检测。
ECC校验码生成算法:ECC校验每次对256字节的数据进行操作,包含列校验和行校验。对每个待校验的Bit位求异或,若结果为0,则表明含有偶数个1;若结果为1,则表明含有奇数个1。列校验规则如表所示。256字节数据形成256行、8列的矩阵,矩阵每个元素表示一个Bit位。
列校验如上图所示:
CP0 == 所有的 Bit0 ^ Bit2 ^ Bit4 ^ Bit6
CP1 == 所有的 Bit1 ^ Bit3 ^ Bit5 ^ Bit7
CP2 == 所有的 Bit0 ^ Bit1 ^ Bit4 ^ Bit5
CP3 == 所有的 Bit2 ^ Bit3 ^ Bit6 ^ Bit7
CP4 == 所有的 Bit0 ^ Bit1 ^ Bit2 ^ Bit3
CP5 == 所有的 Bit4 ^ Bit5 ^ Bit6 ^ Bit7
--> 每个列校验都是 1024 位异或的结果
--> 如果结果为1,说明有奇数个1位,结果如果为0,说明为偶数个1.
--> 列校验结果一共有上面6位
行校验如上图所示:
RP0 第0行以及每隔 1 行所有位的异或结果。
RP1 第1行以及每隔 1 行所有位的异或结果。
RP2,RP3 是间隔 2 行所有位的异或结果。
RP4,RP5 是间隔 4 行所有位的异或结果。
RP6,RP7 是间隔 8 行所有位的异或结果。
RP8,RP9 是间隔 16 行所有位的异或结果。
RP10,RP11 是间隔 32 行所有位的异或结果。
RP12,RP13 是间隔 64 行所有位的异或结果。
RP14,RP15 是间隔 128 行所有位的异或结果。
--> 这些结果都是8 * 128 = 1024 位的异或结果。
--> 如果结果为1,说明有奇数个1位,结果如果为0,说明为偶数个1.
--> 行校验一共有16位。
综上所示,8位宽的256字节数据生成6个bit的列校验数据,16个bit的行校验数据,总共22个bit,可得512字节的数据生成6个bit的列校验数据,18个行校验数据,总共24bit。同理,16位宽的256字节数据生成8个bit的列校验数据,14个bit的行校验数据,可得512字节的数据生成8个bit的列校验数据,16个bit的行校验数据。
ECC校验纠错
当往NAND Flash的page中写入数据的时候,每256字节我们生成一个ECC校验和,称之为原ECC校验和,保存起来。当从NAND Flash中读取数据的时候,每256字节我们生成一个ECC校验和,称之为新ECC校验和。将原ECC校验和与新ECC校验和按位异或,若结果为0,则表示不存在错误(或是出现了ECC无法检测的错误);若22个bit校验和结果中存在11个bit为1,表示存在一个bit数据错误,且可以纠正;若结果中只存在1个bit为1,表示出现了无法纠正的错误。因为每个字节中的每个bit至少会参加列校验总数(CP0…CPn)n/2次列校验计算和行校验总是(RP0…RPm)m/2次行校验校验。因此当256字节的数据校验和结果22个bit校验和结果中存在11个bit为1时,才会表示一个bit数据发送错误。
定位出错的bit位的方式为:先确定行地址(即哪个字节出错),再确定列地址(即该字节的哪一个bit位出错)。将原ECC校验和与新ECC校验和按位异或之后,得到新的“RP15,RP14…RP1,RP0,CP5,CP4…CP1,CP0”校验和结果。抽取RP15,RP13,RP11,RP9,RP7,RP5,RP3,RP1组成新的8bit数据,表示的值就是出错的字节的行地址(范围0~ 255)。抽取CP5,CP3,CP1组成新的3bit数据,表示的值就是出错的字节的列地址(范围0~ 7)。
如上图列地址为例:若CP5发生变化(异或后的CP5=1),则出错处肯定在 Bit 4 ~ Bit 7中;若CP5无变化(异或后的CP5=0),则出错处在 Bit 0 ~ Bit 3 中,这样就筛选掉了一半的Bit位。剩下的4个Bit位中,再看CP3是否发生变化,又选出2个Bit位。剩下的2Bit位中再看CP1是否发生变化,则最终可定位1个出错的Bit位。从异或结果中抽取CP5,CP3,CP1位,便可定位出错bit位的列地址。同理行校验抽取异或结果的RP15,RP13,RP11,RP9,RP7,RP5,RP3,RP1位便可定位出哪个Byte出错,再用CP5,CP3,CP1定位哪个Bit出错。
ECC校验示例:
assign CP0 = ecc_data[14] ^ ecc_data[12] ^ ecc_data[10] ^ ecc_data[8] ^ ecc_data[6] ^ ecc_data[4] ^ ecc_data[2] ^ ecc_data[0];
assign CP1 = ecc_data[15] ^ ecc_data[13] ^ ecc_data[11] ^ ecc_data[9] ^ ecc_data[7] ^ ecc_data[5] ^ ecc_data[3] ^ ecc_data[1];
assign CP2 = ecc_data[13] ^ ecc_data[12] ^ ecc_data[9] ^ ecc_data[8] ^ ecc_data[5] ^ ecc_data[4] ^ ecc_data[1] ^ ecc_data[0];
assign CP3 = ecc_data[15] ^ ecc_data[14] ^ ecc_data[11] ^ ecc_data[10] ^ ecc_data[7] ^ ecc_data[6] ^ ecc_data[3] ^ ecc_data[2];
assign CP4 = ecc_data[11] ^ ecc_data[10] ^ ecc_data[9] ^ ecc_data[8] ^ ecc_data[3] ^ ecc_data[2] ^ ecc_data[1] ^ ecc_data[0];
assign CP5 = ecc_data[15] ^ ecc_data[14] ^ ecc_data[13] ^ ecc_data[12] ^ ecc_data[7] ^ ecc_data[6] ^ ecc_data[5] ^ ecc_data[4];
assign CP6 = ecc_data[7] ^ ecc_data[6] ^ ecc_data[5] ^ ecc_data[4] ^ ecc_data[3] ^ ecc_data[2] ^ ecc_data[1] ^ ecc_data[0];
assign CP7 = ecc_data[15] ^ ecc_data[14] ^ ecc_data[13] ^ ecc_data[12] ^ ecc_data[11] ^ ecc_data[10] ^ ecc_data[9] ^ ecc_data[8];
assign RP = CP6 ^ CP7;
assign ecc_precalc = {CP7, CP6, CP5, CP4, CP3, CP2, CP1, CP0};
always @(posedge ahb3_hclk or negedge ahb3_hresetn)
begin : b_counter_eccps
if (!ahb3_hresetn) begin
/*AUTORESET*/
// Beginning of autoreset for uninitialized flops
counter_eccps <= 16'h0;
// End of automatics
end else begin
if (ecc_cnt_clear) begin
counter_eccps <= eccps;
end else if (ecc_cnt) begin
counter_eccps <= counter_eccps - 1;
end
end
end // block: b_counter_eccps
always @(posedge ahb3_hclk or negedge ahb3_hresetn)
begin : b_ecc_column
if (!ahb3_hresetn) begin
/*AUTORESET*/
// Beginning of autoreset for uninitialized flops
ecc_column <= 8'h0;
// End of automatics
end else begin
if (ecc_cnt_clear) begin
ecc_column <= 8'h0;
end else if (ecc_cnt) begin
ecc_column <= ecc_column ^ ecc_precalc;
end
end
end // block: b_ecc_column
always @(posedge ahb3_hclk or negedge ahb3_hresetn)
begin : b_ecc_raw1
if (!ahb3_hresetn) begin
/*AUTORESET*/
// Beginning of autoreset for uninitialized flops
ecc_raw1 <= 13'h0;
// End of automatics
end else begin
if (ecc_cnt_clear) begin
ecc_raw1 <= 13'h0;
end else if (ecc_cnt & RP) begin
ecc_raw1 <= ecc_raw1 ^ (eccps - counter_eccps);
end
end
end // block: b_ecc_raw1
always @(posedge ahb3_hclk or negedge ahb3_hresetn)
begin : b_ecc_raw2
if (!ahb3_hresetn) begin
/*AUTORESET*/
// Beginning of autoreset for uninitialized flops
ecc_raw2 <= 13'h0;
// End of automatics
end else begin
if (ecc_cnt_clear) begin
ecc_raw2 <= 13'h0;
end else if (ecc_cnt & RP) begin
ecc_raw2 <= ecc_raw2 ^ ~(eccps - counter_eccps);
end
end
end // block: b_ecc_raw2
ecc_data[15:0]为需要进行校验的数据,若外部nand flash的I/O宽度为16位,那么ecc_data[15:0]全部有效,如果外部nand flash的I/O宽度为8位,那么ecc_data[15:0]只有低8位有效。CPx为每个字节列校验值,RP为每个字节行校验的值。ecc_cnt代表每次ecc_data数据有效表示位,counter_eccps为数据传输计数,当counter_eccps==0时,数据全部传输完成。
b_ecc_column是计算列校验过程,ecc_precalc为每个字节内部计算CP7~CP0,每次ecc_data数据有效时,与上一次ecc_precalc进行异或计算。当最后一个字节传输结束时,ecc_column中bit7代表CP7,bit6代表CP6,bit5代表CP5,bit4代表CP4,bit3代表CP3,bit2代表CP2,bit1代表CP1,bit0代表CP0。
b_ecc_raw1和b_ecc_raw2是计算行校验过程,eccps代表要传输的数据字节总数,counter_eccps为递减计数,得(eccps-counter_eccps)为第n(n<eccps)次传输,(eccps-counter_eccps)称为行索引。通过行校验规律得到:将行号编成二进制后从右往左,如果第一位是0,例如行号0,2,4,…,252,254就列入RP0;如果第一位是1,例如行号1,3,5,…,253,255就列入RP1,如果第二位是0,例如行号0,1,4,6,…,252,253就列入RP2;如果第二位是1,例如行号2,3,6,7,…,254,255就列入RP3,以此类推。
行号 | 0 | 1 | 2 | 3 | … | 252 | 253 | 254 | 255 |
二进制 | 0000 | 0001 | 0010 | 0011 | … | 11111100 | 11111101 | 11111110 | 11111111 |
因此可以得到:
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的行;
...
ecc_cnt & RP作用为只判断每个字节RP为1的个数,和RP为0的字节没有关系。如果有偶数个1则,异或结果为0,如果有奇数个1则异或结果为1。因此b_ecc_raw1判断所有RP为1的行中,
属于RP1计算范围内的行有多少个由ecc_raw1的Bit 0指示,0表示有偶数个,1表示有奇数个;
属于RP3计算范围内的行有多少个由ecc_raw1的Bit 1指示,0表示有偶数个,1表示有奇数个;
属于RP5计算范围内的行有多少个由ecc_raw1的Bit 2指示,0表示有偶数个,1表示有奇数个;
属于RP7计算范围内的行有多少个由ecc_raw1的Bit 3指示,0表示有偶数个,1表示有奇数个;
属于RP9计算范围内的行有多少个由ecc_raw1的Bit 4指示,0表示有偶数个,1表示有奇数个;
属于RP11计算范围内的行有多少个由ecc_raw1的Bit 5指示,0表示有偶数个,1表示有奇数个;
属于RP13计算范围内的行有多少个由ecc_raw1的Bit 6指示,0表示有偶数个,1表示有奇数个;
属于RP15计算范围内的行有多少个由ecc_raw1的Bit 7指示,0表示有偶数个,1表示有奇数个;
...
b_ecc_raw2判断所有RP为1的行中,
属于RP0计算范围内的行有多少个由ecc_raw2的Bit 0指示,0表示有偶数个,1表示有奇数个;
属于RP2计算范围内的行有多少个由ecc_raw2的Bit 1指示,0表示有偶数个,1表示有奇数个;
属于RP4计算范围内的行有多少个由ecc_raw2的Bit 2指示,0表示有偶数个,1表示有奇数个;
属于RP6计算范围内的行有多少个由ecc_raw2的Bit 3指示,0表示有偶数个,1表示有奇数个;
属于RP8计算范围内的行有多少个由ecc_raw2的Bit 4指示,0表示有偶数个,1表示有奇数个;
属于RP10计算范围内的行有多少个由ecc_raw2的Bit 5指示,0表示有偶数个,1表示有奇数个;
属于RP12计算范围内的行有多少个由ecc_raw2的Bit 6指示,0表示有偶数个,1表示有奇数个;
属于RP14计算范围内的行有多少个由ecc_raw2的Bit 7指示,0表示有偶数个,1表示有奇数个;
...
31 | 30 | 29 | 28 | 27 | 26 | 25 | 24 | 23 | 22 | 21 | 20 | 19 | 18 | 17 | 16 |
RP25 | RP24 | RP23 | RP22 | RP21 | RP20 | RP19 | RP18 | RP17 | RP16 | RP15 | RP14 | RP13 | RP12 | RP11 | RP10 |
raw1[12] | raw2[12] | raw1[11] | raw2[11] | raw1[10] | raw2[10] | raw1[9] | raw2[9] | raw1[8] | raw2[8] | raw1[7] | raw1[7] | raw1[6] | raw2[6] | raw1[5] | raw2[5] |
15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
RP9 | RP8 | RP7 | RP6 | RP5 | RP4 | RP3 | RP2 | RP1 | RP0 | CP5 | CP4 | CP3 | CP2 | CP1 | CP0 |
raw1[4] | raw2[4] | raw1[3] | raw2[3] | raw1[2] | raw2[2] | raw1[1] | raw2[1] | raw1[0] | raw1[0] | column[5] | column[4] | column[3] | column[2] | column[1] | column[0] |
上表所示为当外部nand flash的I/O宽度为8bit时,eccr寄存器中校验位放置顺序,其中raw1为ecc_raw1,raw2为ecc_raw2,column为ecc_column。
31 | 30 | 29 | 28 | 27 | 26 | 25 | 24 | 23 | 22 | 21 | 20 | 19 | 18 | 17 | 16 |
RP23 | RP22 | RP21 | RP20 | RP19 | RP18 | RP17 | RP16 | RP15 | RP14 | RP13 | RP12 | RP11 | RP10 | RP9 | RP8 |
raw1[11] | raw2[11] | raw1[10] | raw2[10] | raw1[9] | raw2[9] | raw1[8] | raw2[8] | raw1[7] | raw2[7] | raw1[6] | raw1[6] | raw1[5] | raw2[5] | raw1[4] | raw2[4] |
15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
RP7 | RP6 | RP5 | RP4 | RP3 | RP2 | RP1 | RP0 | CP7 | CP6 | CP5 | CP4 | CP3 | CP2 | CP1 | CP0 |
raw1[3] | raw2[3] | raw1[2] | raw2[2] | raw1[1] | raw2[1] | raw1[0] | raw2[0] | column[7] | column[6] | column[5] | column[4] | column[3] | column[2] | column[1] | column[0] |
上表所示为当外部nand flash的I/O宽度为16bit时,eccr寄存器中校验位放置顺序,其中raw1为ecc_raw1,raw2为ecc_raw2,column为ecc_column。