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