Writing a Multi-Port ITM Function to Improve Debugging

In “Improve Debug Efficiency with the ITM”, I discussed how the ITM on the Arm® Cortex-M processors can be used to quickly, and efficiently print debug information to a coresight terminal. We saw that the ITM is extremely fast, much faster than a UART which minimizes the overhead associated with printing log and debug information from a system. There are several ITM functions that are included in CMSIS, but unfortunately, they are written to only use one of the thirty-two stimulus ports that are available on the ITM. In this post, we will examine how to create a generic, multi-port ITM function.

Before writing our own ITM function, it’s useful to reexamine the function that is included in the CMSIS. The ITM_SendChar function takes a single parameter, a character to print on the terminal, and then prints it to stimulus port 0 provided that the ITM has been enabled. The functions code can be seen below:

__STATIC_INLINE uint32_t ITM_SendChar (uint32_t ch)
{
  if (((ITM->TCR & ITM_TCR_ITMENA_Msk) != 0UL) &&      /* ITM enabled */
      ((ITM->TER & 1UL               ) != 0UL)   )     /* ITM Port #0 enabled */
  {
    while (ITM->PORT[0].u32 == 0UL) { __NOP(); }
    ITM->PORT[0].u8 = (uint8_t)ch;
  }
  return (ch);
}

Copying ITM_SendChar is a good starting point to create our multi-port function but there are several changes that we would want to make in order to create a useful. These changes include:

  • Changing the name to ITM_SendData
  • Add the ITM port as parameter to the function
  • Add the data size in bytes as a parameter
  • Add assertions to check the parameter inputs are within valid ranges
  • Call the right data size member when transmitting the data

These changes are pretty simple to make and can be seen in the updated function below:

__STATIC_INLINE uint32_t ITM_SendData (uint8_t Channel, uint32_t Data, uint8_t DataSizeInBytes)
{
  assert(Channel < 32);
  assert(DataSizeInBytes <= 4);                       /* 1, 2, 4 are valid */
  assert((ITM->TCR & ITM_TCR_ITMENA_Msk) != 0UL);  /* ITM enabled */
  assert((ITM->TER & (1UL << Channel)  ) != 0UL);  /* ITM Port enabled? */

  /* Wait for the buffer to clear */
  while (ITM->PORT[Channel].u32 == 0UL)
  {
      __NOP();
  }

  if(DataSizeInBytes == 1)
  {
      ITM->PORT[Channel].u8 = (uint8_t)Data;
  }
  else if(DataSizeInBytes == 2)
  {
     ITM->PORT[Channel].u16 = (uint16_t)Data;
  }
  else
  {
     ITM->PORT[Channel].u32 = (uint32_t)Data;
  }

  return (Data);
}

You’ll notice that I converted the checks that the ITM is enabled from being C executable code to assertion checks. I did this because if we are using the ITM, we are undoubtedly using this for development purposes and I personally prefer to perform these checks as pre-requisites for using the function. In other words, if the ITM is disabled and someone calls this function, it’s a bug and the developer should be notified.

A second item to notice is the __STATIC_INLINE that precedes the function. Every time that the code makes a call to this function, the compiler will substitute the function call with all the code that is in the function. I mention this because this is meant to be a feature that further minimizes the overhead associated with ITM calls. Removing it will add a little overhead for the function call but it will also decrease the code size. A further optimization would be to remove the assertions and just make sure it’s clear that what the limits for the function are.

With this simple function, it’s now possible to easily transmit different debug information across different stimulus ports quickly and easily. Port 0 could be used for standard messages, Port 1 for the state of a state machine, Port 2 for sensor messages and so on and so forth. Enjoy!

Share >

4 thoughts on “Writing a Multi-Port ITM Function to Improve Debugging

  1. Appreciate this solution for debug!
    JLink has application to monitor multi port SWO, but other hw debugger no. In my case ULinkPro or STLink has not the possibility to select different port ITM. Do you have any suggestion for ULink or STLink?

    • All 32 Stimulus ports are sent back from the ITM over the SWO. You’ll notice that most IDE’s will let you select which port to enable and then you’ll be able to use the ITM Terminal to see the return data. Whether you are using a ULink, JLink or STLink should not matter.

  2. Is it safe to call this ITM function from multiple tasks under an RTOS environment? Or Should it be protected by a mutex?

    • The call should be protected in some way. You could use a mutex or you could have a gate keeping task that accepts a queue and then manages access to the ITM.

Leave a Reply to Jacob Beningo Cancel 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.