SWD part 3 – SWO and nRST

The SWD interface does not only consist of the SWDIO and SWDCK lines, but also has optional signals used in more specific cases. In this post, we will present the SWO and nRST signals.

SWO – The debugging trace output

Developers often use logging functions to help them trace the execution of their programs. Unfortunately, this is more difficult on embedded devices, as there is sometimes no way to make these messages available to the developer. ARM provides some nice improvements to the classical debugging tools based on the ITM (Instrumentation Trace Macrocell) present in many of the Cortex cores. This component allows a programmer to use printf like statements to help the debugging process.

In the most common case, the programmer can instruct the ITM to send a message through the ITM_SendChar()function. This function is available through the CMSIS HAL and is common to all Cortex-based MCUs. The ITM will forward the message to the TPIU (Trace Port Interface Unit) that is responsible to transfer it to its final destination. The TPIU will format the message, add some header information and push it to its final destination.

This destination is most of the times the SWO (Serial Wire Output) interface, a single wire paired with the SWD interface and used to transmit trace messages to an external debugger. This interface can be configured in two different modes : Manchester or UART encoding (see here).

As an example, here is a simple application based on a STM32F405. This application configures the SWO pin, and sends a message through the SWO pin :

// Includes omitted for clarity

void print_swo(const char *data, const uint32_t size)
{
    uint32_t i;
    for(i = 0; i<size; i++)
        ITM_SendChar(data[i]); /* core_cm4.h */
    }
}

void main(void)
{
    GPIO_InitTypeDef gpio_init;

    /* GPIO pin configuration, PB3 is the SWO on this chip */
    gpio_init.Mode = GPIO_MODE_AF_PP;
    gpio_init.Pin = 1 << 3;
    gpio_init.Speed = GPIO_SPEED_HIGH;
    gpio_init.Pull = GPIO_NOPULL;
    gpio_init.Alternate = GPIO_AF0_TRACE;
    HAL_GPIO_Init(GPIOB, &gpio_init);

    while(1) {
        print_swo("Hello", 5);
    }
}

In order to see the messages, we need to enable the ITM macrocell as well as the TPIU. For that, we can either set the registers by hand (see the registers list here) or use openOCD to enable the TPIU when debugging. In the example below, we’re telling openOCD that our target runs at 168MHz, and that we want an UART-encoded SWO at 2Mb/s.

$ telnet 127.0.0.1 4444
Trying 127.0.0.1...
Connected to 127.0.0.1.
Escape character is '^]'.
Open On-Chip Debugger
> help tpiu
  stm32f4x.cpu tpiu
        tpiu command group (command valid any time)
    stm32f4x.cpu tpiu config (disable | ((external | internal <filename>) (sync <port width> |
              ((manchester | uart) <formatter enable>)) <TRACECLKIN freq>
              [<trace freq>]))
> tpiu config external uart off 168000000 2000000
> itm ports on
> reset run
HelloHelloHello[...]

Then, looking at the logic analyzer trace :

Our message is correctly sent to the SWO interface

The TPIU can also be used by the ETM, the tracing macrocell. When in use, the ETM will send periodic messages showing the full execution trace. This trace however does just contain information about the number of instructions executed, and if branches are taken or not. This cannot be used as a way to dump the actual instructions.

Semihosting

There is another option that can be used to send messages to the debugger, namely the semihosting. This mode takes advantage of a specific service request (interrupt) to ask the debugger to take action.

Technically, a compatible debugger must trap service requests numbers 0x123456 in ARM mode, or 0xAB in thumb mode. Once the debugger is triggered, it will read the operation number in r0, and a pointer to the arguments in r1 by using standard MEM-AP queries. It will then parse the arguments, complete the operation, change the pc value to point to the next instruction and resume the core execution.

As an example, the C code below uses the SYS_WRITE operation to write to the host’s debugger STDOUT, similar to a print() :

print_semihosting(char * data, size)
{
    uint32_t args[3];
    args[0] = 1;
    args[1] = (uint32_t)data;
    args[2] = size;
    asm(  "mov r0, #5\n"
          "mov r1, %0\n"
          "bkpt 0x00AB" : : "r"(args) : "r0", "r1");
}
void main(void)
{
    while(1) {
        print_semihosting("Hello", 5);
    }
}

Running the code from within a openOCD, the string is displayed directly inside of the session :

$ telnet 127.0.0.1 4444
Trying 127.0.0.1...
Connected to 127.0.0.1.
Escape character is '^]'.
Open On-Chip Debugger
> arm semihosting enable
> reset run
HelloHelloHello[...]

This feature is really interesting, and provides nice ways to transfer test data from and to the device for testing purposes.

nRST : Not just a reset signal

While testing a new chip (The STM32L011D3), I had some trouble connecting to it using the standard SWD interface. The SWD interface was not able to communicate with the debug port. Digging through the datasheet, I found out that this chip has to be programmed using the “connect under reset” method.

Connect under reset ?

On smaller (read: low pin count) devices, the SWD DP is not mapped to GPIOs at boot time but can be enabled by software afterwards. However, we have a chicked and egg problem here. To avoid this, engineers added the connect-under-reset feature, where the SWD pins are mapped to the DP when the chip is under reset condition. There is usually a dedicated pin for the reset, most of the times labelled nRST.

In order to connect to the DP, the debugging interface must pull the nRST low, then issue the JTAG->SWD switch command.

The nRST signal is asserted before running the JTAG to SWD request

A J-Link interface is able to handle such a chip, and it was just a matter of connecting the nRST pin of the chip to the RESET pin of the J-Link. The same is possible using an official ST-Link interface as well.

Conclusion

In this short article, we presented two additional signals often used along with the SWD interface, as well as some common usages. The SWO pin can sometimes be useful if it is still used by the firmware of an embedded device, and can help reverse-engineer it.

Bonus: Adding nRST and SWO to an ST-Link clone

If you are using one of those cheap ST-Link clones you can buy online, you’re out of luck because the nRST pin is not present on the exposed header. But since this clone runs the same firmware as an official ST-Link, it is possible to change a pin function by connecting it to somewhere else on the PCB. Since the RESET pin is used to program STM8 devices, I decided to repurpose it as nRST.

Looking at the St-Link v2 schematics, we can see that we need to connect the T_NRST signal to our pin. This signal comes from the GPIO named PB0, located on pin number 18 on the chip. We also have to make sure that we add a 22 Ohm impedance matching resistor like on the original design (see R13 on the schema). Taking a closer look at our PCB, we can see that the RESET pin is connected to a 100 Ohm resistor on the opposite site of the header pin. After removing this resistor and replacing it with a 22 Ohm, we can cut the trace using a knife and connect a wire on the MCU side of the resistor. While doing this, I also repurposed the SWI pin (used for STM8 chips as well) to get the SWO debug output. For that, I connected the GPIO PA10 located on pin number 31, also through a 22 Ohm resistor.

Once we have the two wires connected, we can bring them on the other side of the board to connect them on the corresponding pins of the MCU :

Testing

Using the ST-Link utility, it is possible to select the connect under reset feature, click connect, and…

Success !

This is quite nice and all, but the ST-Link utility is a Windows-only tool. On GNU/Linux, the mostly used software is OpenOCD. The connect on reset feature is also supported by using the reset_config directive in the configuration file.

source [find interface/stlink.cfg]
transport select hla_swd
reset_config srst_nogate connect_assert_srst

source [find target/stm32l0.cfg]

$_TARGETNAME configure -event gdb-attach {
    reset halt
}

Running openOCD with this configuration allows to access the MCU and program it, mission complete !

One comment

  1. I’m trying to use this article. I’m connected to an ST-Link V3 over Visual Studio on open ocd. then I connect to localhost 4444 over telnet, I enter the commands but nothing comes from the trace. Any idea what I am doing wrong?

Leave a Reply