tcl: add memory testing functions for board diagnostics 55/1455/4
authorPaul Fertser <fercerpav@gmail.com>
Fri, 21 Jun 2013 21:16:41 +0000 (01:16 +0400)
committerSpencer Oliver <spen@spen-soft.co.uk>
Tue, 29 Oct 2013 22:50:33 +0000 (22:50 +0000)
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>
doc/openocd.texi
tcl/tools/memtest.tcl [new file with mode: 0644]

index a2bcaf8..c7776b1 100644 (file)
@@ -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 (file)
index 0000000..02f94d3
--- /dev/null
@@ -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
+}