Examples

Quick CPU Construction Using JSON Files

It is possible to quickly instantiate a system configuration including CPUs, memory and peripherals, this can be done by loading a JSON file with the serialized state of an existing system.

The JSON files are easy to understand, and can be edited by hand if needed (e.g. to change memory sizes).

Several examples of already defined JSON files are available in: /opt/temu/latestshare/temu/sysconfig/.

CLI

To load a system configuration in the current directory from the CLI:

snapshot-restore Leon2.json

API

To restore a JSON file from the API, call the temu_deserialiseJSON function with the file name as argument. The function returns non-zero on failure.

temu_deserialiseJSON("Leon2.json");

Command Line CPU Construction

Command line script for constructing a LEON2 CPU with on-chip devices. Note that constructing your own machine configuration from scratch is not trivial. Several CLI scripts are provided with the emulator and installed in /opt/temu/latest/share/temu/sysconfig/.

import Leon2
import Leon2SoC
import Memory
import Console

object-create class=Leon2 name=cpu0
object-create class=Leon2SoC name=leon2soc0
object-create class=MemorySpace name=mem0
object-create class=Rom name=rom0
object-create class=Ram name=ram0

# Console is a virtual serial port sink that prints output to STDOUT
object-create class=Console name=tty0

object-prop-write prop=rom0.size val=8192
object-prop-write prop=ram0.size val=8192

# Map in RAM and SOCs at the relvant address
memory-map memspace=mem0 addr=0x00000000 length=0x80000 object=rom0
memory-map memspace=mem0 addr=0x40000000 length=0x80000 object=ram0
memory-map memspace=mem0 addr=0x80000000 length=0x100 object=leon2soc0

connect a=cpu0.memAccess b=mem0:MemAccessIface
connect a=cpu0.memory b=mem0:MemoryIface
connect a=mem0.invalidaccess b=cpu0:InvalidMemAccessIface

# We only use the Leon2 SoC for the IRQ controller interface
# the interface is required by the CPU

connect a=leon2soc0.irqControl b=cpu0:IrqIface
connect a=cpu0.irqClient b=leon2soc0:IrqClientIface
connect a=leon2soc0.queue b=cpu0:EventIface
connect a=cpu0.devices b=leon2soc0:DeviceIface

connect a=tty0.serial b=leon2soc0:UartAIface
connect a=tty0.queue b=cpu0:EventIface

objsys-check-sanity

# Load binary (supports ELF files as well)
load obj=cpu0 file=myobsw.srec
set-reg cpu=cpu0 reg="%fp" value=0x40050000
set-reg cpu=cpu0 reg="%sp" value=0x40050000
run cpu=cpu0 pc=0x40000000 steps=1000000000 perf=1

Note that there are several of these configuration files available for different machine configurations.

Programmatic CPU Construction

To construct a CPU using the API, the following code sequence illustrates how. It is straight forward to translate the CLI construction (see previous section) to the C-API if needed.

#include "temu-c/Support/Init.h"
#include "temu-c/Support/Objsys.h"
#include "temu-c/Memory/Memory.h"
#include "temu-c/Target/Cpu.h"
#include "temu-c/Support/Loader.h"

#include <stdio.h>

int
main(int argc, const char *argv[argc])
{
  temu_CreateArg Args = TEMU_NULL_ARG;

  // Init support library, this will check your license
  // the program will terminate if you do not have a valid license
  temu_initSupportLib();

  // Look up the temu command and setup the plugin paths based on
  // its location.
  temu_initPathSupport("temu");

  // Load the plugins needed
  temu_loadPlugin("libTEMULeon2.so");
  temu_loadPlugin("libTEMULeon2SoC.so");
  temu_loadPlugin("libTEMUMemory.so");
  temu_loadPlugin("libTEMUConsole.so");

  // Create needed objects, no arguments are given (see init above)
  void *Cpu = temu_createObject("Leon2", "cpu0", &Args);
  void *L2SoC = temu_createObject("Leon2SoC", "leon2soc0", &Args);
  void *MemSpace = temu_createObject("MemorySpace", "mem0", &Args);
  void *Rom = temu_createObject("Rom", "rom0", &Args);
  void *Ram = temu_createObject("Ram", "ram0", &Args);
  void *Console = temu_createObject("Console", "tty0", &Args);

  // Allocate space for ROM and RAM
  temu_writeValueU64(Rom, "size", 0x80000, 0);
  temu_writeValueU64(Ram, "size", 0x80000, 0);

  // Map in ROM, RAM and the IO modules in the memory space
  temu_mapMemorySpace(MemSpace, 0x00000000, 0x80000, Rom);
  temu_mapMemorySpace(MemSpace, 0x40000000, 0x80000, Ram);
  temu_mapMemorySpace(MemSpace, 0x80000000, 0x100, L2SoC);

  // For the L2 (without MMU) we connect memAccess directly to the
  // memspace, for MMU systems, we will need to connect memAccessL2
  // instead, and to connect memAccess to the CPU memory access
  // interface. See /opt/temu/x/share/temu/sysconfig dir for exmples.
  temu_connect(Cpu, "memAccess", MemSpace, "MemAccessIface");
  temu_connect(Cpu, "memory", MemSpace, "MemoryIface");
  temu_connect(MemSpace, "invalidaccess", Cpu,
               "InvalidMemAccessIface");

  // In TEMU 2.2, we do not need to connect the reverse link
  // this has been automated through the "ports" mechanism.
  temu_connect(Cpu, "irqClient", L2SoC, "IrqClientIface");

  // Attach the L2SoC to its time source.
  temu_setTimeSource(L2SoC, Cpu);

  // Add Device to CPU device array, this is used to distribute
  // CPU resets to device models.
  temu_connect(Cpu, "devices", L2SoC, "DeviceIface");

  // The console implements the serial interface and simply
  // redirects it to stdout. For a GUI console use the ConsoleUI
  // class instead
  temu_connect(Console, "serial", L2SoC, "UartAIface");
  temu_setTimeSource(Console, Cpu);

  // Check sanity of the object graph, pass non-zero to enable
  // automatic printouts (with info on which objects are not
  // sane). 0 means the function is silent, and we only care
  // about the result. Note, this is a debugging help, some
  // interfaces do not need to be connected (e.g. the LEON2
  // cache interfaces).
  if (temu_checkSanity(1)) {
    fprintf(stderr, "Sanity check failed\n");
  }

  // Can pass CPU or MemorySpace, which you pass doesn't matter
  // loadImage handles both SREC and ELF files.
  temu_loadImage(Cpu, "hello");

  // To get the CPU interface to run the CPU directly, we query
  // for the interface. The CpuIface implements the basic CPU
  // control functionality like RESET
  temu_CpuIface *CpuIf = temu_getInterface(Cpu, "CpuIface", 0);

  CpuIf->reset(Cpu, 0); // Cold-reset, 1 is a warm reset
  CpuIf->setPc(Cpu, 0x40000000);        // Starting location

  // Fake low level boot software setting up the stack pointers
  CpuIf->setGpr(Cpu, 24+6, 0x40050000); // %i6 or %fp
  CpuIf->setGpr(Cpu, 8+6, 0x40050000);  // %o6 or %sp

  // You can step or run the CPU. Running runs for N cycles
  // while stepping executes the given number of instructions
  // as an instruction can take longer than a cycle, these are
  // not the same. For multi-core systems, you will not run or
  // step the CPU but rather a machine object, which will ensure
  // that all of the CPUs advance as requested. Also a CPU in
  // idle or powerdown mode does not advance any steps, but only
  // cycles.
  CpuIf->run(Cpu, 1000000); // Run 1 M cycles
  //  CpuIf->step(Cpu, 1000000); // Step 1 M steps
  //  CpuIf->runUntil(Cpu, 1000000); // Run until absolute time is
                                     // 1000000 cycles

  // Step 10 instructions, but return early if until absolute time
  // reaches 1000000 cycles
  //  CpuIf->stepUntil(Cpu, 10, 1000000);
  return 0;
}