aboutsummaryrefslogtreecommitdiff
path: root/tcl/tools/memtest.tcl
blob: c7fa591f3cee2dec7bcfc81f61bf7b1139bc64b2 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
# Algorithms by Michael Barr, released into public domain
# Ported to OpenOCD by Shane Volpe, additional fixes by Paul Fertser

set CPU_MAX_ADDRESS 0xFFFFFFFF
source [find bitsbytes.tcl]
source [find memory.tcl]

proc runAllMemTests { baseAddress nBytes } {
    memTestDataBus $baseAddress
    memTestAddressBus $baseAddress $nBytes
    memTestDevice $baseAddress $nBytes
}

#***********************************************************************************
# *
# * Function:    memTestDataBus()
# *
# * Description: Test the data bus wiring in a memory region by
# *              performing a walking 1's test at a fixed address
# *              within that region.  The address (and hence the
# *              memory region) is selected by the caller.
# *		 Ported from:
# *		 http://www.netrino.com/Embedded-Systems/How-To/Memory-Test-Suite-C
# * Notes:
# *
# * Returns:     Empty string if the test succeeds.
# *              A non-zero result is the first pattern that failed.
# *
#***********************************************************************************
proc memTestDataBus { address } {
    echo "Running memTestDataBus"

    for {set i 0} {$i < 32} {incr i} {
	# Shift bit
	set pattern [expr {1 << $i}]

	# Write pattern to memory
	memwrite32 $address $pattern

	# Read pattern from memory
	set data [memread32 $address]

	if {$data != $pattern} {
	    echo "FAILED DATABUS: Address: $address, Pattern: $pattern, Returned: $data"
	    return $pattern
	}
    }
}

#***********************************************************************************
# *
# * Function:    memTestAddressBus()
# *
# * Description: Perform a walking 1's test on the relevant bits
# *              of the address and check for aliasing.  This test
# *              will find single-bit address failures such as stuck
# *              -high, stuck-low, and shorted pins.  The base address
# *              and size of the region are selected by the caller.
# *		 Ported from:
# *		 http://www.netrino.com/Embedded-Systems/How-To/Memory-Test-Suite-C
# *
# * Notes:       For best results, the selected base address should
# *              have enough LSB 0's to guarantee single address bit
# *              changes.  For example, to test a 64-Kbyte region,
# *              select a base address on a 64-Kbyte boundary.  Also,
# *              select the region size as a power-of-two--if at all
# *              possible.
# *
# * Returns:     Empty string if the test succeeds.
# *              A non-zero result is the first address at which an
# *              aliasing problem was uncovered.  By examining the
# *              contents of memory, it may be possible to gather
# *              additional information about the problem.
# *
#***********************************************************************************
proc memTestAddressBus { baseAddress nBytes } {
    set addressMask [expr {$nBytes - 1}]
    set pattern 0xAAAAAAAA
    set antipattern 0x55555555

    echo "Running memTestAddressBus"

    echo "addressMask: [convertToHex $addressMask]"

    echo "memTestAddressBus: Writing the default pattern at each of the power-of-two offsets..."
    for {set offset 32} {[expr {$offset & $addressMask}] != 0} {set offset [expr {$offset << 1}] } {
	set addr [expr {$baseAddress + $offset}]
	memwrite32 $addr $pattern
    }

    echo "memTestAddressBus: Checking for address bits stuck high..."
    memwrite32 $baseAddress $antipattern

    for {set offset 32} {[expr {$offset & $addressMask}] != 0} {set offset [expr {$offset << 1}]} {
	set addr [expr {$baseAddress + $offset}]
	set data [memread32 $addr]

	if {$data != $pattern} {
	    echo "FAILED DATA_ADDR_BUS_SHIGH: Address: [convertToHex $addr], Pattern: [convertToHex $pattern], Returned: [convertToHex $data]"
	    return $pattern
	}
    }

    echo "memTestAddressBus: Checking for address bits stuck low or shorted..."
    memwrite32 $baseAddress $pattern
    for {set testOffset 32} {[expr {$testOffset & $addressMask}] != 0} {set testOffset [expr {$testOffset << 1}] } {
	set addr [expr {$baseAddress + $testOffset}]
	memwrite32 $addr $antipattern

	set data [memread32 $baseAddress]
	if {$data != $pattern} {
	    echo "FAILED DATA_ADDR_BUS_SLOW: Address: [convertToHex $addr], Pattern: [convertToHex $pattern], Returned: [convertToHex $data]"
	    return $pattern
	}

	for {set offset 32} {[expr {$offset & $addressMask}] != 0} {set offset [expr {$offset << 1}]} {
	    set addr [expr {$baseAddress + $offset}]
	    set data [memread32 $baseAddress]

            if {(($data != $pattern) && ($offset != $testOffset))} {
		echo "FAILED DATA_ADDR_BUS_SLOW2: Address: [convertToHex $addr], Pattern: [convertToHex $pattern], Returned: [convertToHex $data], offset: [convertToHex $offset], testOffset [convertToHex $testOffset]"
		return $pattern
	    }
        }
	set addr [expr {$baseAddress + $testOffset}]
	memwrite32 $addr $pattern
    }
}

#***********************************************************************************
# *
# * Function:    memTestDevice()
# *
# * Description: Test the integrity of a physical memory device by
# *              performing an increment/decrement test over the
# *              entire region.  In the process every storage bit
# *              in the device is tested as zero and as one.  The
# *              base address and the size of the region are
# *              selected by the caller.
# *		 Ported from:
# *		 http://www.netrino.com/Embedded-Systems/How-To/Memory-Test-Suite-C
# * Notes:
# *
# * Returns:     Empty string if the test succeeds.
# *              A non-zero result is the first address at which an
# *              incorrect value was read back.  By examining the
# *              contents of memory, it may be possible to gather
# *              additional information about the problem.
# *
#***********************************************************************************
proc memTestDevice { baseAddress nBytes } {
    echo "Running memTestDevice"

    echo "memTestDevice: Filling memory with a known pattern..."
    for {set pattern 1; set offset 0} {$offset < $nBytes} {incr pattern; incr offset 32} {
	memwrite32 [expr {$baseAddress + $offset}] $pattern
    }

    echo "memTestDevice: Checking each location and inverting it for the second pass..."
    for {set pattern 1; set offset 0} {$offset < $nBytes} {incr pattern; incr offset 32} {
	set addr [expr {$baseAddress + $offset}]
	set data [memread32 $addr]

	if {$data != $pattern} {
	    echo "FAILED memTestDevice_pattern: Address: [convertToHex $addr], Pattern: [convertToHex $pattern], Returned: [convertToHex $data], offset: [convertToHex $offset]"
	    return $pattern
	}

	set antiPattern [expr {~$pattern}]
	memwrite32 [expr {$baseAddress + $offset}] $antiPattern
    }

    echo "memTestDevice: Checking each location for the inverted pattern and zeroing it..."
    for {set pattern 1; set offset 0} {$offset < $nBytes} {incr pattern; incr offset 32} {
	set antiPattern [expr {~$pattern & ((1<<32) - 1)}]
	set addr [expr {$baseAddress + $offset}]
	set data [memread32 $addr]
	set dataHex [convertToHex $data]
	set antiPatternHex [convertToHex $antiPattern]
	if {$dataHex != $antiPatternHex} {
	    echo "FAILED memTestDevice_antipattern: Address: [convertToHex $addr], antiPattern: $antiPatternHex, Returned: $dataHex, offset: $offset"
	    return $pattern
	}
    }
}

proc convertToHex { value } {
    format 0x%08x $value
}