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.\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");


}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM