产品的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 : %xn",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 %xn",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%xn",calc_ecc[0],calc_ecc[1],calc_ecc[2]);
? ? //printf("read_ecc : 0x%x 0x%x 0x%xn",read_ecc[0],read_ecc[1],read_ecc[2]);
? ? printf(" %2x %2x %2x,?",s0,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 : %dn",(data - pVirtualAddress) / 256);
? ? ? ? printf("calc_ecc : 0x%x 0x%x 0x%xn",calc_ecc[2]);
? ? ? ? printf("read_ecc : 0x%x 0x%x 0x%xn",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的操作统一接口,根据实际情况这一步是最麻烦的,因为你要找到原系统中所有的读写操作,将其改为统一的接口,而且还不能带来新的问题。因为此步因系统而已,所以也没什么说的了,改好不要带来新的问题就可以了。
? ?最后全面调试代码,按照新代码的待遇测试它,确保无问题。