Migrating from TEMU 4 to TEMU 5
TEMU 5 is a major version, so interfaces deprecated during the TEMU 4 series
have been removed.
This guide is specifically about migration from TEMU 4.6.x to TEMU 5.1.
Most .temu scripts and most device models keep working with no or only small changes.
What Changed
| Area | TEMU 4 | TEMU 5 |
|---|---|---|
System execution |
|
Removed, scheduler object created with |
Embedding applications |
Scheduler part of the support library |
Separate scheduler and profiler libraries, global scheduler created explicitly |
Memory space decode |
4-byte granularity |
8-byte granularity |
Reset |
|
|
Source level debugging |
|
Removed, debugging contexts through the TCF server and the TEMU GUI |
GDB server |
|
|
Device registers |
Experimental |
Replaced by |
Bulk model memory access |
|
|
Embedded Python |
Python 2.7, |
Python 3.13, SWIG wrappers |
| Area | TEMU 4 | TEMU 5 |
|---|---|---|
Scheduling |
Single-level scheduler (identical to and backwards compatible with the TEMU 4 |
Multi-level scheduler with synchronization domains and reverse execution |
Board description |
|
YAML component library (see Components) |
Debugging |
GDB server |
TEMU GUI, TCF and MCP servers, OS awareness |
Architectures |
SPARC, PowerPC, ARMv7-R |
Adds ARM Cortex-M and RISC-V |
Scheduling
The Machine model tracked time for a group of processors and was the object
that run acted on.
It was removed in TEMU 5 and replaced by schedulers.
There can only be one scheduler in a simulation.
The machine-reset command is gone, and the DEFAULT_MACHINE variable is no
longer consulted by any command.
DEFAULT_RUNNABLE still exists, but run with no obj argument runs the
scheduler.
A TEMU 4 script that looked like this:
import Machine
Machine.new name=machine0
DEFAULT_RUNNABLE=machine0
DEFAULT_MACHINE=machine0
connect a=machine0.cpus b=cpu0:CpuIface
connect a=machine0.cpus b=cpu1:CpuIface
cpu0.parentTimeSource = machine0
cpu1.parentTimeSource = machine0
machine0.quanta = 10000
becomes, using the single-level scheduler
(which already existed in TEMU 4 and if it was already used
no changes are needed, using init-scheduler is recommended though):
init-scheduler variant=single-level
sched.set-frequency frequency=cpu0.freq
sched.set-quanta quanta=10000
sched.set-threads threads=1
sched.bind-to-group group=0 cpu=cpu0
sched.bind-to-group group=0 cpu=cpu1
cpu0.parentTimeSource = sched
cpu1.parentTimeSource = sched
init-scheduler creates the scheduler as an object called sched.
The variant argument selects the implementation.
Use single-level when porting.
It is the TEMU 4 scheduler, and its command surface is unchanged:
set-threads, bind-to-group, set-frequency, set-quanta, run and
step.
Scheduler is a class-name alias for SingleLevelScheduler,
Scheduler.new still works.
TEMU 5 also has a second scheduler, selected with multi-level.
It adds synchronization domains, which let a multi-core processor stay in one
deterministic synchronization domain while running on several host threads, and
it is a prerequisite for reverse execution.
See Running the Simulated System for how to configure either
scheduler, and Emulation Performance for
thread count, barrier and affinity choices.
The Machine C API is gone: temu_machineRun(),
temu_machineReset() and temu_cpuGetMachine() no longer exist.
Use temu_runSecs() to run, and call reset() on the object’s ResetIface to
reset.
Library Initialisation
The support library no longer initialises everything, and it no longer creates
anything runnable on its own.
Scheduling and profiling live in separate libraries which must be initialised
after temu_initSupportLib(), and the global scheduler has to be created
explicitly.
A TEMU 4 embedder wrote:
#include "temu-c/Support/Init.h"
temu_initPathSupport(argv[0]);
temu_initSupportLib();
The TEMU 5 equivalent is:
#include "temu-c/Support/Init.h"
#include "temu-c/Scheduler/Init.h"
temu_initSupportLib();
temu_initScheduler();
temu_initProfilerLib();
// Create the global scheduler, this replaces the Machine model.
temu_createScheduler(teSV_MultiLevel);
temu_initSupportLib() now calls temu_initPathSupportAuto() itself, so the
explicit temu_initPathSupport(argv[0]) call is only needed if the path must be
derived from a specific executable.
temu_initSnapshotLib() and temu_initFluxLib() are needed additionally if the
application uses snapshots or reverse execution.
Large Memory Transactions
This is the change most likely to alter the behavior of a working model without producing any compiler diagnostic.
A large transaction is one with Size > 3.
It carries a pointer in Value rather than a scalar, Size >> 2 gives the
number of units and 1 << (Size & 3) the width of each unit.
| Processors never emit large transactions. They can happen because of DMA engines in e.g. SpaceWire or Ethernet controllers, or initiated through the TEMU C API. If you never encounter them, then this section does not apply. |
The memory space splits a large transaction into unit-sized accesses for any
endpoint that does not set LargeTransactions in its capabilities
(see temu_MemAccessIface).
The splitting rule is the same in TEMU 4 and TEMU 5, but TEMU 4 ignored the
capabilities of page-aligned whole-page mappings and always split there.
TEMU 5 honours LargeTransactions on every mapping, so a model or bridge that
declares it now receives large transactions it never saw in TEMU 4.
What changed is the granularity at which the memory space decodes an address to a mapping. TEMU 4 resolved within a partially mapped page through a per-word table, giving 4-byte granularity. Whole-page mappings had a single entry. In TEMU 5, this is now 8 bytes. This only makes a difference if you have sub-8-byte-sized mappings.
Endianness of the Payload
The memory space never byte-swaps the payload of a large transaction as a whole. Swapping is applied per unit, and only on the path where the memory space splits the transaction itself.
An endpoint that declares LargeTransactions therefore receives the buffer
exactly as the initiator wrote it, and is responsible for honoring the
endianness bit in the transaction flags.
This is true of both TEMU 4 and TEMU 5, but it becomes visible the moment a
model changes whether it declares the capability, so it is worth knowing when
comparing the two.
Capabilities Are Not Enforced
temu_MemAccessCapabilities is not enforced by default, the TEMU memory subsystem
only ever reads LargeTransactions.
TransactionBaseSizes and Endianness are not read on that path.
A model that declares 32-bit-only base sizes will still be handed 8-bit and
16-bit accesses, in TEMU 4 and in TEMU 5 alike.
If you do want the declaration honored, TEMU 5 has a MemorySnooper model
that enforces it.
Inserted between an initiator and a device, it fails a transaction whose unit
size the device does not list, or can instead modify the access
(splitting it into smaller supported ones or widening it through a read-modify-write).
|
Removed Models and Classes
Three classes were removed outright.
Machine-
The whole
Machineplugin is gone, soimport Machinefails. See Scheduling. Scheduler-
Renamed to
SingleLevelScheduler, withSchedulerkept as a built-in class alias. BreakManager-
The memory space break manager was replaced by the breakpoint management in the new debugging library, which is reached through the TCF server and the GUI rather than by instantiating an object.
No device models were removed. TEMU 5 adds a large number of them for new boards.
Reset
In TEMU 4, reset was an alias for cpu-reset, it took a cpu argument and
fell back to the DEFAULT_CPU variable.
In TEMU 5, reset, cpu-reset and object-reset are the same command.
It accepts either obj or cpu, prefers the generic ResetIface over
CpuIface, and has no default object.
A bare reset with no argument fails in TEMU 5.
Pass obj or cpu explicitly.
|
TEMU 5 also has a ResetController model, which pairs naturally with
components.
A component instantiates one, fans its reset output out to the ResetIface of
every object on the board, and exports it.
Resetting the board is then a single reset obj=board0, which reaches devices
that a CPU-local reset never touched.
Command Line Changes
Removed Commands
| Removed | Replacement |
|---|---|
|
|
|
See Debugging |
The experimental register commands, |
|
|
No replacement, licenses are released on exit |
New Commands
init-scheduler-
Create the global scheduler, see Scheduling. This is the one command a TEMU 4 script has to gain.
Model Interface Changes
All model plugins must be rebuilt against the TEMU 5 headers.
The support library soname changed from libTEMUSupport.so.4 to
libTEMUSupport.so.5, so a TEMU 4 plugin binary cannot be loaded at all.
Memory
temu_MemoryIface, with its readBytes() and writeBytes() members, is
marked deprecated in TEMU 5, as are both of its members.
Models doing bulk memory access should issue transactions through
MemAccessIface instead, using temu_memoryReadTransaction(),
temu_memoryWriteTransaction() and temu_memoryExchangeTransaction(), or the
temu_memoryRead* and temu_memoryWrite* helpers.
MemorySpace publishes two MemAccessIface implementations.
The one reached through the vtable, and therefore through
temu_memoryWriteTransaction(), is the fast non-locking path.
The one published under the name MemAccessIface, which is what real system
configurations connect through connect a=cpuN.memAccessL2 b=mem0:MemAccessIface,
takes the memory space lock for teMK_IO mappings when the transaction carries
TEMU_MT_LOCK_ACQUIRE.
|
Serial and Ethernet
Both interfaces gained one optional member at the end of the structure, and
both changes are backward compatible.
An existing model that leaves the new member as NULL behaves exactly as it
did in TEMU 4.
temu_SerialIface-
New
isClearToReceive(). A receiver can report whether its receive path can accept another byte, and a sender can consult its peer before pushing one instead of overrunning it.NULLmeans always clear to receive. temu_PHYIface-
New
setPromiscuous(), so a MAC can tell its PHY or link that its receive filtering changed.temu_EthernetIfacealready had one in TEMU 4 and is unchanged.
CAN
CAN FD support was added, with a new temu_CanFDFrame type and receiveFD()
and sendFD() members on the device and bus interfaces.
The classic CAN frame type and its interface members are unchanged, so existing
CAN models keep working.
Register API
The register API was experimental in TEMU 4 and has been replaced. Models that used it will not compile until they are ported.
Where TEMU 4 built a register bank object and hung registers and fields off it, TEMU 5 registers memory mapped registers directly on the class:
// TEMU 4
temu_RegisterBank *Bank = temu_addRegisterBank(Cls, "regs", &MemAccessIface);
temu_addRegister(Bank, "ctrl", offsetof(MyDevice, Ctrl), teTY_U32, 1,
writeCtrl, readCtrl, "Control register", 0x00, 4);
// TEMU 5
temu_addMemoryMappedRegister32(Cls, "ctrl", offsetof(MyDevice, Ctrl), 1,
"Control register");
TEMU 4.6 already had the experimental temu-c/Support/NewRegisterAPI.h
header, it is now temu-c/Support/Register.h with the same function
signatures.
Register metadata, including fields, now lives in temu_RegisterMetaData and
temu_FieldMetaData in temu-c/Support/Register.h, and is what drives the
register views in the GUI and the TCF register service.
Device models that never used the experimental register API are not
affected.
Registers implemented as ordinary properties with a MemAccessIface dispatch
switch keep working.
|
Logging
temu_logSetFunc() and temu_logSetAdvancedFunc() still work.
TEMU 5 adds a sink model on top, where several sinks can be active at once and
each is identified by a temu_LogSinkHandle:
temu_LogSinkHandle H = temu_logAddFunc(myLoggingFunc);
temu_logRemoveSink(H);
temu_logClearSinks();
See Logging System.
Plugin Metadata
TEMU 5 plugins can declare metadata that the plugin manager reads at load time.
It is optional in TEMU 5, a plugin without it still loads, but declaring it
means plugin-list can report the vendor, version and license of your models.
Add it once, at file scope, anywhere in the plugin:
#include "temu-c/Support/Objsys.h"
TEMU_PLUGIN_MD_DEF_V1 = {
1, // Metadata version, always 1 for this macro
"Example Company", // Vendor
"MyModels", // Plugin name
1, 0, 0, // Plugin version, major/minor/patch
5, 1, 0, // TEMU version compiled against
0, // Flags, see TEMU_PLUGIN_FLAG_*
"abc1234", // Revision, for example a Git hash
"Proprietary", // License
"Example device models",
"(c) Example Corp 2026",
};
The ABI version fields are the TEMU version the plugin was compiled against, which lets the plugin manager warn about a mismatch rather than failing on an unresolved symbol later.
See Plugins.
Embedded Python
TEMU 4 embedded Python 2.7. TEMU 5 embeds Python 3.13 and bundles it, so the host system Python version does not matter.
Python scripts run with --run-script or the script-run command must be
ported to Python 3.
The temu.c.* modules keep their import paths, so from temu.c.support import
objsys still works.
As of TEMU 5.1 they are generated with SWIG rather than being raw ctypes
bindings.
from temu.c.support import objsys
cpu = objsys.objectForName("cpu0")
Python callbacks for events, notifications and commands are wrapped as well.
The ctypes wrappers are still available, selected by
the python/legacy-wrappers configuration key, but they are deprecated.
|
Debugging
TEMU 4 offered source level debugging through the experimental-debug-* command
family, and an in-process GDB server started with the gdb-server command.
In TEMU 5 the experimental-debug-* commands are gone.
Source level debugging is built around debugging contexts, exposed through the
TCF server, the MCP server and the TEMU GUI.
See Software Debugging,
Model Context Protocol Server, and
the GUI guide.
The gdb and gdb-server commands are gone too.
The GdbServer object, which TEMU 4.6 already had, is now the only way to
start a GDB server:
import GdbDebugger
GdbServer.new name=gdb port=6666 mem=mem0
connect-timesource obj=gdb ts=cpu0
For scripts that used the command this is a behavioral change as well as a
syntactic one.
The TEMU 4 gdb-server command blocked until GDB disconnected.
The object runs the server on its own thread, so the script continues.
Snapshots
TEMU 5 adds a binary snapshot format, TSnap, saved and restored with
tsnap-write and tsnap-read.
JSON snapshots, snapshot-save and snapshot-restore, still work unchanged
but are deprecated, see Snapshots.
tsnap-write file=boot.tsnap
tsnap-read file=boot.tsnap
JSON (snapshot-save) |
TSnap (tsnap-write) |
|
|---|---|---|
Files |
JSON object graph plus host endian RAM and ROM dumps |
A single |
Contents |
Objects and properties |
Plugins, classes, objects, properties and memory |
Restore |
Recreates objects, or keeps them with |
Always disposes all objects, reloads the plugins and recreates the objects |
Relative paths |
Current directory |
Configurable via |
Inspection |
No dedicated tool |
|
TSnap is the format behind reverse execution and plugin-reload, so a model
that restores correctly from a .tsnap file also works with both.
Model state must be in properties or pseudo properties.
TSnap does not call ObjectIface::serialise() and deserialise()
(see Custom Snapshots).
A model that needs to fix up derived state can subscribe to the temu.snapshot.take
and temu.snapshot.restore notifications instead, or use pseudo-properties.
New in TEMU 5
Migrating also unlocks functionality that has no TEMU 4 equivalent:
-
Reverse execution, driven by the multi-level scheduler.
-
Projects, see Project Files.
-
The TEMU GUI and the TCF server behind it.
-
A Model Context Protocol server, see Model Context Protocol Server.
-
OS awareness, see OS Awareness.
-
RISC-V support, including NOEL-V and GR765, with binary translation.
-
ARM Cortex-M4 and Cortex-M7, with an STM32F4 device set.
-
Additional console models: PTY, Telnet and TCF, see Serial Consoles.
-
Graphics output models, I2C, SpaceFibre, CAN FD and TTEthernet.
-
Guest software race detection (ThreadSanitizer style).