Design#

pw_emu: Flexible emulators frontend

Parallelism#

pw_emu supports running multiple instances simultaneously. This is helpful for developers who work on multiple downstream Pigweed projects or who want to run multiple tests in parallel on the same machine.

Each instance is identified by a system absolute path that is also used to store state about the running instance such as pid files for running processes, current emulator and target, etc. This directory also contains information about how to access the emulator channels, e.g. socket ports, PTY paths, etc.

graph TD; TemporaryEmulator & pw_emu_cli[pw emu cli] <--> Emulator Emulator <--> Launcher & Connector Launcher <--> Handles Connector <--> Handles Launcher <--> Config Handles --Save--> WD --Load--> Handles WD[Working Directory]

API surface#

The implementation uses the following classes:

Emulator properties#

The implementation exposes the ability to list, read, and write emulator properties. The frontend does not abstract properties in a way that is emulator-independent or even emulator-target-independent, other than mandating that each property is identified by a path. Note that the format of the path is also emulator-specific and not standardized.

QEMU#

The QEMU frontend is using QMP to communicate with the running QEMU process and implement emulator-specific functionality like reset, list properties, reading properties, etc.

QMP is exposed to the host through two channels: a temporary one to establish the initial connection that is used to read the dynamic configuration (e.g. TCP ports, PTY paths) and a permanent one that can be used throughout the life of the QEMU processes. The frontend is configuring QEMU to expose QMP to a localhost TCP port reserved by the frontend and then waiting for QEMU to establish the connection on that port. Once the connection is established the frontend reads the configuration of the permanent QMP channel (which can be either a TCP port or a PTY path) and save it as a channel named qmp in the pw_emu.core.Handles object.

Renode#

The Renode frontend uses robot port to interact with the Renode process. Although the robot interface is designed for testing and not as a control interface, it is more robust and better suited to be used as a machine interface than the alternative monitor interface which is a user-oriented, ANSI-colored, echoed, log-mixed, telnet interface.

Bugs#

While Renode allows passing 0 for ports to allocate a dynamic port, it does not have APIs to retrieve the allocated port. Until support for such a feature is added upstream, the implementation is using the following technique to allocate a port dynamically:

sock = socket.socket(socket.SOCK_INET, socket.SOCK_STREAM)
sock.bind(('', 0))
_, port = socket.getsockname()
sock.close()

There is a race condition that allows another program to fetch the same port, but it should work in most light use cases until the issue is properly resolved upstream.

More pw_emu docs#

Get started & guides

How to set up and use pw_emu

CLI reference

Reference details about the pw_emu command line interface

API reference

Reference details about the pw_emu Python API

Configuration

Reference details about pw_emu declarative configuration

Design

Design details about pw_emu

SEED-0108: Emulators Frontend

The RFC explaining the initial design and motivations for pw_emu

Source code

Source code for pw_emu