Improve Debug Efficiency with the ITM

Let’s face it. No matter how sophisticated our debug tools get, there will always be a place for an old fashioned printf statement. Unfortunately, printf tends be efficient and can dramatically affect the systems real-time performance. For developers using an Arm Cortex®-M3 or better, printf can be remapped to the Instrumentation Trace Macrocell (ITM) which will not only allow printf to be used but also dramatically improve its efficiency and remove any real-time performance hits. Let’s examine how we can use the ITM.

Before we dive into the details, its important to note that the ITM is faster and more efficient than using our normal printf through a UART or other peripheral. The ITM is faster for several reasons such as:

  • it was designed to transmit printf style messages through hardware
  • it uses a dedicated pin, SWO, to transmit the messages
  • the baud rate is based on the internal clock frequency which can make it very fast
  • it has multiple stimulus ports to easily separate out data and trace messages

Basically, a developer just drops a character into the ITM buffer and the hardware adds the time stamp and associated data and transmits it all on its own without any additional software interaction. The message is transmitted through the serial wire out (SWO) pin on the microcontroller directly into the debugger. The debugger can then log and display the message in the IDE console.

The ITM contains 32 separate stimulus ports that can be used to transmit different messages to the development environment such as debug, data and trace information. In general, port 0 is used for printf messages while port 31 is used for RTOS event messages. Other than that, ports 1 through 30 can be used for any additional information that a developer wants to transmit. This could include state machine status, sensor readings, or any other information that will be helpful when debugging a system. Each stimulus port can also have up to 10 bytes transmitted within the same ITM message.

Using the ITM to transmit printf messages is simple. Arm has included three different helper functions in the Arm CMSIS standard for the ITM. These include:

  • ITM_SendChar
  • ITM_ReceiveChar
  • ITM_CheckChar

From these API’s developers can send, receive and check for individual characters. By default, these API’s assume the port 0 stimulus port which means if another port or multiple ports are to be used, a developer will need to write those functions themselves. Using the API’s are otherwise simple. To transmit a character, a developer can simply write:

ITM_SendChar(Character);

This is helpful if we want to transmit a single character but if we want to redirect printf statements, we’ll need to make some changes to the way printf works. The modifications will depend on the development environment and compiler that is used. For an eclipse-based IDE that is using GCC, a developer can modify the write function rather than changing printf. The nanolib library that contains printf uses a write function with the following prototype to redirect printf to any source a developer may want to use:

int _write(int file, char *ptr, int len);

This function is usually not implemented by default. As you can see from the prototype, all that needs to be done is to utilize the pointer to a character buffer and use the length that defines how many characters are in the buffer. With this information, a developer can create a write function that uses the ITM that would be like the following:

int _write(int file, char *ptr, int len)
{
   int i=0;

   (void)file;

   for(i=0 ; i<len ; i++)
   {
      ITM_SendChar((uint32_t)(*ptr++));
   }

   return len;
}

The ITM can be an invaluable tool to developers who are looking for a fast and efficient method to use printf statements. We’ve looked at the ITM in general along with some tricks for getting it up and running in software. Please be aware that in order to successfully receive messages, the ITM stimulus port(s) that are used will need to be enabled and may require the debug configuration to be modified for serial wire viewing (SWV) along with the microcontroller clock rate. Other than that, enjoy fast deterministic printf messages!

 

 

If you are interested in learning more about how to debug Arm Cortex-M applications or how to effectively manage software defects, please contact info@beningo.com to learn more about how we can assist you through our training and consulting programs.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.