West: Flashing and Debugging

West provides three commands for running and interacting with Zephyr programs running on a board: flash, debug, and debugserver.

These use information stored in the CMake cache [1] to flash or attach a debugger to a board supported by Zephyr. The CMake build system commands with the same names directly delegate to West.

Flashing: west flash

Tip

Run west flash -h for additional help.

Basics

From a Zephyr build directory, re-build the binary and flash it to your board:

west flash

Without options, the behavior is the same as ninja flash (or make flash, etc.).

To specify the build directory, use --build-dir (or -d):

west flash --build-dir path/to/build/directory

Choosing a Runner

If your board’s Zephyr integration supports flashing with multiple programs, you can specify which one to use using the --runner (or -r) option. For example, if West flashes your board with nrfjprog by default, but it also supports JLink, you can override the default with:

west flash --runner jlink

See Library Backend: west.runners below for more information on the runner library used by West. The list of runners which support flashing can be obtained with west flash -H; if run from a build directory or with --build-dir, this will print additional information on available runners for your board.

Configuration Overrides

The CMake cache contains default values West uses while flashing, such as where the board directory is on the file system, the path to the kernel binaries to flash in several formats, and more. You can override any of this configuration at runtime with additional options.

For example, to override the HEX file containing the Zephyr image to flash (assuming your runner expects a HEX file), but keep other flash configuration at default values:

west flash --kernel-hex path/to/some/other.hex

The west flash -h output includes a complete list of overrides supported by all runners.

Runner-Specific Overrides

Each runner may support additional options related to flashing. For example, some runners support an --erase flag, which mass-erases the flash storage on your board before flashing the Zephyr image.

To view all of the available options for the runners your board supports, as well as their usage information, use --context (or -H):

west flash --context

Important

Note the capital H in the short option name. This re-runs the build in order to ensure the information displayed is up to date!

When running West outside of a build directory, west flash -H just prints a list of runners. You can use west flash -H -r <runner-name> to print usage information for options supported by that runner.

For example, to print usage information about the jlink runner:

west flash -H -r jlink

Debugging: west debug, west debugserver

Tip

Run west debug -h or west debugserver -h for additional help.

Basics

From a Zephyr build directory, to attach a debugger to your board and open up a debug console (e.g. a GDB session):

west debug

To attach a debugger to your board and open up a local network port you can connect a debugger to (e.g. an IDE debugger):

west debugserver

Without options, the behavior is the same as ninja debug and ninja debugserver (or make debug, etc.).

To specify the build directory, use --build-dir (or -d):

west debug --build-dir path/to/build/directory
west debugserver --build-dir path/to/build/directory

Choosing a Runner

If your board’s Zephyr integration supports debugging with multiple programs, you can specify which one to use using the --runner (or -r) option. For example, if West debugs your board with pyocd-gdbserver by default, but it also supports JLink, you can override the default with:

west debug --runner jlink
west debugserver --runner jlink

See Library Backend: west.runners below for more information on the runner library used by West. The list of runners which support debugging can be obtained with west debug -H; if run from a build directory or with --build-dir, this will print additional information on available runners for your board.

Configuration Overrides

The CMake cache contains default values West uses for debugging, such as where the board directory is on the file system, the path to the kernel binaries containing symbol tables, and more. You can override any of this configuration at runtime with additional options.

For example, to override the ELF file containing the Zephyr binary and symbol tables (assuming your runner expects an ELF file), but keep other debug configuration at default values:

west debug --kernel-elf path/to/some/other.elf
west debugserver --kernel-elf path/to/some/other.elf

The west debug -h output includes a complete list of overrides supported by all runners.

Runner-Specific Overrides

Each runner may support additional options related to debugging. For example, some runners support flags which allow you to set the network ports used by debug servers.

To view all of the available options for the runners your board supports, as well as their usage information, use --context (or -H):

west debug --context

(The command west debugserver --context will print the same output.)

Important

Note the capital H in the short option name. This re-runs the build in order to ensure the information displayed is up to date!

When running West outside of a build directory, west debug -H just prints a list of runners. You can use west debug -H -r <runner-name> to print usage information for options supported by that runner.

For example, to print usage information about the jlink runner:

west debug -H -r jlink

Library Backend: west.runners

In keeping with West’s Design Constraints, the flash and debug commands are wrappers around a separate library that is part of West, and can be used by other code.

This library is the west.runners subpackage of West itself. The central abstraction within this library is ZephyrBinaryRunner, an abstract class which represents runner objects, which can flash and/or debug Zephyr programs. The set of available runners is determined by the imported subclasses of ZephyrBinaryRunner. ZephyrBinaryRunner is available in the west.runners.core module; individual runner implementations are in other submodules, such as west.runners.nrfjprog, west.runners.openocd, etc.

Developers can add support for new ways to flash and debug Zephyr programs by implementing additional runners. To get this support into upstream Zephyr, the runner should be added into a new or existing west.runners module, and imported from west/runner/__init__.py.

Important

Submit any changes to West to its own separate Git repository (https://github.com/zephyrproject-rtos/west), not to the copy of West currently present in the Zephyr tree. This copy is a temporary measure; when West learns how to manage multiple repositories, the copy will be removed.

API documentation for the core module follows.

Zephyr binary runner core interfaces

This provides the core ZephyrBinaryRunner class meant for public use, as well as some other helpers for concrete runner classes.

class west.runners.core.BuildConfiguration(build_dir)

This helper class provides access to build-time configuration.

Configuration options can be read as if the object were a dict, either object[‘CONFIG_FOO’] or object.get(‘CONFIG_FOO’).

Configuration values in .config and generated_dts_board.conf are available.

class west.runners.core.NetworkPortHelper

Helper class for dealing with local IP network ports.

get_unused_ports(starting_from)

Find unused network ports, starting at given values.

starting_from is an iterable of ports the caller would like to use.

The return value is an iterable of ports, in the same order, using the given values if they were unused, or the next sequentially available unused port otherwise.

Ports may be bound between this call’s check and actual usage, so callers still need to handle errors involving returned ports.

class west.runners.core.RunnerCaps(commands={'debugserver', 'attach', 'debug', 'flash'}, flash_addr=False)

This class represents a runner class’s capabilities.

Each capability is represented as an attribute with the same name. Flag attributes are True or False.

Available capabilities:

  • commands: set of supported commands; default is {‘flash’, ‘debug’, ‘debugserver’, ‘attach’}.
  • flash_addr: whether the runner supports flashing to an arbitrary address. Default is False. If true, the runner must honor the –dt-flash option.
class west.runners.core.RunnerConfig(build_dir, board_dir, kernel_elf, kernel_hex, kernel_bin, gdb=None, openocd=None, openocd_search=None)

Runner execution-time configuration.

This is a common object shared by all runners. Individual runners can register specific configuration options using their do_add_parser() hooks.

This class’s __slots__ contains exactly the configuration variables.

board_dir

Zephyr board directory

build_dir

Zephyr application build directory

gdb

‘Path to GDB compatible with the target, may be None.

kernel_bin

Path to kernel binary in .bin format

kernel_elf

Path to kernel binary in .elf format

kernel_hex

Path to kernel binary in .hex format

openocd

Path to OpenOCD to use for this target, may be None.

directory to add to OpenOCD search path, may be None.

class west.runners.core.ZephyrBinaryRunner(cfg)

Abstract superclass for binary runners (flashers, debuggers).

Note: these APIs are still evolving, and will change!

With some exceptions, boards supported by Zephyr must provide generic means to be flashed (have a Zephyr firmware binary permanently installed on the device for running) and debugged (have a breakpoint debugger and program loader on a host workstation attached to a running target).

This is supported by three top-level commands managed by the Zephyr build system:

  • ‘flash’: flash a previously configured binary to the board, start execution on the target, then return.
  • ‘debug’: connect to the board via a debugging protocol, program the flash, then drop the user into a debugger interface with symbol tables loaded from the current binary, and block until it exits.
  • ‘debugserver’: connect via a board-specific debugging protocol, then reset and halt the target. Ensure the user is now able to connect to a debug server with symbol tables loaded from the binary.
  • ‘attach’: connect to the board via a debugging protocol, then drop the user into a debugger interface with symbol tables loaded from the current binary, and block until it exits. Unlike ‘debug’, this command does not program the flash.

This class provides an API for these commands. Every runner has a name (like ‘pyocd’), and declares commands it can handle (like ‘flash’). Zephyr boards (like ‘nrf52_pca10040’) declare compatible runner(s) by name to the build system, which makes concrete runner instances to execute commands via this class.

If your board can use an existing runner, all you have to do is give its name to the build system. How to do that is out of the scope of this documentation, but use the existing boards as a starting point.

If you want to define and use your own runner:

  1. Define a ZephyrBinaryRunner subclass, and implement its abstract methods. You may need to override capabilities().
  2. Make sure the Python module defining your runner class is imported, e.g. by editing this package’s __init__.py (otherwise, get_runners() won’t work).
  3. Give your runner’s name to the Zephyr build system in your board’s build files.

For command-line invocation from the Zephyr build system, runners define their own argparse-based interface through the common add_parser() (and runner-specific do_add_parser() it delegates to), and provide a way to create instances of themselves from a RunnerConfig and parsed runner-specific arguments via create().

Runners use a variety of target-specific tools and configuration values, the user interface to which is abstracted by this class. Each runner subclass should take any values it needs to execute one of these commands in its constructor. The actual command execution is handled in the run() method.

classmethod add_parser(parser)

Adds a sub-command parser for this runner.

The given object, parser, is a sub-command parser from the argparse module. For more details, refer to the documentation for argparse.ArgumentParser.add_subparsers().

The lone common optional argument is:

  • –dt-flash (if the runner capabilities includes flash_addr)

Runner-specific options are added through the do_add_parser() hook.

call(cmd)

Subclass subprocess.call() wrapper.

Subclasses should use this method to run command in a subprocess and get its return code, rather than using subprocess directly, to keep accurate debug logs.

classmethod capabilities()

Returns a RunnerCaps representing this runner’s capabilities.

This implementation returns the default capabilities.

Subclasses should override appropriately if needed.

check_call(cmd)

Subclass subprocess.check_call() wrapper.

Subclasses should use this method to run command in a subprocess and check that it executed correctly, rather than using subprocess directly, to keep accurate debug logs.

check_output(cmd)

Subclass subprocess.check_output() wrapper.

Subclasses should use this method to run command in a subprocess and check that it executed correctly, rather than using subprocess directly, to keep accurate debug logs.

classmethod create(cfg, args)

Create an instance from command-line arguments.

  • cfg: RunnerConfig instance (pass to superclass __init__)
  • args: runner-specific argument namespace parsed from execution environment, as specified by add_parser().
classmethod do_add_parser(parser)

Hook for adding runner-specific options.

do_run(command, **kwargs)

Concrete runner; run() delegates to this. Implement in subclasses.

In case of an unsupported command, raise a ValueError.

classmethod get_flash_address(args, build_conf, default=0)

Helper method for extracting a flash address.

If args.dt_flash is true, get the address from the BoardConfiguration, build_conf. (If CONFIG_HAS_FLASH_LOAD_OFFSET is n in that configuration, it returns CONFIG_FLASH_BASE_ADDRESS. Otherwise, it returns CONFIG_FLASH_BASE_ADDRESS + CONFIG_FLASH_LOAD_OFFSET.)

Otherwise (when args.dt_flash is False), the default value is returned.

static get_runners()

Get a list of all currently defined runner classes.

classmethod name()

Return this runner’s user-visible name.

When choosing a name, pick something short and lowercase, based on the name of the tool (like openocd, jlink, etc.) or the target architecture/board (like xtensa, em-starterkit, etc.).

popen_ignore_int(cmd)

Spawn a child command, ensuring it ignores SIGINT.

The returned subprocess.Popen object must be manually terminated.

run(command, **kwargs)

Runs command (‘flash’, ‘debug’, ‘debugserver’, ‘attach’).

This is the main entry point to this runner.

run_server_and_client(server, client)

Run a server that ignores SIGINT, and a client that handles it.

This routine portably:

  • creates a Popen object for the server command which ignores SIGINT
  • runs client in a subprocess while temporarily ignoring SIGINT
  • cleans up the server after the client exits.

It’s useful to e.g. open a GDB server and client.

Doing it By Hand

If you prefer not to use West to flash or debug your board, simply inspect the build directory for the binaries output by the build system. These will be named something like zephyr/zephyr.elf, zephyr/zephyr.hex, etc., depending on your board’s build system integration. These binaries may be flashed to a board using alternative tools of your choice, or used for debugging as needed, e.g. as a source of symbol tables.

By default, these West commands rebuild binaries before flashing and debugging. This can of course also be accomplished using the usual targets provided by Zephyr’s build system (in fact, that’s how West does it).

Footnotes

[1]The CMake cache is a file containing saved variables and values which is created by CMake when it is first run to generate a build system. See the cmake(1) manual for more details.