In this second blog post on Serial Wire Viewer (SWV) real-time tracing, I will cover how the instrumentation trace macrocell (ITM) in Cortex-M devices (such as STM32, Kinetis, LPC, EFM32, etc.) can be used for printf() re-direction such that the output goes to a console window in the debugger using the JTAG cable, removing the need for any USB or UART cable.
My first blog post in this article series was a basic background and introduction to Serial Wire Viewer tracing, and starting with this article, I will cover the specific debugger capabilities that are enabled by SWV and its related technologies SWD, SWO and ITM. First out is this article on how to use ITM.
The Instrumentation Trace Macrocell (ITM) enables applications to write arbitrary data to the SWO pin (see the previous blog post for details) using any of 32 parallel ports/channels. The data is transmitted from the Cortex-M core to the desktop PC using the JTAG cable, and modern debuggers can read and visualize that data.
A Cortex-M application can be instrumented to send data on an ITM port for multiple purposes, but the most basic and popular use is probably to use ITM to re-direct printf() output to a console view in the debugger. The ARM CMSIS standard is to use ITM port 0 for this purpose, while the remaining 31 ports can be used for other purposes.
Since ITM has 32 channels, the debugger can interpret or visualize the data on various channels differently, for example the application may write to port 0 while the RTOS may be instrumented to send trace output to channel 1. Writing a byte to the ITM port only takes one write cycle, thus taking almost no execution time from the application logic, and ITM instrumentation can thus typically remain in the shipping product, enabling for example in-the-field analysis of troubled units.
printf() redirection using ITM requires three things to work:
- The application (or rather, the compiler runtime library) must be instrumented to send printf() output to an ITM channel
- The JTAG probe must support SWV (and therefore, SWD debug mode too)
- The debugger must have SWV tracing with the corresponding ITM channel enabled, as well as SWV visualization capabilities.
Instrumenting the application for printf() output
To make the application output data to the debugger using ITM, special software instrumentation is needed. This is easily done by calling a macro defined in a CMSIS header file, typically supplied with the chip vendor’s CMSIS compatible device driver library.
To send a byte on ITM channel 0, simply call this macro (as defined in the CMSIS standard):
ITM_SendChar( 65 ); // Send ASCII code 65 = ’A’
A similar macro is available to send a byte to any ITM channel 0-31, but the macro above is a short form for the most common case of writing to ITM channel 0.
To re-direct printf() output, you need to change how printf() output its data. On the GNU GCC compiler using the Newlib or Newlib Nano libraries, printf() calls the function _write() to output the data to wherever it is supposed to be sent (such as a UART channel, an LCD display, etc.).
Also the Atollic TrueSTUDIO “tiny” printf() implementation calls _write() to output the data. In Atollic TrueSTUDIO, the function _write() is implemented in the syscalls.c file in the generated project template. To make printf() output its data to the TrueSTUDIO debugger, simply change the _write() implementation in syscalls.c to send the data to the ITM port using the macro mentioned above, like this:
int _write(int file, char *ptr, int len)
/* Implement your write code here, this is used by puts and printf for example */
for(i=0 ; i<len ; i++)
Before this works, the syscalls.c file needs to find the ITM_SendChar macro. The next step is thus to locate the core_cmX.h file which contains the macro ITM_SendChar(). The core_cmX.h file is included by the Device Peripheral Access Layer Header File (i.e. stm32f4xx.h). That file in turn needs to be included in the syscalls.c file.
If you recompile and download the application with the above changes, it will now send printf() output (via _write) to the debugger using ITM and the JTAG cable. The next step is to configure the debugger to show the data.
Configuring the debugger to show printf() output from the ITM port
A pre-requisite for using Serial Wire Viewer real-time tracing is that your debugger has SWV support. This disqualifies low-end ECLIPSE tools. Also the GNU gdb debugger is not capable of handling SWV tracing. In practice, Serial Wire Viewer is an advanced debugger capability that requires proprietary debugger functionalities to work. Having said that, SWV is typically supported by the high-end commercial IDE’s, including Atollic TrueSTUDIO (which is based on ECLIPSE and gcc/gdb).
Enable SWV tracing
To be able to watch the ITM output in the debugger console, the following configurations must be made:
- SWD mode must be selected (SWV only works with SWD mode, not JTAG mode)
- SWV tracing must be enabled
- Enter the Core Clock frequency. This is H/W board specific.
- Enter the desired SWO Clock frequency. The latter depends on the JTAG Probe and must be a multiple of the Core Clock value.
In Atollic TrueSTUDIO, this is done in the debug configuration dialog box, An example can be seen in this screenshot:
Configure SWV for ITM
One the debug configuration has been enabled for SWD and SWV, with correct clock settings, launch a debug session and run to main() or any other suitable location and stop on a breakpoint, or click the “Pause” command to stop execution.
1) In the Views menu, expand the submenu SWV and open the docking view “SWV Console”.
2) Open the SWV settings panel by clicking on the Configure Serial Wire Viewer button in the SWV Console view toolbar.
3) Configure the data ports to be traced by enabling the ITM channel 0 checkbox in the ITM stimulus ports group according to this screenshot. Uncheck all other checkboxes in this dialog box.
I recommend not enabling other SWV trace functionalities at the same time, as this may over-use the SWO pin causing packet loss due to limited bandwidth (certain other SWV tracing capabilities can send a lot of data at very high speed). Save the SWV configuration by clicking the OK button. The configuration is saved together with other debug configurations and will remain effective until changed.
4) Press the red Start/Stop Trace button to send the SWV configuration to the target board and enable SWV trace recoding. The board will not send any SWV packages until it is properly configured. The SWV Configuration must be resent, if the configuration registers on the target board are reset. Also, actual tracing will not start until the target starts to execute.
5) Start the target execution again by pressing the green Resume Debug button.
The SWV console will now show the printf() output:
By inserting a simple software instrumentation macro in the target system, and by enabling SWD/SWV and the desired ITM channel in the debugger, you can easily view the output of your Cortex-M application in a debugger console view, using the JTAG cable for transmission. And this adds minimal performance penalties as sending on ITM only cost one write cycle per byte written. If you want to test this on your own, it may be useful to know that the Atollic TrueSTUDIO IDE supports SWV and ITM using both SEGGER J-Link and STMicroelectronics ST-LINK JTAG probes.
This article covers how to re-direct all target system output to the same ITM console view. In a part 2 follow-up article to be published next week, I will write about how this concept can be extended to let different parts of the target system write to different console views, using different ITM ports (channels). This enables for example an embedded designer to send application logic output to console 1, RTOS trace information to console 2, and other (possibly “encoded”) software trace data on other consoles.
A later article will also cover how ITM can be used for generic software instrumented tracing, which does not send human readable text strings, but rather “encoded” trace packets with special meaning to the application and developer.
Once I have covered printf() re-direction (part 1 and part 2 of this article) and the generic ITM software trace article, I will continue the SWV article series by explaining how Serial Wire Viewer tracing can be used for other advanced Cortex-M debugging capabilities, including:
- Event trace (log and timeline graph)
- Exception trace (log and timeline graph)
- Exception nesting analysis
- Interrupt statistics graph
- Real-time data and memory watch
- Data trace and memory access history log
- Data trace graph (real-time “oscilloscope plot” visualisations of data)
- Speed performance analysis (statistical profiling graph)
- Execution time measurement
Read the superset blog post that covers the entire topic of Cortex-M debugging: