tcl: add memory testing functions for board diagnostics
[openocd.git] / tcl / tools / memtest.tcl
1 # Algorithms by Michael Barr, released into public domain
2 # Ported to OpenOCD by Shane Volpe, additional fixes by Paul Fertser
3
4 set CPU_MAX_ADDRESS 0xFFFFFFFF
5 source [find bitsbytes.tcl]
6 source [find memory.tcl]
7
8 proc runAllMemTests { baseAddress nBytes } {
9 memTestDataBus $baseAddress
10 memTestAddressBus $baseAddress $nBytes
11 memTestDevice $baseAddress $nBytes
12 }
13
14 #***********************************************************************************
15 # *
16 # * Function: memTestDataBus()
17 # *
18 # * Description: Test the data bus wiring in a memory region by
19 # * performing a walking 1's test at a fixed address
20 # * within that region. The address (and hence the
21 # * memory region) is selected by the caller.
22 # * Ported from:
23 # * http://www.netrino.com/Embedded-Systems/How-To/Memory-Test-Suite-C
24 # * Notes:
25 # *
26 # * Returns: Empty string if the test succeeds.
27 # * A non-zero result is the first pattern that failed.
28 # *
29 #***********************************************************************************
30 proc memTestDataBus { address } {
31 echo "Running memTestDataBus"
32
33 for {set i 0} {$i < 32} {incr i} {
34 # Shift bit
35 set pattern [expr {1 << $i}]
36
37 # Write pattern to memory
38 memwrite32 $address $pattern
39
40 # Read pattern from memory
41 set data [memread32 $address]
42
43 if {$data != $pattern} {
44 echo "FAILED DATABUS: Address: $address, Pattern: $pattern, Returned: $data"
45 return $pattern
46 }
47 }
48 }
49
50 #***********************************************************************************
51 # *
52 # * Function: memTestAddressBus()
53 # *
54 # * Description: Perform a walking 1's test on the relevant bits
55 # * of the address and check for aliasing. This test
56 # * will find single-bit address failures such as stuck
57 # * -high, stuck-low, and shorted pins. The base address
58 # * and size of the region are selected by the caller.
59 # * Ported from:
60 # * http://www.netrino.com/Embedded-Systems/How-To/Memory-Test-Suite-C
61 # *
62 # * Notes: For best results, the selected base address should
63 # * have enough LSB 0's to guarantee single address bit
64 # * changes. For example, to test a 64-Kbyte region,
65 # * select a base address on a 64-Kbyte boundary. Also,
66 # * select the region size as a power-of-two--if at all
67 # * possible.
68 # *
69 # * Returns: Empty string if the test succeeds.
70 # * A non-zero result is the first address at which an
71 # * aliasing problem was uncovered. By examining the
72 # * contents of memory, it may be possible to gather
73 # * additional information about the problem.
74 # *
75 #***********************************************************************************
76 proc memTestAddressBus { baseAddress nBytes } {
77 set addressMask [expr $nBytes - 1]
78 set pattern 0xAAAAAAAA
79 set antipattern 0x55555555
80
81 echo "Running memTestAddressBus"
82
83 echo "addressMask: [convertToHex $addressMask]"
84
85 echo "memTestAddressBus: Writing the default pattern at each of the power-of-two offsets..."
86 for {set offset 32} {[expr $offset & $addressMask] != 0} {set offset [expr $offset << 1] } {
87 set addr [expr $baseAddress + $offset]
88 memwrite32 $addr $pattern
89 }
90
91 echo "memTestAddressBus: Checking for address bits stuck high..."
92 memwrite32 $baseAddress $antipattern
93
94 for {set offset 32} {[expr $offset & $addressMask] != 0} {set offset [expr $offset << 1]} {
95 set addr [expr $baseAddress + $offset]
96 set data [memread32 $addr]
97
98 if {$data != $pattern} {
99 echo "FAILED DATA_ADDR_BUS_SHIGH: Address: [convertToHex $addr], Pattern: [convertToHex $pattern], Returned: [convertToHex $data]"
100 return $pattern
101 }
102 }
103
104 echo "memTestAddressBus: Checking for address bits stuck low or shorted..."
105 memwrite32 $baseAddress $pattern
106 for {set testOffset 32} {[expr $testOffset & $addressMask] != 0} {set testOffset [expr $testOffset << 1] } {
107 set addr [expr $baseAddress + $testOffset]
108 memwrite32 $addr $antipattern
109
110 set data [memread32 $baseAddress]
111 if {$data != $pattern} {
112 echo "FAILED DATA_ADDR_BUS_SLOW: Address: [convertToHex $addr], Pattern: [convertToHex $pattern], Returned: [convertToHex $data]"
113 return $pattern
114 }
115
116 for {set offset 32} {[expr $offset & $addressMask] != 0} {set offset [expr $offset << 1]} {
117 set addr [expr $baseAddress + $offset]
118 set data [memread32 $baseAddress]
119
120 if {(($data != $pattern) && ($offset != $testOffset))} {
121 echo "FAILED DATA_ADDR_BUS_SLOW2: Address: [convertToHex $addr], Pattern: [convertToHex $pattern], Returned: [convertToHex $data], offset: [convertToHex $offset], testOffset [convertToHex $testOffset]"
122 return $pattern
123 }
124 }
125 set addr [expr $baseAddress + $testOffset]
126 memwrite32 $addr $pattern
127 }
128 }
129
130 #***********************************************************************************
131 # *
132 # * Function: memTestDevice()
133 # *
134 # * Description: Test the integrity of a physical memory device by
135 # * performing an increment/decrement test over the
136 # * entire region. In the process every storage bit
137 # * in the device is tested as zero and as one. The
138 # * base address and the size of the region are
139 # * selected by the caller.
140 # * Ported from:
141 # * http://www.netrino.com/Embedded-Systems/How-To/Memory-Test-Suite-C
142 # * Notes:
143 # *
144 # * Returns: Empty string if the test succeeds.
145 # * A non-zero result is the first address at which an
146 # * incorrect value was read back. By examining the
147 # * contents of memory, it may be possible to gather
148 # * additional information about the problem.
149 # *
150 #***********************************************************************************
151 proc memTestDevice { baseAddress nBytes } {
152 echo "Running memTestDevice"
153
154 echo "memTestDevice: Filling memory with a known pattern..."
155 for {set pattern 1; set offset 0} {$offset < $nBytes} {incr pattern; incr offset 32} {
156 memwrite32 [expr $baseAddress + $offset] $pattern
157 }
158
159 echo "memTestDevice: Checking each location and inverting it for the second pass..."
160 for {set pattern 1; set offset 0} {$offset < $nBytes} {incr pattern; incr offset 32} {
161 set addr [expr $baseAddress + $offset]
162 set data [memread32 $addr]
163
164 if {$data != $pattern} {
165 echo "FAILED memTestDevice_pattern: Address: [convertToHex $addr], Pattern: [convertToHex $pattern], Returned: [convertToHex $data], offset: [convertToHex $offset]"
166 return $pattern
167 }
168
169 set antiPattern [expr ~$pattern]
170 memwrite32 [expr $baseAddress + $offset] $antiPattern
171 }
172
173 echo "memTestDevice: Checking each location for the inverted pattern and zeroing it..."
174 for {set pattern 1; set offset 0} {$offset < $nBytes} {incr pattern; incr offset 32} {
175 set antiPattern [expr ~$pattern & ((1<<32) - 1)]
176 set addr [expr $baseAddress + $offset]
177 set data [memread32 $addr]
178 set dataHex [convertToHex $data]
179 set antiPatternHex [convertToHex $antiPattern]
180 if {[expr $dataHex != $antiPatternHex]} {
181 echo "FAILED memTestDevice_antipattern: Address: [convertToHex $addr], antiPattern: $antiPatternHex, Returned: $dataHex, offset: $offset"
182 return $pattern
183 }
184 }
185 }
186
187 proc convertToHex { value } {
188 format 0x%08x $value
189 }