Import xPack OpenOCD 0.12.0-7 and GNU Make 4.4.1 from x86_64-w64-mingw32.
This commit is contained in:
@@ -0,0 +1,115 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
echo "\n\nFirmware recovery helpers"
|
||||
echo "Use -c firmware_help to get help\n"
|
||||
|
||||
set known_boards {
|
||||
"asus-rt-n16 ASUS RT-N16"
|
||||
"asus-rt-n66u ASUS RT-N66U"
|
||||
"linksys-wag200g Linksys WAG200G"
|
||||
"linksys-wrt54gl Linksys WRT54GL v1.1"
|
||||
"netgear-dg834v3 Netgear DG834G v3"
|
||||
"tp-link_tl-mr3020 TP-LINK TL-MR3020"
|
||||
"bt-homehubv1 BT HomeHub v1"
|
||||
}
|
||||
|
||||
proc firmware_help { } {
|
||||
echo "
|
||||
Your OpenOCD command should look like this:
|
||||
openocd -f interface/<jtag adapter>.cfg -f tools/firmware-recovery.tcl -c \"<commands>*; shutdown\"
|
||||
|
||||
Where:
|
||||
<jtag adapter> is one of the supported devices, e.g. ftdi/jtagkey2
|
||||
<commands> are firmware-recovery commands separated by semicolon
|
||||
|
||||
Supported commands:
|
||||
firmware_help get this help
|
||||
list_boards list known boards and exit
|
||||
board <name> select board you work with
|
||||
list_partitions list partitions of the currently selected board
|
||||
dump_part <name> <filename> save partition's contents to a file
|
||||
erase_part <name> erase the given partition
|
||||
flash_part <name> <filename> erase, flash and verify the given partition
|
||||
ram_boot <filename> load binary file to RAM and run it
|
||||
adapter speed <freq> set JTAG clock frequency in kHz
|
||||
|
||||
For example, to clear nvram and reflash CFE on an RT-N16 using TUMPA, run:
|
||||
openocd -f interface/ftdi/tumpa.cfg -f tools/firmware-recovery.tcl \\
|
||||
-c \"board asus-rt-n16; erase_part nvram; flash_part CFE cfe-n16.bin; shutdown\"
|
||||
\n\n"
|
||||
shutdown
|
||||
}
|
||||
|
||||
# set default, can be overridden later
|
||||
adapter speed 1000
|
||||
|
||||
proc get_partition { name } {
|
||||
global partition_list
|
||||
dict get $partition_list $name
|
||||
}
|
||||
|
||||
proc partition_desc { name } { lindex [get_partition $name] 0 }
|
||||
proc partition_start { name } { lindex [get_partition $name] 1 }
|
||||
proc partition_size { name } { lindex [get_partition $name] 2 }
|
||||
|
||||
proc list_boards { } {
|
||||
global known_boards
|
||||
echo "List of the supported boards:\n"
|
||||
echo "Board name\t\tDescription"
|
||||
echo "-----------------------------------"
|
||||
foreach i $known_boards {
|
||||
echo $i
|
||||
}
|
||||
echo "\n\n"
|
||||
}
|
||||
|
||||
proc board { name } {
|
||||
script [find board/$name.cfg]
|
||||
}
|
||||
|
||||
proc list_partitions { } {
|
||||
global partition_list
|
||||
set fstr "%-16s%-14s%-14s%s"
|
||||
echo "\nThe currently selected board is known to have these partitions:\n"
|
||||
echo [format $fstr Name Start Size Description]
|
||||
echo "-------------------------------------------------------"
|
||||
for {set i 0} {$i < [llength $partition_list]} {incr i 2} {
|
||||
set key [lindex $partition_list $i]
|
||||
echo [format $fstr $key [partition_start $key] [partition_size $key] [partition_desc $key]]
|
||||
}
|
||||
echo "\n\n"
|
||||
}
|
||||
|
||||
# Magic to work with any targets, including semi-functional
|
||||
proc prepare_target { } {
|
||||
init
|
||||
catch {halt}
|
||||
catch {reset init}
|
||||
catch {halt}
|
||||
}
|
||||
|
||||
proc dump_part { name filename } {
|
||||
prepare_target
|
||||
dump_image $filename [partition_start $name] [partition_size $name]
|
||||
}
|
||||
|
||||
proc erase_part { name } {
|
||||
prepare_target
|
||||
flash erase_address [partition_start $name] [partition_size $name]
|
||||
}
|
||||
|
||||
proc flash_part { name filename } {
|
||||
prepare_target
|
||||
flash write_image erase $filename [partition_start $name] bin
|
||||
echo "Verifying:"
|
||||
verify_image $filename [partition_start $name]
|
||||
}
|
||||
|
||||
proc ram_boot { filename } {
|
||||
global ram_boot_address
|
||||
prepare_target
|
||||
load_image $filename $ram_boot_address bin
|
||||
resume $ram_boot_address
|
||||
}
|
||||
|
||||
echo ""
|
||||
@@ -0,0 +1,191 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
# 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
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
# Description:
|
||||
# Measure the CPU clock frequency of an ARM Cortex-M based device.
|
||||
#
|
||||
# Return:
|
||||
# The CPU clock frequency in Hz. A negative value indicates that the loop
|
||||
# counter was saturated.
|
||||
#
|
||||
# Note:
|
||||
# You may need to adapt the number of cycles for your device.
|
||||
#
|
||||
add_help_text cortex_m_test_cpu_speed "Measure the CPU clock frequency of an ARM Cortex-M based device"
|
||||
add_usage_text cortex_m_test_cpu_speed {address [timeout [cycles_per_loop]]}
|
||||
proc cortex_m_test_cpu_speed { address { timeout 200 } { cycles_per_loop 4 } } {
|
||||
set loop_counter_start 0xffffffff
|
||||
|
||||
halt
|
||||
|
||||
# Backup registers and memory.
|
||||
set backup_regs [get_reg -force {pc r0 xpsr}]
|
||||
set backup_mem [read_memory $address 16 3]
|
||||
|
||||
# We place the following code at the given address to measure the
|
||||
# CPU clock frequency:
|
||||
#
|
||||
# 3801: subs r0, #1
|
||||
# d1fd: bne #-2
|
||||
# e7fe: b #-4
|
||||
write_memory $address 16 {0x3801 0xd1fd 0xe7fe}
|
||||
|
||||
set_reg "pc $address r0 $loop_counter_start"
|
||||
resume
|
||||
sleep $timeout
|
||||
halt
|
||||
|
||||
# Get the loop counter value from register r0.
|
||||
set loop_counter_end [dict values [get_reg r0]]
|
||||
set loop_counter_diff [expr {$loop_counter_start - $loop_counter_end}]
|
||||
|
||||
# Restore registers and memory.
|
||||
set_reg $backup_regs
|
||||
write_memory $address 16 $backup_mem
|
||||
|
||||
if { [expr {$loop_counter_end == 0}] } {
|
||||
return -1
|
||||
}
|
||||
|
||||
return [expr {double($loop_counter_diff) * $cycles_per_loop / $timeout * 1000}]
|
||||
}
|
||||
Reference in New Issue
Block a user