tcl: add memory testing functions for board diagnostics
This is a tcl implementation of public domain tests by Michael Barr, http://www.barrgroup.com/Embedded-Systems/How-To/Memory-Test-Suite-C The initial porting is done by Shane Volpe and posted to the mailing list: http://www.mail-archive.com/openocd-development@lists.berlios.de/msg16676.html This patch includes some cosmetic amendments plus hardcodes 32bit word size (as the code depends on memread32/memwrite32 anyway) which fixes original code's issue of testing only the first quarter of the specified nBytes. Change-Id: I5f3a66f1f16fc4082c7a5a6aba338430646ed21c Signed-off-by: Paul Fertser <fercerpav@gmail.com> Reviewed-on: http://openocd.zylin.com/1455 Tested-by: jenkins Reviewed-by: Spencer Oliver <spen@spen-soft.co.uk> Reviewed-by: Mathias Küster <kesmtp@freenet.de>__archive__
parent
d4e195ad1b
commit
b16a7f9f6e
|
@ -79,6 +79,7 @@ Free Documentation License''.
|
|||
* Architecture and Core Commands:: Architecture and Core Commands
|
||||
* JTAG Commands:: JTAG Commands
|
||||
* Boundary Scan Commands:: Boundary Scan Commands
|
||||
* Utility Commands:: Utility Commands
|
||||
* TFTP:: TFTP
|
||||
* GDB and OpenOCD:: Using GDB and OpenOCD
|
||||
* Tcl Scripting API:: Tcl Scripting API
|
||||
|
@ -7926,6 +7927,53 @@ If @emph{xsvfdump} shows a file is using those opcodes, it
|
|||
probably will not be usable with other XSVF tools.
|
||||
|
||||
|
||||
@node Utility Commands
|
||||
@chapter Utility Commands
|
||||
@cindex Utility Commands
|
||||
|
||||
@section RAM testing
|
||||
@cindex RAM testing
|
||||
|
||||
There is often a need to stress-test random access memory (RAM) for
|
||||
errors. OpenOCD comes with a Tcl implementation of well-known memory
|
||||
testing procedures allowing to detect all sorts of issues with
|
||||
electrical wiring, defective chips, PCB layout and other common
|
||||
hardware problems.
|
||||
|
||||
To use them you usually need to initialise your RAM controller first,
|
||||
consult your SoC's documentation to get the recommended list of
|
||||
register operations and translate them to the corresponding
|
||||
@command{mww}/@command{mwb} commands.
|
||||
|
||||
Load the memory testing functions with
|
||||
|
||||
@example
|
||||
source [find tools/memtest.tcl]
|
||||
@end example
|
||||
|
||||
to get access to the following facilities:
|
||||
|
||||
@deffn Command {memTestDataBus} address
|
||||
Test the data bus wiring in a memory region by performing a walking
|
||||
1's test at a fixed address within that region.
|
||||
@end deffn
|
||||
|
||||
@deffn Command {memTestAddressBus} baseaddress size
|
||||
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.
|
||||
@end deffn
|
||||
|
||||
@deffn Command {memTestDevice} baseaddress size
|
||||
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.
|
||||
@end deffn
|
||||
|
||||
@deffn Command {runAllMemTests} baseaddress size
|
||||
Run all of the above tests over a specified memory region.
|
||||
@end deffn
|
||||
|
||||
@node TFTP
|
||||
@chapter TFTP
|
||||
@cindex TFTP
|
||||
|
|
|
@ -0,0 +1,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 {[expr $dataHex != $antiPatternHex]} {
|
||||
echo "FAILED memTestDevice_antipattern: Address: [convertToHex $addr], antiPattern: $antiPatternHex, Returned: $dataHex, offset: $offset"
|
||||
return $pattern
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
proc convertToHex { value } {
|
||||
format 0x%08x $value
|
||||
}
|
Loading…
Reference in New Issue