Tutorial

SWO plotting on STM32: interrupt timing and fast signals over one pin

SWO (Serial Wire Output) is a single debug pin that streams data from the ITM peripheral of an Arm Cortex-M core. One register write in your code marks the start or end of an interrupt, or sends a sample of a fast signal - MCUViewer timestamps the data and plots it. No logic analyzer, no spare GPIO and next to no overhead.

What you need

  • A Cortex-M core with SWO, for example STM32F1, F4, G4, H5 or H7. Cortex-M0 and M0+ have no SWO.
  • An ST-Link or J-Link with the SWO line - many low-cost ST-Link V2 clones don't have it.
  • The SWDIO, SWCLK, SWO and GND lines connected between the probe and your target.
  • The core clock frequency of your target.
  • MCUViewer for Windows, Linux or macOS - free download.
  1. 1Enable the SWO pin

    The SWO pin has to be configured as a trace output. In STM32CubeMX set Debug to Trace Asynchronous Sw - it's under Trace and Debug → DEBUG on newer series such as STM32H5, or System Core → SYS on older ones - and regenerate the code. Then check that SWO is wired to the probe - on most STM32 parts it's PB3.

  2. 2Mark the code you want to measure

    Write an enter tag before the code and an exit tag after it. Each tag is a single write to an ITM stimulus port, and the port number is the channel number in MCUViewer. For example, to see how long a timer interrupt takes:

    void TIM2_IRQHandler(void)
    {
        ITM->PORT[0].u8 = 0xaa;  // enter tag - channel 0 goes high
        HAL_TIM_IRQHandler(&htim2);
        ITM->PORT[0].u8 = 0xbb;  // exit tag - channel 0 goes low
    }
  3. 3Flash the firmware and select the trace probe

    Flash the firmware, switch MCUViewer to the Trace Viewer tab and open Options → Acquisition. Choose STLINK or JLINK as the trace probe, pick its serial number and close the window.

  4. 4Set the core frequency and the prescaler

    Enter the core frequency in kHz - for example 250000 for 250 MHz - and start with a relatively high trace prescaler, around 15-20. The resulting SWO clock is shown below it. After the first start MCUViewer also shows the maximum SWO clock of your probe and turns the field red if you're above it.

  5. 5Enable channel 0 and start

    Enable only channel 0 for the first run and press the large STOPPED button. Every interrupt now shows up as a pulse: its width is the execution time and the spacing between pulses is the period. Stop the acquisition to measure it with cursors or open the statistics window.

  6. 6Plot fast signals too

    Channels can carry values as well. Write the sample to the port, then set the channel's domain to analog and its type to match what you send:

    float a = sinf(10.0f * t);
    ITM->PORT[4].u32 = *(uint32_t*)&a;  // float on channel 4
    
    uint16_t adc = getAdcSample();
    ITM->PORT[5].u16 = adc;             // uint16_t on channel 5

Troubleshooting

I see no data and get a timeout error

Check that SWO is really active - put a logic analyzer or an oscilloscope on the pin and see if it starts toggling after you press start. Usually the pin isn't configured as a trace output or isn't wired to the probe.

I get a lot of error frames (red markers)

The probe can't keep up with the SWO clock. Increase the prescaler and double-check the core frequency - with a wrong value the SWO clock is wrong too.

I get a lot of delayed frames (yellow markers)

You're close to the bandwidth limit of the probe. Disable the channels you don't need, or lower the prescaler as long as no error frames show up.

Next steps

Download MCUViewer for free