Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

ThreadX (Linux sim / RISC-V64 QEMU)

Single-node starter on Microsoft Azure RTOS ThreadX + NetX Duo (BSD socket layer). Two flavours ship in-tree:

  • threadx-linux — ThreadX user-space simulator on Linux. Fast build, host network stack, ideal for development.
  • threadx-riscv64 — QEMU virt machine with the RISC-V64 GCC toolchain. Full kernel + NetX Duo TCP/IP stack.

Rust, C, and C++ are supported on both flavours — just <flavour> build-fixtures produces threadx_cpp_* and riscv64_threadx_cpp_* binaries alongside the Rust + C ones. See the coverage matrix for the per-RMW cell status.

Prereqs. Install the nros CLI once, then run nros setup <board> --rmw <rmw> for the flavour you need (see Setup). It provisions the cross-compiler, emulator, RMW host daemon, and ThreadX/NetX sources — no hand-installed riscv64 cross toolchain, qemu-system-riscv64, or ROS 2 required.

Setup

nros setup is the single canonical command to prepare a machine to build nano-ros for a board. It ships prebuilt toolchains per platform per RMW — the cross-compiler, emulator, RMW host daemon, and SDK sources (the ThreadX/NetX sources, and for threadx-linux the POSIX-sim sources) are fetched from a pinned index into a shared store at ${NROS_HOME:-~/.nros}/sdk. You do not need ROS 2 installed.

Build the in-tree nros CLI (Phase 218):

source ./activate.sh        # OR: direnv allow / source ./activate.fish
just setup-cli              # builds packages/cli/target/release/nros

Provision the ThreadX flavour you need (+ the RMW):

nros setup threadx-linux --rmw zenoh          # POSIX-sim flavour; --rmw defaults to zenoh
nros setup qemu-riscv64-threadx --rmw zenoh   # only if you need the RISC-V64 QEMU flow
source ./setup.bash

The RMW host daemon must be running before any example: zenohd for zenoh, the Micro-XRCE-DDS agent for xrce. nros setup … --rmw <rmw> installs it.

Project layout

Each example is a standalone Cargo or CMake project under examples/threadx-{linux,riscv64}/<lang>/<example>/:

examples/threadx-linux/
├── rust/talker/                 # Cargo, target = x86_64-unknown-linux-gnu
│   ├── Cargo.toml
│   ├── package.xml
│   ├── generated/                # codegen output — build.rs runs
│   │                             #   `nros generate-rust` on first
│   │                             #   `cargo build`; gitignored.
│   └── src/main.rs
└── c/talker/                    # CMake, add_subdirectory
    ├── CMakeLists.txt
    ├── package.xml
    └── src/main.c

examples/qemu-riscv64-threadx/
├── rust/talker/                 # Cargo, target = riscv64gc-unknown-linux-gnu
│   └── ...
└── c/talker/
    └── ...

ThreadX-linux runs as a regular host process — no QEMU. NetX Duo uses the nx_bsd_* BSD socket shim layered on the host TCP stack (threadx-linux variant) or on its own NetX Duo TCP/IP stack (riscv64 variant).

Configure

Each talker carries a per-flavour nros.toml. Both files are reproduced verbatim below.

threadx-linux — examples/threadx-linux/rust/talker/nros.toml:

# nano-ros config (direct mode). See
# docs/design/0004-configuration-and-transports.md.

[node]
domain_id = 0

[[transport]]
kind    = "ethernet"
ip      = "192.0.3.10/24"
mac     = "02:00:00:00:00:00"
gateway = "192.0.3.1"
interface = "tap-tx0"
locator = "tcp/127.0.0.1:7455"

threadx-riscv64 — examples/qemu-riscv64-threadx/c/talker/nros.toml:

# nano-ros config (direct mode). See
# docs/design/0004-configuration-and-transports.md.

[node]
domain_id = 0

[[transport]]
kind    = "ethernet"
ip      = "10.0.2.40/24"
mac     = "52:54:00:12:34:56"
gateway = "10.0.2.2"
locator = "tcp/10.0.2.2:7553"

ThreadX-Linux normally uses a veth pair (tap-tx0) for an isolated host link, but nros setup threadx-linux does not create the interface — the test fixtures fall back to a loopback path when tap-tx0 is absent, which is fine for the happy-path tutorial. Bring up tap-tx0 by hand (ip link add … type veth …) only when you need real-network bridging. The QEMU-RISC-V64 fixture uses Slirp’s default 10.0.2.2 gateway just like the FreeRTOS QEMU flow.

Build

# threadx-linux:
just threadx_linux build-fixtures   # build all rust + c examples

# Single example:
cd examples/threadx-linux/rust/talker
cargo build --release

# threadx-riscv64:
just threadx_riscv64 build-fixtures

First setup builds ThreadX + NetX Duo (~3 min). Subsequent example builds finish in seconds.

Run

# threadx-linux (no QEMU). Step 1 brings up the in-tree zenohd on
# the threadx-linux port (7455). Step 2 runs the talker via the
# matching just recipe — same binary the example dir builds.
just threadx_linux zenohd &
just threadx_linux talker
# Expected (per src/main.rs structured logs):
#   Declaring publisher on /chatter (std_msgs/Int32)
#   Publisher declared
#   Published: 0
#   Published: 1
#   ...

# threadx-riscv64 (QEMU virt). Same shape — zenohd on 7453 first,
# then the talker recipe boots `qemu-system-riscv64` with the
# virtio-net + Slirp wiring baked in:
just threadx_riscv64 zenohd &
just threadx_riscv64 talker

# Verify from stock ROS 2:
source /opt/ros/humble/setup.bash
export RMW_IMPLEMENTATION=rmw_zenoh_cpp
# Talker publishes best-effort; stock `ros2 topic echo` defaults to
# RELIABLE, so the QoS-mismatched echo silently delivers nothing.
# Force best-effort to receive:
ros2 topic echo /chatter std_msgs/msg/Int32 --qos-reliability best_effort

For batch testing: just threadx_linux test runs every pubsub / service / action against an in-test zenohd.

Readiness signal. threadx-linux: Published: 0 within 3 seconds of just threadx_linux talker on a warm cache; a cold first run rebuilds the Rust example (~80 s on a fresh checkout) before the first publish lands. threadx-riscv64 (QEMU): within ~15 seconds of QEMU boot. If no Published: line:

  1. Confirm zenohd reachable on the locator from nros.toml (threadx-linux uses 127.0.0.1; riscv64 QEMU uses 10.0.2.2).
  2. threadx-linux: confirm the veth bridge came up via nros setup threadx-linux.
  3. See Troubleshooting — First 10 Minutes.

GitHub source

Next

  • Subscriber + service + action peers in the same example tree.
  • DDS on ThreadX: Cyclone DDS is the surviving DDS backend (nros-rmw-cyclonedds, selected via -DNANO_ROS_RMW=cyclonedds); see Choosing an RMW Backend.
  • Real hardware: same code runs against ThreadX vendor BSPs (Renesas Synergy, MIMXRT, etc.); replace the QEMU board crate with a vendor board crate.