OpenOCD Notes
Page Contents
From the website:
The Open On-Chip Debugger (OpenOCD) aims to provide debugging, in-system programming and boundary-scan testing for embedded target devices.
It does so with the assistance of a debug adapter, which is a small hardware module which helps provide the right kind of electrical signaling to the target being debugged.
References
Install
Clone from Github and compile from source works out of the box. As part of the configuration enable STLink compatibility if using with any of the STM ARM Cortex chips.
TEMP_DIR=/tmp
git clone https://git.code.sf.net/p/openocd/code "${TEMP_DIR}/openocd-code"
cd "${TEMP_DIR}/openocd-code" && ./bootstrap && ./configure --enable-stlink && make -j 4 && make install
Use
Use one config file, named openocd.cfg
to let your OCD server know about the adaptor being used and board etc.
You should use configuration files provided by OEMs and the like found in /usr/local/share/openocd/scripts
.
/usr/local/share/openocd/scripts/interface/...
- contains a number of.cfg
files, one for each debug adaptor./usr/local/share/openocd/scripts/board/...
- contains a number of.cfg
files, one for each board./usr/local/share/openocd/scripts/target/...
- contains a number of.cfg
files, one for each target.
The board config files for boards with a single MCU will normally contain target config, so if one is available you shouldn't need the target config.
For example, for the STM Nucleo ... the following is sufficient:
source [find interface/stlink.cfg]
source [find board/st_nucleo_f0.cfg]
When using with GDB one can specify extra options such as shown below [GDB Configuration]:
source [find interface/stlink.cfg]
gdb_flash_program enable
gdb_breakpoint_override hard
source [find board/st_nucleo_f0.cfg]
Then if using GDB, launch your GDB and type:
target extended-remote:3333
You can also get GDB to start OpenOCD itself [GDB and OpenOCD]:
target extended-remote | openocd -f openocd.cfg -c "gdb_port pipe; log_output openocd.log"
OpenOCD and RTT
RTT is a great alternative to semihosting on ARM. It is way less intrusive and much faster.
Easy when you have a SEGGER debug probe and can just use their OZONE debugger. But if you have neither OpenOCD has good support!
Just install expect
and moreutils
(to get the ts
command to timestamp output) and create the
following two scripts:
The main script is:
#!/usr/bin/expect
# If expect not installed then run `sudo apt-get install -y expect`
# If moreutils not installed then run `sudo apt-get install -y moreutils`
# CLion starts OpenOCD with a telnet port of 4444. So need to `telnet 127.0.0.1 4444` and then run these commands.
spawn telnet 127.0.0.1 4444
# Configure RTT to search for the control block from the start of RAM, with a length of 12K
expect > {send "rtt setup 0x20000000 0x4000 \"SEGGER RTT\"\n"}
# Start
expect > {send "rtt start\n"}
# Kill any previous server instance
expect > {send "rtt server stop 19021\n"}
# Setup a server that will send the RTT commands as raw text over a TCP socket. When the client connects it will
# send the string "Welcome". This, in combination with port 19021, allows the JLinkRTTClient to be used, although
# a simple telnet session would suffice.
expect > {send "rtt server start 19021 0 \"Welcome\"\n"}
expect "Listening on port 19021 for rtt connections"
trap {
send_user "\n\nCTRL-C pressed. Stopping RTT server and exiting\n"
send "rtt server stop 19021\n"
exit
} SIGINT
# Exit
expect > {send "exit\n"}
# Spawn a Telnet session to get the RTT debug output!
spawn ./openocd_telnet_rtt.sh
set timeout -1
expect "some string that wont occur so we wait forever" {send "exit\n"}
And a little helper that will timestamp telnet output and is spawnable from expect
,
called openocd_telnet_rtt.sh
:
telnet 127.0.0.1 19021 | ts '[%Y-%m-%d %H:%M:%S]'
Then execute the first script and you have an RTT debug feed. Hooray.
OpenOCD And Multiple STLink Devices
Follow the method found here:
lsusb -vvv
The output will give something like:
Bus 001 Device 003: ID 0483:374e STMicroelectronics STLINK-V3
Device Descriptor:
bLength 18
bDescriptorType 1
bcdUSB 2.00
bDeviceClass 239 Miscellaneous Device
bDeviceSubClass 2
bDeviceProtocol 1 Interface Association
bMaxPacketSize0 64
idVendor 0x0483 STMicroelectronics
idProduct 0x374e STLINK-V3
bcdDevice 1.00
iManufacturer 1 STMicroelectronics
iProduct 2 STLINK-V3
iSerial 3 123456789012345678901234
bNumConfigurations 1
Configuration Descriptor:
...
...
Just take the iSerial
string and add it to your config as:
hla_serial 123456789012345678901234
Connect DDD to OpenOCD Debug Server, Debug ARM Target From x86 Linux Host
ddd --debugger "/opt/gcc-arm-none-eabi/bin/arm-none-eabi-gdb" --gdb --eval-command="target extended-remote localhost:3333" <path to ELF file>
Example Memory Test Script
# Connect to the target
source [find interface/.. your interface here ...]
source [find target/.. your target here..]
# Start OpenOCD server
init
# Halt the CPU
reset halt
# Set SRAM boundaries etc
set start_address .. your SRAM start address here ..
set end_address .. your SRAM end address here ..
set mismatch_count 0
set patterns { 0x00 0xFF 0xAA 0x55 }
for {set pattern_idx 0} {$pattern_idx < [llength $patterns]} {incr pattern_idx} {
set curr_pattern [lindex $patterns $pattern_idx]
# Zero out RAM in a loop
puts "Writing pattern $curr_pattern to SRAM..."
for {set address $start_address} {$address < $end_address} {incr address} {
write_memory $address 8 $curr_pattern
}
puts "DONE"
puts "Checking SRAM..."
# Check if it is all zero
for {set address $start_address} {$address < $end_address} {incr address} {
# Read memory contents
set memory_content [read_memory $address 8 1]
# Check if content is not zero
if {$memory_content != $curr_pattern} {
incr mismatch_count
puts "Mismatch at address $address, value: $memory_content, expected $curr_pattern"
}
}
puts "DONE"
}
# Resume CPU execution
resume
# Exit OpenOCD
shutdown
Command Cheat Sheet
- Target control:
reset halt
- Perform as hard a reset as possible and immediately halt the targetreset init
- Perform as hard a reset as possible, immediately halt the target, and execute the reset-init script
- Memory R/W
read_memory <address> <width> <count>
, e.g.read_memory 0x20000000 32 2
write_memory <address> <width> <data>
, e.g.write_memory 0x20000000 32 {0xdeadbeef 0x00230500}
- Registers
get_reg {pc sp}
- Flash
program filename.elf verify reset exit
program filename.bin verify reset exit 0x08000000
flash erase <bank num> <first> <last>
, e.g.,flash erase_sector 0 0 last
- erase all of flashflash md[whb] <addr> [<count>]
flash fill[dwhb] <addr> <value> <length>
flash info <bank num>
Flash Programming Notes
To program flash on STM devices, generally OpenOCD writes a program to flash (which is why WORKAREASIZE
is important - this is the amount of RAM that can be used for this program). This program is then used to write to flash. If this fails, it is possible to fall back to a host-controlled halfword by halfword access. For F1's, for example, see stm32x_write_block
in src/flash/nor/stm32f1x.c
of OpenOCD source.
To find the algorithm used for target controlled flashing of the device see stm32x_write_block_async
. It loads the algorithm into
a buffer called stm32x_flash_write_code
in that function:
static const uint8_t stm32x_flash_write_code[] = {
#include "../../../contrib/loaders/flash/stm32/stm32f1x.inc"
};
So thats for F1's. The algorithm is, of course, different, for different STM targets, although its not 1 to 1, i.e. one algorithm could cover more than one STM device family.
Currently I'm interested in the STMF401x family. What algorithm does that use? To get a clue one can look in the config file for that
family, stm32f4x.cfg
. In it the following lines can be seen:
set _FLASHNAME $_CHIPNAME.flash
flash bank $_FLASHNAME stm32f2x 0 0 0 0 $_TARGETNAME
flash bank $_CHIPNAME.otp stm32f2x 0x1fff7800 0 0 0 $_TARGETNAME
The flash bank
command is flash bank name driver base size chip_width bus_width target [driver_options]
. Thus we can see that the driver is the STM32F2x device driver so we can get the algorithm for that from contrib/loaders/flash/stm32/stm32f2x.inc
.