Skip to content

User guide

How to run and embed the Tero emulator. This is the User Manual: its audience is people who run tero-emu or link tero_runtime into their own program. For how the emulator works internally, see the architecture overview (the Developer Manual).

Map of this guide

Page What it covers
CLI reference Every tero-emu flag — meaning, default, when to use it.
Embedding as a library Emulator::create, the lifecycle, load_elf, run_for/run_until, the memory API, injecting services, the GDB stub.
Configuration Every EmulatorConfig field — meaning, default, effect; the recipe pattern.
UART and console ICharacterDevice, StdoutCharDevice, the APBUART, multiple UARTs, capturing and routing output.
Debugging with GDB Attaching a GDB front-end, RTEMS thread-awareness, supported RSP packets, behaviour under JIT/Switch/MultiThread.
Hello World guide Compiling an RTEMS guest from scratch with RCC + mkprom2.
GR712RC reference The GR712RC memory map, peripheral instances, and IRQ assignments.

If you are brand new, start with the getting-started section (install, build, first boot) and come back here for depth.

The shape of using Tero

Whether you use the CLI or the library, the flow is the same five steps. The CLI just wraps them:

flowchart LR
    A["Build an EmulatorConfig<br/>(gr712rc_config / gr740_config<br/>+ overrides)"] --> B["Emulator::create(cfg)"]
    B --> C["set_logger / set_uart_character_device<br/>(optional)"]
    C --> D["initialize()"]
    D --> E["load_elf / load_binary"]
    E --> F["run_for / run_until<br/>(loop)"]
  1. Configure by struct. Start from a SoC recipe (gr712rc_config() or gr740_config()) and override the fields you care about. There are no config files and no global state — see Configuration.
  2. Create. Emulator::create(cfg) validates the config and returns a Result<std::unique_ptr<Emulator>>.
  3. Inject services (optional). Replace the default logger, console UART chardev, or observer — but only before initialize().
  4. Initialize. Wires RAM, peripherals, the bus, and (if requested) the GDB stub.
  5. Load and run. load_elf() / load_binary(), then run_for() or run_until() in a loop, inspecting the RunResult between calls.

Two execution methods, one binary

Both execution methods are compiled into every build and selected at runtime by EmulatorConfig::translation (default true):

  • Binary translation (JIT) — decode each block to an architecture- neutral IR, run it through a tiered LLVM JIT, with the IR interpreter as a fallback. This is the fast default.
  • Switch interpreter — fetch-decode-execute one instruction at a time; the reference path and correctness oracle.

You never rebuild to switch. The library exposes this as a config field; the CLI exposes diagnostic flags (--no-translation, --ir-interp-only). See Execution model and IR and LLVM JIT for how it works.

Two execution modes (host threading)

Orthogonal to the execution method, EmulatorConfig::execution_mode selects how simulated cores map onto host threads:

  • SingleThread (default) — all cores advance cooperatively in one host thread (round-robin quantum). SMP2-compatible; zero locking overhead.
  • MultiThread — each simulated core runs on its own host thread, for standalone throughput. CLI flag: --mt.

See Multi-core and timing.

Where to go from here