Before start talking about OpenOCD lets remember the different pieces of software and hardware you should have in order to successfully code, build and flash any microcontroller. The next diagram shows those different parts:

  • Editor.- (plain text editor) Anything that allows you to write your programs in C, like notepad or VS Code
  • Built Toolchain.- The compiler, linker assembler, etc. In our case is GCC
  • Debug Server.- The piece of software the communicate your PC with the external debugger probe, OpenOCD is our choice
  • Debugger Probe.- Thew hardware that communicates your PC with the microcontroller. ST-Link is the ones that comes with my Nucleo-G0B1 board
  • Target.- The actual microcontroller, for me is the stm32g0b1re
  • Debugger.- Optional but very useful, the program that among other things lets you run the program step by step, watch variables and etc. Its GDB btw

None of these part are optional otherwise it would impossible to run any program but each of them are interchangeable, maybe you change the debugger probe for a Jlink or the build toolchain for clang. But our choice is one of the most known debug server in the open source world.

To run openocd you must do it with sudo because it access to usb port and that required privilieg access. Apart from that you must specify a series of commands to recognize the target and the debugger probe amount other important things, most of the time these options can be compiled into what is called configuration files, you can create your own using Tcl.

$ openocd -f <configuration file> -c "<optional command>"

OpenOCD installation comes with predefined configuration files that can be found at /usr/share/openocd/scripts, ( in windows should be /c/tools/openocd/share/openocd ) these scripts are group in three different folders:

  • target.- specific instructions for microcontroller and/or processors
  • interface.- scripts with information regarding the debugger/flahser probe to use in combination with the target
  • board.- combine the scripts target and interface and are written for development board already in the market ( like the nucleo boards )

In our case we need the script for the stlink and the target stm32g0, also we must specify the communication protocol between the debugger probe and the micro is SWD plus some reset options.

$ openocd -f interface/stlink.cfg -c "transport select hla_swd" -f target/stm32g0x.cfg -c "reset_config srst_only"
Open On-Chip Debugger 0.11.0+dev-00715-g480d4e177-dirty (2022-06-22-18:51)
Licensed under GNU GPL v2
For bug reports, read
        http://openocd.org/doc/doxygen/bugs.html
hla_swd
Info : The selected transport took over low-level target control. The results might differ compared to plain JTAG/SWD
srst_only separate srst_nogate srst_open_drain connect_deassert_srst

Info : Listening on port 6666 for tcl connections
Info : Listening on port 4444 for telnet connections
Info : clock speed 2000 kHz
Info : STLINK V2J40M27 (API v2) VID:PID 0483:374B
Info : Target voltage: 3.232941
Info : [stm32g0x.cpu] Cortex-M0+ r0p1 processor detected
Info : [stm32g0x.cpu] target has 4 breakpoints, 2 watchpoints
Info : starting gdb server for stm32g0x.cpu on 3333
Info : Listening on port 3333 for gdb connections

Woooohuu!!!, OpenOCD just open a connection between your PC and the microcontroller, now what?. Well take a look to what is saying, it already identify the debugger probe and the processor in use plus the connection speed of 2MHz. It is called debug server for a reason, what is doing is listening for connections on several ports, 6666 for Tcl, 4444 for telnet connections and 3333 for GDB ( we gonna use this one for debug session, later on ).

I think Telnet does not come with windows but ...

$ choco install telnet

Open another terminal and type telnet localhost 4444

$ telnet localhost 4444
Trying ::1...
Connection failed: Connection refused
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
Open On-Chip Debugger
> 

Don't stop looking at OpenOCD terminal, you will see how the connection is accepted.

...
Info : starting gdb server for stm32g0x.cpu on 3333
Info : Listening on port 3333 for gdb connections
Info : accepting 'telnet' connection on tcp/4444

With telnet connected to OpenOCD we can send some commands to interact with our microcontroller, lets try for instance read some memory addresses, ( you can also write memory locations )

> mdw 0x20000000 16
0x20000000: 00f42400 00000000 00000001 20000010 00000000 08002fe4 08003004 08002fc4 
0x20000020: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000

>

Read some Cortex-M internal registers like the Program Counter

> reg pc
pc (/32): 0x08002e86

>

Or all of them

> reg
===== arm v7m registers
(0) r0 (/32): 0x00000001
(1) r1 (/32): 0x20023fb4
(2) r2 (/32): 0x10000000
(3) r3 (/32): 0x00000003
(4) r4 (/32): 0x20023fb4
(5) r5 (/32): 0x00000000
(6) r6 (/32): 0x00000001
(7) r7 (/32): 0x20023fd8
(8) r8 (/32): 0xffffffff
(9) r9 (/32): 0xffffffff
(10) r10 (/32): 0xffffffff
(11) r11 (/32): 0xffffffff
(12) r12 (/32): 0xffffffff
(13) sp (/32): 0x20023fa8
(14) lr (/32): 0x080001dd
(15) pc (/32): 0x08002e86
(16) xPSR (/32): 0x01000000
(17) msp (/32): 0x20023fa8
(18) psp (/32): 0xfffffffc
(20) primask (/1): 0x00
(21) basepri (/8): 0x00
(22) faultmask (/1): 0x00
(23) control (/3): 0x00
===== Cortex-M DWT registers

>

But what we really want is to flash the binary files we generate in our previous program to the microcontroller flash memory, well then we need to write the next series of commands

> reset halt
Unable to match requested speed 2000 kHz, using 1800 kHz
Unable to match requested speed 2000 kHz, using 1800 kHz
target halted due to debug-request, current mode: Thread 
xPSR: 0xf1000000 pc: 0x08000008 msp: 0x20024000

> flash write_image erase main.elf
device idcode = 0x10006467 (STM32G0B/G0Cx - Rev A : 0x1000)
RDP level 0 (0xAA)
flash size = 512kbytes
flash mode : dual-bank
Padding image section 0 at 0x0800005c with 4 bytes (bank write end alignment)
Adding extra erase range, 0x08000060 .. 0x080007ff
auto erase enabled
wrote 96 bytes from file main.elf in 0.066907s (1.401 KiB/s)

> reset run
Unable to match requested speed 2000 kHz, using 1800 kHz
Unable to match requested speed 2000 kHz, using 1800 kHz

> exit
Connection closed by foreign host

For simple things a would say using telnet is a little too much specially if you are not using a remote connection, in this case for flashing the micro we can do the entire process with OpenOCD all alone.

$ openocd -f interface/stlink.cfg -c "transport select hla_swd" -f target/stm32g0x.cfg -c "reset_config srst_only" -c "program main.elf verify reset" -c shutdown

We can simplify the last line using a pre-build configuration file for our nucleo board

 $ openocd -f board/st_nucleo_g0.cfg -c "program main.elf verify reset" -c shutdown

But why bother typing long instructions in the terminal if we can automate the process with our makefile. Lets create a new target called flash

#Target to build the program
all:
# Compile main.c
	arm-none-eabi-gcc -c main.c -o main.o -mcpu=cortex-m0plus
# link to generate main.elf
	arm-none-eabi-gcc main.o -o main.elf -nostdlib -mcpu=cortex-m0plus -Tlinker.ld -Wl,-Map=main.map
	
# Target to flash the mcu	
flash :
    openocd -f board/st_nucleo_g0.cfg -c "program main.elf verify reset" -c shutdown

As easy as typing …

$ make flash
openocd -f board/st_nucleo_g0.cfg -c "program main.elf verify reset" -c shutdown
Open On-Chip Debugger 0.11.0+dev-00715-g480d4e177-dirty (2022-06-22-18:51)
Licensed under GNU GPL v2
For bug reports, read
        http://openocd.org/doc/doxygen/bugs.html
Info : The selected transport took over low-level target control. The results might differ compared to plain JTAG/SWD
srst_only separate srst_nogate srst_open_drain connect_deassert_srst

Info : clock speed 2000 kHz
Info : STLINK V2J40M27 (API v2) VID:PID 0483:374B
Info : Target voltage: 3.234510
Info : [stm32g0x.cpu] Cortex-M0+ r0p1 processor detected
Info : [stm32g0x.cpu] target has 4 breakpoints, 2 watchpoints
Info : starting gdb server for stm32g0x.cpu on 3333
Info : Listening on port 3333 for gdb connections
Info : Unable to match requested speed 2000 kHz, using 1800 kHz
Info : Unable to match requested speed 2000 kHz, using 1800 kHz
target halted due to debug-request, current mode: Thread 
xPSR: 0xf1000000 pc: 0x08000008 msp: 0x20024000
** Programming Started **
Info : device idcode = 0x10006467 (STM32G0B/G0Cx - Rev A : 0x1000)
Info : RDP level 0 (0xAA)
Info : flash size = 512kbytes
Info : flash mode : dual-bank
Info : Padding image section 0 at 0x0800005c with 4 bytes (bank write end alignment)
Warn : Adding extra erase range, 0x08000060 .. 0x080007ff
** Programming Finished **
** Verify Started **
** Verified OK **
** Resetting Target **
Info : Unable to match requested speed 2000 kHz, using 1800 kHz
Info : Unable to match requested speed 2000 kHz, using 1800 kHz
shutdown command invoked

Told you is gonna blink…

Want to compile and flash within a single line

$ make && make flash