STM32F439 openocd ITM Debugger
Experimenting with a Cortex-M4 on a Nucleo Board, goal is to get printf-style debugging to work.
This microcontroller has as Instrumentation Trace Macrocell Unit (ITM), that allows for writing printf-style debug messages to "stimulus registers". Those messages then flow through a Trace Port Interface Unit (TPIU), which outputs data to a Serial Wire Output (SWO), which can then be collected using openocd
Clocks
The development board has a couple of different clocks that can drive SYSCLK. I had some pretty inconsistent results - likely due to clock drifts / temperature - using the on-board 16 MHz High-Speed Internal (HSI) oscillator. There's an 8 MHz High-Speed External (HSE) clock source available which is sourced from the on-board STLink that was working better for me.
Enable HSE using RCC_RC
RCC Clock Control Register (RCC_CR) allows us to enable HSE. We also need to wait until it's ready.
#![allow(unused)] fn main() { RCC_CR.set(RCC_CR_HSEON); while RCC_CR.get(RCC_CR_HSERDY) == 0 {} }
Feed HSE into SYSCLK
RCC Clock Configuration Register (RCC_CFGR) is used to feed the system clock with HSE.
#![allow(unused)] fn main() { RCC_CFGR.set_from(RCC_CFGR_SW, 1); while RCC_CFGR.get(RCC_CFGR_SWS) != 1 {} }
Disable HSI
HSI is useless from now on so we can disable.
#![allow(unused)] fn main() { RCC_CR.clear(RCC_CR_HSION); while RCC_CR.get(RCC_CR_HSIRDY) != 0 {} }
Configure ITM / TPIU
38.14.2 of the board's reference manual goes into great detail how the ITM need to be configured:
- Configure the TPIU and assign TRACE I/Os by configuring the DBGMCU_CR (refer to Section 38.17.2 and Section 38.16.3)
- Write
0xC5ACCE55
to the ITM Lock Access register to unlock the write access to the ITM registers - Write
0x00010005
to the ITM Trace Control register to enable the ITM with Sync enabled and an ATB ID different from 0x00 - Write
0x1
to the ITM Trace Enable register to enable the Stimulus Port 0 - Write
0x1
to the ITM Trace Privilege register to unmask stimulus ports 7:0 - Write the value to output in the Stimulus Port register 0: this can be done by software (using a printf function)
#![allow(unused)] fn main() { DBGMCU_CR.set(DBGMCU_CR_TRACE_IOEN); DBGMCU_CR.clear(DBGMCU_CR_TRACE_MODE); ITM_LOCK_ACCESS.write(0xC5AC_CE55u32); ITM_TRACE_CONTROL.write(0x0001_0005u32); ITM_TRACE_ENABLE.set(ITM_TRACE_ENABLE_PORT0); ITM_TRACE_PRIVILEGE.set(ITM_TRACE_PRIVILEGE_PORT0_7); }
However, I have reasons to believe that all of this is being handled by openocd, which we'll use next.
Write to Stimulus Port
We can write ASCII bytes to the ITM with something like this:
#![allow(unused)] fn main() { ITM_STIMULUS_PORT_0.write(character as u8); }
And before each write, we need to make sure that the stimulus port can accept writes
#![allow(unused)] fn main() { while ! ITM_STIMULUS_PORT_0.all_set(BIT0) {} }
Bake Binary
I am using rust to compile my programs
$ cargo build --release
And objcopy to make the ELF a real binary
$ aarch64-unknown-linux-gnu-objcopy \
-O binary \
target/thumbv7em-none-eabihf/release/main \
target/thumbv7em-none-eabihf/release/flash
And openocd to bake that image into the microcontroller's flash memory
$ openocd \
-f /usr/share/openocd/scripts/board/st_nucleo_f4.cfg \
-c "init; reset halt; flash write_image erase target/thumbv7em-none-eabihf/release/flash 0x08000000 bin; reset run; exit"
Running openocd
Finally we can run openocd, attach to the board and print message we wrote to the ITM to stdout. It's important to match traceclk with SYSCLK, which was 8 MHz in my case.
$ openocd \
-f /usr/share/openocd/scripts/board/st_nucleo_f4.cfg \
-c "init; stm32f4x.tpiu configure -protocol uart -traceclk 8000000 -output /dev/stdout -formatter 0; itm ports on; stm32f4x.tpiu enable"
Open On-Chip Debugger 0.12.0
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 V2J39M27 (API v2) VID:PID 0483:374B
Info : Target voltage: 3.227694
Info : [stm32f4x.cpu] Cortex-M4 r0p1 processor detected
Info : [stm32f4x.cpu] target has 6 breakpoints, 4 watchpoints
Info : starting gdb server for stm32f4x.cpu on 3333
Info : Listening on port 3333 for gdb connections
Info : SWO pin data rate adjusted by adapter to 2000000 Hz
Info : Unable to match requested speed 2000 kHz, using 1800 kHz
Info : Unable to match requested speed 2000 kHz, using 1800 kHz
[stm32f4x.cpu] halted due to debug-request, current mode: Thread
xPSR: 0x01000000 pc: 0x080001b4 msp: 0x20020000
Info : Listening on port 6666 for tcl connections
Info : Listening on port 4444 for telnet connections
Hello World