Running Simulated Systems

TEMU models and functionality is primarily built around the TEMU Object System.

Runnable items are no different, and they are implemented following a common type called a Time Source.

A time source is responsible for tracking its own time, clock rate, an event queue and a parent time sources.

It is possible to run any time source in TEMU, this includes:

  • Clocks

  • Processors

  • Schedulers

The Machine model was removed in TEMU 5, following previous deprecation.

Schedulers

Schedulers in TEMU are the key driver of execution. There can only be one scheduler instantiated at any one time.

There are two schedulers at the moment:

  • The initial multi-threaded scheduler implemented by the Scheduler class.

  • A new multi-level scheduler which is required for reverse execution.

It is up to the user to select the scheduler by running one of the following lines:

// To pick the TEMU 4 single-level scheduler, run this line
init-scheduler model=single-level

// To pick the TEMU 5 multi-level scheduler, run this line:
init-scheduler model=multi-level

Alternatively, the C-API can be used:

temu_createScheduler(teSV_SingleLevel);
temu_createScheduler(teSV_MultiLevel);

Single-Level Scheduler

The single-level scheduler needs to be configured by e.g. setting the thread count and quanta.

Configuring a Single-Level Scheduler
init-scheduler variant=single-level
sched.set-frequency frequency=cpu0.freq
sched.set-quanta quanta=20000

# Configure scheduler to run in 2 threads,
# binding cpu0 and cpu2 to thread 0,
# and cpu1 and cpu3 to thread 1.
sched.set-threads threads=2
sched.bind-to-group group=0 cpu=cpu0
sched.bind-to-group group=1 cpu=cpu1
sched.bind-to-group group=0 cpu=cpu2
sched.bind-to-group group=1 cpu=cpu3

# In single threaded mode, the processor will ignore flush
# instructions, but in multi-threaded mode we need to
# treat this as a proper instruction cache synchronization.
cpu0.config.exitOnSync = 1
cpu1.config.exitOnSync = 1
cpu2.config.exitOnSync = 1
cpu3.config.exitOnSync = 1

# The memory space must collect invalid page lists in multi-threaded mode,
# in single threaded mode the pages can be purged when written directly.
mem0.config.processorHasSync = 1

Multi-Level Scheduler

The multi-level scheduler supports different synchronization domains. Each domain has its own quanta, and may have one or more threads in it. Domains running processors are called leaf-domains, and leaf domains have parent domains. The top domain is called the root-domain. Domains thus form a tree, where the root domain defines a global synchronization quanta.

Different domains can handle e.g. separate multi-core processors. It is also possible to define systems that run separate processors in parallel, while maintaining full determinism.

Setting up the Multi-Level Scheduler
init-scheduler variant=multi-level

// Insert processors in scheduler
sched.add-cpu cpu=cpu0
sched.add-cpu cpu=cpu1
sched.add-cpu cpu=cpu2
sched.add-cpu cpu=cpu3

// Add scheduler domain
sched.add-domain id=1 quantum=10000
sched.set-domain-frequency id=1 frequency=cpu0.freq

// Attach CPUs to domain
sched.assign-cpu-domain cpu=cpu0 domain=1
sched.assign-cpu-domain cpu=cpu1 domain=1
sched.assign-cpu-domain cpu=cpu2 domain=1
sched.assign-cpu-domain cpu=cpu3 domain=1
sched.set-domain-workers id=1 workers=1

// Ensure CPU signals instruction sync to scheduler
cpu0.config.exitOnSync = 1
cpu1.config.exitOnSync = 1
cpu2.config.exitOnSync = 1
cpu3.config.exitOnSync = 1
mem0.config.processorHasSync = 1

Running, Stepping and Tracing

TEMU distinguishes between running and stepping a system.

Running implies we run the complete system. Stepping and tracing, implies we run the complete system, but we focus on a specific processor.

In single threaded mode, we run each processor in round-robin order. The same is done when stepping. However, when stepping, the processor under control is the last to run, meaning that processors after the one under control will still be at the beginning of their quanta.

Note that when stepping it may overrun of its quanta. This will in turn result in all other processors running their complete quanta. This way the behavior between run and step is identical.

Tracing is identical to stepping, except the last executed disassembled instruction is printed out.

Note that although the run, step and trace commands support working with single CPUs, this is not the normal approach for running a system.

// Run scheduler for 1 second
run time=1.0

// Step CPU0 two steps
step cpuid=0 steps=2

// Trace CPU1 ten steps
trace cpuid=1 steps=10

API

Running an Already Created Scheduler in C
// Run the scheduler for a 2 seconds
temu_runSecs(2.0);

// Step the cpu (which is a temu_TimeSource pointer) for 10 steps.
temu_step(cpu, 10);

// Stop the currently running scheduler and wait until it is stopped.
temu_stop();

// Stop the currently running scheduler, but return immediately.
// The function is async-safe and can be run in signal handlers.
temu_asyncStop();