• Nand_ECC_校验和纠错_详解


    word下载

    ECC的全称是Error Checking and Correction,是一种用于Nand的差错检测和修正算法。如果操作时序和电路稳定性不存在问题的话,NAND Flash出错的时候一般不会造成整个Block或是Page不能读取或是全部出错,而是整个Page(例如512Bytes)中只有一个或几个bit出错。ECC能纠正1个比特错误和检测2个比特错误,而且计算速度很快,但对1比特以上的错误无法纠正,对2比特以上的错误不保证能检测。
    校验码生成算法:ECC校验每次对256字节的数据进行操作,包含列校验和行校验。对每个待校验的Bit位求异或,若结果为0,则表明含有偶数个1;若结果为1,则表明含有奇数个1。列校验规则如表1所示。256字节数据形成256行、8列的矩阵,矩阵每个元素表示一个Bit位。
    clip_image002[1]
    其中CP0 ~ CP5 为六个Bit位,表示Column Parity(列极性),
    CP0
    为第0246列的极性,CP1为第1357列的极性,
    CP2
    为第0145列的极性,CP3为第2367列的极性,
    CP4
    为第0123列的极性,CP5为第4567列的极性。
    用公式表示就是:CP0=Bit0^Bit2^Bit4^Bit6表示第0列内部256Bit位异或之后再跟第2256Bit位异或,再跟第4列、第6列的每个Bit位异或,这样,CP0其实是256*4=1024Bit位异或的结果。CP1 ~ CP5 依此类推。
    行校验如下图所示
    clip_image004[2]
    其中RP0 ~ RP15 为十六个Bit位,表示Row Parity(行极性),
    RP0
    为第0246….252254 个字节的极性
    RP1-----1
    357……253255
    RP2----0
    14589…..252253 (处理2Byte,跳过2Byte

    RP3---- 2
    3671011…..254255 (跳过2Byte,处理2Byte
    RP4----
    处理4Byte,跳过4Byte
    RP5----
    跳过4Byte,处理4Byte
    RP6----
    处理8Byte,跳过8Byte
    RP7----
    跳过8Byte,处理8Byte

    RP8----
    处理16Byte,跳过16Byte
    RP9----
    跳过16Byte,处理16Byte

    RP10----
    处理32Byte,跳过32Byte
    RP11----
    跳过32Byte,处理32Byte

    RP12----
    处理64Byte,跳过64Byte
    RP13----
    跳过64Byte,处理64Byte

    RP14----
    处理128Byte,跳过128Byte
    RP15----
    跳过128Byte,处理128Byte

    可见,RP0 ~ RP15 每个Bit位都是128个字节(也就是128行)即128*8=1024Bit位求异或的结果。
    综上所述,对256字节的数据共生成了6Bit的列校验结果,16Bit的行校验结果,共22Bit。在Nand中使用3个字节存放校验结果,多余的两个Bit位置1。存放次序如下表所示:
    clip_image006[1]
    K9F1208为例,每个Page页包含512字节的数据区和16字节的OOB区。前256字节数据生成3字节ECC校验码,后256字节数据生成3字节ECC校验码,共6字节ECC校验码存放在OOB区中,存放的位置为OOB区的第012367字节。

    clip_image007[2]

    文件:

    MakeEccTable.rar

    大小:

    0KB

    下载:

    下载

    校验码生成算法的C语言实现
    Linux内核中ECC校验算法所在的文件为drivers/mtd/nand/nand_ecc.c,其实现有新、旧两种,在2.6.27及更早的内核中使用的程序,从2.6.28开始已经不再使用,而换成了效率更高的程序。可以在Documentation/mtd/nand_ecc.txt 文件中找到对新程序的详细介绍。
    首先分析一下2.6.27内核中的ECC实现,源代码见:
    http://lxr.linux.no/linux+v2.6.27/drivers/mtd/nand/nand_ecc.c


    43/*
    44 * Pre-calculated 256-way 1 byte column parity
    45 */
    46static const u_char

         nand_ecc_precalc_table[] = {
    47   0x00, 0x55, 0x56, 0x03, 0x59, 0x0c, 0x0f, 0x5a, 0x5a, 0x0f, 0x0c, 0x59, 0x03, 0x56, 0x55, 0x00,
    48   0x65, 0x30, 0x33, 0x66, 0x3c, 0x69, 0x6a, 0x3f, 0x3f, 0x6a, 0x69, 0x3c, 0x66, 0x33, 0x30, 0x65,
    49   0x66, 0x33, 0x30, 0x65, 0x3f, 0x6a, 0x69, 0x3c, 0x3c, 0x69, 0x6a, 0x3f, 0x65, 0x30, 0x33, 0x66,
    50   0x03, 0x56, 0x55, 0x00, 0x5a, 0x0f, 0x0c, 0x59, 0x59, 0x0c, 0x0f, 0x5a, 0x00, 0x55, 0x56, 0x03,
    51   0x69, 0x3c, 0x3f, 0x6a, 0x30, 0x65, 0x66, 0x33, 0x33, 0x66, 0x65, 0x30, 0x6a, 0x3f, 0x3c, 0x69,
    52   0x0c, 0x59, 0x5a, 0x0f, 0x55, 0x00, 0x03, 0x56, 0x56, 0x03, 0x00, 0x55, 0x0f, 0x5a, 0x59, 0x0c,
    53   0x0f, 0x5a, 0x59, 0x0c, 0x56, 0x03, 0x00, 0x55, 0x55, 0x00, 0x03, 0x56, 0x0c, 0x59, 0x5a, 0x0f,
    54   0x6a, 0x3f, 0x3c, 0x69, 0x33, 0x66, 0x65, 0x30, 0x30, 0x65, 0x66, 0x33, 0x69, 0x3c, 0x3f, 0x6a,
    55   0x6a, 0x3f, 0x3c, 0x69, 0x33, 0x66, 0x65, 0x30, 0x30, 0x65, 0x66, 0x33, 0x69, 0x3c, 0x3f, 0x6a,
    56   0x0f, 0x5a, 0x59, 0x0c, 0x56, 0x03, 0x00, 0x55, 0x55, 0x00, 0x03, 0x56, 0x0c, 0x59, 0x5a, 0x0f,
    57   0x0c, 0x59, 0x5a, 0x0f, 0x55, 0x00, 0x03, 0x56, 0x56, 0x03, 0x00, 0x55, 0x0f, 0x5a, 0x59, 0x0c,
    58   0x69, 0x3c, 0x3f, 0x6a, 0x30, 0x65, 0x66, 0x33, 0x33, 0x66, 0x65, 0x30, 0x6a, 0x3f, 0x3c, 0x69,
    59   0x03, 0x56, 0x55, 0x00, 0x5a, 0x0f, 0x0c, 0x59, 0x59, 0x0c, 0x0f, 0x5a, 0x00, 0x55, 0x56, 0x03,
    60   0x66, 0x33, 0x30, 0x65, 0x3f, 0x6a, 0x69, 0x3c, 0x3c, 0x69, 0x6a, 0x3f, 0x65, 0x30, 0x33, 0x66,
    61   0x65, 0x30, 0x33, 0x66, 0x3c, 0x69, 0x6a, 0x3f, 0x3f, 0x6a, 0x69, 0x3c, 0x66, 0x33, 0x30, 0x65,
    62   0x00, 0x55, 0x56, 0x03, 0x59, 0x0c, 0x0f, 0x5a, 0x5a, 0x0f, 0x0c, 0x59, 0x03, 0x56, 0x55, 0x00
    63 
    };
    为了加快计算速度,程序中使用了一个预先计算好的列极性表。这个表中每一个元素都是unsigned char类型,表示8位二进制数。
    表中8位二进制数每位的含义:
    clip_image009[1]
    这个表的意思是:对0~255256个数,计算并存储每个数的列校验值和行校验值,以数作数组下标。比如 nand_ecc_precalc_table[ 13 ]  存储13的列校验值和行校验值,13的二进制表示为 00001101CP0 = Bit0^Bit2^Bit4^Bit6 = 0
    CP1 = Bit1^Bit3^Bit5^Bit7 = 1

    CP2 = Bit0^Bit1^Bit4^Bit5 = 1;
    CP3 = Bit2^Bit3^Bit6^Bit7 = 0;
    CP4 = Bit0^Bit1^Bit2^Bit3 = 1;
    CP5 = Bit4^Bit5^Bit6^Bit7 = 0;
    其行极性RP = Bit0^Bit1^Bit2^Bit3^Bit4^Bit5^Bit6^Bit7 = 1
    nand_ecc_precalc_table[ 13 ] 处存储的值应该是 0101 0110,即0x56.
    注意,数组nand_ecc_precalc_table的下标其实是我们要校验的一个字节数据。

    理解了这个表的含义,也就很容易写个程序生成这个表了。程序见附件中的 MakeEccTable.c文件。
    有了这个表,对单字节数据dat,可以直接查表 nand_ecc_precalc_table[ dat ] 得到 dat的行校验值和列校验值。但是ECC实际要校验的是256字节的数据,需要进行256次查表,对得到的256个查表结果进行按位异或,最终结果的 Bit0 ~ Bit5 即是256字节数据的 CP0 ~ CP5.
    /* Build up column parity */
      81        for(i = 0; i < 256; i++) {
      82
                      /* Get CP0 - CP5 from table */
      83
                          idx = nand_ecc_precalc_table[*dat++];
      84
                          reg1 ^= (idx & 0x3f);
      85
      86            //
    这里省略了一些,后面会介绍
      91        }
    Reg1
    clip_image011[1]
    在这里,计算列极性的过程其实是先在一个字节数据的内部计算CP0 ~ CP5, 每个字节都计算完后再与其它字节的计算结果求异或。而表1中是先对一列Bit0求异或,再去异或一列Bit2这两种只是计算顺序不同,结果是一致的。因为异或运算的顺序是可交换的。
    行极性的计算要复杂一些。
    nand_ecc_precalc_table[]
    表中的 Bit6 已经保存了每个单字节数的行极性值。对于待校验的256字节数据,分别查表,如果其行极性为1,则记录该数据所在的行索引(也就是for循环的i值),这里的行索引是很重要的,因为RP0 ~ RP15 的计算都是跟行索引紧密相关的,如RP0只计算偶数行,RP1只计算奇数行,等等。
    /* Build up column parity */
      81        for(i = 0; i < 256; i++) {
      82      /* Get CP0 - CP5 from table */
      83      idx = nand_ecc_precalc_table[*dat++];
      84      reg1 ^= (idx & 0x3f);
      85
      86        /* All bit XOR = 1 ? */
      87                if (idx & 0x40) {
      88                           reg3 ^= (uint8_t) i;
      89                           reg2 ^= ~((uint8_t) i);
      90                }
      91        }
    这里的关键是理解第8889行。Reg3reg2都是unsigned char 型的变量,并都初始化为零。
    行索引(也就是for循环里的i)的取值范围为0~255,根据表2可以得出以下规律:
    RP0
    只计算行索引的Bit00的行,RP1只计算行索引的Bit01的行;
    RP2
    只计算行索引的Bit10的行,RP3只计算行索引的Bit11的行;
    RP4
    只计算行索引的Bit20的行,RP5只计算行索引的Bit21的行;
    RP6
    只计算行索引的Bit30的行,RP7只计算行索引的Bit31的行;
    RP8
    只计算行索引的Bit40的行,RP9只计算行索引的Bit41的行;
    RP10
    只计算行索引的Bit50的行,RP11只计算行索引的Bit51的行;
    RP12
    只计算行索引的Bit60的行,RP13只计算行索引的Bit61的行;
    RP14
    只计算行索引的Bit70的行,RP15只计算行索引的Bit71的行;


    已经知道,异或运算的作用是判断比特位为1的个数,跟比特位为0的个数没有关系。如果有偶数个1则异或的结果为0,如果有奇数个1则异或的结果为1
    那么,程序第88行,对所有行校验为1的行索引按位异或运算,作用便是:
    判断在所有行校验为1的行中,
    属于RP1计算范围内的行有多少个------reg3Bit 0指示,0表示有偶数个,1表示有奇数个;
    属于RP3计算范围内的行有多少个------reg3Bit 1指示,0表示有偶数个,1表示有奇数个;
    属于RP5计算范围内的行有多少个------reg3Bit 2指示,0表示有偶数个,1表示有奇数个;
    属于RP7计算范围内的行有多少个------reg3Bit 3指示,0表示有偶数个,1表示有奇数个;
    属于RP9计算范围内的行有多少个------reg3Bit 4指示,0表示有偶数个,1表示有奇数个;
    属于RP11计算范围内的行有多少个------reg3Bit 5指示,0表示有偶数个,1表示有奇数个;
    属于RP13计算范围内的行有多少个------reg3Bit 6指示,0表示有偶数个,1表示有奇数个;
    属于RP15计算范围内的行有多少个------reg3Bit 7指示,0表示有偶数个,1表示有奇数个;
    所以,reg3每个Bit位的作用如下表所示:
    Reg3
    clip_image013[1]
    89行,对所有行校验为1的行索引按位取反之后,再按位异或,作用就是判断比特位为0的个数。比如reg2Bit00表示:所有行校验为1的行中,行索引的Bit00的行有偶数个,也就是落在RP0计算范围内的行有偶数个。所以得到结论:
    在所有行校验为1的行中,
    属于RP0计算范围内的行有多少个------reg2Bit 0指示,0表示有偶数个,1表示有奇数个;
    属于RP2计算范围内的行有多少个------reg2Bit 1指示,0表示有偶数个,1表示有奇数个;
    属于RP4计算范围内的行有多少个------reg2Bit 2指示,0表示有偶数个,1表示有奇数个;
    属于RP6计算范围内的行有多少个------reg2Bit 3指示,0表示有偶数个,1表示有奇数个;
    属于RP8计算范围内的行有多少个------reg2Bit 4指示,0表示有偶数个,1表示有奇数个;
    属于RP10计算范围内的行有多少个------reg2Bit 5指示,0表示有偶数个,1表示有奇数个;
    属于RP12计算范围内的行有多少个------reg2Bit 6指示,0表示有偶数个,1表示有奇数个;
    属于RP14计算范围内的行有多少个------reg2Bit 7指示,0表示有偶数个,1表示有奇数个;
    所以,reg2每个Bit位的作用如下表所示:
    Reg2
    clip_image015[1]
    至此,只用了一个查找表和一个for循环,就把所有的校验位CP0 ~ CP5 RP0 ~ RP15全都计算出来了。下面的任务只是按照表3的格式,把这些比特位重新排列一下顺序而已。
    reg2reg3中抽取出 RP8~RP15放在tmp1中,抽取出RP0~RP7放在tmp2中,
    Reg1
    左移两位,低两位置1
    然后把tmp2, tmp1, reg1 放在 ECC码的三个字节中。
    程序中还有CONFIG_MTD_NAND_ECC_SMC又进行了一次取反操作,暂时还不知为何。


     ECC
    纠错算法


    当往NAND Flashpage中写入数据的时候,每256字节我们生成一个ECC校验和,称之为原ECC校验和,保存到PAGEOOBout-of-band)数据区中。当从NAND Flash中读取数据的时候,每256字节我们生成一个ECC校验和,称之为新ECC校验和。
    将从OOB区中读出的原ECC校验和新ECC校验和按位异或,若结果为0,则表示不存在错(或是出现了 ECC无法检测的错误);若3个字节异或结果中存在11个比特位为1,表示存在一个比特错误,且可纠正;若3个字节异或结果中只存在1个比特位为1,表示 OOB区出错;其他情况均表示出现了无法纠正的错误。
    假设ecc_code_raw[3] 保存原始的ECC校验码,ecc_code_new[3] 保存新计算出的ECC校验码,其格式如下表所示:
    clip_image017[1]
    ecc_code_raw[3] ecc_code_new[3] 按位异或,得到的结果三个字节分别保存在s0,s1,s2中,如果s0s1s2中共有11Bit位为1,则表示出现了一个比特位错误,可以修正。定位出错的比特位的方法是,先确定行地址(即哪个字节出错),再确定列地址(即该字节中的哪一个Bit位出错)。
    确定行地址的方法是,设行地址为unsigned char byteoffs,抽取s1中的Bit7,Bit5,Bit3,Bit1,作为 byteoffs的高四位,抽取s0中的Bit7,Bit5,Bit3,Bit1 作为byteoffs的低四位,byteoffs的值就表示出错字节的行地址(范围为0 ~ 255)。
    确定列地址的方法是:抽取s2中的Bit7,Bit5,Bit3 作为 bitnum 的低三位,bitnum其余位置0,则bitnum的表示出错Bit位的列地址(范围为0 ~ 7)。
    下面以一个简单的例子探索一下这其中的奥妙。
    假设待校验的数据为两个字节,0x45(二进制为0100 0101)和0x38(二进制为0011 1000),其行列校验码如下表所示:
    clip_image019[1]
    从表中可以计算出CP5 ~ CP0的值,列在下表的第一行(原始数据)。假设现在有一个数据位发生变化,0x38变为0x3A,也就是Byte
    1
    Bit 10变成了1,计算得到新的CP5 ~ CP0值放在下表第2行(变化后数据)。新旧校验码求异或的结果放在下表第三行。

    可见,当 Bit
    1
    发生变化时,列校验值中只有CP1CP2CP4发生了变化,而CP0CP3CP5没变化,也就是说6Bit校验码有一半发生变化,则求异或的结果中有一半为1。同理,行校验求异或的结果也有一半为1。这就是为什么前面说256字节数据中的一个Bit位发生变化时,新旧22Bit校验码求异或的结果中会有11Bit 位为1

    clip_image021[1]
    再来看怎么定位出错的Bit位。以列地址为例,若CP5发生变化(异或后的CP5=1),则出错处肯定在 Bit 4 ~ Bit 7中;若CP5无变化(异或后的CP5=0,则出错处在 Bit 0 ~ Bit 3 中,这样就筛选掉了一半的Bit位。剩下的4Bit位中,再看CP3是否发生变化,又选出2Bit位。剩下的2Bit位中再看CP1是否发生变化,则最终可定位1个出错的Bit位。下面的树形结构更清晰地展示了这个判决过程:
    clip_image023[1]

    图表出错Bit列地址定位的判决树

    注意:图中的CP指的是求异或之后的结果中的CP
    为什么只用CP4CP2CP0呢?其实这里面包含冗余信息,因为CP5=1则必有CP4=0CP5=0则必有CP4=1,也就是CP5CP4一定相反,同理,CP3CP2一定相反,CP1CP0一定相反。所以只需要用一半就行了。

    这样,我们从异或结果中抽取出CP5CP3CP1位,便可定位出错Bit位的列地址。比如上面的例子中CP5/CP3/CP1 = 001,表示Bit 1出错。
    同理,行校验RP1发生变化,抽取RP1,可知Byte 1发生变化。这样定位出Byte 1Bit 0出错。
    当数据位256字节时,行校验使用RP0 ~ RP15,抽取异或结果的RP15RP13RP11RP9RP7RP5RP3RP1位便可定位出哪个Byte出错,再用CP5,CP3,CP1定位哪个Bit出错。

    clip_image007[3]

    文件:

    TestEcc.rar

    大小:

    2KB

    下载:

    下载

    原文地址 http://linux.chinaunix.net/bbs/viewthread.php?tid=1116253&extra=page%3D1

    终于基本看懂了。。。。
    下面解释一下,也许可以给和我曾经一样迷茫的人一点帮助:
    对于这个,别人总结出来的规则:

    RP0
    只计算行索引的Bit00的行,RP1只计算行索引的Bit01的行;
    RP2
    只计算行索引的Bit10的行,RP3只计算行索引的Bit11的行;
    RP4
    只计算行索引的Bit20的行,RP5只计算行索引的Bit21的行;
    RP6
    只计算行索引的Bit30的行,RP7只计算行索引的Bit31的行;
    RP8
    只计算行索引的Bit40的行,RP9只计算行索引的Bit41的行;
    RP10
    只计算行索引的Bit50的行,RP11只计算行索引的Bit51的行;
    RP12
    只计算行索引的Bit60的行,RP13只计算行索引的Bit61的行;
    RP14
    只计算行索引的Bit70的行,RP15只计算行索引的Bit71的行;
    在接下来的描述中,称为行与位的对应关系
    另注:
    1.
    上述规则中的RP意思是Row Parity,更多的叫法叫做LPLine Parity)。为了解释更容易看懂,依旧采用RP的说法。
    2.
    对于第几行,采用Line的说法,比如第1行,其实就是行号为0Line0.
    3.
    对于行的奇偶性,此处采用Line Parity的说法。

    Line5Line Parity1的时候,
    首先最简单的理解,也是最直接的理解,那就是,要把所有RP0~RP14中,对应包含着此行的那些最后要计算的值找出来,
    我们可以先手动地根据下图:
    clip_image004[3]
    一点点,掰手指头,慢慢地写出来,那就是:
    RP1
    RP2RP5RP6RP8RP10RP12RP14
    换句话说,如果Line5Line Parity1的时候,

    我们应该要计算RP1RP2RP5RP6RP8RP10RP12RP14
    关于这点,我想大家没有什么好疑问的吧,因为这就是按照其规则的最简单,最通俗的理解。
    所以,不论你用什么复杂的算法,反正是要记录并且计算这些RP的值,以便和后面的值进行计算。
    但是,程序在此处,并没有将这些RP找出来,而只是直接对行号进行XOR异或:
    reg3 ^= (uint8_t) i;
    表面上看,这和我们要找出来,并计算的那些RP,并没啥关系,这也是我开始很困惑的问题。
    按理来说,应该是找出那些行号,然后计算对应的RP的值,并保存,这样才对。
    而此处之所以可以这么处理,主要是有以下原因:
    1.       
    行与位的有如下对应关系:
    RP0
    只计算行索引的Bit00的行,RP1只计算行索引的Bit01的行;
    RP2
    只计算行索引的Bit10的行,RP3只计算行索引的Bit11的行;
    RP4
    只计算行索引的Bit20的行,RP5只计算行索引的Bit21的行;
    RP6
    只计算行索引的Bit30的行,RP7只计算行索引的Bit31的行;
    RP8
    只计算行索引的Bit40的行,RP9只计算行索引的Bit41的行;
    RP10
    只计算行索引的Bit50的行,RP11只计算行索引的Bit51的行;
    RP12
    只计算行索引的Bit60的行,RP13只计算行索引的Bit61的行;
    RP14
    只计算行索引的Bit70的行,RP15只计算行索引的Bit71的行;
    2.       
    某一行号的二进制分解的对应bit,对应了所要计算的RP
    比如是第6行,也就是Line55的二进制是:

    Bit7

    Bit6

    Bit5

    Bit4

    Bit3

    Bit2

    Bit1

    Bit0

    0

    0

    0

    0

    0

    1

    0

    1


    5
    的二进制值
    而根据上面别人分析出来的,行与位的对应关系,我们可以找出,此二进制的每一位所对应了哪些RP
    bit
    1的位,分别是02,对应代表的是RP1RP5
    bit
    0的位,分别是134567,对应代表的是RP2RP6RP8RP10RP12
    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
    的二进制值和二进制对应的行
    上表中,比如bit21,而别人说了“RP5只计算行索引的Bit21的行
    所以,此处如果bit21,对应着RP5将要被计算,
    那么我们可以肯定地得出来的是,
    如果此行,Line5,的Line Parity1的话,RP5是要被计算的。
    而仔细观察就会发现,RP5,就包含在我们上面自己手动找出来的那些LP中:
    RP1
    RP2RP5RP6RP8RP10RP12RP14
    而,剩下的bit位,也依次对应着这些LP。比如bit01,对应
    RP1.
    这就是我们上面说的某一行号的二进制分解的对应bit,对应了所要计算的
    RP”
    也是理解如此处理的关键点之一。

    同样地,除了bit1bit0bit2,对应的RP1RP5之外,
    剩下的几个bit对应的RP2RP6RP8RP10RP12RP14,由于对应位是0,所以,即使拿过来抑或,也还是0,无法记住这些bit的值,所以,采用将其取反,这样,对应这些为0bit,就变成1了,就可以记住这些对应的bit了:
    reg2 ^= ~((uint8_t) i);
    这样,当从0255检测的过程中,如果发现某行的Line Parity1
    那么就将其行号数值进行抑或,以存储奇数的LP,将行号取反,以保存偶数的LP
    也就是:
    Reg3
    对应的就是RP1RP3RP5,。。。,RP15
    Reg2
    对应的就是RP0RP2RP4,。。。,
    RP14
    然后再调用函数nand_trans_result(reg2, reg3, ecc_code);去将reg3reg2中存储的信息,

    重新组织到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), hoakee@gmail.com
    
    * 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.
    ", 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("
    Raw ECC Code: ");
    
    
    for( i=0; i< 3; i++)
    
    {
    
    printf("0x%02X ", ecc_code_raw[i] );
    
    }
    
    
    printf("
    New ECC Code: ");
    
    
    for( i=0; i< 3; i++)
    
    {
    
    printf("0x%02X ", ecc_code_new[i] );
    
    }
    
    
    printf("
    ");
    
    
    nand_correct_data( ecc_code_raw, ecc_code_new );
    
    
    printf("
    ");
    
    
    }
  • 相关阅读:
    dubbox 入门demo
    manjaro 安装后的基本配置
    ajax传递参数与controller接收参数映射关系
    如何等待ajax完成再执行相应操作
    Java之取余操作 "%"
    javascript基本属性访问对象的属性和方法
    jQuery之过滤选择器
    Spring Framework
    大佬帮忙看一下
    Python笔记
  • 原文地址:https://www.cnblogs.com/pengdonglin137/p/3438001.html
Copyright © 2020-2023  润新知