From: Paul Fertser Date: Fri, 21 Jun 2013 21:16:41 +0000 (+0400) Subject: tcl: add memory testing functions for board diagnostics X-Git-Tag: v0.8.0-rc1~202 X-Git-Url: https://git.sur5r.net/?a=commitdiff_plain;h=b16a7f9f6e3c7c417fa3d0d0bf042f032ee446f6;p=openocd 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 Reviewed-on: http://openocd.zylin.com/1455 Tested-by: jenkins Reviewed-by: Spencer Oliver Reviewed-by: Mathias Küster --- diff --git a/doc/openocd.texi b/doc/openocd.texi index a2bcaf85..c7776b10 100644 --- a/doc/openocd.texi +++ b/doc/openocd.texi @@ -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 diff --git a/tcl/tools/memtest.tcl b/tcl/tools/memtest.tcl new file mode 100644 index 00000000..02f94d30 --- /dev/null +++ b/tcl/tools/memtest.tcl @@ -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 +}