/******************************************************************************* HWDD Memory module Copyright (c) 2011 Network Appliance, Inc. hwdd_cache_algos.c: cache alogorithms *******************************************************************************/ #include #include #include "hwdd_mem.h" #define CACHE_BLK_ZONE 0x1000 //4KB #define L1_SIZE 0x7D00 //32KB static u64 hwdd_seed = 0xdeadbeefdeadbeef; static void mem_error(u64 addr, u64 val_exp, u64 val_act, void* ptr); /*********************************************************************** * cache_data_walk () * Performs a walking 1 in a field of zeroes and a walking 0 in a * field of ones. This test is repeated at each L1 cache size. * * This test is intended to verify data lines b/w L1,L2,L3 cache. * Expected to detect hard faults such as shorts and opens. **********************************************************************/ int cache_data_walk(u64 cache_base, u64 cache_end, u64 phys_start, pid_t ppid, void *log_ptr) { register u8 i; /* bit position within long word */ register volatile u64 *location,*cache_base_loc; /* address being tested */ register u64 read_data, data; /* data read from memory */ u64 error_cnt = 0; /* error count */ unsigned long expire = SLEEP_AFTER; if ((cache_end - cache_base) <= 0) { /* see if any cache to test. block can be looped to stress cache */ hwdd_printk(HWDD_DEBUG, log_ptr, "No memory to test\n"); return FAIL; } cache_base_loc = (u64 *)cache_base; location = (u64 *)(cache_base + L1_SIZE); /* get addr outside L1 cache */ while ((u64)location < cache_end) { /* walking one pattern */ data = 1; for ( i = 1; i <= 64; i++) { *location = data; /* store pattern */ *cache_base_loc = 0xffffffffffffffff; /* hit base, flush cache */ read_data = *location; if (read_data != data) { mem_error(((u64)location - cache_base + phys_start), data, read_data, log_ptr); error_cnt++; } data <<= 1; /* make next pattern */ } /* walking zero pattern */ data = 0xfffffffffffffffe; for (i = 1; i <= 64; i++) { *(location + 1) = data; /* store pattern */ *cache_base_loc = 0; /* hit base, flush cache */ read_data = *(location + 1); if (read_data != data) { mem_error(((u64)location - cache_base + phys_start), data, read_data, log_ptr); error_cnt++; } data <<= 1; /* make next pattern */ data |= 1; } location = (u64 *)((u64)location + L1_SIZE); /* move to another spot */ /* return FAIL if user had issued a STOP */ if (hwdd_sleep_checkstop(&expire, ppid)) { return FAIL; } } if (error_cnt) { hwdd_printk(HWDD_ERROR, log_ptr, "No of errors detected %d\n",error_cnt); return FAIL; } return PASS; } /* *mem_error - report error if expected value differs from read value */ static void mem_error(u64 addr, u64 val_exp, u64 val_act, void *ptr) { u64 val_diff; val_diff = val_exp ^ val_act; hwdd_printk(HWDD_ERROR, ptr, " ERROR Addr =%016Lx : Exp =%016Lx Act =%016Lx Diff =%016Lx\n",addr, val_exp, val_act, val_diff); } /*********************************************************************** * cache_stuck_faults() * Algorithm as follows: * 1) store zero-bits to all of cache * 2) check for zero-bits in each location * 3) store one-bits in all of cache * 4) check for one-bits in each location * 5) store zero-bits to all of cache * 6) check for zero-bits in each location * * This test is intended to check each and every cell in SRAM(Cache) for * stuck-at faults errors quickly. An extra pass * is used to verify that both 0->1 and 1->0 transitions are tested. **********************************************************************/ int cache_stuck_faults(u64 cache_base, u64 cache_end, u64 phys_start, pid_t ppid, void *log_ptr) { register u64 val_read; register volatile u64 *wordloc, *endloc; u64 error_cnt = 0; unsigned long expire = SLEEP_AFTER; if ((cache_end - cache_base) <= 0) { /* see if any cache to test */ hwdd_printk(HWDD_DEBUG, log_ptr, "No memory to test\n"); return FAIL; } wordloc = (u64 *)cache_base; endloc = (u64 *)cache_end; while (wordloc < endloc) *wordloc++ = 0x0000000000000000; /* all zero bits to mem */ do { wordloc = (u64 *)cache_base; /* point to first addr */ do { if ((val_read = *wordloc) != 0x0000000000000000) { /* is data what we want? */ mem_error(((u64)wordloc - cache_base + phys_start), 0x00000, val_read, log_ptr); error_cnt++; } } while (++wordloc < endloc); /* spin through all mem */ /* return FAIL if user had issued a STOP */ if (hwdd_sleep_checkstop(&expire, ppid)) { return FAIL; } wordloc = (u64 *)cache_base; while (wordloc < endloc) *wordloc++ = 0xffffffffffffffff; /* all one bits to mem */ wordloc = (u64 *)cache_base; /* point to first addr */ do { if ((val_read = *wordloc) != 0xffffffffffffffff) { /* is data what we want? */ mem_error(((u64)wordloc - cache_base + phys_start), 0xffffffffffffffff, val_read, log_ptr); error_cnt++; } } while (++wordloc < endloc); /* spin through all mem */ /* return FAIL if user had issued a STOP */ if (hwdd_sleep_checkstop(&expire, ppid)) { return FAIL; } wordloc = (u64 *)cache_base; /* one more time */ while (wordloc < endloc) *wordloc++ = 0x0000000000000000; /* all zero bits to mem */ wordloc = (u64 *)cache_base; /* point to first addr */ do { if ((val_read = *wordloc) != 0x00000000) { /* is data what we want? */ mem_error(((u64)wordloc - cache_base + phys_start), 0x00000000, val_read, log_ptr); error_cnt++; } } while (++wordloc < endloc); /* spin through all mem */ } while (0); /* change the count based on level of stressing */ if (error_cnt) { hwdd_printk(HWDD_ERROR, log_ptr, "No of errors detected %d\n",error_cnt); return FAIL; } return PASS; } /*********************************************************************** * cache_rand_rw() * Algorithm as follows: * 1) fill cache with address-in-address pattern * 2) at each 32KB base address of cache do: * 3) randomly read and write the block * 4) repeat step 3 * 5) check cache for address pattern * * This test is intended to access chunks of memory within an individual * SIMM as rapidly as possible to stress the SIMM. Block size is chosen * to be large enough to force cache collisions. Operation (read/write) * is chosen randomly, along with the block, so this test causes some * unique stressing of the memory system. **********************************************************************/ int cache_rand_rw(u64 cache_base, u64 cache_end, u64 phys_start, pid_t ppid, void *log_ptr) { register volatile u64 *wordloc, *baseloc, *endloc; register u64 val_read,seed,i; u64 error_cnt = 0; unsigned long expire = SLEEP_AFTER; if ((cache_end - cache_base) <= 0) { /* see if any cache to test */ hwdd_printk(HWDD_DEBUG, log_ptr, "No memory to test\n"); return FAIL; } seed = hwdd_seed; /* get starting seed */ wordloc = (u64 *)cache_base; /* point to memory */ endloc = (u64 *)cache_end; /* point to end of memory */ while (wordloc < endloc) { *wordloc = (u64)wordloc; /* store address to address */ wordloc++; } /* return FAIL if user had issued a STOP */ if (hwdd_sleep_checkstop(&expire, ppid)) { return FAIL; } baseloc = (u64 *)cache_base; /* point to memory */ while ((u64)(baseloc + CACHE_BLK_ZONE) < cache_end) { i = 1000000; /* get loop count */ while (i--) { /* now grind awhile */ mix_seed(seed); /* spin random gen */ wordloc = baseloc + (seed & CACHE_BLK_ZONE); if ((s64)seed < 0) /* decide if read or write */ val_read = *wordloc; /* read a word */ else *wordloc = (u64)wordloc; /* write out the location */ } baseloc += L1_SIZE; /* advance to next test zone */ } /* return FAIL if user had issued a STOP */ if (hwdd_sleep_checkstop(&expire, ppid)) { return FAIL; } wordloc = (u64 *)cache_base; /* point to memory */ do { /* now check what we wrote */ if ((val_read = *wordloc) != (u64)wordloc) { mem_error(((u64)wordloc - cache_base + phys_start), (u64)wordloc, val_read, log_ptr); error_cnt++; } } while (++wordloc < endloc); /* spin through all cache */ if (!error_cnt) { hwdd_seed = seed; return PASS; } hwdd_printk(HWDD_ERROR, log_ptr, "No of errors detected %d\n",error_cnt); return FAIL; } /*********************************************************************** * cache_rand_data() * Algorithm as follows: * 1) store different random pattern in each location * 2) store complement of pattern in each location (toggles all bits) * 3) for each location, check for complement pattern and rewrite original * 4) check each location for original pattern * 5) repeat steps #1:4 three times * * This test is intended to verify all locations of SRAM cache and is * expected to detect all common SRAM failures such as stuck cells or * cell coupling. Intermittent errors may also be encountered, caused * by noise or margin problems. Parity and ECC are stressed by the * random patterns used. **********************************************************************/ int cache_rand_data(u64 cache_base, u64 cache_end, u64 phys_start, pid_t ppid, void *log_ptr) { register u64 seed, rand, val_read; register volatile u64 *wordloc, *endloc; u64 save_seed, error_cnt = 0; u8 i; unsigned long expire = SLEEP_AFTER; if ((cache_end - cache_base) <= 0) { /* see if any cache to test */ hwdd_printk(HWDD_DEBUG, log_ptr, "No memory to test\n"); return FAIL; } seed = hwdd_seed; /* get starting seed */ endloc = (u64 *)cache_end; /* get end address */ for (i=0; i < 8; i++) { /* loop this puppy */ save_seed = seed; /* save random seed */ wordloc = (u64 *)cache_base; /* point to mem base */ do { /* write to all mem */ mix_random(seed, rand); /* spin random gen */ *wordloc = rand; /* different value in each */ } while (++wordloc < endloc); /* spin through all mem */ seed = save_seed; /* reset random seed */ wordloc = (u64 *)cache_base; /* point to mem base */ do { /* write to all mem */ mix_random(seed, rand); /* spin random gen */ *wordloc = ~rand; /* complement value in each */ } while (++wordloc < endloc); /* spin through all mem */ /* return FAIL if user had issued a STOP */ if (hwdd_sleep_checkstop(&expire, ppid)) { return FAIL; } seed = save_seed; /* reset random seed again */ wordloc = (u64 *)cache_base; /* point to mem base */ do { /* now read back and check */ mix_random(seed, rand); /* spin random gen */ if ((val_read = *wordloc) != ~rand) { /* read what we wrote? */ mem_error(((u64)wordloc - cache_base + phys_start), ~rand, val_read, log_ptr); error_cnt++; } *wordloc = rand; /* write back original pat */ } while (++wordloc < endloc); /* spin through all mem */ /* return FAIL if user had issued a STOP */ if (hwdd_sleep_checkstop(&expire, ppid)) { return FAIL; } seed = save_seed; /* reset seed one last time */ wordloc = (u64 *)cache_base; /* point to mem base */ do { /* now read back and check */ mix_random(seed, rand); /* spin random gen */ if ((val_read = *wordloc) != rand) { /* read what we wrote? */ mem_error(((u64)wordloc - cache_base + phys_start), rand, val_read, log_ptr); error_cnt++; } } while (++wordloc < endloc); /* spin through all mem */ /* return FAIL if user had issued a STOP */ if (hwdd_sleep_checkstop(&expire, ppid)) { return FAIL; } } if (!error_cnt){ hwdd_seed = seed; return PASS; } hwdd_printk(HWDD_ERROR, log_ptr, "No of errors detected %d\n",error_cnt); return FAIL; } /*********************************************************************** * cache_rand_addr() * Algorithm as follows: * 1) store address-specific pattern in each location * 2) for N times through loop: * 3) randomly generate valid address * 4) read address, complement pattern read and re-write * 5) check each location (sequentially) for pattern or complement * * This test is intended to verify overall operation of L2 cache, * with particular emphasis on noise, signal coupling and simultaneous * switching problems. Detection of intermittents and margin problems * is also expected. (Note that we don't worry about L1$ hits. The * few times we hit L1$ will just scramble the timing a little and * make the test that much more random.) **********************************************************************/ int cache_rand_addr(u64 cache_base, u64 cache_end, u64 phys_start, pid_t ppid, void *log_ptr) { register u64 seed,amask,save_seed; register u64 *wordloc, *baseloc, *endloc; u64 val_read, val_exp, val_off, error_cnt = 0,i, cache_size; u8 n; unsigned long expire = SLEEP_AFTER; cache_size = (cache_end - cache_base); if (cache_size <= 0) { /* see if any cache to test */ hwdd_printk(HWDD_DEBUG, log_ptr, "No memory to test\n"); return FAIL; } save_seed = hwdd_seed; /* get starting seed */ baseloc = (u64 *)cache_base; /* point to mem base */ wordloc = baseloc; endloc = (u64 *)cache_end; seed = save_seed; do { /* fill all of mem */ mix_random(seed, val_exp); /* spin random gen */ *wordloc = val_exp; /* write random data */ } while (++wordloc < endloc); /* spin through all mem */ /* return FAIL if user had issued a STOP */ if (hwdd_sleep_checkstop(&expire, ppid)) { return FAIL; } amask = (cache_size >> 3) - 1; for (n = 0; n < 8; n++) { i = 1500000; /* get loop count */ while (i--) { /* now grind awhile */ mix_seed(seed); /* spin random gen */ wordloc = baseloc + ((seed >> 7) & amask); /* make address */ *wordloc = ~(*wordloc); /* flip data at loc */ } } /* return FAIL if user had issued a STOP */ if (hwdd_sleep_checkstop(&expire, ppid)) { return FAIL; } wordloc = baseloc; /* now check every addr */ seed = save_seed; do { /* be sure correct pat */ mix_random(seed, val_exp); /* spin random gen */ val_read = *wordloc; /* fetch data from mem */ if (val_read != val_exp && val_read != ~val_exp) { val_off = val_exp ^ val_read; /* compute difference */ i = 0; /* init diff counter */ while (val_off) { /* now count bits */ if (val_off & 0x1) i++; /* count 'em up */ val_off = val_off >> 1; } if (i > 32) /* which expected to use */ val_off = ~val_exp; else val_off = val_exp; /* this value closer? */ mem_error(((u64)wordloc - cache_base + phys_start), val_off, val_read, log_ptr); /* report and log error */ } } while (++wordloc < endloc); /* spin through all mem */ if (!error_cnt){ hwdd_seed = seed; return PASS; } hwdd_printk(HWDD_ERROR, log_ptr, "No of errors detected %d\n",error_cnt); return FAIL; } /*********************************************************************** * cache_spill() * Algorithm as follows (same as rand_addr, but larger address space): * 1) store address-specific pattern in each location * 2) for N times through loop: * 3) randomly generate valid address * 4) read address, complement pattern read and re-write * 5) check each location (sequentially) for pattern or complement * * This test is intended to verify operation of L3 cache, emphasizing * the interface to main memory. Since we've already tested the cache * ram itself with the previous tests, we are particularly interested * in control logic and timing of operations between cache and memory. * We'll use a test address range of 4X the size of Bcache. This will * give a 75% miss rate, forcing lots of spills and refills. **********************************************************************/ /* int cache_spill(void *log_ptr) { register u64 seed, amask,i,save_seed; register vng lots of spills and refillsclatile u64 *wordloc, *baseloc, *endloc; u64 val_read, val_exp, val_off; u64 n, error_cnt = 0; save_seed = hwdd_seed; // get starting seed seed = save_seed; if (cache_size) { // see if any cache to test baseloc = cache_base; // point to mem base wordloc = baseloc; endloc = cache_end; do { // fill all of mem mix_random(seed, val_exp); // spin random gen *wordloc = val_exp; // write random data } while (++wordloc < endloc); // spin through all mem amask = bcache_size - 1; // 4X size for (n = 0; n < 8; n++) { // loop, allowing rupt i = 2000000; // get loop count while (i--) { // now grind awhile mix_seed(seed); // spin random gen wordloc = baseloc + ((seed >> 7) & amask); // make address *wordloc = ~(*wordloc); // flip data at loc } } wordloc = baseloc; // now check every addr seed = save_seed; // get starting seed do { // be sure correct pat mix_random(seed, val_exp); // spin random gen val_read = *wordloc; // fetch data from mem if (val_read != val_exp && val_read != ~val_exp) { val_off = val_exp ^ val_read; // compute difference i = 0; // init diff counter while (val_off) { // now count bits if (val_off & 0x1) i++; val_off = val_off >> 1; } if (i > 32) // which expected to use val_off = ~val_exp; // way off, use other else val_off = val_exp; // this value closer? mem_error((u64)wordloc, val_off, val_read); // report and log error } } while (++wordloc < endloc); // spin through all mem } if (!error_cnt) hwdd_seed = seed; return error_cnt; } */ /*********************************************************************** * mk_random_tbl () * builds the random_tbl with 0000 - ffff **********************************************************************/ static void mk_random_tbl(u64 cache_base) { register volatile u16 *tblloc; register u16 i = 0; tblloc = (u16 *)(cache_base); /* point to the start of cache */ do { *tblloc++ = i; i++; } while (i != 0); /* 64k times */ } /*********************************************************************** * ck_random_tbl () * does a check sum on the random_tbl **********************************************************************/ static u32 ck_random_tbl(u64 cache_base) /* check random_tbl for 0000 - ffff */ { register u16 *tblloc; register u16 i = 0, tmp; register u32 cksum = 0; tblloc = (u16 *)cache_base; /* point to random_tbl */ do { tmp = *tblloc++; /* read 2 bytes */ cksum += tmp; /* total into 4 bytes */ i++; } while (i != 0); /* 64k times */ return cksum; } /*********************************************************************** * mix_random_tbl () * changes the order of the random_tbl **********************************************************************/ static void mix_random_tbl(u64 cache_base) { register volatile u16 *lowloc, *hiloc, *hiloc_val; register u16 tmp1, tmp2, tmp3; u16 i; lowloc = (u16 *)(cache_base); /* point to base of cache */ hiloc = lowloc + 0xffff -1 ; hiloc_val = hiloc; for (i = 0; i < 10; i++) { tmp1 = *lowloc; tmp2 = *hiloc; *hiloc-- = tmp1; *lowloc++ = tmp2; } lowloc = (u16 *)(cache_base); /* point to top of cache */ hiloc = lowloc + 5; do { /* rotate the random_tbl */ tmp1 = *hiloc; tmp2 = *lowloc; *hiloc++ = tmp2; *lowloc++ = tmp1; } while (hiloc < hiloc_val); /* 2 power 32 times */ lowloc = (u16 *)(cache_base); /* point to base of cache */ hiloc = lowloc + 0xffff - 1 ; do { tmp1 = *lowloc; tmp2 = *hiloc; *lowloc = tmp2; /* move hi to low */ *hiloc = tmp1; /* and low to hi */ lowloc += 3; hiloc -= 3; } while (lowloc < hiloc); lowloc = (u16 *)cache_base; /* point to top of cache */ tmp3 = *lowloc; /* random data */ tmp3 &= 0x00fe; /* not too much */ hiloc = (u16 *)((u64)lowloc + tmp3); do { tmp1 = *lowloc; tmp2 = *hiloc; *lowloc = tmp2; *hiloc = tmp1; lowloc = (u16 *)((u64)lowloc + tmp3); hiloc = (u16 *)((u64)hiloc + tmp3 + 10); } while (hiloc < hiloc_val); } /*********************************************************************** * test_cache_tag () * Compares a random address from memory then redoes the last 100 random address's **********************************************************************/ static u64 test_cache_tag(u64 cache_base, u64 phys_start, u64 *endloc, void *log_ptr) { register u16 *tblloc, *tblend; register u64 *wordloc; register u64 val_read = 0, random, error_cnt = 0; u64 i, adrs[100]; /* the last one hundred address */ u8 rand_val; tblloc = (u16 *)(cache_base); /* start of the cache */ tblend = (u16 *)((u16 *)cache_base + 0xffff); /* end of random table */ wordloc = (u64 *)((u16 *)cache_base + 0x10020); for (i = 0; i < 100; i++) adrs[i] = 0; do { rand_val = (u8)*tblloc; random = *tblloc++; random = random * (rand_val + 1); // obtaining a random address (rand_val can be zero many times since we are accessing only first byte, so adding 1) // random >>= (shift_sz + 3); /* word offset i.e 8 bytes(2 pow 3) */ if (((void *)(wordloc + random) > (void *)wordloc) && ((void *)(wordloc + random) < (void *)endloc)) { if ((val_read = *(wordloc + random)) != (u64)(wordloc + random)) { mem_error(((u64)(wordloc + random) - cache_base + phys_start), (u64)(wordloc + random), val_read, log_ptr); ++error_cnt; } for (i = 0; i < 99; i++) adrs[i] = adrs[i + 1]; /* push the queue down one */ adrs[99] = random; for (i = 0; i < 100; i++) { /* read the last 100 random tags */ if ((val_read = *(wordloc + adrs[i])) != (u64)(wordloc + adrs[i])) { mem_error(((u64)(wordloc + adrs[i]) - cache_base + phys_start), (u64)(wordloc + adrs[i]), val_read, log_ptr); ++error_cnt; } // if(shift_sz == -1) /* just because it makes it work on alpha */ // random++; } } } while (tblloc < tblend); /* done? */ return error_cnt; } /*********************************************************************** * Cache_tag() * Algorithm as follows: * 1) build random_tbl with all numbers 0x0000 to 0xffff * 2) make random_tbl data random * 3) Fill memory with address to address (this takes the most time) * 4) Read a random address from memory * 5) Random read the last 100 cache address's read fast * 6) Loop to 4 (about 800k reads, about 2 seconds) * * This test is intended to access cache with random memory address's * as rapidly as possible to stress the cache tags **********************************************************************/ int cache_tag(u64 cache_base, u64 cache_end, u64 phys_start, pid_t ppid, void *log_ptr) { register u64 *wordloc,*endloc; u32 lstpass = 50, pass, error_cnt=0; u64 i ; unsigned long expire = SLEEP_AFTER; if ((cache_end - cache_base) < LAST_LEVEL_CACHE_SIZE) { // check for memory range availability return PASS; } wordloc = (u64 *)((u16 *)cache_base + 0x10000); /* point to memory */ endloc = (u64 *)(cache_end); // for (shift_sz = 0; (endloc = (u64 *)((u64 )endloc >> 1)) != 0; shift_sz++); // shift_sz = (63 - shift_sz); // endloc = (u64 *)(sys_mem_end); // if ((uint32_t)(long)endloc == (0x80000000 >> shift_sz)) // 2 power n numbers // shift_sz++; if (ck_random_tbl(cache_base) != 0x7fff8000) { /* only make the random table if needed */ mk_random_tbl(cache_base); /* build random table */ } for (i = 0; i < 100; i++) mix_random_tbl(cache_base); /* mix the random_tbl */ /* return FAIL if user had issued a STOP */ if (hwdd_sleep_checkstop(&expire, ppid)) { return FAIL; } for (i = 0; (wordloc + i) < endloc; i++) *(wordloc + i) = (u64)(wordloc + i); /* store address to address */ /* return FAIL if user had issued a STOP */ if (hwdd_sleep_checkstop(&expire, ppid)) { return FAIL; } for (pass = 0; pass < lstpass; pass++) { /* routine loop here */ mix_random_tbl(cache_base); /* mix the random_tbl */ if (ck_random_tbl(cache_base) != 0x7fff8000) { /* check that the data is OK */ hwdd_printk(HWDD_ERROR, log_ptr, " Data error in cache tag test %Lx \n", ck_random_tbl(cache_base)); error_cnt++; break; } if (error_cnt +=test_cache_tag (cache_base, phys_start, endloc, log_ptr) != 0) /* test the cache tags */ break; /* return FAIL if user had issued a STOP */ if (hwdd_sleep_checkstop(&expire, ppid)) { return FAIL; } } if (error_cnt) { hwdd_printk(HWDD_ERROR, log_ptr, "No of errors detected %d\n",error_cnt); return FAIL; } return PASS; }