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); }