The TEMU Object System

Object System Glossary
class

A description of a POD type with properties, interfaces and a name. Realised as a registered struct type.

property

A variable in a POD type, associated with an count (e.g. 1 for scalars, > 1 for arrays), an offset, a type and a name. A property can also have a set of accessor functions registered which enables the association of semantics with a property write or read.

pseudo property

A virtual variable for a class. A pseudo property is ONLY manipulated through accessor functions and does not have an associated storage location.

interface

A record of function pointer that is associated with a class. The function pointers take as first argument a pointer to the object.

object

An instance of a class.

As mentioned, TEMU device models are written using the object system. This entail a certain amount of boiler plate code that must be written to register a class in the object system. This boiler plate is typically written in a function with the signature extern "C" void temu_pluginInit(void) (or using the macro TEMU_PLUGIN_INIT). This function is called when a TEMU device plugin model is loaded by the object system. Normally, each plugin provides only one device class, but there is nothing preventing a plugin from registering multiple device model classes if needed.

Interfaces are one of the key concepts in the TEMU object system. They provide a way to have standardised functionality associated multiple classes. For example, the IrqCtrlIface provides a standard way to raise and lower interrupts. An object implementing interrupt control can use this to provide the functionality in a uniform way. Typical uses of that particular interface are processor models and external interrupt controllers.

Plugin Mechanism

TEMU is structured around plugins. Plugins are dynamically loaded code libraries that at load time registers any classes that the plugin provides. Plugins can use the TEMU_PLUGIN_INIT macro to define a function that will be called at plugin load time. Loading a plugin can be done using the temu_loadPlugin() function or in the command line interface using the import command. The temu_loadPlugin() function use the normal system paths (i.e. RPATH, LD_LIBRARY_PATH, RUNPATH, and system loader paths), while the command line interface has an additional plugin path variable that can be controlled using the plugin-append-path, plugin-remove-path and plugin-show-paths commands.

To add the plugin initialiser function to your file add the following code to your device model plugin:

Plugin Initialisation Function
#include "temu-c/Support/Objsys.h"

TEMU_PLUGIN_INIT
{
  temu_Class *Cls = temu_registerClass(...);
  //...
}

The plugin does not have to link to the emulator library since the plugin is loaded by the library. The system linker will resolve any references from the plugin to functions in the emulator libraries.

Classes and Properties

A class description in TEMU is a registration of a POD-type in the object system, where the fields of the type are also registered as properties. An instance of a class is known as an object.

Classes and Instantiation

As mentioned, classes are managed by the TEMU object system. The classes are created by registering a name together with a create and dispose function. These functions are used to allocate and delete memory of objects of the relevant class. In the most basic implementation the create function simply returns a block of data allocated with malloc() or new, and the dispose function pass the object to the counterpart (free() and delete respectively).

All classes must inherit temu_Object. This is accomplished by ensuring that the first field in the struct corresponding to the class is temu_Object Super;.

The following snippet shows how to register a new internal class:

Registering an Internal Class
typedef struct {
  temu_Object Super; // Must be first field in struct
  // ...
} MyDevice;

// Register new internal class
temu_Class*
temu_registerClass(const char *ClsName,
                   temu_ObjectCreateFunc Create,
                   temu_ObjectDisposeFunc Dispose);

// Creating objects of internal classes
void* temu_createObject(const char *ClsName, const char *ObjName,
                        const temu_CreateArg *Args);

Interfacing with External API Models

TEMU3 does not support the registration of external objects and classes. Instead a wrapper class must be created which wraps the external class.

If the creation can be controlled by TEMU, it is conventent to create a TEMU class with a pointer (or a std::unique_ptr<> that instantiates the external object). If not, then the TEMU object should simply contiain a normal non-managed pointer that can be used to access the external object.

For the cases where TEMU is allowed to manage the external object, it is also possible to use the IndirectClass<T> template if the models are implemented in C++.

Interfaces and Interface References

An interface is a collection of function pointers, normally stored in a struct. These interface types are used to allow for model compatibility with different subsystems. An interface is registered to a class at class creation time. The first parameter of each function in an interface should by convention take an object pointer (i.e. a self or this pointer of type void*), as the object system API is expressed in C, the self/this pointer must be passed explicitly.

Interface references however are properties that refer to an object and interface pair, that is the interface reference bundle the self/this pointer together with the interface pointer. This is realised as a struct with two pointers (object and interface pointer, the fields are named Obj and Iface respectively). The object system header (temu-c/Support/Objsys.h) provides a macro to define a typed version of the interface reference struct. This macro is named TEMU_IFACE_REFERENCE_TYPE and will simply define a struct with the suffix IfaceRef. Note that the interface type (i.e. the struct with the function pointers) must have a suffix Iface for this macro to work.

There is also an untyped interface reference type with the name temu_IfaceRef.

Interface references are central to understand in order to be able to work with the object graph provided by the object system.

The emulator provides a function with the name temu_connect(), this function can be used to connect an interface reference in a specific object to an object and an interface implemented the latter object’s class.

The following code snippet illustrate how to connect objects using the TEMU API.

Connecting Objects
// Equivalent to CLI: connect a=obja.propName b=objb:SomeIface
temu_connect(ObjA, "propName", ObjB, "SomeIface");

The first parameter to temu_connect() is an object pointer, the second parameter is the property name (which must be either an interface reference property or an interface reference array property). Third is the destination object pointer, and fourth is the interface name for the destination object. Note that it is legal to connect an a property to an interface implemented by the object itself, to do this, pass the same pointer to the source and destination object arguments.

The connection function essentially fills the named property with a pointer for the destination object and the pointer for the interface struct, which is looked up by name. To call some function in the connected object one simply call the function in the interface, passing the object pointer as first parameter as illustrated in the following snippet:

Calling a Function in an Interface
ObjA->PropName.Iface->someFunc(ObjA->PropName.Obj);

It is possible to connect interface reference properties in two ways. In the first case the property is of type teTY_IfaceRef, in that case the connection is done by looking for the first free entry in the property field. In case all entries in the property is already connected, the connection operation fails.

The second case is when the type is teTY_IfaceRefArray, which is a dynamic array of interface references. This connection will always succeed assuming the destination object and interface are valid (and unless the system runs out of memory).

Temu_connect() returns non-zero on failure (i.e. invalid property name, invalid interface name, invalid object pointers).
Connecting an interface reference property which is a static array will place the interface reference in the first free array entry (i.e. where the object and interface pointer pair are both NULL).

The Object Graph

The object system provides a registry of objects. The objects are connected to each other using interface references. This object connectivity is known as the object graph. As mentioned in Interfaces and Interface References, the objects are connected using the temu_connect() function.