产品的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的操作统一接口,根据实际情况这一步是最麻烦的,因为你要找到原系统中所有的读写操作,将其改为统一的接口,而且还不能带来新的问题。因为此步因系统而已,所以也没什么说的了,改好不要带来新的问题就可以了。
最后全面调试代码,按照新代码的待遇测试它,确保无问题。