General Usage

Before writing models or embedding TEMU in another simulator, it is useful to know the normal workflow for using an existing project. This section uses the tutorial project created in the previous chapter. It assumes that components/board.yaml extends leon3.yaml and that the project startup scripts create the board0 component.

Opening a Project

Start TEMU from the project directory:

$ temu --project=temu-project.yaml

The project file applies the project search paths, runtime directories, and startup scripts. In the generated tutorial project, scripts/temu/imports.temu imports component support and scripts/temu/startup.temu initializes the scheduler, creates board0, and adds the board CPU to the scheduler:

temu> object-list

The component owns the board objects, so the CPU and memory space are named board0-cpu and board0-mem. The scheduler object is named sched.

The relevant startup commands are:

init-scheduler variant=multi-level
sched.set-stop-mode mode=cli
Component.new name=board0 file=board.yaml
sched.add-cpu cpu=board0-cpu

TEMU 5 runs target processors through a scheduler. If you create a project manually, call init-scheduler before running software and add every processor that should execute with sched.add-cpu. The generated project startup script does this for the tutorial board.

Loading Software

Target software is loaded into an object that implements the memory interface. For the tutorial LEON3 board, use the component memory space:

temu> load obj=board0-mem file=./rtems-hello.prom

If the image contains an entry point, TEMU will use it when the loader supports that format. You can inspect or adjust the CPU state before running:

temu> regs cpu=board0-cpu
temu> cpu-set-pc cpu=board0-cpu pc=0x40000000

Running and Stepping

The tutorial project uses the multi-level scheduler. After loading software and finishing scheduler configuration, start the scheduler once:

temu> sched.start workers=1

Then use the normal execution commands. run, step, trace, and stop are the preferred user-facing commands, also when the active scheduler is the multi-level scheduler. They route execution through the scheduler that was configured during startup:

temu> run time=1.0
temu> run cycles=100000

Use step when you need instruction-level control for one processor. Other processors in the scheduler may still run when the step crosses a scheduler quantum:

temu> step cpuidx=0 steps=1
temu> trace cpuidx=0 steps=5

Use stop to request that a running multi-level scheduler stops.

When a program stops because of a trap, breakpoint, or error mode, inspect the CPU and disassemble around the program counter:

temu> regs cpu=board0-cpu
temu> disassemble cpu=board0-cpu addr=0x40000000 count=8

Use the scheduler object explicitly in startup scripts for scheduler configuration, and use the top-level execution commands for normal interactive work.

Scheduler Quanta and Domains

The scheduler quantum controls how far processors can run before the scheduler synchronizes time and dispatches pending synchronized events. Larger quanta reduce scheduler overhead, while smaller quanta make cross-processor and event timing synchronization more frequent.

The multi-level scheduler is the preferred scheduler for new TEMU 5 projects. It is newer and less tested than the single-level scheduler. For systems that would use only one MLS domain, the single-level scheduler may still be faster.

For the default single-domain tutorial board, adding the CPU is enough:

temu> init-scheduler variant=multi-level
temu> Component.new name=board0 file=board.yaml
temu> sched.add-cpu cpu=board0-cpu
temu> sched.start workers=1

If no domains are configured, the multi-level scheduler creates a default domain when it starts. To choose a CPU quantum explicitly, create a leaf domain and assign the CPU to it before sched.start:

temu> init-scheduler variant=multi-level
temu> Component.new name=board0 file=board.yaml
temu> sched.add-cpu cpu=board0-cpu
temu> sched.add-domain id=0 quantum=1000
temu> sched.assign-cpu-domain cpu=board0-cpu domain=0
temu> sched.start workers=1

For systems with several processors, add every processor before starting the scheduler. For a normal multi-core board, the usual deterministic setup is to place all CPUs for that board in the same leaf domain and run that domain with one worker thread. This keeps the CPUs ordered by the scheduler instead of by host-thread timing:

temu> sched.add-cpu cpu=board0-cpu0
temu> sched.add-cpu cpu=board0-cpu1
temu> sched.add-domain id=0 quantum=1000
temu> sched.assign-cpu-domain cpu=board0-cpu0 domain=0
temu> sched.assign-cpu-domain cpu=board0-cpu1 domain=0
temu> sched.set-domain-workers id=0 workers=1
temu> sched.start workers=1

You can create more workers for higher performance, for example one worker per CPU in a large leaf domain. That trades determinism for speed because host-thread scheduling can affect the order in which runnable CPUs make progress.

Use explicit domains when groups of processors need their own synchronization quantum. A leaf domain owns processors. A parent domain synchronizes child domains at a coarser boundary:

temu> sched.add-domain id=100 quantum=50000
temu> sched.add-domain id=0 quantum=1000 parent=100
temu> sched.add-domain id=1 quantum=2000 parent=100
temu> sched.add-cpu cpu=board0-cpu0
temu> sched.add-cpu cpu=board0-cpu1
temu> sched.assign-cpu-domain cpu=board0-cpu0 domain=0
temu> sched.assign-cpu-domain cpu=board0-cpu1 domain=1
temu> sched.start workers=2
temu> sched.tree

In this example, board0-cpu0 synchronizes at 1000-cycle leaf boundaries, board0-cpu1 synchronizes at 2000-cycle leaf boundaries, and both domains meet at the 50000-cycle parent-domain boundary. Domains, CPU assignment, domain worker counts, and domain frequencies must be configured before sched.start. Use sched.set-domain-workers id=…​ workers=…​ and sched.set-domain-frequency id=…​ frequency=…​ for those optional domain settings.

For multi-board simulations, a common deterministic layout is one leaf domain per board. Put the CPUs of each board in that board’s leaf domain, run each board domain with one worker, and connect the boards through delay/replay-capable links so cross-board traffic is recorded and delivered at deterministic synchronization points. The domain quantum should be chosen together with the link latency; cross-domain communication needs enough delay that delivery can be ordered at domain boundaries.

Multi-board systems do not have to use separate domains. If deterministic ordering is more important than parallelism, or if the board-to-board communication is too tightly coupled for delayed links, put all board CPUs in a single leaf domain and run it with one worker.

Inspecting Objects

The object system is the main way to inspect a running simulation. Use object-list to find object names and info to inspect one object:

temu> object-list
temu> info obj=board0-cpu
temu> info obj=board0-mem

Use class-info when you need to know which properties, interfaces, and ports a class provides:

temu> class-info class=Leon3
temu> class-info class=MemorySpace

Properties can also be read and written directly:

temu> board0-cpu.freq
temu> board0-cpu.freq = 50000000

Working with Memory

The memory space can print its current mappings:

temu> pp obj=board0-mem

This is useful before adding a device model, but it is also useful during normal usage because it shows which address ranges are occupied by ROM, RAM, and peripherals.

The pp command is an abbreviation for pretty-print, it works for every model that implements the temu_ObjectIface::printObject function.

The CPU-specific assembler and disassembler can be used for small experiments:

temu> board0-cpu.assemble instr="nop" addr=0x40000000
temu> disassemble cpu=board0-cpu addr=0x40000000 count=1

Saving and Restoring State

Snapshots let you preserve a configured or partially executed system. Project scaffolds place runtime files below .temu/, so this tutorial saves snapshots there:

temu> tsnap-write file=.temu/snapshots/after-load.tsnap
temu> run time=0.1
temu> tsnap-read file=.temu/snapshots/after-load.tsnap

The snapshot stores the object graph and properties, in a binary format.

Running Project Targets

Projects can define named targets for common workflows. If a project contains a target named hello, start it directly from the shell:

$ temu --project=temu-project.yaml --target=hello

Use targets for repeatable usage flows such as loading one software image, setting up a debugger, or running a test script. Keep ad-hoc interactive experiments in the shell until the command sequence is stable enough to move into a project startup script.