为SRAM添加校验

来源:互联网 发布:企业样本设计制作软件 编辑:程序博客网 时间:2024/05/21 17:29

产品的SRAM中的数据出现莫名奇妙的乱,碰到过不少。一直未确定是何种原因引起,其中一个原因是由于备用电池没电,电压过低引起的数据丢失,堵住这个原因后,后续仍有零零星星的问题出现,一直未有一个有效的方法可以解决。

       终于忍受不了,准备大动一下程序,给SRAM的数据增加纠错码,如同NAND FLASH的校验,至少可以检测到是否为人为造成的数据紊乱,如果非人为造成数据紊乱,程序会给出提示数据出现了错误。使用的是128K-WORD by 16bit的SRAM。由于SRAM空间比较紧张,所以只就抠出了1K WORD的空间用于放置冗余位(校验码)。

       研究了一下nand flash的ECC校验算法,最终确定按照256word/page将整个SRAM空间分成512个页面,每个页面三个校验字节,所有页面的校验字节加起来大小为1.5KBYTE。方案可行,付诸实施。

       首先,改算法。由于nand flash的ECC算法大都采用256byte/page进行校验,所以根据我们采用的256wrod/page的实际情况,必须要修改算法。对于原ECC校验算法,由于网上有很多,这里就不在赘述,仅贴上我改过的算法代码。

#include "sramecc.h"

static inline int countbits(INT16U byte)
{
int res = 0;


for (;byte; byte >>= 1)
res += byte & 0x01;
return res;
}


static INT32S sram_calculate_ecc(const INT16U *data, INT8U *ecc_code, INT16U ecc_code_num)
{
    INT16U i, idx;
    INT8U  reg1, reg2, reg3, tmp_ecc0, tmp_ecc1;


    reg1 = 0;
    reg2 = 0;
    reg3 = 0;
    tmp_ecc0 = 0;
    tmp_ecc1 = 0;
 
    for (i = 0; i < 256; i++)
    {
        //Get CP0 ~ CP7 from table
        if (i < ecc_code_num)
            idx = ecc_precalc_table[*data++];
        else
            idx = ecc_precalc_table[0];
        
        reg1 ^= (idx & 0xff); 


        //Row bit xor is 1
        if (idx & 0x100)
        {
            reg3 ^= i;
            reg2 ^= ~i;
        }
    }
    //printf("reg2 : %x reg3 : %x\n", reg2, reg3);
    //---------------------------------------------------------
    //|reg2| RP14 | RP12 | RP10 | RP8 | RP6 | RP4 | RP2 | RP0 |
    //|--------------------------------------------------------
    //|reg3| RP15 | RP13 | RP11 | RP9 | RP7 | RP5 | RP3 | RP1 |
    //---------------------------------------------------------


    //adjust 
    tmp_ecc1 |= (reg3 & 0x80) >> 0;  // RP15
    tmp_ecc1 |= (reg2 & 0x80) >> 1;  // RP14
    tmp_ecc1 |= (reg3 & 0x40) >> 1;  // RP13
    tmp_ecc1 |= (reg2 & 0x40) >> 2;  // RP12
    tmp_ecc1 |= (reg3 & 0x20) >> 2;  // RP11
    tmp_ecc1 |= (reg2 & 0x20) >> 3;  // RP10
    tmp_ecc1 |= (reg3 & 0x10) >> 3;  // RP9
    tmp_ecc1 |= (reg2 & 0x10) >> 4;  // RP8


    tmp_ecc0 |= (reg3 & 0x08) << 4;  // RP7
    tmp_ecc0 |= (reg2 & 0x08) << 3;  // RP6
    tmp_ecc0 |= (reg3 & 0x04) << 3;  // RP5
    tmp_ecc0 |= (reg2 & 0x04) << 2;  // RP4
    tmp_ecc0 |= (reg3 & 0x02) << 2;  // RP3
    tmp_ecc0 |= (reg2 & 0x02) << 1;  // RP2
    tmp_ecc0 |= (reg3 & 0x01) << 1;  // RP1
    tmp_ecc0 |= (reg3 & 0x01) << 0;  // RP0


    ecc_code[0] = tmp_ecc0;
    ecc_code[1] = tmp_ecc1;
    ecc_code[2] = reg1;
    //printf("tmp_ecc0 : %x %x %x\n", tmp_ecc0, tmp_ecc1, reg1);
    return 0;
}


static INT32S sram_correct_data(INT16U *data, INT8U *read_ecc, INT8U *calc_ecc)
{
    INT8U s0, s1, s2;

    s0 = calc_ecc[0] ^ read_ecc[0];
    s1 = calc_ecc[1] ^ read_ecc[1];
    s2 = calc_ecc[2] ^ read_ecc[2];
#ifdef ECC_DEBUG       
    //printf("sram_correct_data!\n");
    //printf("calc_ecc : 0x%x 0x%x 0x%x\n", calc_ecc[0], calc_ecc[1], calc_ecc[2]);
    //printf("read_ecc : 0x%x 0x%x 0x%x\n", read_ecc[0], read_ecc[1], read_ecc[2]);
    printf(" %2x %2x %2x,  ", s0, s1, s2);
#endif
    if ((s0 | s1 | s2) == 0)
    {
        return 0;
    }


    if (((s0 ^ (s0 >> 1)) & 0x55) == 0x54 &&
   ((s1 ^ (s1 >> 1)) & 0x55) == 0x55 &&
   ((s2 ^ (s2 >> 1)) & 0x55) == 0x55)
    {
        INT32U byteoffs = 0, bitnum = 0;
byteoffs |= (s1 << 0) & 0x80;  //RP15
byteoffs |= (s1 << 1) & 0x40;  //RP13
byteoffs |= (s1 << 2) & 0x20;  //RP11
byteoffs |= (s1 << 3) & 0x10;  //RP9


byteoffs |= (s0 >> 4) & 0x08;  //RP7
byteoffs |= (s0 >> 3) & 0x04;  //RP5
byteoffs |= (s0 >> 2) & 0x02;  //RP3
byteoffs |= (s0 >> 1) & 0x01;  //RP1


        //The value of bitnum is the bit which error
        bitnum   |= (s2 >> 4) & 0x08;
        bitnum   |= (s2 >> 3) & 0x04;
        bitnum   |= (s2 >> 2) & 0x02;
        bitnum   |= (s2 >> 1) & 0x01;


        //correct data
        data[byteoffs] ^= (1 << bitnum);
        
#ifdef ECC_DEBUG   
        printf("data[%2x] = %4x %x,  ", byteoffs, data[byteoffs], bitnum);
#endif
        return 1;
    }
    else if (countbits(s0 | ((INT16U)s1 << 8) | ((INT16U)s2 <<16)) == 1)
    {
        return 1;
    }
    else
    {
        printf("page : %d\n", (data - pVirtualAddress) / 256);
        printf("calc_ecc : 0x%x 0x%x 0x%x\n", calc_ecc[0], calc_ecc[1], calc_ecc[2]);
        printf("read_ecc : 0x%x 0x%x 0x%x\n", read_ecc[0], read_ecc[1], read_ecc[2]);
        return 2;
    }
}

    与256byte/page的算法相比,改动的地方并不多,不过说实话,就这仅有不多的改动,花费了我整整一天的时间,不全面弄清楚原算法不敢动刀啊~  sramecc.h存放了ecc_precalc_table,该表式预先计算好的65536个校验码,其中低8位为列校验位,第8位为行校验位,数据很多,知晓算法就可以生成,这里便不列出了,下面是其生成算法的代码:

#define     BIT0(X)     (((X) & 0X0001) >> 0)
#define     BIT1(x)     (((x) & 0x0002) >> 1)
#define     BIT2(x)     (((x) & 0x0004) >> 2)
#define     BIT3(x)     (((x) & 0x0008) >> 3)
#define     BIT4(x)     (((x) & 0x0010) >> 4)
#define     BIT5(x)     (((x) & 0x0020) >> 5)
#define     BIT6(x)     (((x) & 0x0040) >> 6)
#define     BIT7(x)     (((x) & 0x0080) >> 7)
#define     BIT8(x)     (((x) & 0x0100) >> 8)
#define     BIT9(x)     (((x) & 0x0200) >> 9)
#define     BIT10(x)    (((x) & 0x0400) >> 10)
#define     BIT11(x)    (((x) & 0x0800) >> 11)
#define     BIT12(x)    (((x) & 0x1000) >> 12)
#define     BIT13(x)    (((x) & 0x2000) >> 13)
#define     BIT14(x)    (((x) & 0x4000) >> 14)
#define     BIT15(x)    (((x) & 0x8000) >> 15)

#include <stdio.h>

int main(void)
{
int i, j = 0;
unsigned short xByte;
for (i = 0; i < 65536; i++)
{
xByte = 0;
if (BIT0(i) ^ BIT2(i) ^ BIT4(i) ^ BIT6(i) ^ BIT8(i) ^ BIT10(i) ^ BIT12(i) ^ BIT14(i))
xByte |= 0x0001;
if (BIT1(i) ^ BIT3(i) ^ BIT5(i) ^ BIT7(i) ^ BIT9(i) ^ BIT11(i) ^ BIT13(i) ^ BIT15(i)) 
xByte |= 0x0002;
 if (BIT0(i) ^ BIT1(i) ^ BIT4(i) ^ BIT5(i) ^ BIT8(i) ^ BIT9(i) ^ BIT12(i) ^ BIT13(i))
  xByte |= 0x0004;
 if (BIT2(i) ^ BIT3(i) ^ BIT6(i) ^ BIT7(i) ^ BIT10(i) ^ BIT11(i) ^ BIT14(i) ^ BIT15(i))
  xByte |= 0x0008;
 
 if (BIT0(i) ^ BIT1(i) ^ BIT2(i) ^ BIT3(i) ^ BIT8(i) ^ BIT9(i) ^ BIT10(i) ^ BIT11(i))
  xByte |= 0x0010;
 if (BIT4(i) ^ BIT5(i) ^ BIT6(i) ^ BIT7(i) ^ BIT12(i) ^ BIT13(i) ^ BIT14(i) ^ BIT15(i))
  xByte |= 0x0020;
 if (BIT0(i) ^ BIT1(i) ^ BIT2(i) ^ BIT3(i) ^ BIT4(i) ^ BIT5(i) ^ BIT6(i) ^ BIT7(i))
  xByte |= 0x0040;
 if (BIT8(i) ^ BIT9(i) ^ BIT10(i) ^ BIT11(i) ^ BIT12(i) ^ BIT13(i) ^ BIT14(i) ^ BIT15(i))
  xByte |= 0x0080;
 
 if (BIT0(i) ^ BIT1(i) ^ BIT2(i) ^ BIT3(i) ^ BIT4(i) ^ BIT5(i) ^ BIT6(i) ^ BIT7(i) ^ BIT8(i) ^ BIT9(i) ^ BIT10(i) ^ BIT11(i) ^ BIT12(i) ^ BIT13(i) ^ BIT14(i) ^ BIT15(i))
  xByte |= 0x0100;
 
 if (j == 31)
 {
  printf("0x%03x,\n", xByte);
  j = 0;
 }
 else
 {
  printf("0x%03x, ", xByte);
  j++;
 }
 
}
return 0;
}

    到此为止,算法工作已经完成。只需要在需要的地方调用相关函数就可以了。

    其次,改接口。这一步又分为两小步,①写接口代码。须将上面的算法函数嵌入到接口代码里,并实现每个页面的ECC码的存取位置的算法。如果是多线程的,那么还需要对每个页面都要加上互斥锁。②统一系统中的接口。将系统中对SRAM的操作统一接口,根据实际情况这一步是最麻烦的,因为你要找到原系统中所有的读写操作,将其改为统一的接口,而且还不能带来新的问题。因为此步因系统而已,所以也没什么说的了,改好不要带来新的问题就可以了。

   最后全面调试代码,按照新代码的待遇测试它,确保无问题。

原创粉丝点击