West Commands Every Zephyr User Should Know
West is the meta tool used by Zephyr to wrap myriad commands and tools. Zephyr calls west a Swiss army knife, and just like its namesake, there are some west commands you have available but likely have never used.
Today we’ll survey useful west commands that you may not know about. But let’s start with the obvious ones!
West commands you already know
west west build west flash west debug west attach
These are the go-to’s that should already be at the tips of your fingers. A simple west
without anything else prints out usage where you’ll see all of the commands covered in the rest of the post.
The west build
command will rebuild a project, but you will need to specify the board and path the first time to get things set up. Programming the board is handled by west flash
, and depending on the runner, you can open a debugging session with west attach
or west debug
.
Installing toolchains
west sdk
One of the newer commands, west sdk
will pull up information about the currently installed SDK and toolchains versions. Add the “i” flag to it (west sdk -i
) to drop into the interactive installation script. This is the same as you get when you go to the releases page, download, and run an SDK release. But it cuts out those steps.
Help me with Kconfig
west build -t menuconfig
Another one of those you should always have under your fingers, west is used to launch the ncurses-style menuconfig editor for working with Kconfig symbols. Once inside, use /
to search, or navigate the menu system. You can use the ?
to bring up help information on any symbol currently highlighted.
Working with Boards
west boards west boards -n 52840
List every board definition in the Zephyr tree with a simple west boards
command. However, the search option using -n
is likely more useful. The command above will return (almost) every board that is built on an nRF52840 chip. I found that it does not search the SoC of the Hardware Model v2 naming scheme, so rak5010
wasn’t returned in the search even though its more verbose rak5010/nrf52840
name is .
Working with runners
west flash --context
This one is a bit obscure, but absolutely crucial when you need it. Zephyr uses the concept of runners to flash and debug targets. Using the context flag pulls up a ton of useful information. Bear with me while I paste a really long example output so we can discuss.
➜ west flash --context -- west flash: rebuilding ninja: no work to do. build configuration: build directory: /home/mike/golioth-compile/golioth-firmware-sdk/modules/lib/golioth-firmware-sdk/build board: mimxrt1024_evk/mimxrt1024 runners.yaml: /home/mike/golioth-compile/golioth-firmware-sdk/modules/lib/golioth-firmware-sdk/build/zephyr/runners.yaml zephyr runners which support "west flash": blackmagicprobe, nrfutil, arc-nsim, dediprog, uf2, dfu-util, xsdb, teensy, ezflashcli, linkserver, nrfjprog, silabs_commander, esp32, spi_burn, pyocd, stm32cubeprogrammer, native, mdb-nsim, gd32isp, nios2, intel_adsp, canopen, openocd, probe-rs, hifive1, stm32flash, misc-flasher, bossac, trace32, intel_cyclonev, jlink, mdb-hw Note: not all may work with this board and build directory. Available runners are listed below. available runners in runners.yaml: linkserver, jlink, pyocd default runner in runners.yaml: linkserver common runner configuration: - build_dir: /home/mike/golioth-compile/golioth-firmware-sdk/modules/lib/golioth-firmware-sdk/build - board_dir: /home/mike/golioth-compile/golioth-firmware-sdk/zephyr/boards/nxp/mimxrt1024_evk - elf_file: /home/mike/golioth-compile/golioth-firmware-sdk/modules/lib/golioth-firmware-sdk/build/zephyr/zephyr.elf - exe_file: None - hex_file: /home/mike/golioth-compile/golioth-firmware-sdk/modules/lib/golioth-firmware-sdk/build/zephyr/zephyr.hex - bin_file: /home/mike/golioth-compile/golioth-firmware-sdk/modules/lib/golioth-firmware-sdk/build/zephyr/zephyr.bin - uf2_file: None - file: None - file_type: FileType.OTHER - gdb: /home/mike/zephyr-sdk-0.17.0/arm-zephyr-eabi/bin/arm-zephyr-eabi-gdb-py - openocd: /home/mike/zephyr-sdk-0.17.0/sysroots/x86_64-pokysdk-linux/usr/bin/openocd - openocd_search: ['/home/mike/zephyr-sdk-0.17.0/sysroots/x86_64-pokysdk-linux/usr/share/openocd/scripts'] - rtt_address: None runner-specific context: linkserver capabilities: RunnerCaps(commands={'debugserver', 'flash', 'debug', 'attach'}, dev_id=True, flash_addr=True, erase=True, reset=False, extload=False, tool_opt=True, file=True, hide_load_files=False, rtt=False) linkserver options: -i DEV_ID, --dev-id DEV_ID Device identifier. Use it to select which debugger, device, node or instance to target when multiple ones are available or connected. --dt-flash {Y,y,N,n,yes,no,YES,NO} If 'yes', try to use flash address information from devicetree when flash addresses are unknown (e.g. when flashing a .bin) -f FILE, --file FILE path to binary file -t FILE_TYPE, --file-type FILE_TYPE type of binary file --elf-file FILE Deprecated, use -f/--file instead. --hex-file FILE Deprecated, use -f/--file instead. --bin-file FILE Deprecated, use -f/--file instead. --erase, --no-erase mass erase flash before loading, or don't. Default action depends on each specific runner. -O TOOL_OPT, --tool-opt TOOL_OPT Option to pass on to the underlying tool used by this runner. This can be given multiple times; the resulting arguments will be given to the tool in the order they appear on the command line. --device DEVICE device name --core CORE core of the device --probe PROBE interface to use (index, or serial number, default is #1 --tui if given, GDB uses -tui --gdb-port GDB_PORT gdb port to open, defaults to 3333 --semihost-port SEMIHOST_PORT semihost port to open, defaults to the empty string and runs a gdb server --linkserver LINKSERVER LinkServer executable, default is LinkServer --override OVERRIDE configuration overrides as defined bylinkserver. Example: /device/memory/0/location=0xcafecafe linkserver arguments from runners.yaml: --dt-flash=y --device=MIMXRT1024xxxxx:MIMXRT1024-EVK jlink capabilities: RunnerCaps(commands={'debugserver', 'rtt', 'debug', 'flash', 'attach'}, dev_id=True, flash_addr=True, erase=True, reset=True, extload=False, tool_opt=True, file=True, hide_load_files=False, rtt=True) jlink options: -i DEV_ID, --dev-id DEV_ID Device identifier. Use it to select the J-Link Serial Number of the device connected over USB. If the J-Link is connected over ip, the Device identifier is the ip. --dt-flash {Y,y,N,n,yes,no,YES,NO} If 'yes', try to use flash address information from devicetree when flash addresses are unknown (e.g. when flashing a .bin) -f FILE, --file FILE path to binary file -t FILE_TYPE, --file-type FILE_TYPE type of binary file --elf-file FILE Deprecated, use -f/--file instead. --hex-file FILE Deprecated, use -f/--file instead. --bin-file FILE Deprecated, use -f/--file instead. --erase, --no-erase mass erase flash before loading, or don't. Default action depends on each specific runner. --reset, --no-reset reset device after flashing, or don't. Default action depends on each specific runner. -O TOOL_OPT, --tool-opt TOOL_OPT Additional options for JLink Commander, e.g. '-autoconnect 1' --rtt-address RTT_ADDRESS address of RTT control block. If not supplied, it will be autodetected if possible --device DEVICE device name --loader LOADER specifies a loader type --id DEV_ID obsolete synonym for -i/--dev-id --iface IFACE interface to use, default is swd --speed SPEED interface speed, default is autodetect --tui if given, GDB uses -tui --gdbserver GDBSERVER GDB server, default is JLinkGDBServer --gdb-host GDB_HOST custom gdb host, defaults to the empty string and runs a gdb server --gdb-port GDB_PORT pyocd gdb port, defaults to 2331 --commander COMMANDER J-Link Commander, default is JLinkExe --reset-after-load, --no-reset-after-load obsolete synonym for --reset/--no-reset --rtt-client RTT_CLIENT RTT client, default is JLinkRTTClient --rtt-port RTT_PORT jlink rtt port, defaults to 19021 jlink arguments from runners.yaml: --dt-flash=y --device=MIMXRT1024xxx5A pyocd capabilities: RunnerCaps(commands={'debugserver', 'rtt', 'debug', 'flash', 'attach'}, dev_id=True, flash_addr=True, erase=True, reset=False, extload=False, tool_opt=True, file=False, hide_load_files=False, rtt=True) pyocd options: -i DEV_ID, --dev-id DEV_ID Device identifier. Use it to select the probe's unique ID or substring thereof. --dt-flash {Y,y,N,n,yes,no,YES,NO} If 'yes', try to use flash address information from devicetree when flash addresses are unknown (e.g. when flashing a .bin) --elf-file FILE path to zephyr.elf --hex-file FILE path to zephyr.hex --bin-file FILE path to zephyr.bin --erase, --no-erase mass erase flash before loading, or don't. Default action depends on each specific runner. -O TOOL_OPT, --tool-opt TOOL_OPT Additional options for pyocd commander, e.g. '--script=user.py' --rtt-address RTT_ADDRESS address of RTT control block. If not supplied, it will be autodetected if possible --target TARGET target override --daparg DAPARG Additional -da arguments to pyocd tool --pyocd PYOCD path to pyocd tool, default is pyocd --flash-opt FLASH_OPT Additional options for pyocd flash, e.g. --flash-opt="-e=chip" to chip erase --frequency FREQUENCY SWD clock frequency in Hz --gdb-port GDB_PORT pyocd gdb port, defaults to 3333 --telnet-port TELNET_PORT pyocd telnet port, defaults to 4444 --tui if given, GDB uses -tui --board-id DEV_ID obsolete synonym for -i/--dev-id pyocd arguments from runners.yaml: --dt-flash=y --target=mimxrt1024 Note: use -r RUNNER to limit information to one runner.
You can see so many helpful tidbits when running the west flash --context
command after a successful build. First up, you get a list of what runners are currently supported for this board (line 17), along with all of the runners available to Zephyr (line 8). Not able to flash your chip? Perhaps the default runner (line 19) is different from the the programmer you have connected. This is the case for me with the mimxrt1024_evk board, so adding --runner jlink
to all flash/attach commands is how I work with this board.
The rest of the output covers runner-specific configuration. Most of the time the defaults work great. But when you need to pass a different GDB port, or make some other tweak, this is where to go for help.
Managing Manifests
west manifest --path west config manifest.file my-custom-manifest.yml west manifest --resolve west manifest --freeze
Manifests are near and dear to my heart. I think they are among the best features of the Zephyr ecosystem!
The Golioth Firmware SDK is an interesting use case for multiple distinct manifest files. We have one for projects that build on Zephyr, and another for projects that build on Nordic’s nrfConnect SDK (NCS). On workspaces installed with the Golioth SDK as the main manifest, I can switch between the two types of installations using west config manifest.file west-zephyr.yml
.
When you just want to know where your manifest file is located, reach for west manifest --path
. During development you might want to know what versions are being pulled in from inherited manifests. The west manifest --resolve
will print that info out for you. When you’re getting to the end of the project, use west manifest --freeze
to generate your manifest file with commit hashes for versions (locking in any branch names you used).
This tool goes deep
The west meta tool wraps up an incredible amount of functionality. Most of the time, you’ll find west can already do the things you want, it just takes a little digging to discover the syntax.
If west does fall short of your needs, you can always add your own custom commands. We do that here at Golioth, but Zephyr also does it to enable most of the commands mentioned above. But that’s a story best saved for a future blog post.
Start the discussion at forum.golioth.io