Skip to content

Getting started with Zephyr RTOS

Tools for programming the codelabs and the project

To program your target device in the different codelabs, you need to use different tools. This codelab introduces these tools and allows you to write, compile and run your first Zephyr RTOS application on your target device.

What you’ll build

  • How to develop an application using the different Zephyr Development Environment and Zephyr RTOS
  • In this codelab, you will get started with your first Zephyr RTOS programs through a simple example.

What you’ll learn

  • How to develop an application using the different Zephyr Development Environment and the Zephyr RTOS framework.
  • How to add tracing in your application for efficient debugging.

What you’ll need

  • Zephyr Development Environment for developing and debugging C++ code snippets.
  • Some prior basic knowledge of C++ programming.

Installation of required software components

We will practise all the codelabs using Zephyr Development Environment, which will allow you to to develop and debug Zephyr RTOS applications. For the codelabs and the project, we recommend that you use VS Code as your IDE and we also recommend that you create a Python virtual environment. The use of these tools and environment is not mandatory, but we will not document alternatives.

The first steps for installing the development framework are:

  • Install VS Code as documented here.
  • Install Python as documented here.
  • Launch a terminal and create a directory where you will store your codelabs and project.
  • Launch VS Code and open the folder you just created.
  • In this workspace, create a venv virtual Python environment in VS Code as documented here.
  • Launch a new terminal in VS Code. This terminal should run in your Python virtual environment - a (.venv) should print in the command prompt.

Note

For making sure that when you launch a terminal in VS Code, it always activate the virtual environment, you must add the following line in the “.vscode/settings.json” file. You must create the file if it does not exist.

"python.terminal.activateEnvironment": true

Installing west

west is a repository management system distributed as a Python package. It is closely related to Zephyr RTOS, but is developed in its own repository. With Zephyr RTOS, west is used to manage project dependencies, but also to build, debug and flash programs. This is possible because west allows the definition of extension commands that add additional functionality, and the Zephyr SDK adds such commands to west for building, debugging and flashing applications.

If you type west in the terminal window, you should get an error saying that west is unknown because it does not exist in your virtual environment. To install west in this virtual environment, follow these instructions. After installing west, you can type west --version and the installed version should print in the terminal.

Zephyr OS application types

When developing Zephyr RTOS applications, you can choose between different application types, depending on where the application folder is located. The application folder is the folder in which you write your own application, and this folder can be located:

  • in the Zephyr RTOS repository, where the Zephyr RTOS source code is located. These applications are called repository applications.
  • in the west workspace where the Zephyr RTOS is installed. These applications are called workspace applications and support different west topologies. We will use this model and give more details on how to use it below.
  • elsewhere on your system. These applications are called freestanding applications.

West Workspaces and Manifest File

west uses a manifest file, usually named west.yml. The directory in which this file resides is called the manifest repository. For workspace applications, the manifest repository may be located in different folders, defining different west [topologies]https://docs.zephyrproject.org/latest/develop/west/workspaces.html#topologies-supported){target="blank”}:

  • In the T1 star topology, the manifest repository is the zephyr repository. The manifest file is thus the west.yml file located in the zephyr folder and the zephyr repository acts as the super project with external modules imported in west.yml.
  • In the T2 star topology, the application folder is the manifest repository and the manifest file is the west.yml file located in the application folder. This topology is useful if you are focusing on a single application and you want to restrict dependencies with other applications.
  • In the T3 forest topology, the west.yml file is located in a separate folder. This topology is useful if you are developing several applications relying on the same dependencies and you want to restrict dependencies with other applications relying on different versions or modules.

As explained above, for our codelabs and project, we will create Zephyr RTOS workspace applications. Since we will develop several applications, we will apply the T3 forest topology. This configuration should not be confused with the freestanding application configuration, in which the manifest repository is the Zephyr RTOS repository. Because Zephyr RTOS evolves fast, it is also very important to work with fixed versions of all modules and Zephyr RTOS’s source code.

In our case, the folder structure of our workspace will look like:

tsm_advembsof-workspace/
├── bike-computer/           # .git/ project   ├── CMakeLists.txt
│   ├── prj.conf
│   └── src/
│       └── main.cpp
├── codelab-1/               # .git/ project   ├── CMakeLists.txt
│   ├── prj.conf
│   └── src/
│       └── main.cpp
├── blinky/                  # .git/ project   ├── CMakeLists.txt
│   ├── prj.conf
│   └── src/
│       └── main.cpp
├── manifest-repo/      # .git/ never modified by west   └── west.yml        # main manifest with optional import(s) and override(s)
├── deps/
│   └── modules/
│       └── lib/
│           └── zpp-lib/  # .git/ project from either the main manifest or                         #       from some import   └── zephyr/       # .git/ project       └── west.yml  # This can be partially imported with lower precedence or ignored.                     # Only the 'manifest-rev' version can be imported
This folder structure corresponds to a T3 topology with 3 applications named “bike-computer”, “codelab-1” and “blinky”. All applications rely on the same west manifest file and use the same versions of Zephyr RTOS and modules. Note that all dependencies of the applications are stored in a folder named “dep”. This is arbitrary and these dependencies could be stored using a different folder structure.

Initializing your West workspace

For specifying the a west workspace using T3 forest topology, you must:

  • Create a “manifest-repo” (or similar name) in your workspace folder.
  • In the “manifest-repo” folder, create a file west.yml. This file will contain the list of all external dependencies. It is important to note that the manifest repository (containing the manifest file) is a direct sibling of the workspace root directory, and it is a git repository.
  • Start editing the west.yml file:
    manifest-repo/west.yml
    manifest:
      # lowest version of the manifest file schema that can parse this file’s data
      version: 0.13
    
  • Open a terminal and change directory to the “manifest-repo” folder. For initializing the west workspace, type west init -l .. This creates a “.west” directory in the workspace root directory, which in turn contains a configuration file “config”. The “config” file contains the name and the location of the west.yml, in our case:
    .west/config
    [manifest]
    path = manifest-repo
    file = west.yml
    
  • Change directory back to the workspace root directory and run west update. Given that no dependency were added in the west.yml file, nothing should happen.

Note

It is important to note that in YAML, structure is determined by indentation, not by indicators (like {} in JSON). It is also important to note that indentation is defined as zero or more spaces at the beginning of a line, and that tabs may not be used in indentation. Each node must be indented further than its parent node. All sibling nodes must use the exact same indentation level.

For full details on the YAML specification see the official documentation.

Specifying project dependencies

Dependencies can be added in the west.yml using the `manifest.projects’ key. With this key you can specify all the git repositories you want to add to your workspace.

Dependencies are not necessarly related to Zephyr RTOS and can be any utility that is used in a project. Let’s say we plan to use Doxygen documentation for our project, so we want to add Doxygen Awesome to our workspace. We can add this external dependency as an entry in the manifest.projects key of our manifest file:

manifest-repo/west.yml
manifest:
  # lowest version of the manifest file schema that can parse this file’s data
  version: 0.13

  projects:
    - name: doxygen-awesome
      url: https://github.com/jothepro/doxygen-awesome-css
      # you can also use SSH instead of HTTPS:
      # url: git@github.com:jothepro/doxygen-awesome-css.git
      revision: main
      path: deps/utilities/doxygen-awesome-css

At this point, it is important to note the following regarding the west.yml file:

  • In YAML terms, the manifest file contains a mapping, with a key named manifest. Any other keys and their contents are ignored.
  • The manifest key contains subsections, like defaults, remotes, projects, and self. In YAML terms, the value of the manifest key is also a mapping, with these subsections as keys. All of these subsection keys are optional.
  • The projects key contains a list of repositories managed by west and associated metadata:

    • Every projet has a unique name, here doxygen-awesome for example.
    • A url of the repository is typically specified. This can also be done through a remote that is specified in the remotes key.
    • In the example above, the “doxygen-awesome” repository is cloned to the path “deps/utilities/doxygen-awesome-css” relative to the root directory of the west workspace, since it has an explicit `path’ attribute. Without specifying the path, West uses the project name as the path. Note that the path cannot be outside the root directory of the west workspace, but the location inside the workspace is a personal choice. However, grouping dependencies into a single folder makes them easier to manage - for example, to delete this folder to save disk space when needed.
    • The revision key specifies that the current tip of the main branch should be fetched. Normally, one would specify a tag or even a specific commit in the revision, rather than the latest version.
  • You can find examples and more detailed explanation of all available options in the official documentation.

At this point, if you run west update after modifying the west.yml file, you should notice that the “doxygen-awesome-css” repository is fetched to your west workspace under the “deps/utilities” folder.

Note

Running west update only checks the contents of the local west.yml. If your west.yml is stored on a git repository, it won’t try to pull new changes in the manifest repository and it also won’t attempt to read the file from the remote. If there were any changes to the west.yml file in the repository, you must git pull them in your manifest repository. In fact, west update will never attempt to modify the manifest repository.

Create the Blinky application

For any Zephyr RTOS application, the basic file structure is as follows:

├── blinky/                  # .git/ project   ├── CMakeLists.txt
│   ├── prj.conf
│   └── src/
│       └── main.cpp
Basically, each application is made of at least these three files:

  • “main.cpp”: It contains the definition of the main function. Here, we put it in a subdirectory called “src” and it has a “cpp” extension since we use C++ for coding our applications.
blinky/src/main.cpp
int main(void) {
  while (true) {}
  return 0;
}
  • “prj.conf”: This is the configuration file required for any Zephyr RTOS application. It is empty at this point.

    blinky/prj.conf
    
    

  • “CMakeLists.txt”: The build system is based on cmake and a “CMakeLists.txt” file is required. More details on this topic will be given later.

blinky/CMakeLists.txt
cmake_minimum_required(VERSION 3.20.0)
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})

project(
    Blinky
    VERSION 0.1
    DESCRIPTION "CPP Blinky application."
    LANGUAGES C CXX
)

target_sources(
    app
    PRIVATE
    src/main.cpp
)

Create this file structure for your application in your workspace, copying the content of each file as documented above. Once complete, type ‘west build blinky’ from the top directory of your workspace. You should get an error saying that the “build” command is unknown. This is because the “build” command is not part of the basic west installation. The “build” command is a command that is part of the Zephyr RTOS extension commands, such as the “flash” command. So the next step is to introduce the dependency of our application on the Zephyr RTOS library.

Add Zephyr OS and Build the Blinky Application

Zephyr RTOS can be added as dependency to the blinky application like any other dependency:

manifest-repo/west.yml
manifest:
  # lowest version of the manifest file schema that can parse this file’s data
  version: 0.13
  projects:
    ...
    - name: zephyr
      revision: v4.2.0
      url: https://github.com/zephyrproject-rtos/zephyr
      path: deps/zephyr
      # explicitly add Zephyr-specific West extensions
      west-commands: scripts/west-commands.yml
With this definition, the v4.2.0 revision of the Zephyr RTOS repository will be cloned into the relative path “deps/zephyr” of the top directory. The west.yml file also indicates that the commands defined in “scripts/west-command.yml” of the Zephyr RTOS repository will be imported. To apply the changes to your local workspace, you need to run west update again. This may take some time as the Zephyr RTOS library is quite large.

After updating your workspace, you can try running west build blinky again. It should not run successfully and you should see an error saying that some tools are missing. At this point, you need to install additional tools:

  • Since we are using a Python virtual environment, we need to install all Python packages required by Zephyr. This should be done by running pip install -r deps/zephyr/scripts/requirements.txt.
  • The west build command is based on the use of cmake. If cmake is not installed on your machine, you should get an error. In this case, follow the instructions in the Zephyr RTOS Getting Started Guide - Install Dependencies to install cmake and the other required development tools.
  • A toolchain is required to build Zephyr RTOS applications. For this purpose, you need to install the Zephyr SDK.
  • Setup environment variables as described here. In particular, you must make sure that the following variables are set correctly:

    • ZEPHYR_BASE: it should contain the full path to your “deps/zephyr” folder where the Zephyr RTOS repository was cloned.
    • ZEPHYR_SDK_INSTALL_DIR: it should contain the full path to the folder in which you installed the Zephyr SDK.
    • ZEPHYR_TOOLCHAIN_VARIANT: it should be set to zephyr.

After installing all required tools and setting the environement properly, if you run west build blinky again, the build command should start but you should still get an error saying that you need to specify a board for the build. This is illustrated below:

ERROR: Cached board not defined, please provide it (provide --board, set default with "west config build.board <BOARD>", or set BOARD in the environment)       
FATAL ERROR: refusing to proceed without --force due to above error


Specifying the board at build time can be done in two different ways:

  - Add this configuration in the _west_ config with the `west config build.board nrf5340dk/nrf5340/cpuapp` command.
  - Add the `-b nrf5340dk/nrf5340/cpuapp` option to the `west build` command.

You can now run the _west_ build command again. And you will get another
error! We are not done yet... The error is related to the fact that we have
included only _Zephyr RTOS_ without importing other specific dependencies. You
first need to modify the `west.yml` file as follows:
```yaml title="manifest-repo/west.yml"
manifest:
  # lowest version of the manifest file schema that can parse this file’s data
  version: 0.13
  projects:
    ...
    - name: zephyr
      revision: v4.2.0
      url: https://github.com/zephyrproject-rtos/zephyr
      # path: deps/zephyr -> no longer needed since we are now using path-prefix in the import section
      # explicitly add Zephyr-specific West extensions
      # west-commands: scripts/west-commands.yml -> no longer needed
      # because we import the west.yml file from zephyr where commands are defined
      import:
        path-prefix: deps
        file: west.yml # not strictly needed since it is the value by default
        name-allowlist:
          - cmsis_6
          - hal_nordic 

This west.yml file allows to import the west.yml file from Zephyr RTOS and to import the cmsis_6 and hal_nordic projects defined in this west.yml file. If you run west update again, these projects will be imported under “deps/modules/hal”:

├── deps/                     ├── ...
│   └── modules/
│       └── hal/
│           └── cmsis_6
│           └── hal_nordic 
For instance, the repository https://github.com/zephyrproject-rtos/cmsis_6 for the cmsis_6 project was cloned to the “deps/modules/hal” folder. Details on how the west.yml file from Zephyr RTOS is imported are given here and here.

You can now try to build your application. It should compile and produce a binary file, as illustrated below:

145/145] Linking CXX executable zephyr\zephyr.elf
Memory region         Used Size  Region Size  %age Used
           FLASH:       19880 B         1 MB      1.90%
             RAM:        4448 B       448 KB      0.97%
        IDT_LIST:          0 GB        32 KB      0.00%
Generating files from D:/zephyr_projects/tsm_aes_workspace/blinky/build/zephyr/zephyr.elf for board: nrf5340dk

Note

To flash your board, you may need to install a specific programmer. For STM32 boards, install the STM32CubeProgrammer software as described here. For Nordic boards, you need to install the nRFUtil software as described here. After installing nrfutil, you may need to run the nrfutil upgrade device command to be able to flash your nRF board.

You can now flash your target device with the west flash command. The application should be copied to the target device and it should start.

Note

It is important to note that, in some situations, it is necessary to force a clean build. This is the case, for instance, when some application configuration parameters are changed or when files are added to the project. To force a clean build, add the ‘–pristine’ parameter to the end of the ‘west’ command.

Make the Blinky Application Work

The main() function of our program is essentially empty, and to make one of the leds on the development kit blink, we need to add some code. In this lecture, we will use a library written in C++ to encapsulate the Zephyr RTOS kernel and drivers into C++ classes.

The required steps for adding the library and writing the application are:

  • Add the library to your west.yml manifest file:
    manifest-repo/west.yml
    manifest:
    ...
       projects:
        ...
        - name: zpp_lib
          url: https://github.com/AdvEmbSof/zpp_lib.git
          revision: main
          path: deps/zpp_lib
    
  • Enable the “zpp_lib” library in the application configuration file:
    blinky/prj.conf
    CONFIG_USE_ZPP_LIB=y
    
  • Enable the support for C++ when building your application:
    blinky/prj.conf
    CONFIG_CPP=y
    CONFIG_REQUIRES_FULL_LIBCPP=y
    CONFIG_STD_CPP17=y
    
  • Add the following code in your “main.cpp” file:
    blinky/src/main.cpp
    // stl
    #include <chrono>
    
    // zpp-lib
    #include "zpp_include/thread.hpp"
    #include "zpp_include/this_thread.hpp"
    #include "zpp_include/digital_out.hpp"
    
    void blink() {
        zpp_lib::DigitalOut led(zpp_lib::DigitalOut::PinName::LED0);
        using namespace std::literals;
        static std::chrono::milliseconds blinkInterval = 1000ms;
    
        while (true) {
            led = !led;
            zpp_lib::ThisThread::sleep_for(blinkInterval);
        }
    }
    
    int main(void) {
        zpp_lib::Thread thread;
        auto res = thread.start(blink);
        if (! res) {
            return -1;
        }
    
      return 0;
    }
    

After applying these changes, you need to run west update and west build again. Your application should compile and when you run west flash, the LED should blink!

Adding Logging to Your Application

Debugging is an important and necessary part of the development process. On embedded systems, debugging capabilities are often more limited than on other systems. Parts of the debugging process will be covered in a future codelab. In this section we will show how to use logging statements and predefined macros for efficient and flexible debugging via printing.

Zephyr RTOS offers advanced logging capabilities which are fully documented here. Logging is implemented through macros that allow easy configuration of logging at both compile time and run time:

  • Using Kconfig logging options, logs can be added or removed at compile time. This can reduce image size and speed up execution if certain logs are not needed. Logging can be configured both at the global application level as well as for each specific library/module that implements logging.
  • Logs can also be filtered at runtime and this can be done for each logging backend and source.
  • Logging is based on four severity levels, each corresponding to a dedicated set of macros: error (ERR), warning (WRN), info (INF) and debug (DBG). When adding a logging message, a specific severity level is chosen, e.g. LOG_ERR for logging an error message. When compiling the application, the severity level to be applied can be chosen for the entire application or for each specific module/library, using configuration parameters such as CONFIG_LOG_DEFAULT_LEVEL).

Enabling Logging for the blinky Application

For configuring logging for the “blinky” application, we must:

  • Enable logging globally by adding the following configuration parameter in the application configuration file:
    blinky/prj.conf
    CONFIG_LOG=y
    
  • Build and flash your application. Open a serial terminal and connect it to your board. You can use any serial monitor for this purpose. The default configuration parameters are 115200/8/1 without parity. For example, if you use picocom, the command would be be picocom -b 115200 -r -l /dev/ttyACM0. The terminal should display the following message:

    console
    *** Booting Zephyr OS build v4.2.0 ***
    

  • Enable logging for the Zephyr RTOS kernel at severity level DBG:

    blinky/prj.conf
    CONFIG_KERNEL_LOG_LEVEL_DBG=y
    

  • After enabling logging for the kernel, the terminal should display messages tagged as <dbg> os:

    console
    *** Booting Zephyr OS build v4.1.0 ***
    [00:00:00.428,955] <dbg> os: z_impl_k_mutex_lock: 0x200006d0 took mutex 0x20000020, count: 1, orig prio: 0
    [00:00:00.428,955] <dbg> os: z_impl_k_mutex_unlock: mutex 0x20000020 lock_count: 1
    [00:00:00.428,985] <dbg> os: z_impl_k_mutex_unlock: new owner of mutex 0x20000020: 0 (prio: -1000)
    [00:00:00.428,985] <dbg> os: k_sched_unlock: scheduler unlocked (0x200006d0:0)
    
    As you can see, the message indicates the severity level, the module name and the function/method.

  • Enable logging for the “zpp_lib” library at severity level DBG:

    blinky/prj.conf
    CONFIG_ZPP_RTOS_LOG_LEVEL_DBG=y
    CONFIG_ZPP_DRIVERS_LOG_LEVEL_DBG=y
    

  • After enabling logging for the kernel, the terminal should display messages tagged as <dbg> zpp_rtos and `<dbg> zpp_drivers:
    console
    *** Booting Zephyr OS build v4.1.0 ***
    [00:00:00.426,391] <dbg> zpp_rtos: constructor: Thread constructed with stack size 1024
    [00:00:00.426,422] <dbg> zpp_rtos: start: Creating thread with stack size 1024 and priority 11
    [00:00:00.426,452] <dbg> zpp_rtos: start: Thread created
    [00:00:00.426,483] <dbg> zpp_rtos: _thunk: Thread _thunk called
    [00:00:00.426,513] <dbg> zpp_drivers: DigitalOut: Pin gpio@842500 initialized
    

Adding Logging to the Blinky Application

For debugging purposes, it is also useful to add application specific logging messages to the application code. This can be implemented in few steps:

  • Add a “Kconfig” file at the root of the application folder with the following content:
    blinky/Kconfig
    menu "Zephyr"
    source "Kconfig.zephyr"
    endmenu
    
    module = APP
    module-str = APP
    source "subsys/logging/Kconfig.template.log_config"
    

This is required for defining the logging symbols specific to the application. More details will be given on Kconfig in a next codelab. If you open the “subsys/logging/Kconfig.template.log_config” file located in the Zephyr RTOS directory, you will see that it defines a number of symbols used for logging, all based on the module variable defined in the “blinky/Kconfig” file.

  • Enable logging for the application at severity level DBG:
    blinky/prj.conf
    CONFIG_APP_LOG_LEVEL_DBG=y
    
  • Add logging messages in the application code, such as:
    blinky/src/main.cpp
    // zephyr
    #include <zephyr/logging/log.h>
    ...
    // LOG_MODULE_REGISTER needs to be placed after all #includes
    // outside any namespace
    LOG_MODULE_REGISTER(blinky, CONFIG_APP_LOG_LEVEL);
    ...
    int main(void) {
      LOG_DBG("Running on board %s", CONFIG_BOARD_TARGET);
      LOG_DBG("Starting thread");
      zpp_lib::Thread thread;
      auto res = thread.start(blink);
      if (! res) {
        LOG_ERR("Could not start thread (%d)", static_cast<int>(res.error()));
        return -1;
      }
      return 0;
    }
    
  • After applying these changes, the terminal should display messages tagged as <dbg> main:
    console
    *** Booting Zephyr OS build v4.1.0 ***
    [00:00:00.433,105] <dbg> main: main: Running on board nrf5340dk/nrf5340/cpuapp
    [00:00:00.433,135] <dbg> main: main: Starting thread
    

Note

The LOG_MODULE_REGISTER() must be placed after all #include and outside any namespace. If the module consists of multiple files, then LOG_MODULE_REGISTER() should appear in exactly one file. Each other file should use LOG_MODULE_DECLARE() to declare its membership in the module. The second parameter for both statements is optional and it is a compile time log level for the module. Default log level (CONFIG_LOG_DEFAULT_LEVEL) is used if no custom log level is provided.

Debugging a Zephyr Application

You can also debug any Zephyr application using the debugger. The following steps are required for this purpose:

  • First, install the cortex-debug extension available here. Extensions in VSCode can be installed as documented here.

  • In the “.vscode” folder, add the “launch.json” file with the following content

    .vscode/launch.json
    {
        "version": "0.2.0",
        "configurations": [
             // Adopted from https://github.com/Marus/cortex-debug/issues/969
            {
                "name": "Launch nrf5340dk/nrf5340/cpuapp",
                "device": "nRF5340_xxAA_app",
                "cwd": "${workspaceFolder}",
                "executable": path to executable file, e.g. "build/zephyr/zephyr.elf",
                "request": "launch",
                "type": "cortex-debug",
                "runToEntryPoint": "main",
                "servertype": "jlink",
                "gdbPath": path to arm-zephyr-eabi-gdb.exe, e.g. "D:/zephyr-sdk-0.17.0/arm-zephyr-eabi/bin/arm-zephyr-eabi-gdb.exe",            
                "rtos": path to RTOSPlugin_Zephyr library, e.g. on Windows "C:/Program Files/SEGGER/JLink_V860/GDBServer/RTOSPlugin_Zephyr.dll"
                // or alternatively "rtos": auto
            },
            {
                "name": "Attach nrf5340dk/nrf5340/cpuapp",
                "device": "nRF5340_xxAA_app",
                "cwd": "${workspaceFolder}",
                "executable": path to executable file e.g. "build/zephyr/zephyr.elf",
                "request": "attach",
                "type": "cortex-debug",
                "runToEntryPoint": "main",
                "servertype": "jlink",
                "gdbPath": path to arm-zephyr-eabi-gdb.exe, e.g. "D:/zephyr-sdk-0.17.0/arm-zephyr-eabi/bin/arm-zephyr-eabi-gdb.exe",
                "rtos": path to RTOSPlugin_Zephyr library, e.g. on Windows "C:/Program Files/SEGGER/JLink_V860/GDBServer/RTOSPlugin_Zephyr.dll"
                // or alternatively "rtos": auto
            },
        ]
    }
    

  • Start the debugger by selecting Run > Start Debugging. The debugger should then stop at the start of the main() function. You can then continue execution and step through the code as you would with any other debugger.

Note

For a better experience in debugging the application, it is recommended to define the configuration symbol CONFIG_DEBUG_OPTIMIZATIONS=y in the prj.conf file.

Note

The debugger will halt at the start of the main() function by default. However, it may get stuck during startup. In this case, removing any breakpoints before starting may help it to start successfully.

Adding Tracing and Visualizing the Application Behavior

One very powerful tool for analysing and visualising any Zephyr OS application is Segger SystemView. This tool enables you to trace the application, including any kernel operation. It allows you to record and visualise application behaviour in real time.

The SystemView tool is available for any platform that supports SEGGER_RTT. One can verify this for a given platform by looking at the configuration file of the correspoding SoC. For example for our nrf5340dk/nrf5340/cpuapp board, it would be “deps/zephyr/soc/nordic/nrf53/Kconfig”. The file should contain a line like

select HAS_SEGGER_RTT if ZEPHYR_SEGGER_MODULE
This states that it supports SEGGER_RTT if the Segger module is installed.

The steps for enabling Segger SystemView are the following:

  • Install the Segger SystemView application available here. The application is available on Windows, Linux and MacOS.

  • Add the “Segger” module with the following line in your west.yml file:

    manifest-repo/west.yml
    ...
     import:
     name: zephyr
          revision: v4.2.0
          url: https://github.com/zephyrproject-rtos/zephyr
          # path: deps/zephyr -> no longer needed since we are now using path-prefix in the import section
          # explicitly add Zephyr-specific West extensions
          # west-commands: scripts/west-commands.yml -> no longer needed
          # because we import the west.yml file from zephyr where commands are defined
          import:
            path-prefix: deps
            file: west.yml # not strictly needed since it is the value by default
            name-allowlist:
              ...
              - segger
    ...
    

  • Run “west update” for installing the module.
  • Add the use of the “rtt-tracing” snippet to the application build using the command west build ... -S rtt-tracing.... Using the snippet is an easy way to add configuration options to your program. The configuration parameters are defined in the “zephyr/snippets/rtt-tracing/rtt-tracing.conf” file as shown below:
zephyr/snippets/rtt-tracing/rtt-tracing.conf
CONFIG_USE_SEGGER_RTT=y

CONFIG_DEBUG_OPTIMIZATIONS=n
CONFIG_DEBUG_THREAD_INFO=y

CONFIG_TRACING=y
CONFIG_SEGGER_SYSTEMVIEW=y
CONFIG_SEGGER_SYSTEMVIEW_BOOT_ENABLE=n
CONFIG_SEGGER_SYSVIEW_POST_MORTEM_MODE=n

It is also possible to add these configuration parameters directly in the prj.conf file or to override some of these definitions when required. You may for instance disable the compiler optimizations with CONFIG_NO_OPTIMIZATIONS=y or activate the tracing at boot time with CONFIG_SEGGER_SYSTEMVIEW_BOOT_ENABLE=y.

  • Launch the “Segger SystemView” application and configure the recorder by selection “Target -> Recorder Configuration” as shown below:

SystemView configuration SystemView configuration Configuration of the SystemView recorder

  • Launch recording by clicking on the “play” button. You should then be able to visualize precisely the application behavior on the timeline.