Embedding TEMU in a Simulator

Now that you know how to use TEMU in stand alone mode, we will look on how we can integrate TEMU in a simulator. We will do this by creating our own program for driving TEMU. It is easy enough to create the simulator that drives TEMU. In fact, the command line interface that was used in the previous section, is a relatively simple program that just invokes the TEMU API.

TEMU CMake Support

TEMU installs a CMake package configuration file. External projects should point CMake at the TEMU installation or build tree with -DTEMU_ROOT=…​, then use find_package(TEMU REQUIRED CONFIG). The package defines imported targets such as temu::support, temu::scheduler, temu::snapshots, and temu::flux.

For a project with one embedded executable, the relevant CMake pattern is:

cmake_minimum_required(VERSION 3.20)
project(MyTemuSimulator C)

find_package(TEMU REQUIRED CONFIG)

add_executable(simulator simulator.c)
target_include_directories(simulator PRIVATE ${TEMU_INCLUDE_DIRS})
target_link_libraries(simulator PRIVATE
  temu::support
  temu::scheduler
  temu::snapshots
  temu::flux)

Configure and build your project with:

$ cmake -S . -B build -DTEMU_ROOT=/opt/temu/{temu-version}
$ cmake --build build

For plug-ins, build a MODULE library and link it with temu::support.

Embedded Simulator

Create a new file in your tutorial directory named simulator.c. Then write the following program in that file:

The example loads the tutorial project, starts the multi-level scheduler, and then runs through the multi-level scheduler API. It also shows the single-level scheduler fallback for projects that still use it. This matches the command-line flow: configure the scheduler first, start it once when using the multi-level scheduler, and then execute the target through the scheduler.

#include "temu-c/Flux/Flux.h"
#include "temu-c/Scheduler/MultiLevelScheduler.h"
#include "temu-c/Snapshots/Snapshots.h"
#include "temu-c/Support/CommandLine.h"
#include "temu-c/Support/Events.h"
#include "temu-c/Support/Init.h"
#include "temu-c/Support/Loader.h"
#include "temu-c/Support/Objsys.h"
#include "temu-c/Support/Project.h"
#include "temu-c/Support/Scheduler.h"
#include "temu-c/Support/Time.h"
#include <stdint.h>

int
main(int argc, const char *argv[])
{
  // Initialise TEMU support library and additional support,
  // this is mandatory, it will
  // among other things ensure you have a valid license.
  temu_initConfigDirs();
  temu_initSupportLib();
  temu_initScheduler();
  temu_initSnapshotLib();
  temu_initFluxLib();
  // Load the tutorial project. The project startup scripts initialize the
  // scheduler, create board0 from components/board.yaml, and add its CPU to
  // the scheduler.
  if (temu_loadProject("temu-project.yaml", NULL)) {
    return 1;
  }

  // board0-mem is created by the board0 component.
  void *board0_mem = temu_objectForName("board0-mem");
  // You can load an image to any object implementing MemoryIface
  temu_loadImage(board0_mem, "rtems-hello.prom");
  // Start and run through the multi-level scheduler used by the tutorial
  // project. The fallback shows how the same embedded simulator can support
  // projects that still use the single-level scheduler.
  temu_TimeSource *mls = temu_getMultiLevelScheduler();
  temu_Sched *sched = temu_getMultiLevelSchedulerInstance();
  if (mls && sched) {
    if (temu_schedStart(sched, 1)) {
      return 1;
    }

    temu_StopInfo stop = {0};
    uint64_t freq = temu_eventQueueGetFreq(mls);
    uint64_t cycles = (uint64_t)temu_secsToCycles(10.0, freq);
    if (temu_schedRunFor(sched, cycles, &stop)) {
      return 1;
    }
  } else {
    temu_runForTime(10.0);
  }
  return 0;
}

When you run the program above, you should see the same output as in the previous section.

Now you know the basics of using the TEMU APIs and compiling. We will move on to plug-in development in the next couple of sections.