In this third 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 multichannel console output re-direction such that the output goes to several different console windows in the debugger using the JTAG cable, offering separate console views for different parts of your S/W.
Before reading this article on multichannel console output using SWV/ITM, I recommend you first read the previous blog post on singlechannel printf() redirection using ITM, and perhaps also the general overview of SWV event- and data- tracing available in Cortex-M devices.
As I wrote in the previous blog post, the Instrumentation Trace Macrocell (ITM) in Cortex-M cores enables applications to write arbitrary data to the SWO pin 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 previous blog post outlines this in quite some detail). The ARM CMSIS standard is to use ITM port 0 for this purpose, while the remaining 31 ports can be used for other purposes.
This article will explain how the other channels (1-31) can be used to send various types of console output from different parts of the S/W to the debugger. For example, you may want to let the application perform printf() re-direction on channel 0 as outlined in the previous blog post, while at the same time the RTOS sends log information to ITM channel 1, the TCP/IP stack logs to ITM channel 2, etc.
In the previous blog article, we used the CMSIS macro ITM_SendChar() to output a byte of data from printf(). ITM_SendChar() uses ITM channel 0, and so it cannot be used to send data on the other ITM channels. For this purpose, we write a function ITM_Out() that takes the ITM channel number and a data byte as parameters:
void ITM_Out(uint32_t port, uint32_t ch)
while (ITM->PORT[port].u32 == 0)
ITM->PORT[port].u8 = (uint8_t) ch;
ITM_Out() write a byte to any ITM channel 0-31 using low-level access. In the example above, for STM32F4 devices, the data structure “ITM” (defined in CMSIS support file stm32f4xx.h supplied by STMicroelectronics) is used to write a byte to an arbitrary ITM channel. For other targets, you may check for alternative low-level implementations to write to the ITM port.
With ITM_Out() in place, we can easily build ourselves a string-printing function that outputs a string to any ITM port/channel – the function takes ITM port/channel, a pointer to a string, and the string length as parameters:
void swvPrint(int port, char* ptr, int len)
int i = 0;
for (i = 0; i < len; i++)t
ITM_Out(port, (uint32_t) *ptr++);
With the support function swvPrint() in place, we can use that to print to various ITM ports/channels from different parts of the software. For example, we may instrument the application logic to print to ITM channel 0, and instrument the RTOS to output trace log data to ITM channel 1.
This can for example be done with FreeRTOS, which has readymade “trace hook macros” that are called by the RTOS in certain situations, such as a task switch.
In the case of FreeRTOS:
- FreeRTOS “trace hook macros” can instrument the RTOS for event tracing
- Re-define the default-empty macros to add tracing for certain RTOS events
- For example, print FreeRTOS task switch events to SWV/ITM port 1
The screenshot below show how a FreeRTOS trace hook macro can be re-defined to print task switch data on ITM port 1, using the swvPrint() and ITM_Out() helper functions outlined above.
With this RTOS trace hook instrumentation, the SWV/ITM port 1 console view will receive trace log data from the RTOS on every task switch, while port 0 for example can contain trace information from the application logic, etc.
The final part to wire it all up is to enable the ITM channel 1 in the SWV configuration dialog box (see the previous blog article for reference), as well as add a new console view for the extra ITM port in use. In Atollic TrueSTUDIO, just click on the green “+” icon in the SWV console toolbar, to add more tabs containing views that receive data from more ITM ports/channels.
Using multichannel ITM “printf re-direction” is a very powerful way to aid your debugging tasks, as you can have different SWV/ITM console views for different purposes, providing focus and filtering of a lot of software print-outs.
I have now provided an introduction to SWV event- and data- tracing, as well as covered single-channel and multi-channel “printf() re-direction” over ITM. In the next upcoming blog article, I will 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.
Later, 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
In case you want to get a good overview of Serial Wire Viewer real-time tracing on Cortex-M cores (in particular with the GNU gcc/gdb tools on Eclipse), you can also read this white paper on the subject: