OpenOCD – One software to rule debug them all!

OpenOCD or Open On-Chip Debugger is a perfect example of a powerful open-source project used ubiquitously in the embedded community – yet not as well-recognised as quite a few others. If you are an embedded developer working with microcontrollers or microprocessors for some time, there is a very good chance that you have directly or indirectly used software that uses OpenOCD under-the-hood. Cutting across embedded platform vendors, OpenOCD simplifies debug access using a simple set of FOSS (Free and Open Source Software).

If you have been looking for a good intro to OpenOCD or find this an interesting topic to know more about, read on!

So, what exactly is OpenOCD?

Simply put, OpenOCD is an open-source software that lets you program, debug and perform boundary scans for embedded platforms by utilising a debug adapter. In most cases, this debug adapter is a separate hardware block connected to the development computer over USB or serial ports. But, you could also run OpenOCD and have the debug I/O on the same hardware. For eg: BeagleBone Black, BeaglePlay, etc.

Why is OpenOCD a big deal?

Before we acknowledge the value that OpenOCD brings to the ecosystem, it is important to know how a typical microcontroller gets programmed. The diagram below gives a 30000 ft view of what happens when you ask the IDE to Debug the application on your target device.

Would you like to know more about the internals of a typical microcontroller debug session? Do drop a comment below about what you would want to see!

In the above diagram, it is clear to see that this process has two distinct roles.

  • Command interpreter and orchestrator – Accepting the developer’s request and breaking it down into smaller operations supported by and/or needed for that target device.
  • Executor – Low-level hardware signalling operations to transmit commands and receive responses as per the JTAG/SWD standard (BTW here’s a nice primer for knowing more about JTAG)

OpenOCD almost entirely implements the first role i.e. command interpreter and orchestrator. The second role is generally performed by a hardware debug probe that OpenOCD can talk to over a communications port. However, there are examples where OpenOCD’s own implementation can carry out the signalling operations as well. Eg: Bitbanging the IO lines on a BeagleBone Black or a Raspberry Pi.

The act of sending requests to OpenOCD is done usually by the IDE that the developer uses. However, one can also use Telnet or GDB to do this. In one of the sections below, we see how GDB can be used to send commands to OpenOCD.

OK, OpenOCD is awesome… but why should I use it?

Two primary reasons to consider using OpenOCD are:

  • Extensive target support across chip vendors – OpenOCD releases have chip/target/board configurations for products from ST, NXP, Broadcom, Microchip, Texas Instruments, Raspberry Pi, Nordic Semi, Espressif and many more mainstream vendors. Besides, you could always write your own configuration too (if you know what you are doing).
  • Enables stable development flow – It is an open secret that multiple vendors create and actively maintain their own set of IDE, compilers and related tools to not only give their customers the most streamlined experience but also to ensure that it is not possible to easily migrate code from their platforms to their competition’s. If someone were to architect a unified development flow that should work for multiple platforms, OpenOCD would be a great addition to that cause for the debugging and programming part – especially if you don’t want to spend money.

Another key reason to consider using OpenOCD is gaining a more granular control of the debug process and avoid any unnecessary operations that may slow you or your computer down. For instance, due to the graphical nature of the IDEs and multiple consumers like Breakpoint window, memory browser, register maps, etc., the overall time taken to do your operations increases.

How does OpenOCD do what it does?

OpenOCD essentially starts a set of servers – OpenOCD, GDB and Telnet. When the OpenOCD server starts up, it enters a configuration stage followed by a run stage.

The OpenOCD server can accept commands from the GDB server or the Telnet server, translate them into specific command(s) that the hardware debug adapter can understand and sends them out over the communications port like USB, serial, etc. It sounds simple enough but it really is not that simple. The act of supporting so many different targets – each with their own programming quirks and configurations makes it very challenging to keep upgrading features while not breaking something else.

The example below will make the usage of OpenOCD clearer.

Configuration Stage

During this stage, certain commands (referred to as Config Command in the official documentation) are available and can not be issued interactively. These are part of a config file which is passed as an argument during the OpenOCD invocation.

Some examples are init, gdb_port, telnet_port, etc.

Run Stage

This stage is entered immediately after the configuration stage ends. A large number of commands become available once this stage is entered. All the non-config commands can be executed in this stage and can be issued interactively.

Refer the official documentation for more details.

Getting started with OpenOCD

The easiest way to get started with OpenOCD is to choose one of the targets already supported in OpenOCD in the latest available release. You can get the latest releases from here or here.

For this post, I will use a CC1352P7 launchpad as I have this one handy (and it is a supported board!).

Step 1 – Install OpenOCD

OpenOCD releases are available here. Pick up the right one for your OS and you are good to go! The installation process is uneventful and should go thru without any hassles.

If you are doing this on an SBC like BeaglePlay, BeagleBone Black , Raspberry Pi, etc., you can also choose to build OpenOCD yourself with the appropriate drivers selected – the documentation to do so is succinct and works correctly for the above-mentioned SBCs.

Step 2 – Install GDB

You can use a GDB client for the CPU architecture of your target. For example, my target is a CC1352P7 chip which is based on a Cortex-M4F CPU. arm-none-eabi-gdb is the correct GDB client for me.

Step 3 – Tweak/confirm target configurations (optional but recommended)

To keep things simple, I chose the CC1352P7 which is a supported configuration in OpenOCD. However, we do have to make a tiny change. From the TRM of the CC1352P7 device, we see that the expected device ID is 0x1BB7702F whereas the device ID mentioned in the OpenOCD cc13x2.cfg is 0x0BB4102F. This is because there are multiple variants of the CC13x2 device and probably one of them actually has this value.

Hence, it is always a good idea to cross-check such minute details although you may find a matching target configuration in the OpenOCD release.

Step 4 – Start OpenOCD

We are almost there! All that is remaining now is to start OpenOCD and connect to it using Telnet. This is what the command looks like for my CC1352P7 launchpad.

> openocd -s /path/to/openocd/scripts/folder -f board/ti_cc13x2_launchpad.cfg

If all goes well and OpenOCD booted correctly, the output should resemble the below logs.

Open On-Chip Debugger 0.12.0 (2023-01-14-23:37)
Licensed under GNU GPL v2
For bug reports, read
http://openocd.org/doc/doxygen/bugs.html
cortex_m reset_config vectreset

Info : Listening on port 6666 for tcl connections
Info : Listening on port 4444 for telnet connections
Info : XDS110: connected
Info : XDS110: vid/pid = 0451/bef3
Info : XDS110: firmware version = 3.0.0.26
Info : XDS110: hardware version = 0x0023
Info : XDS110: connected to target via JTAG
Info : XDS110: TCK set to 2500 kHz
Info : clock speed 5500 kHz
Info : JTAG tap: cc13x2.jrc tap/device found: 0x1bb7702f (mfg: 0x017 (Texas Instruments), part: 0xbb77, ver: 0x1)
Info : JTAG tap: cc13x2.cpu enabled
Info : [cc13x2.cpu] Cortex-M4 r0p1 processor detected
Info : [cc13x2.cpu] target has 6 breakpoints, 4 watchpoints
Info : starting gdb server for cc13x2.cpu on 3333
Info : Listening on port 3333 for gdb connections

Step 5 – A few basic operations with OpenOCD

Let us look at how some basic microcontroller operations can be done using OpenOCD with a GDB client. Let us say we have a blinky example and the executable ELF file is named blinky.out.

Start GDB client

> arm-none-eabi-gdb.exe \path\to\my\blinky.out

GNU gdb (GNU Tools for Arm Embedded Processors 7-2018-q2-update) 8.1.0.20180315-git
Copyright (C) 2018 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "--host=i686-w64-mingw32 --target=arm-none-eabi".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from path\to\my\blinky.out...done.

Connect to OpenOCD server

(gdb) tar ext :3333
Remote debugging using :3333
0x1000060a in ?? ()

Erase the flash memory of the CC1352P7

(gdb) monitor flash erase_sector 0 0 86
erased sectors 0 through 86 on flash bank 0 in 0.243280s

Program our blinky.out into the flash memory

Since we started gdb with the file, we simply call load here. Otherwise, one has to first specify the file using the file command and then use load.

(gdb) load
Loading section .resetVecs, size 0xd8 lma 0x0
Loading section .text, size 0x2964 lma 0xd8
Loading section .rodata, size 0x157 lma 0x2a3c
Loading section .args, size 0x8 lma 0x2b94
Loading section .cinit, size 0x68 lma 0x2b9c
Loading section .ccfg, size 0x58 lma 0xaffa8
Start address 0x2648, load size 11355
Transfer rate: 8 KB/sec, 1892 bytes/write.

Reset and run the application

LEDs should start blinking after this step.

(gdb) r
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: path\to\my\blinky.out

Halt execution and add a breakpoint in the code

We put a breakpoint at line 96 in a file named pwmled2.c. We then continue execution after hitting the breakpoint every time using continue or simply c.

<CTRL + C pressed>
Program received signal SIGINT, Interrupt.
0x1000060a in ?? ()
(gdb) monitor reset init
JTAG tap: cc13x2.jrc tap/device found: 0x1bb7702f (mfg: 0x017 (Texas Instruments), part: 0xbb77, ver: 0x1)
JTAG tap: cc13x2.cpu enabled
[cc13x2.cpu] Only resetting the Cortex-M core, use a reset-init event handler to reset any peripherals or configure hardware srst support.
[cc13x2.cpu] halted due to debug-request, current mode: Thread
xPSR: 0x01000000 pc: 0x00002648 msp: 0x20024000

(gdb) break pwmled2.c:96
Breakpoint 1 at 0x15ae: file ../pwmled2.c, line 96.
(gdb) c
Continuing.
Note: automatically using hardware breakpoints for read-only addresses.

Breakpoint 1, mainThread (arg0=<optimized out>) at ../pwmled2.c:96
96 duty = (duty + dutyInc);
(gdb) c
Continuing.

Breakpoint 1, mainThread (arg0=<optimized out>) at ../pwmled2.c:96
96 duty = (duty + dutyInc);
(gdb) c
Continuing.

Breakpoint 1, mainThread (arg0=<optimized out>) at ../pwmled2.c:96
96 duty = (duty + dutyInc);
(gdb) c
Continuing.
...

Quit GDB client

<CTRL + C pressed>
Program received signal SIGINT, Interrupt.
0x1000060a in ?? ()
(gdb) q
A debugging session is active.

Inferior 1 [Remote target] will be detached.

Quit anyway? (y or n) y
Detaching from program: path\to\my\blinky.out, Remote target

Conclusion

OpenOCD is a very useful tool for embedded developers who like to control every aspect of their development process.

Also, it is a great way to debug and program a large variety of embedded targets using the same set of software tools – this ultimately reduces the time invested in learning new tools.

OpenOCD supports both Telnet and GDB – this helps address a wide audience as not everyone is well-versed with either of these.

In the next post, we see how the BeagleBone Black can be used to program a microcontroller and help in establishing a setup that can be programmed and debugged remotely.

Leave a ReplyCancel reply