SWO: An ARM Printf by Any Other Name [Hackaday]

View Article on Hackaday

I’ll confess. Although printf-style debugging has a bad rep, I find myself turning to it on occasion. Sure, printf is expensive and brings in a lot of code, but if you have the space and time to use it while debugging you can always remove it before you are finished. However, what if you don’t have an output device or you are using it for something else? If you are using most modern ARM chips, you have another option — a dedicated output channel that is used for several things, including debugging output. I decided I wanted to try that on the Blackpill running mbed, and found out it isn’t as easy as you might think. But it is possible, and when you are done reading, you’ll be able to do it, too.

I’m writing this using the STM32-specific ST-LINK hardware. If you use other JTAG devices like the BlackMagic probe, you probably already have this set up for you.

What You Get

I’ll start backward with the end result, then talk about the software, so you’ll be good and motivated by the time you get to the hardware requirements. Spoiler alert: your existing hardware might need a quick hack to make it work, although you can buy something off the shelf if you prefer.

Here is a very simple test program:


SWO_Channel debugport;  // requires #include "SWO.h"
int main() 
  {
  unsigned count=0;
  debugport.printf("rnHello World from SWOrn");
  debugport.printf("CPU SystemCoreClock is %d Hzrn", SystemCoreClock);

  while (1) 
    {
    led = !led; // flip LED if output is true
    ThisThread::sleep_for(rate); // sleepy time
    if (count % 10) debugport.putc('*'); else debugport.printf("%drn",count); 
    count++;
    }
}

Nothing hard to imagine here. You can use putc or printf to write to the debugging output. As you can see in the figure, you get a nice window that shows all the output. There are actually 32 channels of output, but channel 0 is reserved for the debugging output. In this case, I picked All because its the only thing coming out of the device, anyway.

What You Need

ST’s STM32CubeProgrammer can display SWO data.

First, you need a compatible ARM chip. Not all ARM chips support ITM — the Integrated Trace Macrocell — but that’s what you need. There will be one pin on the device marked SWO (and probably other things, too). Since I’m using the Blackpill with an STM32F411CE, we know it should work and the output pin will be PB3.

You also need an ST-Link dongle that has an SWO pin. Unfortunately, the cheap ones that look like a USB memory device you typically get don’t have the SWO pin. You can, however, easily hack them. The “full” ST-Link V2 has the pin brought out, but is usually a lot more expensive. However, if you shop the usual Chinese shops, you can usually find one for a reasonable price. I paid less than $10.

Of course, you also need some sort of tool to read the output. A normal terminal won’t do it, but ST’s STM32CubeProgrammer can easily read the data. There are, of course, other options, too. Many IDEs and debuggers can read SWO output. There are also some open source tools, but the Ubuntu packages are too old and the release packages didn’t work. Building it from scratch did work, though.

Software Setup

Since I’m using Mbed, the first thing I did was go looking for a library. I wasn’t disappointed. The library is a thin wrapper around the ITM functions in CMSIS, so if you aren’t using Mbed, just have a look at those functions and you’ll be able to figure it out. If you prefer STM32Duino, check this out for something similar.

Once I added it to the project, I had to fix one small thing. It probably didn’t matter, but there is an instance where an array is allocated for a file name and then deleted improperly. Note the delete in the code below:


bool SWO_Channel::claim (FILE *stream) {
  if ( FileBase::getName() == NULL) {
  error("claim requires a name to be given in the instantiator of the SWO instance!rn");
  }

//Add '/' before name:
  char *path = new char[strlen(FileBase::getName()) + 2];
  sprintf(path, "https://hackaday.com/%s", FileBase::getName());

  if (freopen(path, "w", stream) == NULL) {
// Failed, should not happen
  return false;
}

  delete [] path;   //  fixed

//No buffering
  setvbuf(stream, NULL, _IONBF, 32);
  return true;
}

Once that’s done, you are good to go. You just need some hardware.

Hardware Setup

If you have the “normal” ST dongle like the white one in the picture below, the setup is just the normal setup. Connect power, ground, and the two debugging pins to the back connector of the Blackpill and then run a wire from SWO to the B4 pin on the device.

If you have one of the cheap clones like the purple one sitting next to the white device, you’ll need to do some surgery to bring out an extra pin.

Load a program that does some simple SWO output and then fire everything up. You may need to upgrade the ST-Link’s firmware — the STM32CubeProgrammer software can do that, too.

When connecting to the hardware with the programmer, I found that the white dongle didn’t reliably connect at 4000 kHz, so I had to select 1800 kHz. That may just be that device or my haphazard wiring. You can see the connection info I’m using in the adjacent screenshot. Press Connect to get started.

When you select the SWV item, you’ll need to set a clock of 96 MHz for this setup. Presumably, if you are running at a different frequency, you’d know the right value for your setup. When you press Start, you should see output from the program.

The only thing to remember is that your software will fight over the dongle unless it has been made to work in the “shared” mode. In my case, Mbed Studio didn’t seem to care about that setting so you have to disconnect if you want it to reprogram the chip. Of course, you could use the programmer to do everything. It will all depend on your tools and setup.

Of course, once you have it going once, it is pretty easy to replicate for future projects. You only have one extra wire and two extra files in your program.

Going Further

You can go further, though. First, there’s colorful output. If your debug string contains #RED#, #GRN#, or #ORG# the remaining characters will be in that color (red, green, or orange) for the rest of the line. Assuming, of course, the viewer understands that and you have it turned on. It is handy to be able to show important messages in red, for example.

However, it is a waste that there are so many extra channels we aren’t using. For example, why not have progress messages on channel 0 and detailed debugging info on channel 1? You could have a dump of what’s coming in from an external device on channel 5. Sure, you could write a prefix on the line and pull the data out that way, but this is more fun.

I rewrote a very small bit of the existing SWO class and, thanks to optional arguments, it still works the same. The only difference is you can add a channel number to the constructor so it is possible to create more than one debug stream:


SWO_Channel debugport;
SWO_Channel dbg2("second",1);

There are very few changes to the code, but I’ll leave the whole project up on GitHub.

If you can’t tell, I enjoy working with the STM32 and Mbed. Sure, you can get better performance by sidestepping Mbed, but the good thing is that you can. Oddly enough, pushing data over one port into several channels is something I’ve done before in a completely different way.