QEMU Emulation for Hexagon

Overview

This board configuration uses QEMU to emulate a Qualcomm Hexagon DSP virtual platform.

The Hexagon DSP is a VLIW digital signal processor designed by Qualcomm and found in Snapdragon SoCs. This board configuration provides a QEMU-based environment for developing and testing Zephyr applications targeting the Hexagon architecture.

Zephyr on Hexagon runs as a guest under the Hexagon VM hypervisor. The zephyr.elf binary cannot be booted directly; it must be loaded via the HVM loadlinux boot loader.

This configuration provides support for the following devices:

  • HVM PIC interrupt controller

  • HVM timer

  • PL011 UART (console)

Hardware

Supported Features

The qemu_hexagon board supports the hardware features listed below.

on-chip / on-board
Feature integrated in the SoC / present on the board.
2 / 2
Number of instances that are enabled / disabled.
Click on the label to see the first instance of this feature in the board/SoC DTS files.
vnd,foo
Compatible string for the Devicetree binding matching the feature.
Click on the link to view the binding documentation.

Devices

System Clock

This board configuration uses a system clock frequency of 1 MHz.

Known Problems or Limitations

The following known issues apply:

  • Some tests that exercise CONFIG_USERSPACE code paths may crash the guest VM at runtime.

Getting Started

This section describes how to set up the required tools and dependencies to build and run Zephyr on the QEMU Hexagon board.

Prerequisites

The following tools are required:

  • west build tool and Zephyr Python dependencies (see Step 5)

  • LLVM/Clang cross-compiler for Hexagon (see below)

  • QEMU with Hexagon support (see below)

  • Hexagon hypervisor loadlinux boot loader (see below)

Step 1: Install the Hexagon LLVM Toolchain

Download the pre-built LLVM cross-compiler for Hexagon from the toolchain_for_hexagon releases page.

The recommended asset for Ubuntu 22.04 and later x86_64 hosts is:

clang+llvm-22.1.4-cross-hexagon-unknown-linux-musl.tar.zst

Download and extract it to a directory of your choice, for example /opt/hexagon-toolchain:

$ wget https://artifacts.codelinaro.org/artifactory/codelinaro-toolchain-for-hexagon/22.1.4_/clang+llvm-22.1.4-cross-hexagon-unknown-linux-musl.tar.zst
$ mkdir -p /opt/hexagon-toolchain
$ tar -xf clang+llvm-22.1.4-cross-hexagon-unknown-linux-musl.tar.zst \
      -C /opt/hexagon-toolchain --strip-components=2

Note

The tarball contains an x86_64-linux-gnu/ subdirectory at its top level. --strip-components=2 strips both the archive name prefix and that subdirectory so that bin/clang lands directly under the install directory.

Select the hexagon toolchain variant and point HEXAGON_TOOLCHAIN_PATH at the extracted directory:

$ export ZEPHYR_TOOLCHAIN_VARIANT=hexagon
$ export HEXAGON_TOOLCHAIN_PATH=/opt/hexagon-toolchain

See Qualcomm Hexagon LLVM Toolchain for details on the hexagon toolchain variant.

Step 2: QEMU with Hexagon Support

Standard QEMU releases do not include Hexagon system emulation. The toolchain installed in Step 1 bundles qemu-system-hexagon in its bin directory, and the build system looks there first, so no further setup is needed for west build -t run or Twister.

To use a different QEMU, build it from the Qualcomm fork:

$ git clone https://github.com/qualcomm/qemu.git -b hexagon-sysemu-22-may-2026 qemu-hexagon
$ cd qemu-hexagon
$ mkdir build && cd build
$ ../configure --target-list=hexagon-softmmu --disable-werror
$ make -j$(nproc)

The resulting binary is qemu-system-hexagon inside the build directory you are currently in. Point QEMU_BIN_PATH at that directory to select it over the bundled one:

$ export QEMU_BIN_PATH="$PWD"

Step 3: Install the Hexagon SDK

The loadlinux bootloader requires the Hexagon SDK, which provides hexagon-clang and related tools. Download and extract the SDK tarball:

$ curl -LO https://github.com/snapdragon-toolchain/hexagon-sdk/releases/download/v6.4.0.2/hexagon-sdk-v6.4.0.2-amd64-lnx.tar.xz
$ tar -xf hexagon-sdk-v6.4.0.2-amd64-lnx.tar.xz -C /opt/hexagon-sdk
$ export HEXAGON_SDK_ROOT=/opt/hexagon-sdk/6.4.0.2
$ export HEXAGON_TOOLS_ROOT=$HEXAGON_SDK_ROOT/tools/HEXAGON_Tools/19.0.04
$ export PATH=$HEXAGON_TOOLS_ROOT/Tools/bin:$PATH

Verify the SDK toolchain is accessible:

$ hexagon-clang --version

Step 4: Build the Hexagon Hypervisor Boot Loader

Zephyr must run as a guest under the Hexagon VM (H2) hypervisor. The loadlinux binary from the hypervisor repository is used as the QEMU -bios argument; it then loads and boots the Zephyr ELF.

Clone the hypervisor repository and check out the required tag. Cloning it directly into $ZEPHYR_BASE lets the Zephyr build system find loadlinux automatically:

$ git clone https://github.com/androm3da/hexagon-hypervisor.git \
      $ZEPHYR_BASE/hexagon-hypervisor
$ cd $ZEPHYR_BASE/hexagon-hypervisor
$ git checkout h2-bcain-1-june-2026

Build the H2 hypervisor libraries first. NULL_ANGEL_TRAP=1 disables the angel semihosting handler, which would otherwise cause a silent hang on the QEMU virt machine:

$ ARCHV=73
$ make USE_PKW=0 ARCHV=$ARCHV TARGET=opt NULL_ANGEL_TRAP=1 -j$(nproc)

Then build loadlinux from the linux/ subdirectory. The makefile defaults its build paths to ../install and ../kernel, but the artifacts from the step above land in artifacts/v${ARCHV}/opt/. Export the correct paths before invoking make:

$ export INSTALLPATH=$(pwd)/artifacts/v${ARCHV}/opt/install
$ export KERNELPATH=$(pwd)/artifacts/v${ARCHV}/opt/build/kernel
$ make -C linux USE_PKW=0 ARCHV=$ARCHV NO_LOAD=1 \
      NULL_ANGEL_TRAP=1 LINUX_LINK_ADDR=0xa0000000 loadlinux

The resulting linux/loadlinux is passed to QEMU via the -bios flag. The Zephyr build system looks for it at $ZEPHYR_BASE/hexagon-hypervisor/linux/loadlinux by default. You can override this path by setting the HEXAGON_H2_LOADLINUX environment variable or passing -DHEXAGON_H2_LOADLINUX=<path> to CMake.

Step 5: Set Up the Zephyr Environment

Follow the standard Getting Started Guide guide to install Zephyr dependencies and initialize the workspace.

Create a Python 3.12 virtual environment, install west and the Zephyr Python requirements into it, then activate it:

$ python3.12 -m venv ~/zephyr-venv
$ ~/zephyr-venv/bin/pip install west
$ ~/zephyr-venv/bin/pip install -r $ZEPHYR_BASE/scripts/requirements.txt
$ source ~/zephyr-venv/bin/activate

Source the Zephyr environment script:

$ source $ZEPHYR_BASE/zephyr-env.sh

Programming and Debugging

The qemu_hexagon board supports the runners and associated west commands listed below.

flash debug

Building

Build a Zephyr application for this board. The toolchain must be specified explicitly because Hexagon is built with an LLVM cross-compiler rather than the Zephyr SDK:

# From the root of the zephyr repository
west build -b qemu_hexagon/qemu_hexagon_virt samples/hello_world -- -DZEPHYR_TOOLCHAIN_VARIANT=hexagon -DHEXAGON_TOOLCHAIN_PATH=/opt/hexagon-toolchain

Note

Replace /opt/hexagon-toolchain with the actual value of $HEXAGON_TOOLCHAIN_PATH set in Step 1. Both arguments can be left out when the environment variables of Step 1 are exported.

The build produces zephyr.elf which is used for both debugging and runtime loading by the hypervisor.

Running

If loadlinux is present at the default path, you can run with:

$ west build -t run

To run manually, pass the path to your QEMU binary and the loadlinux boot loader explicitly:

$ qemu-system-hexagon \
    -machine virt -nographic -m 4G \
    -bios path/to/hexagon-hypervisor/linux/loadlinux \
    -device loader,file=build/zephyr/zephyr.elf

Replace path/to/hexagon-hypervisor/linux/loadlinux with the actual path to the loadlinux binary obtained in Step 3.

Exit QEMU by pressing CTRL+A x.

Expected output for samples/hello_world:

*** Booting Zephyr OS build v4.x.x ***
Hello World! qemu_hexagon/qemu_hexagon_virt

Debugging

Refer to the detailed overview about Application Debugging.

QEMU supports remote debugging via the GDB remote serial protocol. Start QEMU with -s -S flags to pause execution and wait for a debugger connection on port 1234:

$ qemu-system-hexagon \
    -machine virt -nographic -m 4G \
    -bios path/to/hexagon-hypervisor/linux/loadlinux \
    -device loader,file=build/zephyr/zephyr.elf \
    -s -S

Use LLDB to connect to the target. GDB does not support the Hexagon ISA:

$ lldb build/zephyr/zephyr.elf
(lldb) gdb-remote 1234
(lldb) b main
(lldb) continue

References