Since data types and method bodies in DML are written in an extended subset of C, you can easily call C functions from DML by using normal function call syntax, as in f(x) (rather than using the call or inline keywords to call DML methods). For example, the following is a legal DML program, based on the example in Section 2.1:
dml 1.0;
device simple_device;
import "io-memory.dml";
extern int fib(int x);
bank b {
parameter function = 0;
register r0 size 4 @0x0000 {
parameter allocate = false;
method write(val) {
log "info": "Fibonacci(%d) = %d.", val, fib(val);
}
}
}
header %{
int fib(int x);
%}
footer %{
int fib(int x) {
if (x < 2) return 1;
else return fib(x-1) + fib(x-2);
}
%}
Writing to the (pseudo-) register r0 has the effect of printing a log message with the computed Fibonacci number:
simics> phys_mem.set 0x1000 6 [dev1 info] Fibonacci(6) = 13.
Notable points are:
The Simics API (a set of C functions and data types) is defined in DML in the library file simics-api.dml, which is automatically included by dmlc. The corresponding C declarations are defined in header files which are also automatically included by the generated code for a device. Hence, the DML programmer has direct access to the entire Simics API.
For example, this is how the method set_read_value() is implemented in the standard library. It updates a generic_transaction_t structure (a "memop") at the end of a read access to a memory bank has finished, depending on the endianism of the bank:
method set_read_value(generic_transaction_t *memop,
uint64 value) default
{
if (!defined $byte_order)
error
else if ($byte_order == "little-endian")
SIM_set_mem_op_value_le(memop, value);
else
SIM_set_mem_op_value_be(memop, value);
}