Using OpenOCD with the nRF52 (Bluetooth 5 SoC)

2020-03-19 by Jens Hauke

(Image of nRF52832)

Flashing Firmware

I am using OpenOCD with an ST-Link V2 USB dongle for a while now to flash nRF51 modules. The latest released version of OpenOCD is 0.10.0 (as of March 2020). Even if this version is already some years old, it was tagged on Jan 22 2017, it runs very well and does a perfect job for a wide range of microcontrollers with support for many programmers.

As an example, here is a typical session to flash a nRF51822 module. The hex file is from the eddystone beacon example of Nordics SDK 12.3.0, build on Linux/Debian Buster (10.3).

phoenix ~>openocd -f interface/stlink-v2.cfg  -f target/nrf51.cfg -c "telnet_port pipe;tcl_port disabled;gdb_port disabled;log_output /dev/null"
Open On-Chip Debugger 0.10.0
Licensed under GNU GPL v2
For bug reports, read
	http://openocd.org/doc/doxygen/bugs.html
Info : auto-selecting first available session transport "hla_swd". To override use 'transport select <transport>'.
Info : The selected transport took over low-level target control. The results might differ compared to plain JTAG/SWD
adapter speed: 1000 kHz
Open On-Chip Debugger
> nrf51 mass_erase
nrf51 mass_erase
nRF51822-QFAA(build code: H0) 256kB Flash
Target not halted

> halt
halt
target halted due to debug-request, current mode: Handler HardFault
xPSR: 0xc1000003 pc: 0xfffffffe msp: 0xffffffd8
> nrf51 mass_erase
nrf51 mass_erase
> flash write_image /opt/nordic/nRF5_SDK_12.3.0_d7731ad/examples/ble_peripheral/ble_app_eddystone/custom_board/s130/armgcc/_build/nrf51822_xxac.hex
flash write_image /opt/nordic/nRF5_SDK_12.3.0_d7731ad/examples/ble_peripheral/ble_app_eddystone/custom_board/s130/armgcc/_build/nrf51822_xxac.hex
using fast async flash loader. This is currently supported
only with ST-Link and CMSIS-DAP. If you have issues, add
"set WORKAREASIZE 0" before sourcing nrf51.cfg to disable it
target halted due to breakpoint, current mode: Handler HardFault
xPSR: 0x61000003 pc: 0x2000001e msp: 0xffffffd8
wrote 52892 bytes from file /opt/nordic/nRF5_SDK_12.3.0_d7731ad/examples/ble_peripheral/ble_app_eddystone/custom_board/s130/armgcc/_build/nrf51822_xxac.hex in 1.295458s (39.872 KiB/s)
> flash write_image /opt/nordic/nRF5_SDK_12.3.0_d7731ad/components/softdevice/s130/hex/s130_nrf51_2.0.1_softdevice.hex
flash write_image /opt/nordic/nRF5_SDK_12.3.0_d7731ad/components/softdevice/s130/hex/s130_nrf51_2.0.1_softdevice.hex
Padding image section 0 with 2112 bytes
using fast async flash loader. This is currently supported
only with ST-Link and CMSIS-DAP. If you have issues, add
"set WORKAREASIZE 0" before sourcing nrf51.cfg to disable it
target halted due to breakpoint, current mode: Handler HardFault
xPSR: 0x61000003 pc: 0x2000001e msp: 0xffffffd8
wrote 110560 bytes from file /opt/nordic/nRF5_SDK_12.3.0_d7731ad/components/softdevice/s130/hex/s130_nrf51_2.0.1_softdevice.hex in 3.267032s (33.048 KiB/s)
> reset
reset

For newer nRF52832 modules an OpenOCD target target/nrf52.cfg is also defined. But if you try to flash with it, you will get an error message which does not directly describe what went wrong. Neither program {hexfile} nor flash write_image {hexfiles} works:

phoenix ~>/usr/bin/openocd -f interface/stlink-v2.cfg  -f target/nrf52.cfg  -c "telnet_port pipe;tcl_port disabled;gdb_port disabled ;log_output /dev/null"
Open On-Chip Debugger 0.10.0
Licensed under GNU GPL v2
For bug reports, read
	http://openocd.org/doc/doxygen/bugs.html
Info : auto-selecting first available session transport "hla_swd". To override use 'transport select <transport>'.
Info : The selected transport took over low-level target control. The results might differ compared to plain JTAG/SWD
adapter speed: 10000 kHz
reset halt

Open On-Chip Debugger
> reset halt
target halted due to debug-request, current mode: Thread 
xPSR: 0x01000000 pc: 0x000008e8 msp: 0x20000400
> reset
reset
> program nrf52832_xxaa.hex
program nrf52832_xxaa.hex
target halted due to debug-request, current mode: Thread 
xPSR: 0x01000000 pc: 0x000008e8 msp: 0x20000400
** Programming Started **
embedded:startup.tcl:487: Error: ** Programming Failed **
in procedure 'program' 
in procedure 'program_error' called at file "embedded:startup.tcl", line 543
at file "embedded:startup.tcl", line 487
> flash write_image nrf52832_xxaa.hex
flash write_image nrf52832_xxaa.hex
invalid subcommand "write_image nrf52832_xxaa.hex"
in procedure 'flash'
> 

So then what can be done? As it turns out, OpenOCD has developed further and further in recent years. There is an active community with many contributors and, of course, also the nRF52 support improved a lot! It’s just that no official release has been made for a long time. We have to build a more current version by ourselves…

Building OpenOCD

The first thing to do after cloning the git repository is to build an autotools configure file. This will be done with the bootstrap script located in the root folder of the repo. The usual configure; make; make install runs smooth with dev-packages from debian. For “cmsis-dap” support i had to install some usb libraries:

apt install libusb-dev libusb-1.0-0-dev libusb-1.0-0

OpenJTAG requires:

apt install libhidapi-dev libftdi-dev libftdi1-dev

After installing all dependent dev-packages, building should look similar to the following output. To get a full-featured OpenOCD the output of the “OpenOCD configuration summary” should tell “yes” in every line.

phoenix ~>git clone https://git.code.sf.net/p/openocd/code openocd
Cloning into 'openocd'...
remote: Enumerating objects: 61971, done.
remote: Counting objects: 100% (61971/61971), done.
remote: Compressing objects: 100% (25916/25916), done.
remote: Total 61971 (delta 50904), reused 43571 (delta 35892)
Receiving objects: 100% (61971/61971), 14.07 MiB | 1.07 MiB/s, done.
Resolving deltas: 100% (50904/50904), done.
phoenix ~>cd openocd
phoenix ~/openocd>./bootstrap   # will create the ./configure script
…
Bootstrap complete. Quick build instructions:
./configure ....
phoenix ~/openocd>mkdir build; cd build
phoenix ~/openocd/build>../configure --enable-cmsis-dap --enable-openjtag --prefix=/opt/openocd
… <checking 1001 things and finally:> libjaylink configuration summary:
 - Package version ................ 0.2.0-git-f73ad5e
 - Library version ................ 0:0:0
 - Installation prefix ............ /opt/openocd
 - Building on .................... x86_64-pc-linux-gnu
 - Building for ................... x86_64-pc-linux-gnu

Enabled transports:
 - USB ............................ yes
 - TCP ............................ yes



OpenOCD configuration summary
--------------------------------------------------
MPSSE mode of FTDI based devices        yes (auto)
ST-Link Programmer                      yes (auto)
TI ICDI JTAG Programmer                 yes (auto)
Keil ULINK JTAG Programmer              yes (auto)
Altera USB-Blaster II Compatible        yes (auto)
Bitbang mode of FT232R based devices    yes (auto)
Versaloon-Link JTAG Programmer          yes (auto)
TI XDS110 Debug Probe                   yes (auto)
OSBDM (JTAG only) Programmer            yes (auto)
eStick/opendous JTAG Programmer         yes (auto)
Andes JTAG Programmer                   yes (auto)
USBProg JTAG Programmer                 yes (auto)
Raisonance RLink JTAG Programmer        yes (auto)
Olimex ARM-JTAG-EW Programmer           yes (auto)
CMSIS-DAP Compliant Debugger            yes
Cypress KitProg Programmer              yes (auto)
Altera USB-Blaster Compatible           yes (auto)
ASIX Presto Adapter                     yes (auto)
OpenJTAG Adapter                        yes
SEGGER J-Link Programmer                yes (auto)
phoenix ~/openocd/build>make
phoenix ~/openocd/build>make install

The last step “make install” creates /opt/openocd/bin/openocd.

Running OpenOCD with nRF52 target

OpenOCD typically runs as a program in one terminal, and you interact with it via telnet on port 4444 from a second terminal. One neat trick to avoid the additional telnet session is to add -c "telnet_port pipe" to your command line. This will pipe stdin into openocd instead of opening the telnet port. With this you can easily run your session in just one terminal.

Some things have changed with the Dec 2019 version. The ST-Link interface configuration file for example has been renamed.

WARNING: interface/stlink-v2.cfg is deprecated, please switch to interface/stlink.cfg

To erase all flash contents of the chip call nrf5 mass_erase. This is not a typo. “nrf5”, not “nrf52”. But don’t get confused: For nRF51 you still have to use nrf51 mass_erase.

Finally, flashing a nrf52832_xxaa.hex firmware using a ST-Link V2 USB dongle is very similar to the nRF51822 procedure:

phoenix ~> /opt/openocd/bin/openocd -f interface/stlink.cfg -f target/nrf52.cfg -c "telnet_port pipe;log_output /dev/null"
Open On-Chip Debugger 0.10.0+dev-00960-g28556de6-dirty (2019-11-20-20:59)
Licensed under GNU GPL v2
For bug reports, read
	http://openocd.org/doc/doxygen/bugs.html
Info : auto-selecting first available session transport "hla_swd". To override use 'transport select <transport>'.
Info : The selected transport took over low-level target control. The results might differ compared to plain JTAG/SWD

Open On-Chip Debugger
> init
init
> halt;nrf5 mass_erase
halt;nrf5 mass_erase
target halted due to debug-request, current mode: Handler HardFault
xPSR: 0x01000003 pc: 0xfffffffe msp: 0xffffffd8
nRF52832-QFAA(build code: E0) 512kB Flash
> program nrf52832_xxaa.hex verify reset
program nrf52832_xxaa.hex verify reset
target halted due to debug-request, current mode: Thread 
xPSR: 0x01000000 pc: 0xfffffffe msp: 0xfffffffc
** Programming Started **
using fast async flash loader. This is currently supported
only with ST-Link and CMSIS-DAP. If you have issues, add
"set WORKAREASIZE 0" before sourcing nrf51.cfg/nrf52.cfg to disable it
** Programming Finished **
** Verify Started **
** Verified OK **
** Resetting Target **
> exit
exit

OpenOCD, the “Open On-Chip Debugger”, is more than a tool to flash firmware. I also like to use it for debugging via GDB and for collecting diagnostic output via RTT. But that are topics for another day.