CLI reference¶
tero-emu is the standalone front-end built from the tero_app
target (src/app/src/main.cpp). It is a thin shell over
tero::runtime::Emulator: it parses arguments, builds an
EmulatorConfig from a SoC recipe, wires a StdoutLogger and a
StdoutCharDevice (on the console UART) as defaults, loads the image,
and runs.
Run tero-emu --help for the built-in usage summary, or --version to
print the build version.
Synopsis¶
tero-emu --image <path> | --prom <path>
[--bin <path>@<addr>]...
[--soc gr712rc|gr740]
[--ram <MiB>] [--cores <N>]
[--mhz <N>] [--cpi <value>]
[--budget <ns>] [--histogram-out <path>]
[--turbo] [--mt]
[--gdb-port <port>] [--gdb-wait]
[--quantum <N>]
[--verbose]
[--version] [--help|-h]
[diagnostic flags — see below]
If you invoke tero-emu with neither --image nor --prom, it
prints a hint and exits 0 — nothing is run. One of the two image
options is required.
Common options¶
| Option | Default | Meaning / when to use |
|---|---|---|
--image <path> |
— | SPARC big-endian ELF (ET_EXEC) loaded into RAM via load_elf(): segments copied, PC/%sp set to the ELF entry / top of RAM. Required unless --prom boots the guest. |
--prom <path> |
— | PROM image: raw bytes or a mkprom2 ELF .rom (detected by magic, PT_LOAD segments flattened into the PROM window). With --image omitted the guest boots from PROM at reset PC 0x0 — the silicon boot path. Remember --ram must match the size baked by mkprom2 -ramsize. |
--bin <path>@<addr> |
— | Raw binary copied verbatim to physical address <addr> (hex 0x.. or decimal) during initialize(). Repeatable; later images overwrite overlapping earlier ones. Targets must be writable mapped memory (RAM, FTAHBRAM, flash-bank Ram entities) — the PROM window is read-only, use --prom for it. Never touches PC. |
--soc <name> |
gr712rc |
SoC recipe: gr712rc (dual-core LEON3FT) or gr740 (quad-core LEON4FT). Selects the peripheral map, IRQ assignments, RAM base, default clock, and default core count. |
--ram <MiB> |
16 |
RAM size in MiB. Ignored when --soc gr740 (the GR740 recipe fixes 256 MiB at 0x00000000). Must be ≥ 1. |
--cores <N> |
1 |
Number of LEON cores, 1..4. Ignored when --soc gr740 (that recipe is quad-core). On GR712RC use 1 or 2. |
--mhz <N> |
recipe | Override the CPU/system clock in MHz, 1..1000. Default keeps the recipe clock (GR712RC 80 MHz, GR740 250 MHz). Direct-ELF guests need no rebuild — initialize() re-derives the GPTIMER scaler so the RTEMS timer tick stays exact at any clock. |
--cpi <value> |
1.0 |
Cycles per instruction (must be > 0). 1.0 = one cycle per instruction (TEMU-style). Raise it to make the core proportionally slower in simulated time; set below 1 to express an IPC target (cpi = 1/ipc). Only CPU sim-time scales — peripheral/timer clocks stay on the bus clock. |
--budget <ns> |
1_000_000_000 |
Simulated-time budget in nanoseconds (default 1 s). The run stops when current_sim_time() advances by this amount, on HaltedMode, or on a breakpoint. |
--histogram-out <path> |
stderr | Destination for the opcode-histogram CSV. Only a build configured with -DTERO_OPCODE_HISTOGRAM=ON produces one; a normal build emits nothing either way. |
--turbo |
off | Disable wall-clock pacing (PacingMode::Turbo). By default tero-emu runs PacingMode::Realtime (1 s simulated ≈ 1 s real); --turbo runs as fast as the host allows. Use for CI, benchmarks, batch runs. |
--mt, --multithread |
off | MultiThread (ADR-001): run each simulated core on its own host thread — a faithful true-concurrency SMP model. SMP guests that livelock under the cooperative SingleThread round-robin run correctly here. No effect with one core. |
--gdb-port <p> |
0 (off) |
If non-zero, bind the GDB remote stub to 127.0.0.1:p during initialize(). GDB can attach at any time thereafter (late-binding); the run loop polls the listener each quantum. 0 disables the stub (one null-pointer test on the hot path). See Debugging with GDB. |
--gdb-wait |
off | With --gdb-port, block in initialize() until a GDB client connects. Use when you need to inspect the very first instruction; otherwise late-binding is enough. |
--quantum <N> |
recipe | Instructions per core per scheduling round (≥ 1). A scheduling-granularity knob — smaller is finer cross-core interleaving at higher overhead. Mainly diagnostic; leave at the recipe default unless tuning SMP behaviour. |
--verbose |
off | Switch the logger to Debug level (output on stderr). Narrates config validation, peripheral wiring, ELF loading, and idle-skip events. |
--version |
— | Print the emulator version and exit. |
--help, -h |
— | Print the usage summary and exit. |
GR740 ignores --ram and --cores
--ram and --cores only apply to --soc gr712rc. When you pass
--soc gr740 the recipe pins 4 cores and 256 MiB RAM at
0x00000000; the two flags are silently ignored. To change those on
GR740, embed Tero as a library and edit the config
after gr740_config().
Diagnostic options¶
These exist for emulator development and bug-isolation (JIT-vs-interpreter divergence, SMP ordering, instruction tracing). They are stable and safe to use, but most users never need them.
| Option | Effect |
|---|---|
--no-translation |
Force the Switch interpreter (translation = false) — the reference/oracle path, one instruction at a time. Slowest but the canonical correctness baseline. |
--ir-interp-only |
Translation on, but never JIT-compile: every block runs on the IR interpreter (jit_baseline_threshold = ∞, background opt off). Isolates IR-frontend/interpreter semantics from LLVM codegen. |
--no-jit-opt |
Baseline-only JIT: disable the background O2/optimising tier (jit_background_opt = false). Lowest, most deterministic compile latency; lower steady-state throughput. |
--jit-region-blocks <N> |
Cap basic-block fusion per JIT region (jit_max_region_blocks, ≥ 1). 1 disables chaining beyond the self-loop. |
--trace |
Emit a per-instruction cpu <N> <pc> trace to stderr (guest UART stays on stdout). Installing any observer forces the deterministic Switch path, so the trace is reproducible. Used for Tero-vs-SIS lockstep. |
--lockstep |
Run two full N-core emulators on the same image — one Switch (oracle), one IR interpreter — one guest instruction at a time, comparing every core's whole register blob after each step. Prints the first divergence (core, register, recent-PC trace) and exits. |
--oracle-lockstep |
Drive the production SMP round-robin under the IR engine and validate every clean-exit IR block against core::step on a scratch copy. Reports the first block-level divergence. |
--lockstep and --oracle-lockstep are mutually-exclusive run modes
that take over main; they force PacingMode::Turbo and ignore the GDB,
pacing, and observer paths.
Flag-to-config mapping¶
Every common flag maps onto an EmulatorConfig field (or an
Emulator::set_* call). This is exactly what main.cpp does:
| CLI flag | EmulatorConfig field / call |
Notes |
|---|---|---|
--soc |
chooses gr712rc_config() / gr740_config() |
The whole recipe, not one field. |
--ram |
ram_size (× 1 MiB) |
GR712RC only. |
--cores |
num_cores |
GR712RC only. |
--mhz |
cpu_clock_hz (× 1 MHz) |
Recomputes ns_per_insn. |
--cpi |
cpi |
Recomputes ns_per_insn. |
--budget |
argument to run_for(SimTimeNs{...}) |
Not stored in the config. |
--turbo |
pacing = Turbo (else Realtime) |
|
--mt |
execution_mode = MultiThread |
|
--gdb-port |
gdb_stub_port |
|
--gdb-wait |
gdb_stub_wait_for_client |
|
--quantum |
quantum |
|
--no-jit-opt |
jit_background_opt = false |
|
--no-translation |
translation = false |
|
--ir-interp-only |
jit_baseline_threshold = ∞, jit_background_opt = false |
|
--jit-region-blocks |
jit_max_region_blocks |
|
--verbose |
set_logger(StdoutLogger{Debug}) |
else Info. |
--trace |
set_observer(TraceObserver) |
Forces the Switch path. |
Fields the CLI does not expose (ram_base, pacing_slice_ns,
entry_point_override, prom_*, jit_promotion_threshold,
quantum_batch, the peripherals/character_devices vectors) are left
at their recipe/struct defaults. To set them, embed Tero as a
library or extend main.cpp. See
Configuration for every field.
Exit codes¶
| Code | Meaning |
|---|---|
0 |
Time budget consumed cleanly (DurationExpired / DeadlineReached), or --help/--version/no-image hint. |
2 |
Invalid / missing CLI arguments (bad --ram, --cores out of range, unknown flag, missing value). |
3 |
Configuration validation failed (Emulator::create) or initialize() failed. |
4 |
ELF load failure (not SPARC, malformed, IO error). |
5 |
A core entered HaltedMode — a trap fired with PSR.ET = 0, which on SPARC stops the processor. This is usually the guest's own deliberate shutdown (ta 0 / _exit) or an unrecoverable fault. A core-0 post-mortem is dumped to stderr. |
HaltedMode is not an emulator bug
Exit 5 means the guest stopped the processor (RTEMS calls
_CPU_Fatal_halt, or hits an unrecoverable trap with traps disabled).
Emulation behaved correctly and simply has nothing more to run. The
distinct ErrorMode reason is reserved for genuine internal emulator
errors and is not normally observed.
The halt summary¶
On every run the CLI prints a one-line summary to stderr when the run loop returns:
reason is one of DurationExpired, DeadlineReached, HaltedMode,
ErrorMode, Breakpoint.
HaltedMode post-mortem¶
When tero-emu exits with code 5 it dumps a complete snapshot of core 0
— equivalent to attaching a SPARC debugger to a halted CPU:
[tero-emu] core0 post-mortem: pc=0x400034a0 npc=0x400034a4
tbr=0x40000020 tt=0x02 psr=0x00000083 wim=0x00000002
[tero-emu] g0-g7: 0 0x00000000 0x00000000 0x00000000 ...
[tero-emu] i0-i7: 0x400fff40 0x00000008 ...
[tero-emu] l0-l7: 0x00000000 ...
[tero-emu] o0-o7: 0x400fff40 0x00000008 ...
tt = 0x02 is illegal_instruction — typical when a program jumps to
unrelocated memory. tbr tells you exactly which trap handler the CPU was
about to enter. SPARC V8 stores the faulting PC/nPC in %l1/%l2 of the
trap-handler window, so the locals expose the offending instruction even
before the handler runs. The full trap-type table is in
Traps and interrupts.
Logging levels¶
The CLI's StdoutLogger accepts four levels; output goes to stderr so
it never mixes with guest UART output on stdout.
| Level | Shown when | Typical contents |
|---|---|---|
Debug |
--verbose |
Per-peripheral MMIO detail, ELF segment loading, idle-skip events, JIT/IR notes. |
Info |
always | Lifecycle markers (initialize, load_elf, run begin/end), peripheral wiring summary. |
Warn |
always | Configuration adjustments and recoverable oddities. |
Error |
always | Bus / load / config failures. |
Info and above are always shown; Debug is gated on --verbose.
Notes on argument handling¶
- Arguments are parsed by hand (a simple loop in
main.cpp), notgetopt. Flags can appear in any order; each value-taking flag expects its value as the next argument (--ram 64, not--ram=64). --ramis in MiB; it is converted to bytes (MiB × 1024 × 1024) with no further rounding.--budgetis decimal nanoseconds; e.g.--budget 5000000000is "5 simulated seconds".- A malformed value (non-numeric
--ram,--coresoutside1..4,--cpi≤ 0,--mhzoutside1..1000, etc.) prints an error and exits 2. - An unknown flag exits 2 after printing the usage summary.
Pacing: realtime vs turbo¶
tero-emu runs PacingMode::Realtime by default. The Emulator slices
each run into pacing_slice_ns chunks (default 10 ms simulated) and
std::this_thread::sleep_untils on std::chrono::steady_clock between
chunks, so a UART message scheduled to print every simulated second
appears on stdout once per real second.
The simulated clock rate is set by cpu_clock_hz and cpi (which derive
ns_per_insn). Every executed instruction advances simulated time by
ns_per_insn, so a faster simulated clock means the host must execute
more instructions per real second to keep up. If the host is too slow the
simulation falls behind silently — sleep_until cannot make it faster.
--turbo flips to PacingMode::Turbo (free-running). See
Pacing for the full model.
Worked invocations¶
# Boot an RTEMS ELF on the default GR712RC, realtime pacing:
tero-emu --image hello-world.elf
# Same, but fast as possible, 2 cores:
tero-emu --turbo --cores 2 --image hello-world.elf
# GR740 quad-core (RAM/cores fixed by the recipe), 2-second budget:
tero-emu --soc gr740 --budget 2000000000 --image gr740_app.elf
# Model 100 MHz with a 2× CPI penalty (half the effective MIPS):
tero-emu --mhz 100 --cpi 2.0 --image bench.elf
# Boot from PROM (mkprom2 .rom) — the silicon path; --ram matches -ramsize:
tero-emu --prom hello-prom.rom --ram 64
# Flight-style setup: PROM bootloader + raw FSW images in flash banks:
tero-emu --prom bootloader.rom --bin fsw-a.bin@0x01000000 --bin fsw-b.bin@0x01800000
# Debug a PROM image: stub on :1234, late-binding attach:
tero-emu --prom hello-prom.rom --ram 64 --gdb-port 1234
# True-concurrency SMP for a guest that livelocks cooperatively:
tero-emu --soc gr740 --mt --turbo --image smp_app.elf