05: Using RTT and Systemview with Zephyr

By default, any board with a serial port connected to a virtual COM (USB-UART) is configured to work with the console standard output (using the printk function for logging). On our Nordic board, the nrf54l15-dk, the UART20 is set to operate at 115200 baud, 8 data bits, no parity, and 1 stop bit (115200 8N1). You can try the following code to verify what I’m saying.

#include <zephyr/kernel.h>

int main(void)
{
    while (1)
    {
        printk("Message in a bottle.\n");
        k_msleep(1000u);
    }
    return 0;
}

To read the messages on your computer, you can use any serial terminal emulator. I use Minicom. Install Minicom and provide the appropriate permissions. Afterward, you may need to reboot. On Ubuntu, you can install Minicom using the following command: sudo apt-get install minicom.

$ sudo pacman -Sy minicom
$ sudo usermod -a -G dialout $USER

Open a serial connection using minicom to see the messages we sent from the microcntroller. An alternative is to use the VSCode terminal plugin described in previous posts

$ minicom -D /dev/ttyACM1 -b 115200
Welcome to minicom 2.9

OPTIONS: I18n 
Port /dev/ttyACM1, 15:57:45

Press CTRL-A Z for help on special keys

*** Booting Zephyr OS build v4.2.0 ***
Message in a bottle.
Message in a bottle.
Message in a bottle.

SEGGER RTT

But the serial port is quite slow, and for those who have a J-Link, SEGGER RTT is a much better alternative (just remember you need top have a board with an board jlink). Let's see how to configure it to run on your board. First, you need to update your manifest to include the SEGGER module, as the SEGGER libraries are required for RTT to work.

import:
  path-prefix: deps
  name-allowlist:
    - cmsis_6
    - hal_nordic
    - segger

Next on your prj.conf disable uart console and enable RTT options

# disable serial printk mode
CONFIG_UART_CONSOLE=n
# enable RTT and set as console output
CONFIG_RTT_CONSOLE=y
CONFIG_USE_SEGGER_RTT=y

With this in place, rebuild your project and then run it using SEGGER Ozone. You’ll be able to see the logs in the Terminal window. To open it, go to View → Terminal in the menu, or press Alt + Shift + T. If you forgot how we Ozone you can go back to our post 00: Using Containers for good

Using VSCode

Okay, maybe you're not using Ozone and prefer VSCode for debugging—no problem, I totally get it. There’s a simple way to open an RTT terminal in VSCode. Just add the following configuration to your launch.json. For more details, you can check out this post. AN003: Debugging with VSCode

💡
If, like me, you're using containers, this will only work if you have the J-Link software in the same container as Zephyr. Just remember, we covered this in a previous post
{
    "version": "1.12.1",
    "configurations": [
        {
            "type": "cortex-debug",
            "request": "launch",
            "name": "Debug (J-Link)",
            "servertype": "jlink",
            "interface": "swd",
            "cwd": "${workspaceRoot}",
            "runToEntryPoint": "main",
            "executable": "${workspaceRoot}/build/zephyr/zephyr.elf",
            "device": "nrf54l15_m33",
            "svdFile": "${workspaceRoot}/deps/modules/hal/nordic/nrfx/mdk/nrf54l15_application.svd",
            "armToolchainPath": "/opt/zephyr-sdk/arm-zephyr-eabi/bin",
            "toolchainPrefix": "arm-zephyr-eabi",
            "rttConfig": {
              "enabled": true,
              "address": "auto",
              "decoders": [
                {
                  "port": 0,
                  "type": "console"
                }
              ]
            }
        }
    ]
}

Once your program is running in VSCode, open a new RTT Ch:0 terminal (if it's not already open) to view the messages being sent through printk. This time, you'll be using SEGGER RTT

Systemview

But if you wanna really get fancy, the Systemview is by far the fanciest you can get, add the following new options to your prj.conf file

# enable to use thread names
CONFIG_SCHED_CPU_MASK=y
CONFIG_THREAD_NAME=y
CONFIG_THREAD_ANALYZER=y
# enable tracing RAM buffered
CONFIG_TRACING=y
CONFIG_TRACING_BACKEND_RAM=y
# enable segger systemview
CONFIG_SEGGER_SYSTEMVIEW=y
# by default up to three channeles are enable, the number 0 is set for the
# RTT console, we need to specify channel 1 will be choose for systemview
CONFIG_SEGGER_SYSVIEW_RTT_CHANNEL=1

If you're using a locally installed version of SystemView, you can skip this part and simply run your program with Ozone, then open SystemView. But if, like me, you prefer using containers, then check out this post to build a container with the SEGGER tools in order to run SystemView GUI applications in docker

Here’s my docker-compose file for the occasion. As you can see, I don't launch SystemView from here at all. The VSCode devcontainer is the same one we use here

services:
  ozone:      # container name 
    image: mysegger
    devices:        # device mapping to usb ports
      - /dev/bus/usb:/dev/bus/usb
    working_dir: /home/user/workspace
    volumes:        # volume mapping
      - type: bind
        source: ./
        target: /home/user/workspace
    environment:
      - DISPLAY=${DISPLAY}
    network_mode: host
    depends_on:     # run until zephyr is up and running first
      - zephyr
    command:        # keep the container running
      ozone -device nrf54l15_m33 -select USB -if SWD -speed 4000 -programfile /home/user/workspace/build/zephyr/zephyr.elf
    
  zephyr:      # container name 
    image: modularmx/zephyros
    working_dir: /home/user/workspace
    volumes:        # volume mapping
      - type: bind
        source: ./
        target: /home/user/workspace
    command:                # keep the container running
      sleep infinity

In order to run Systemview once your container with the segger tools is running you need to type the following in a new terminal.

$ docker exec -it zephyr-ozone-1 systemview

The image shows the only tasks we are running—well, actually two of them: IDLE and main. Yes, for Zephyr, the main function runs as a task, which they refer to as a system thread

One more thing: there are several options you can use to fine-tune SystemView and RTT. The following is a list of the most common options, but certainly not all of them. I suggest reading the official SEGGER manual and cross-referencing those options with the ones available in Zephyr.

# start loggin events since boot
CONFIG_SEGGER_SYSTEMVIEW_BOOT_ENABLE=y
# number of buffers, 3 by default
CONFIG_SEGGER_RTT_MAX_NUM_UP_BUFFERS=3
# set rtt buffer size, 1024 bytes by default
CONFIG_SEGGER_RTT_BUFFER_SIZE_UP=1024
# set the systemview buffer size in bytes, 4096 are set by default
CONFIG_SEGGER_SYSVIEW_RTT_BUFFER_SIZE=4096

I’m assuming you already have experience using SystemView, but if not, I’d recommend checking out some videos online. The purpose of this post is to show you how to set it up to run alongside Zephyr, and as always, using containers