找回密碼
 立即注冊(cè)

QQ登錄

只需一步,快速開始

搜索
查看: 3131|回復(fù): 0
打印 上一主題 下一主題
收起左側(cè)

uboot 中內(nèi)存測試,內(nèi)存檢測方法

[復(fù)制鏈接]
跳轉(zhuǎn)到指定樓層
樓主
ID:114320 發(fā)表于 2016-4-15 03:04 | 只看該作者 回帖獎(jiǎng)勵(lì) |倒序?yàn)g覽 |閱讀模式
針對(duì)常見的DDR內(nèi)存故障進(jìn)行了嚴(yán)格的檢測處理,下圖描述了該檢測處理過程的三個(gè)步驟:檢測數(shù)據(jù)線、地址線和DDR物理存儲(chǔ)部件,主要涉及這三個(gè)步驟的處理過程和方法。

下面主要是相關(guān)的檢測處理思路及問題:

1、為什么先檢測數(shù)據(jù)線?

因?yàn)槿绻麛?shù)據(jù)線是斷開的,那么一切無從談起!接下來是檢測地址線,只有數(shù)據(jù)線和地址線都通過,檢測內(nèi)存的存儲(chǔ)單元才有意義,這樣的流程也利于分割定位問題。上面testing sequence框圖將整個(gè)檢測過程分成三大步,用三個(gè)虛線方框表示。

2、數(shù)據(jù)線的連接錯(cuò)誤

數(shù)據(jù)線的連接可能存在兩種錯(cuò)誤,一種是被斷開,另一種布線或生產(chǎn)造成互相短路。

3、如何檢測數(shù)據(jù)線的連接錯(cuò)誤

Denx 設(shè)計(jì)的數(shù)據(jù)線檢測算法還是很Tricky和精秒的,整個(gè)處理流程如下例子:如果是兩根數(shù)據(jù)線,只需要寫入并讀出一個(gè)pattern=0b01(0b開頭表示二進(jìn)制數(shù))就能判斷它們是否短路或斷開。很明顯,大部分的嵌入式平臺(tái)不止兩根數(shù)據(jù)線,我們以64位地址線為例,pattern= 0b101010101010101010.... 能檢測出奇偶位之間的數(shù)據(jù)錯(cuò)誤。如果這個(gè)錯(cuò)誤被排除,每兩根數(shù)據(jù)線組成一組(這是理解下一個(gè)pattern的關(guān)鍵),再用相同的辦法,檢測每相鄰兩組之間是否有短路,就得到第二個(gè)pattern,就是0b110011001100...... 依次類推,以4根數(shù)據(jù)線為一組,8根線為一組,相繼得到共6個(gè)pattern,分別是0xaaaaaaaaaaaaaaaa,0xcccccccccccccccc,0xf0f0f0f0f0f0f0f0,0xff00ff00ff00ff00,0xffff0000ffff0000,0xffffffff00000000。只要相繼寫入并讀出這6個(gè)pattern就能驗(yàn)證是否存在數(shù)據(jù)線交叉短路錯(cuò)誤。

4、如何檢測數(shù)據(jù)線與板上其它信號(hào)線交叉短路或斷路

取以上6個(gè)pattern的反碼,總共12個(gè)pattern就能檢測到每一位都可以寫入和讀出0和1。

5、什么是floating buses錯(cuò)誤

floating buses會(huì)“欺騙”測試軟件,如果測試軟件寫入并很快讀出一個(gè)值的時(shí)候,寫操作會(huì)對(duì)數(shù)據(jù)線上的電容充電,總線會(huì)短暫的保持它的狀態(tài)。當(dāng)測試軟件進(jìn)行讀操作時(shí),總線會(huì)返回剛寫入的值,即使實(shí)際上該數(shù)據(jù)線是斷路的。

6、如何檢測數(shù)據(jù)線的floating buses錯(cuò)誤

檢測floating buses錯(cuò)誤的算法不復(fù)雜,在寫入和讀回之間再插入一次對(duì)不同地址寫入不同值的操作。例如,a寫入A位置,b寫入B位置,再從A位置讀出a值則表示floating buses錯(cuò)誤不存在。

7、地址線的錯(cuò)誤

如果地址線存在錯(cuò)誤,其癥狀是地址空間中的兩個(gè)不同位置被映射到同一物理存儲(chǔ)位置。更通俗地講,就是寫一個(gè)位置卻“改變”了另一個(gè)位置。

8、地址線的錯(cuò)誤檢測

地址線的錯(cuò)誤檢測相對(duì)簡單,其算法是:

1)將地址的值作為內(nèi)容寫入該地址處,匯編的表示方法是*addr = addr。即將地址值寫到地址對(duì)應(yīng)的空間里,這樣確保每一個(gè)位置的內(nèi)容不同。

2)依次將內(nèi)存基地址的某一根地址線的值翻轉(zhuǎn)(flip/toggle)得到某個(gè)地址,從該地址取值,如果該值和基地址的值相等,則表示某一位地址線有問題。

這個(gè)算法的特點(diǎn)是每次只檢測一根地址線,方法簡單有效。

9、存儲(chǔ)單元的錯(cuò)誤

以上數(shù)據(jù)線和地址線的檢測都是檢測布線或工廠生產(chǎn)的錯(cuò)誤,而存儲(chǔ)單元的檢測則是真正對(duì)DDR內(nèi)存芯片的檢測。內(nèi)存芯片的常見錯(cuò)誤是bit-stuck,簡而言之,就是讓它是0,它偏為1,讓它為1,它偏為0,檢測方法也很簡單,就是用不同的pattern去寫盡可能所有的地址并讀回比較。有一些常用的pattern如0x5555,0xAAAA等。

10、幾個(gè)簡單的檢測DDR故障的方法

上面的DDR檢測算法,雖然全面,但是耗時(shí)比較長,常常需要好幾個(gè)小時(shí),在Uboot命令行下也有幾個(gè)簡單的命令可以檢測常見內(nèi)存故障,如下所示:

1)mtest addr lenth pattern

這個(gè)命令需要注意,DDR在Uboot啟動(dòng)后被映射到了0地址,但是uboot的代碼和堆、棧空間0x10000000處開始,這些空間是不能被刷的,否則就掛死了。

2)復(fù)制NOR flash的內(nèi)容到內(nèi)存中,如cp.b 0x20080000 0x7fc0 20000,然后比較cmp.b 0x20080000 0x7fc0 20000。

3)下載kernel image到內(nèi)存中,copy NOR flash 或tftp都行,然后調(diào)用iminfo LOAD_ADDR 檢測CRC錯(cuò)誤。

第一種方法是用特定的pattern去刷DDR的空閑空間,第二種和第三種方法可以說Pattern的隨機(jī)性更大一些。

當(dāng)然最徹底的檢測方法當(dāng)然是長時(shí)間跑Linux系統(tǒng),上面的方法更適用于系統(tǒng)不穩(wěn)定時(shí)定位錯(cuò)誤。

      具體代碼實(shí)現(xiàn)如下:


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;



/* 數(shù)據(jù)線檢測 */

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.

*/

/* 預(yù)防floating buses錯(cuò)誤 */

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)

{

/* 對(duì)測試的地址的某一根地址線的值翻轉(zhuǎn) */

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

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

{

/* 由于target是testaddr某一根地址線的值翻轉(zhuǎn)得來

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

   應(yīng)有*testaddr != *target */

*testaddr = ~*target;

readback  = *target;



#ifdef INJECT_ADDRESS_ERRORS

if(xor == 0x00008000)

{

readback = *testaddr;

}

#endif

                     /* 出現(xiàn)此種情況只有testaddr == target,即某根地址線翻轉(zhuǎn)無效 */

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;

}

分享到:  QQ好友和群QQ好友和群 QQ空間QQ空間 騰訊微博騰訊微博 騰訊朋友騰訊朋友
收藏收藏 分享淘帖 頂 踩
回復(fù)

使用道具 舉報(bào)

本版積分規(guī)則

小黑屋|51黑電子論壇 |51黑電子論壇6群 QQ 管理員QQ:125739409;技術(shù)交流QQ群281945664

Powered by 單片機(jī)教程網(wǎng)

快速回復(fù) 返回頂部 返回列表