Warning

This documentation is actively being updated as the project evolves and may not be complete in all areas.

JEP-0010: Renode Integration for Microcontroller Targets

Field

Value

JEP

0010

Title

Renode Integration for Microcontroller Targets

Author(s)

@vtz (Vinicius Zein)

Status

Implemented

Type

Standards Track

Created

2026-04-06

Updated

2026-04-15

Discussion

PR #533


Abstract

This JEP proposes integrating the Renode emulation framework into Jumpstarter as a new driver package (jumpstarter-driver-renode). The driver enables microcontroller-class virtual targets running bare-metal firmware or RTOS on Cortex-M and RISC-V MCUs, complementing the existing QEMU driver which targets Linux-capable SoCs.

Motivation

Jumpstarter provides a driver-based framework for interacting with devices under test, both physical hardware and virtual systems. The existing QEMU driver enables Linux-class virtual targets (aarch64, x86_64) using full-system emulation with virtio devices and cloud-init provisioning.

There is growing demand for microcontroller-class virtual targets running bare-metal firmware or RTOS (Zephyr, FreeRTOS, ThreadX) on Cortex-M and RISC-V MCUs. QEMU’s MCU support is limited in peripheral modeling and does not match Renode’s breadth for embedded platforms. Renode by Antmicro is an open-source emulation framework designed specifically for this domain, with extensive peripheral models for STM32, NXP S32K, Nordic, SiFive, and other MCU platforms.

User Stories

  • As a firmware CI pipeline author, I want to run my Zephyr firmware against a virtual STM32 target in Jumpstarter, so that I can validate UART output and basic functionality without physical hardware.

  • As a SOME/IP stack developer, I want to configure virtual NXP S32K388 and STM32F407 targets through exporter YAML, so that I can test multi-platform firmware builds without modifying driver code.

  • As a Jumpstarter lab operator, I want to mix physical boards and Renode virtual targets in the same test environment, so that my team can scale testing beyond available hardware.

Reference Targets

The initial targets for validation are:

  • STM32F407 Discovery (Cortex-M4F) – opensomeip FreeRTOS/ThreadX ports, Renode built-in platform

  • NXP S32K388 (Cortex-M7) – opensomeip Zephyr port, custom platform description

  • Nucleo H753ZI (Cortex-M7) – openbsw-zephyr, Renode built-in stm32h743.repl

Constraints

  • The driver must follow Jumpstarter’s established composite driver pattern (as demonstrated by jumpstarter-driver-qemu)

  • Users must be able to define new Renode targets through configuration alone, without modifying driver code

  • The solution should minimize external dependencies and runtime requirements

  • The UART/console interface must be compatible with Jumpstarter’s existing PySerial and pexpect tooling

  • The async framework must be anyio (the project’s standard)

Proposal

The jumpstarter-driver-renode package provides a composite driver (Renode) that manages a Renode simulation instance with three child drivers:

  • RenodePower — controls the Renode process lifecycle (on/off)

  • RenodeFlasher — handles firmware loading (sysbus LoadELF / sysbus LoadBinary)

  • PySerial (console) — serial access over a PTY terminal

Users configure targets entirely through exporter YAML:

export:
  Renode:
    platform: "platforms/boards/stm32f4_discovery-kit.repl"
    uart: "sysbus.usart2"
    machine_name: "stm32f4"
    extra_commands:
      - "sysbus WriteDoubleWord 0x40090030 0x0301"
    allow_raw_monitor: false

The driver starts Renode with --disable-xwt --plain --port <N>, connects to the telnet monitor via anyio.connect_tcp, and programmatically sets up the platform, UART terminal, and firmware.

A RenodeMonitor async client handles the telnet protocol:

  • Retry-based connection with fail_after(timeout)

  • Prompt detection using registered machine names

  • Per-line error marker detection

  • Newline injection prevention

  • Configurable command timeout

API / Protocol Changes

No gRPC protocol changes. The driver exposes standard Jumpstarter interfaces (PowerInterface, FlasherInterface) plus:

  • get_platform(), get_uart(), get_machine_name() — read-only config accessors

  • monitor_cmd(command) — raw monitor access, gated behind allow_raw_monitor: true (default: false)

Hardware Considerations

No physical hardware required. Renode is a pure software emulator. The driver uses PTY terminals for UART, which requires a POSIX system (Linux or macOS). The Renode process is managed via subprocess.Popen.

Design Decisions

DD-1: Control Interface — Telnet Monitor

Alternatives considered:

  1. Telnet monitor — Renode’s built-in TCP monitor interface. Simple socket connection, send text commands, read responses. Lightweight, no extra runtime needed.

  2. pyrenode3 — Python.NET bridge to Renode’s C# internals. More powerful but requires .NET runtime or Mono, heavy dependency, less stable API surface.

Decision: Telnet monitor.

Rationale: It is the lowest-common-denominator interface that works with any Renode installation. It mirrors the QEMU driver’s pattern where Popen starts the emulator process and a side-channel protocol (QMP for QEMU, telnet monitor for Renode) provides programmatic control. The monitor client uses anyio.connect_tcp with anyio.fail_after for timeouts, consistent with TcpNetwork and grpc.py in the project.

DD-2: UART Exposure — PTY Terminal

Alternatives considered:

  1. PTY (emulation CreateUartPtyTerminal) — Creates a pseudo-terminal file on the host. Reuses the existing PySerial child driver exactly as QEMU does. Linux/macOS only.

  2. Socket (emulation CreateServerSocketTerminal) — Exposes UART as a TCP socket. Cross-platform. Maps to TcpNetwork driver. Has telnet IAC negotiation bytes to handle.

Decision: PTY as the primary interface.

Rationale: Consistency with the QEMU driver, which uses -serial pty and wires a PySerial child driver to the discovered PTY path. This reuses the same serial/pexpect/console tooling without any adaptation. Socket terminal support can be added later as a fallback for platforms without PTY support.

DD-3: Configuration Model — Managed Mode

Alternatives considered:

  1. Managed mode — The driver constructs all Renode monitor commands from YAML config parameters (platform, uart, firmware path). The driver handles platform loading, UART wiring, and firmware loading programmatically.

  2. Script mode — User provides a complete .resc script. The driver runs it but still manages UART terminal setup.

Decision: Managed mode as primary, with an extra_commands list for target-specific customization.

Rationale: Managed mode gives the driver full control over the UART terminal setup (which must use PTY for Jumpstarter integration, not the CreateFileBackend or showAnalyzer used in typical .resc scripts). The extra_commands list covers target-specific needs like register pokes (e.g., sysbus WriteDoubleWord 0x40090030 0x0301 for S32K388 PL011 UART enablement) and Ethernet switch setup.

DD-4: Firmware Loading — Deferred to Flash

Alternatives considered:

  1. flash() stores the firmware path, on() loads it into the simulation and starts

  2. on() starts the simulation, flash() loads firmware and resets

Decision: Option 1 — flash() stores the path, on() loads and starts.

Rationale: This matches the QEMU driver’s semantic where you flash a disk image first, then power on. It also allows re-flashing between power cycles without restarting the Renode process. The RenodeFlasher additionally supports hot-loading: if the simulation is already running, flash() sends the load command and resets the machine.

DD-5: Security — Restricted Monitor Access

Alternatives considered:

  1. Open access — Expose monitor_cmd to all authenticated clients

  2. Opt-in access — Gate behind allow_raw_monitor config flag

Decision: Opt-in with allow_raw_monitor: false by default.

Rationale: The Renode monitor supports commands that interact with the host filesystem (logFile, include, CreateFileTerminal). In a shared lab environment, unrestricted monitor access from any authenticated client poses a risk. The load_command parameter in flash() is separately validated against an allowlist of known Renode load commands. Newline characters are rejected in all monitor commands to prevent command injection.

Design Details

Component Architecture

Renode (composite driver)
├── RenodePower      → manages Popen lifecycle + RenodeMonitor
├── RenodeFlasher    → writes firmware, sends LoadELF/LoadBinary
└── PySerial         → console over PTY terminal

Monitor Protocol

The RenodeMonitor client connects to Renode’s telnet port and communicates via line-oriented text:

  1. Connection: retry loop with fail_after(timeout), closing leaked streams on retry

  2. Prompt detection: matches (monitor) or registered machine names only — no false positives from output like (enabled)

  3. Error detection: per-line check against markers (Could not find, Error, Invalid, Failed, Unknown)

  4. Timeout: execute() wraps reads in fail_after(30) to prevent indefinite blocking

  5. Injection prevention: newline characters rejected in commands; load_command validated against allowlist

Firmware Auto-Detection

When load_command is not specified, the driver reads the first 4 bytes of the firmware file. If they match the ELF magic (\x7fELF), sysbus LoadELF is used; otherwise sysbus LoadBinary.

Test Plan

Unit Tests

  • TestRenodeMonitor — connection retry, command execution, error detection (per-line), disconnect, newline rejection, stream cleanup on retry, prompt matching against expected prompts only

  • TestRenodePower — command sequence verification, extra commands ordering, firmware-less boot, idempotent on/off, process termination and cleanup

  • TestRenodeFlasher — firmware path storage, hot-load with reset, custom load command, invalid load command rejection, ELF magic detection, dump not-implemented

  • TestRenodeConfig — default values, children wiring, custom config, PTY path construction, lifecycle

Integration Tests

  • TestRenodeClient — round-trip properties via serve(), children accessibility, monitor_cmd disabled by default, monitor_cmd not running error, CLI rendering

E2E Tests

  • test_driver_renode_e2e — full power on/off cycle with real Renode process, skipped when Renode is not installed

CI

Renode is installed in the python-tests.yaml workflow:

  • Linux: .deb package from builds.renode.io

  • macOS: brew install renode

Backward Compatibility

This JEP introduces a new driver package with no changes to existing packages. There are no breaking changes. The jumpstarter-all meta-package includes the new driver as an optional dependency.

Consequences

Positive

  • Single jumpstarter-driver-renode package supports any Renode target through YAML configuration alone

  • No .NET runtime or Mono dependency required

  • Consistent user experience with the QEMU driver (same composite pattern, same console/pexpect workflow)

  • extra_commands provides an escape hatch for target-specific customization without code changes

  • Security-by-default with allow_raw_monitor: false

Negative

  • PTY-only UART exposure limits to Linux/macOS (acceptable since Renode itself primarily targets these platforms)

  • The telnet monitor protocol is text-based and less structured than QMP’s JSON — error detection requires string matching

  • Full .resc script support is deferred; users with complex Renode setups must express their configuration as managed-mode parameters plus extra_commands

Risks

  • Renode’s monitor protocol has no formal specification; prompt detection and error handling rely on observed behavior

  • Renode’s PTY terminal support on macOS may have edge cases not covered in testing

Rejected Alternatives

Beyond the alternatives listed in each Design Decision above, the high-level alternative of not integrating Renode and instead extending the QEMU driver for MCU targets was considered. QEMU’s MCU support (e.g., qemu-system-arm -M stm32vldiscovery) is limited in peripheral modeling and does not match Renode’s breadth for embedded platforms. The QEMU driver remains the right choice for Linux-capable SoCs while Renode fills the MCU gap.

Prior Art

  • jumpstarter-driver-qemu — The existing Jumpstarter QEMU driver established the composite driver pattern, Popen-based process management, and side-channel control protocol (QMP) that this JEP follows.

  • Renode documentationRenode docs for monitor commands, platform descriptions, and UART terminal types.

  • opensomeipgithub.com/vtz/opensomeip provides the reference Renode targets (STM32F407, S32K388) used for validation.

Future Possibilities

  • Socket terminal fallback for Windows/cross-platform UART access

  • .resc script mode for users with complex existing Renode setups

  • Multi-machine simulation for testing inter-MCU communication

  • Renode metrics integration for performance profiling

Implementation History

  • 2026-04-06: JEP proposed

  • 2026-04-06: Initial implementation proposed (PR #533)

  • 2026-04-11: Address review feedback (DEVNULL, try-except cleanup, async wait, RenodeMonitorError, multi-word CLI, docstrings)

  • 2026-04-15: Security hardening (load_command allowlist, allow_raw_monitor, newline rejection, per-line error detection, prompt matching, execute timeout, stream leak fix)

References


This JEP is licensed under the Apache License, Version 2.0, consistent with the Jumpstarter project.