Device Modeling Language 1.4 Reference Manual 2 The DML compiler
Device Modeling Language 1.4 Reference Manual  / 

1 Introduction

Device Modeling Language (DML) is a domain-specific programming language for developing device models to be simulated with Simics. DML has been designed to make it easy to represent the kind of things that are needed by a device model, and uses special syntactic constructs to describe common elements such as memory mapped hardware registers, connections to other Simics configuration objects, and checkpointable device state.

DML is an object-oriented language, where each device is represented through an object, which — as members — may feature pieces of mutable state, configurable parameters and attributes, subroutines (called methods), and subobjects that may have their own members. In contrast to most general-purpose object-oriented languages, objects in DML are statically declared rather than dynamically created, and typically represent logical components of the device.

A complete DML model specifies exactly one device model, together with:

These are the crucial properties of device models that must be made visible to Simics, and each of these have specialized language features in order to declare them. Beyond these, DML also has a number of features to improve the expressive power of the language and simplify development; for instance, DML offers templates, a powerful metaprogramming tool that allows for code reduction and reuse, as well as a means of building abstractions.

The DML compiler is called Device Modeling Language Compiler, dmlc. It translates a device model description written in DML into C source code that can be compiled and loaded as a Simics module.

This document describes the DML language, the standard libraries, and the dmlc compiler, as of version 1.4 of DML. See also Simics Model Builder User's Guide for an introduction to DML.

1.1 Source File Example

The following is an example of a small DML model defining a very simple device. This lacks many details that would appear in a real device.

dml 1.4;

device contraption;

connect wakeup {
    interface signal;
}

bank config_registers {
    register cfg1 size 4 @ 0x0000 {
        field status @ [7:6] is (read, write) {
            method read() -> (uint64) {
                local uint2 ret;
                ret[0] = enable.val;
                ret[1] = coefficient[1] & 1;
                return ret;
            }
        }
        field enable @ [8] is (read_unimpl, write) {
            method write(uint64 val) {
                if (this.val == 0 and val == 1) {
                    wakeup.signal.signal_raise();
                } else if (this.val == 1 and val == 0) {
                    wakeup.signal.signal_lower();
                }
            }
        }
    }

    register coefficient[i < 4] size 8 @ 0x0008 + i * 8 is (read, write) {}
}

1.2 Object types

The above example demonstrates how a DML device is built from a hierarchy of objects, such as banks and register. The hierarchy is composed of the following object types:

1.3 Methods and Mutable Variables

Methods are the DML representation of subroutines. They may be declared as members of any object or template. Any method may have multiple input parameters, specified similarly as C functions. Unlike C, DML methods may have multiple return values, and the lack of a return value is indicated through an empty list of return values rather than void. The following demonstrates a method declaration with no input parameters or return values:

method noop() -> () {
    return;
}

Alternatively:

method noop() {
    return;
}

The following demonstrates a method declaration with multiple input and parameters and return values:

method div_mod(uint64 dividend, uint64 divisor)
    -> (uint64, uint64) {
    local uint64 quot = dividend / divisor;
    local uint64 rem = dividend % divisor;
    return (quot, rem);
}

This also demonstrates how local, stack-allocated variables within methods may be declared; through the local keyword. This is analogous to C’s auto variable kind, but unlike C, the keyword must be explicitly given. DML features two other variable kinds: session and saved. Unlike local variables, session and saved variables may also be declared as members of any object within the DML model, and can only be initialized with constant expressions.

session variables represent statically allocated variables, and act as the DML equivalent of static variables in C. The value of a session variable is preserved for the duration of the current simulation session, but are not automatically serialized and restored during checkpointing. This means that it is the model developer’s responsibility to manually serialize and restore any session variables upon saving or restoring a checkpoint. saved variables behave exactly like session variables, except the value of saved variables are serialized and restored during checkpointing. Because of this, a saved variable must be of a type that DML knows how to serialize. Most built-in non-pointer C types are serializable, and any struct that consists solely of serializable types are also considered serializable. Pointer types are never considered serializable.

Methods have access to a basic exception-handling mechanism through the throw statement, which raises an exception without associated data. Such exceptions may be caught via the try { ... } except { ... } statement. If a method may throw an uncaught exception, that method must be declared throws; for example:

method demand(bool condition) throws {
    if (!condition) {
        throw;
    }
}

1.4 Templates and Parameters

A template specifies a block of code that may be inserted into objects. Templates may only be declared at the top-level, which is done as follows:

template name { body }

where name is the name of the template, and body is a set of object statements. A template may be instantiated through the is object statement, which can be used within either objects, or within templates. For example:

bank regs {
    // Instantiate a single template: templateA
    is templateA;
    // Instantiate multiple templates: templateB and templateC
    is (templateB, templateC);

    register reg size 1 @0x0;
}

The is object statement causes the body of the specified templates to be injected into the object or template in which the statement was used.

is can also be used in a more idiomatic fashion together with the declaration of an object or template as follows:

// Instantiate templates templateA, templateB, and templateC
bank regs is (templateA, templateB, templateC) {
    register reg size 1 @0x0;
}

A language feature closely related to templates are parameters. A parameter can be thought of as an expression macro that is a member of a particular object or template. Parameters may optionally be declared without an accompanying definition — which will result in a compile-time error if not overridden — or with a default, overridable definition. Parameters declared this way can be overridden by any later declaration of the same parameter. This can be leveraged by templates in order to declare a parameter that the template may make use of, while requiring any instance of the template to provide a definition for the parameter (or allow instances to override the default definition of that parameter).

Parameters are declared as follows:

Much of the DML infrastructure, as well as DML’s built-in features, rely heavily on templates. Due to the importance of templates, DML has a number of features to generically manipulate and reference template instances, both at compile time and at run time. These are templates as types, each-in expressions, and in each declarations.

Device Modeling Language 1.4 Reference Manual 2 The DML compiler