Tutorial: Using Single Wire Output SWO with ARM Cortex-M and Eclipse

As a standard procedure, I add some console functionality to my embedded applications. That way I have a command line interface and can inspect and influence the target system. One interesting hardware feature of ARM Cortex-M is Single Wire Output (SWO): it allows to send out data (e.g. strings) over up to 32 different stimulus ports, over a single wire.

swo-pin-on-arm-debug-header

swo-pin-on-arm-debug-header

Debug Trace Output? SWO!

As the standard text and command line interface to my target boards I’m using a normal UART/SCI. However, on many boards the UART’s are used by the application.

There is semihosting, but this is very slow, depends on the debugger and toolchain/library used plus is a wast of FLASH and RAM so I don’t recommend using semihosting at all.

There is USB CDC, but this requires a USB connector, a USB stack and a microcontroller capable of USB. Not applicable in all cases.

There is Segger RTT which is small, fast and best of all does not need any special pins. But works only with Segger debugging probes.

ARM SWO

But there is yet another thing: ARM SWO trace port as defined by ARM for Cortex-M. Technically SWO is a single trace pin which is used to stream out data packets with a certain clock rate, derived from the CPU core clock. You can think of SWO as a kind of UART TX pin using a special format to send out data packets. Up to 32 packet types (or stimulus) can be used. What kind of data is sent is up to the application, and there is only very little CPU processing or code needed.

Common SWO usages are:

  • Sending debug messages as strings
  • Recording interrupt entry/exit
  • Recording function entry/exit
  • Periodic PC value sampling
  • Event notification
  • Variable or memory cell change over time

One of the most common usage is the first one: using SWO to print debug messages from the target in UART style. And this is what I’m going to show in this article. There would be another encoding (Manchester encoded) which is not covered here.

ARM CoreSight

SWO is part of the ARM CoreSight Debug block which usually is part of Cortex-M3, M4 and M7:

As shown in that overview slide, over SWO (or SWV) ITM and DWT trace messages can be sent. For instruction trace up to 4 extra trace pins are required (see “First Steps with Ozone and the Segger J-Link Trace Pro” how to get instruction trace).

SWO Pin

The precondition to use SWO is that the this pin is available on the debug header. This is the case for my TWR-K64F120M board:

trace_swo_pin

trace_swo_pin (Source: TWR-K64F120M Schematics)

As shown above, the SWO trace pin is shared with the JTAG TDO pin. So this means that SWO cannot be used with JTAG, it only can be used with SWD (see “SWD Debugging with the FRDM-KL25Z Board“).

Carefully check your board schematics if it supports SWO. For example on the FRDM-K64F (same device as on above TWR-K64F120M), the SWO pin is *not* routed to the debug header:

no-swo-on-frdm-k64f

no-swo-on-frdm-k64f

Debug Probe and SWO

In order to use SWO, I need a debug probe capable reading the SWO pin. For example the Freescale/NXP OpenSDA onboard debug interface hardware on the Freedom and Tower modules does not support SWO (see “Solving “The connected emulator does not support serial wire output (SWO)”“).

The external Segger J-Link however do support the SWO pin. Below I have a J-Link EDU connected to the debug and trace port of the TWR-K64F120M board:

j-link-edu-connected-to-trace-port

j-link-edu-connected-to-trace-port

Source Code to Send Debug Messages over SWO Trace Pin

In order to write debug message over SWO to the host, a small piece of code is needed.

💡 An example project with all the sources is available on GitHub: https://github.com/ErichStyger/mcuoneclipse/tree/master/Examples/KDS/TWR-K64F120M/TWR-K64F120M_Demo/Sources

External tools (like the Segger RTT viewer) can set up the SWO in the hardware. My recommendation is to initialize it from the application. Because the SWO trace output clock is derived from the CPU clock, the Init function needs that clock plus which SWO port number to be initialized. Below is the code I’m using to initialize the SWO output to a default of 64k baud:

/*!
 * \brief Initialize the SWO trace port for debug message printing
 * \param portBits Port bit mask to be configured
 * \param cpuCoreFreqHz CPU core clock frequency in Hz
 */
void SWO_Init(uint32_t portBits, uint32_t cpuCoreFreqHz) {
  uint32_t SWOSpeed = 64000; /* default 64k baud rate */
  uint32_t SWOPrescaler = (cpuCoreFreqHz / SWOSpeed) - 1; /* SWOSpeed in Hz, note that cpuCoreFreqHz is expected to be match the CPU core clock */

  CoreDebug->DEMCR = CoreDebug_DEMCR_TRCENA_Msk; /* enable trace in core debug */
  *((volatile unsigned *)(ITM_BASE + 0x400F0)) = 0x00000002; /* "Selected PIN Protocol Register": Select which protocol to use for trace output (2: SWO NRZ, 1: SWO Manchester encoding) */
  *((volatile unsigned *)(ITM_BASE + 0x40010)) = SWOPrescaler; /* "Async Clock Prescaler Register". Scale the baud rate of the asynchronous output */
  *((volatile unsigned *)(ITM_BASE + 0x00FB0)) = 0xC5ACCE55; /* ITM Lock Access Register, C5ACCE55 enables more write access to Control Register 0xE00 :: 0xFFC */
  ITM->TCR = ITM_TCR_TraceBusID_Msk | ITM_TCR_SWOENA_Msk | ITM_TCR_SYNCENA_Msk | ITM_TCR_ITMENA_Msk; /* ITM Trace Control Register */
  ITM->TPR = ITM_TPR_PRIVMASK_Msk; /* ITM Trace Privilege Register */
  ITM->TER = portBits; /* ITM Trace Enable Register. Enabled tracing on stimulus ports. One bit per stimulus port. */
  *((volatile unsigned *)(ITM_BASE + 0x01000)) = 0x400003FE; /* DWT_CTRL */
  *((volatile unsigned *)(ITM_BASE + 0x40304)) = 0x00000100; /* Formatter and Flush Control Register */
}

From my main application I initialize it like this (for a system with a 24 MHz core clock):

#define CPU_CORE_FREQUENCY_HZ 120000000 /* CPU core frequency in Hz */

SWO_Init(0x1, CPU_CORE_FREQUENCY_HZ);

The printing is done in SWO_PrintChar():

/*!
 * \brief Sends a character over the SWO channel
 * \param c Character to be sent
 * \param portNo SWO channel number, value in the range of 0 to 31
 */
void SWO_PrintChar(char c, uint8_t portNo) {
  volatile int timeout;

  /* Check if Trace Control Register (ITM->TCR at 0xE0000E80) is set */
  if ((ITM->TCR&ITM_TCR_ITMENA_Msk) == 0) { /* check Trace Control Register if ITM trace is enabled*/
    return; /* not enabled? */
  }
  /* Check if the requested channel stimulus port (ITM->TER at 0xE0000E00) is enabled */
  if ((ITM->TER & (1ul<<portNo))==0) { /* check Trace Enable Register if requested port is enabled */
    return; /* requested port not enabled? */
  }
  timeout = 5000; /* arbitrary timeout value */
  while (ITM->PORT[0].u32 == 0) {
    /* Wait until STIMx is ready, then send data */
    timeout--;
    if (timeout==0) {
      return; /* not able to send */
    }
  }
  ITM->PORT[0].u16 = 0x08 | (c<<8);
}

The above code uses a very simple timeout mechanism: the important point is not to block if SWO is not enabled or if SWO port is not ready, otherwise the application will blocked.

To make it easier to print a string, I’m using the following function:

/*!
 * \brief Sends a string over SWO to the host
 * \param s String to send
 * \param portNumber Port number, 0-31, use 0 for normal debug strings
 */
void SWO_PrintString(const char *s, uint8_t portNumber) {
  while (*s!='\0') {
    SWO_PrintChar(*s++, portNumber);
  }
}

To send a ‘hello’ over SWO it gets as easy as this:

SWO_PrintString("hello world with SWO\r\n", 0);

The first parameter is the string to send, the second is the SWO trace channel number.

GNU Arm Eclipse Viewer

To receive the SWO trace output on the host, the GNU ARM Eclipse plugins have built-in SWO support for the Segger J-Link probes.

SWO only is supported in SWD (Single Wire Debug) mode, and not in JTAG mode. So make sure that SWD is selected as debugging protocol:

swd-debug

swd-debug

In the GNU ARM Eclipse debug configuration, enable SWO and specify the CPU frequency and the SWO frequency (see the documentation about the frequencies on http://gnuarmeclipse.github.io/debug/jlink/). I have to provide the CPU frequency (120 MHz in my case), and can leave the SWO freqenzy at 0 so the J-Link will automatically determine the speed). Specify in the port mask the ports (as bitmask) used, so 0x1 is for using port 0:

swo-settings

swo-settings

With this, running the application on the target it shall show the output in the Eclipse Console View:

eclipse-console-view

eclipse-console-view

Segger SWO Viewer

Segger has a special SWO Viewer (both command line and GUI version).

In the GUI version I specify the device used, and it can sense the trace clock:

segger-gui-swo-viewer

segger-gui-swo-viewer

In the viewer I can turn on/off ports and se the data received:

segger-j-link-swo-viewer

segger-j-link-swo-viewer

Telnet: Putty

But there is no fancy viewer or Eclipse needed to see the SWO data. Segger is using by default the port 2332:

segger-swo-port

segger-swo-port

I can configure any telnet client (e.g. PuTTY) to open a session on port 2332:

putty-telnet-session-settings

putty-telnet-session-settings

And the I get the output in PuTTY:

swo-output-in-putty

swo-output-in-putty

Summary

The ARM SWO trace pin allows to send trace messages to the host. One common usage is to send debug or other messages to the host. SWO only needs a single pin, works only with SWD (not JTAG) and requires little code and ressources on the target. Unfortunately many boards do not have the SWO trace pin routed to the debug header, so if you are making your own design, routing SWO to the debug header should be at least considered.

While SWO trace output is great, it is limited to the higher end Cortex-M, I did not find it in the Cortex-M0(+) available, and it is output only, and requires a debug probe/interface supporting it. At least with Eclipse and GNU ARM Eclipse plugins in combination with Segger J-Link probes SWO output has worked great for me.

On the other side, the Segger RTT is much more versatile and very fast too. It works on all ARM Cortex, and best of all, it does not need an extra pin :-). However, it requires little more overhead and RAM resources on the target system. Plus it allows both to send and receive data. So for the serial debug message printing, the Segger RTT sounds a better solution to me.

Happy SWO’ing 🙂

Links

42 thoughts on “Tutorial: Using Single Wire Output SWO with ARM Cortex-M and Eclipse

  1. The projects generated by the GNU ARM Eclipse templates, and subsequently the projects using the µOS++/CMSIS++ APIs, all benefit from a trace channel, which can be routed either to semihosting, SWO or Segger RTT. The high level API is simple and very convenient: trace_putchar(), trace_puts() and trace_printf() (and their C++ versions, see http://micro-os-plus.github.io/reference/cmsis-plus/group__cmsis-plus-diag.html).

    Highly recommended!

    Like

  2. So many choices for debugging. I attended the LPCxpresso training at NXPFTF which used the LPC43xx boards I will need to check whether the trace used SWO Periodic PC value sampling or accessed the ETB – Embedded trace buffers. Whats the lowest cost method for instruction tracing ? Is it possible to obtain SWO sampling or ETB trace information for Kinetis devices using Ozone?

    Like

  3. Pingback: Getting printf Output from Target to Debugger | SEGGER Blog

  4. Pingback: Tutorial: Getting ETM Instruction Trace with NXP Kinetis ARM Cortex-M4F | MCU on Eclipse

  5. Hi Erich

    Nice article!
    I just got it to work with my stm32 application, but some adjustments were needed:

    Changed ITM->PORT[0].u16 = 0x08 | (c<PORT[portNo].u8 = (uint8_t)c;
    (because I was getting some garbage along with the messages)

    And I also changed 2 references of PORT[0] to PORT[portNo] in the SWO_PrintChar function, to correct the port selection

    Now it works like a charm 🙂

    I am getting the print thru the SWO viewer in the ST-LINK utility, I can even connect the STLINK in the application and start getting the messages “on the fly”, without resetting it! pretty handy

    Like

  6. Freescale/NXP seems to have routed SWO to the header on the FRDM-k64F at some point. The current schematic shows it routed and I’ve been able to get SWO working with my J-Link.
    Bottom of the board says “SCH-28163 REV E3”.

    Like

    • Hi Jeff,
      the swap is about little/big endian. I have to check about the 0x08 thing (I copied that from an example), but I think it is for marking a 16bit ITM transfer. I have to check that example. I see that other code simply writes to .u8, so this should be enough too.

      Like

  7. Hi Erich.
    This is really a nice article, thanks a lot.
    I was wondering, do you know about any SWO alternative for Cortex R4 processors?

    Like

  8. Any thoughts what could be the issue using SWO and an Atmel AT-ICE?

    This debugger isn’t listed in Keil, however, it still works to download and step thru code, if I select “CMSIS-DAP”.

    The default Trace settings even show SWO and ITM Enabled for all 32 bits of the ports, however, it’s all greyed out, so not sure if it’s legit.

    However, if I manually enable in code, and send a character:
    ITM->TCR |= ITM_TCR_ITMENA_Msk;
    ITM->TER |= 1; /* ITM Port #0 enabled */
    ITM->PORT[0U].u8 = (uint8_t)’A’;

    I don’t ever see anything in Keil’s Debug (printf) Viewer. I have a feeling the debugger hardware supports it, and is an Arm configuration issue, or Atmel’s WinUSB.sys driver.

    Any suggestions? I haven’t tried VisualGDB or Atmel Studio or Eclipse, since Keil 64K eval version is so convenient!

    Like

  9. Pingback: Overview of MCUXpresso IDE v10.2.0 | MCU on Eclipse

  10. Thanks Erich for this very useful tutorial!

    I have one question. Could you clarify?

    Without connecting the J-Link tool, you can see any output on SWO line while probing it?

    Like

  11. Pingback: New NXP MCUXpresseo IDE V10.3.0 Release | MCU on Eclipse

  12. Pingback: SWO with NXP i.MX RT1064-EVK Board | MCU on Eclipse

  13. Hello all,

    is there a possibility to have a PC sampling based profiling in eclipse?
    I used this feature in ke*l M*K and liked it very much.

    Thanks a lot in advance!

    Like

  14. When i’m using Segger SWO viewer, i need to input the TRACECLK frequency which supposedly is the same as my CPU frequency. If my HSE (external oscillator) is 8Mhz and my PLL is x9, my SYSCLK is 72Mhz – Is this the “CPU frequency”?

    Or maybe there’s a more reliable to manually verify TRACECLK frequency in the code so I know what to put down in Segger SWO viewer? I am using STM32F302R8.

    Like

    • The MXUXpresso IDE has a ‘SWO detect clock frequency detection’ built in. Yes, in most cases the SWO clock is the CPU clock, and this would be the TRACECLK. The simplest way might be just to hook up an oszilloscope to the SWO pin to verify the actual clock speed.

      Like

  15. Pingback: SWO with ARM Cortex-M33 | MCU on Eclipse

  16. Pingback: Standalone SWO | MCU on Eclipse

  17. Erich,

    Thanks for this example, and many of your others they have helped me get many projects off the ground with Eclipse and ARM.

    I have an interesting issue when trying to setup SWO using Eclipse, and was hoping to get your thoughts on it.

    I have SWO working, and I am getting the printouts in Eclipse, but this only works after I open the J-Link SWO viewer. So if I program and run my project I do not see any printouts at all. If I open the SWO viewer while I have Eclipse connected via the J-Link I get the print outs in Eclipse (and not SWO Viewer) as expected. I have tried running the J-Link SWO viewer with both -swoattach 0, and -swoattach1 thinking that maybe when I was running it there was some configuration going on that Eclipse was not doing properly.

    This is all with a Microchip AATSAME70N21 if that makes any difference (so Cortex-M7).

    Not a huge deal, mostly just an annoyance, but any light you can shine on this would be greatly appreciated.

    Thanks,
    John

    Liked by 1 person

  18. Hi, thanks for the guide and code, really useful and helpful!

    One note though, to get it working I had to change the check line 14 on one of your code snippets as below. It worked for me after doing this change. I believe this is correct because we are checking if the port is enabled, i.e. true?

    Hope this helps someone 🙂

    ORIGINAL
    if ((ITM->TER & (1ul<TER & (1ul<<portNo))==1)

    Liked by 1 person

What do you think?

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