uboot 中内存测试,内存检测方法

来源:互联网 发布:html5 php实现微博 编辑:程序博客网 时间:2024/05/17 05:56

DDR内存子系统常见硬件错误及Uboot中检测流程

在 U-Boot中,Denx(U-Boot的开发商)针对常见的DDR内存故障进行了严格的检测处理,下图描述了该检测处理过程的三个步骤:检测数据线、地址线和DDR物理存储部件,主要涉及这三个步骤的处理过程和方法,对于DDR子系统,是很容易出故障并且是很难debug检测出来的,而Denx所针对 DDR内存故障设计的检测方法是非常严谨,值得学习研究的。

下面主要是相关的检测处理思路及问题:

1、为什么先检测数据线?

因为如果数据线是断开的,那么一切无从谈起!接下来是检测地址线,只有数据线和地址线都通过,检测内存的存储单元才有意义,这样的流程也利于分割定位问题。上面testing sequence框图将整个检测过程分成三大步,用三个虚线方框表示。

2、数据线的连接错误

数据线的连接可能存在两种错误,一种是被断开,另一种布线或生产造成互相短路。

3、如何检测数据线的连接错误

Denx 设计的数据线检测算法还是很Tricky和精秒的,整个处理流程如下例子:如果是两根数据线,只需要写入并读出一个pattern=0b01(0b开头表示二进制数)就能判断它们是否短路或断开。很明显,大部分的嵌入式平台不止两根数据线,我们以64位地址线为例,pattern = 0b101010101010101010.... 能检测出奇偶位之间的数据错误。如果这个错误被排除,每两根数据线组成一组(这是理解下一个pattern的关键),再用相同的办法,检测每相邻两组之间是否有短路,就得到第二个pattern,就是 0b110011001100...... 依次类推,以4根数据线为一组,8根线为一组,相继得到共6个pattern,分别是 0xaaaaaaaaaaaaaaaa,0xcccccccccccccccc,0xf0f0f0f0f0f0f0f0,0xff00ff00ff00ff00,0xffff0000ffff0000,0xffffffff00000000。只要相继写入并读出这6个pattern就能验证是否存在数据线交叉短路错误。

4、如何检测数据线与板上其它信号线交叉短路或断路

取以上6个paatern的反码,总共12个pattern就能检测到每一位都可以写入和读出0和1。

5、什么是floating buses错误

floating buses会“欺骗”测试软件,如果测试软件写入并很快读出一个值的时候,写操作会跟数据线上的电容充电,总线会短暂的保持它的状态。当测试软件读操作时,总线会返回刚写入的值,即使实际上该数据线是断路的。

6、如何检测数据线的floating buses错误

检测floating buses错误的算法不复杂,在写入和读回之间再插入一次对不同地址写入不同值的操作。例如,X写入X1位置,Y写入Y1位置,再从X1位置读出X值则表示floating buses错误不存在。

7、地址线的错误

如果地址线存在错误,其症状是地址空间中的两个不同位置被映射到同一物理存储位置。更通俗地讲,就是写一个位置却“改变”了另一个位置。

8、地址线的错误检测

地址线的错误检测相对简单,其算法是:

1)、将地址的值作为内容写入该地址处,汇编的表示方法是 (addr) = addr。即将地址值写到地址对应的空间里,这样确保每一个位置的内容不同。

2)、依次将内存基地址的某一根地址线的值翻转(flip/toggle)得到某个地址,从该地址取值,如果该值和基地址的值相等,则表示某一位地址线有问题。

这个算法的特点是每次只检测一根地址线,方法简单有效。

9、存储单元的错误

以上数据线和地址线的检测都是检测布线或工厂生产的错误,而存储单元的检测则是真正对DDR内存芯片的检测。内存芯片的常见错误是bit-stuck,简而言之,就是让它是0,它偏为1,让它为1,它偏为0,检测方法也很简单,就是用不同的pattern去写尽可能所有的地址并读回比较。有一些常用的 pattern如0x5555, 0xAAAA等。

10、几个简单的检测DDR故障的方法

上面的DDR检测算法,虽然全面,但是耗时比较长,常常需要好几个小时,在Uboot命令行下也有几个简单的命令可以检测常见内存故障,如下所示:

1)、mtest addr lenth pattern

这个命令需要注意,DDR在Uboot启动后被映射到了0地址,但是uboot的代码和堆、栈空间0x10000000处开始,这些空间是不能被刷的,否则就挂死了。

2)、复制NOR flash的内容到内存中,如 cp.b 0x20080000 0x7fc0 20000,然后比较 cmp.b 0x20080000 0x7fc0 20000。

3)、下载kernel image到内存中,copy NOR flash 或tftp都行,然后调用iminfo LOAD_ADDR 检测CRC错误。

第一种方法是用特定的pattern去刷DDR的空闲空间,第二种和第三种方法可以说Pattern的随机性更大一些。

当然最彻底的检测方法当然是长时间跑Linux系统,上面的方法更适用于系统不稳定时定位错误。

内存检测方法
2011年07月14日 星期四 20:59

内存检测方法

针对常见的DDR内存故障进行了严格的检测处理,下图描述了该检测处理过程的三个步骤:检测数据线、地址线和DDR物理存储部件,主要涉及这三个步骤的处理过程和方法。

下面主要是相关的检测处理思路及问题:

1、为什么先检测数据线?

因为如果数据线是断开的,那么一切无从谈起!接下来是检测地址线,只有数据线和地址线都通过,检测内存的存储单元才有意义,这样的流程也利于分割定位问题。上面testing sequence框图将整个检测过程分成三大步,用三个虚线方框表示。

2、数据线的连接错误

数据线的连接可能存在两种错误,一种是被断开,另一种布线或生产造成互相短路。

3、如何检测数据线的连接错误

Denx 设计的数据线检测算法还是很Tricky和精秒的,整个处理流程如下例子:如果是两根数据线,只需要写入并读出一个pattern=0b01(0b开头表示二进制数)就能判断它们是否短路或断开。很明显,大部分的嵌入式平台不止两根数据线,我们以64位地址线为例,pattern= 0b101010101010101010.... 能检测出奇偶位之间的数据错误。如果这个错误被排除,每两根数据线组成一组(这是理解下一个pattern的关键),再用相同的办法,检测每相邻两组之间是否有短路,就得到第二个pattern,就是0b110011001100...... 依次类推,以4根数据线为一组,8根线为一组,相继得到共6个pattern,分别是0xaaaaaaaaaaaaaaaa,0xcccccccccccccccc,0xf0f0f0f0f0f0f0f0,0xff00ff00ff00ff00,0xffff0000ffff0000,0xffffffff00000000。只要相继写入并读出这6个pattern就能验证是否存在数据线交叉短路错误。

4、如何检测数据线与板上其它信号线交叉短路或断路

取以上6个pattern的反码,总共12个pattern就能检测到每一位都可以写入和读出0和1。

5、什么是floating buses错误

floating buses会“欺骗”测试软件,如果测试软件写入并很快读出一个值的时候,写操作会对数据线上的电容充电,总线会短暂的保持它的状态。当测试软件进行读操作时,总线会返回刚写入的值,即使实际上该数据线是断路的。

6、如何检测数据线的floating buses错误

检测floating buses错误的算法不复杂,在写入和读回之间再插入一次对不同地址写入不同值的操作。例如,a写入A位置,b写入B位置,再从A位置读出a值则表示floating buses错误不存在。

7、地址线的错误

如果地址线存在错误,其症状是地址空间中的两个不同位置被映射到同一物理存储位置。更通俗地讲,就是写一个位置却“改变”了另一个位置。

8、地址线的错误检测

地址线的错误检测相对简单,其算法是:

1)将地址的值作为内容写入该地址处,汇编的表示方法是*addr = addr。即将地址值写到地址对应的空间里,这样确保每一个位置的内容不同。

2)依次将内存基地址的某一根地址线的值翻转(flip/toggle)得到某个地址,从该地址取值,如果该值和基地址的值相等,则表示某一位地址线有问题。

这个算法的特点是每次只检测一根地址线,方法简单有效。

9、存储单元的错误

以上数据线和地址线的检测都是检测布线或工厂生产的错误,而存储单元的检测则是真正对DDR内存芯片的检测。内存芯片的常见错误是bit-stuck,简而言之,就是让它是0,它偏为1,让它为1,它偏为0,检测方法也很简单,就是用不同的pattern去写尽可能所有的地址并读回比较。有一些常用的pattern如0x5555,0xAAAA等。

10、几个简单的检测DDR故障的方法

上面的DDR检测算法,虽然全面,但是耗时比较长,常常需要好几个小时,在Uboot命令行下也有几个简单的命令可以检测常见内存故障,如下所示:

1)mtest addr lenth pattern

这个命令需要注意,DDR在Uboot启动后被映射到了0地址,但是uboot的代码和堆、栈空间0x10000000处开始,这些空间是不能被刷的,否则就挂死了。

2)复制NOR flash的内容到内存中,如cp.b 0x20080000 0x7fc0 20000,然后比较cmp.b 0x20080000 0x7fc0 20000。

3)下载kernel image到内存中,copy NOR flash 或tftp都行,然后调用iminfo LOAD_ADDR 检测CRC错误。

第一种方法是用特定的pattern去刷DDR的空闲空间,第二种和第三种方法可以说Pattern的随机性更大一些。

当然最彻底的检测方法当然是长时间跑Linux系统,上面的方法更适用于系统不稳定时定位错误。

      具体代码实现如下:


static void move64(unsigned long long *src, unsigned long long *dest)

{

*dest = *src;

}

 

/*

 * This is 64 bit wide test patterns.  Note that they reside in ROM

 * (which presumably works) and the tests write them to RAM which may

 * not work.

 *

 * The "otherpattern" is written to drive the data bus to values other

 * than the test pattern.  This is for detecting floating bus lines.

 *

 */

const static unsigned long long pattern[] = {

0xaaaaaaaaaaaaaaaaULL,

0xccccccccccccccccULL,

0xf0f0f0f0f0f0f0f0ULL,

0xff00ff00ff00ff00ULL,

0xffff0000ffff0000ULL,

0xffffffff00000000ULL,

0x00000000ffffffffULL,

0x0000ffff0000ffffULL,

0x00ff00ff00ff00ffULL,

0x0f0f0f0f0f0f0f0fULL,

0x3333333333333333ULL,

0x5555555555555555ULL

};

const unsigned long long otherpattern = 0x0123456789abcdefULL;

 

/* 数据线检测 */

static int memory_post_dataline(unsigned long long * pmem)

{

unsigned long long temp64 = 0;

int num_patterns = sizeof(pattern)/ sizeof(pattern[0]);

int i;

unsigned int hi, lo, pathi, patlo;

int ret = 0;

 

for ( i = 0; i < num_patterns; i++) 

{

move64((unsigned long long *)&(pattern[i]), pmem++);

/*

* Put a different pattern on the data lines: otherwise they

* may float long enough to read back what we wrote.

*/

/* 预防floating buses错误 */

move64((unsigned long long *)&otherpattern, pmem--);

move64(pmem, &temp64);

 

#ifdef INJECT_DATA_ERRORS

temp64 ^= 0x00008000;

#endif

 

if (temp64 != pattern[i])

{

pathi = (pattern[i]>>32) & 0xffffffff;

patlo = pattern[i] & 0xffffffff;

 

hi = (temp64>>32) & 0xffffffff;

lo = temp64 & 0xffffffff;

 

post_log ("Memory (date line) error at %08x, "

 "wrote %08x%08x, read %08x%08x !\n",

 pmem, pathi, patlo, hi, lo);

ret = -1;

}

}

return ret;

}

 

/* 地址线检测 */

static int memory_post_addrline(ulong *testaddr, ulong *base, ulong size)

{

ulong *target;

ulong *end;

ulong readback;

ulong xor;

int   ret = 0;

 

end = (ulong *)((ulong)base + size);/* pointer arith! */

xor = 0;

for(xor = sizeof(ulong); xor > 0; xor <<= 1) 

{

/* 对测试的地址的某一根地址线的值翻转 */

target = (ulong *)((ulong)testaddr ^ xor);

if((target >= base) && (target < end)) 

{

/* 由于target是testaddr某一根地址线的值翻转得来

   故testaddr != target,下面赋值操作后

   应有*testaddr != *target */

*testaddr = ~*target;

readback  = *target;

 

#ifdef INJECT_ADDRESS_ERRORS

if(xor == 0x00008000) 

{

readback = *testaddr;

}

#endif

                     /* 出现此种情况只有testaddr == target,即某根地址线翻转无效 */

if(readback == *testaddr) 

{

post_log ("Memory (address line) error at %08x<->%08x, "

 "XOR value %08x !\n",

testaddr, target, xor);

ret = -1;

}

}

}

return ret;

}

 

static int memory_post_test1 (unsigned long start,

     unsigned long size,

     unsigned long val)

{

unsigned long i;

ulong *mem = (ulong *) start;

ulong readback;

int ret = 0;

 

for (i = 0; i < size / sizeof (ulong); i++) {

mem[i] = val;

if (i % 1024 == 0)

WATCHDOG_RESET ();

}

 

for (i = 0; i < size / sizeof (ulong) && ret == 0; i++) {

readback = mem[i];

if (readback != val) {

post_log ("Memory error at %08x, "

 "wrote %08x, read %08x !\n",

 mem + i, val, readback);

 

ret = -1;

break;

}

if (i % 1024 == 0)

WATCHDOG_RESET ();

}

 

return ret;

}

 

static int memory_post_test2 (unsigned long start, unsigned long size)

{

unsigned long i;

ulong *mem = (ulong *) start;

ulong readback;

int ret = 0;

 

for (i = 0; i < size / sizeof (ulong); i++) {

mem[i] = 1 << (i % 32);

if (i % 1024 == 0)

WATCHDOG_RESET ();

}

 

for (i = 0; i < size / sizeof (ulong) && ret == 0; i++) {

readback = mem[i];

if (readback != (1 << (i % 32))) {

post_log ("Memory error at %08x, "

 "wrote %08x, read %08x !\n",

 mem + i, 1 << (i % 32), readback);

 

ret = -1;

break;

}

if (i % 1024 == 0)

WATCHDOG_RESET ();

}

 

return ret;

}

 

static int memory_post_test3 (unsigned long start, unsigned long size)

{

unsigned long i;

ulong *mem = (ulong *) start;

ulong readback;

int ret = 0;

 

for (i = 0; i < size / sizeof (ulong); i++) {

mem[i] = i;

if (i % 1024 == 0)

WATCHDOG_RESET ();

}

 

for (i = 0; i < size / sizeof (ulong) && ret == 0; i++) {

readback = mem[i];

if (readback != i) {

post_log ("Memory error at %08x, "

 "wrote %08x, read %08x !\n",

 mem + i, i, readback);

 

ret = -1;

break;

}

if (i % 1024 == 0)

WATCHDOG_RESET ();

}

 

return ret;

}

 

static int memory_post_test4 (unsigned long start, unsigned long size)

{

unsigned long i;

ulong *mem = (ulong *) start;

ulong readback;

int ret = 0;

 

for (i = 0; i < size / sizeof (ulong); i++) {

mem[i] = ~i;

if (i % 1024 == 0)

WATCHDOG_RESET ();

}

 

for (i = 0; i < size / sizeof (ulong) && ret == 0; i++) {

readback = mem[i];

if (readback != ~i) {

post_log ("Memory error at %08x, "

 "wrote %08x, read %08x !\n",

 mem + i, ~i, readback);

 

ret = -1;

break;

}

if (i % 1024 == 0)

WATCHDOG_RESET ();

}

 

return ret;

}

 

static int memory_post_tests (unsigned long start, unsigned long size)

{

int ret = 0;

 

if (ret == 0)

ret = memory_post_dataline ((unsigned long long *)start);

WATCHDOG_RESET ();

if (ret == 0)

ret = memory_post_addrline ((ulong *)start, (ulong *)start, size);

WATCHDOG_RESET ();

if (ret == 0)

ret = memory_post_addrline ((ulong *)(start + size - 8),

   (ulong *)start, size);

WATCHDOG_RESET ();

if (ret == 0)

ret = memory_post_test1 (start, size, 0x00000000);

WATCHDOG_RESET ();

if (ret == 0)

ret = memory_post_test1 (start, size, 0xffffffff);

WATCHDOG_RESET ();

if (ret == 0)

ret = memory_post_test1 (start, size, 0x55555555);

WATCHDOG_RESET ();

if (ret == 0)

ret = memory_post_test1 (start, size, 0xaaaaaaaa);

WATCHDOG_RESET ();

if (ret == 0)

ret = memory_post_test2 (start, size);

WATCHDOG_RESET ();

if (ret == 0)

ret = memory_post_test3 (start, size);

WATCHDOG_RESET ();

if (ret == 0)

ret = memory_post_test4 (start, size);

WATCHDOG_RESET ();

 

return ret;

}

0 0
原创粉丝点击