3

I'd like to generate logging messages from within a C function embedded in a DML method. Take the example code below where the fib() function is called from the write() method of the regs bank. The log methods available to C all require a pointer to the current device.

Is there a way to get the device that calls the embedded function? Do I need to pass the device pointer into fib()?

dml 1.2;
device simple_embedded;
parameter documentation = "Embedding C code example for"
    + " Model Builder User's Guide";
parameter desc = "example of C code";

extern int fib(int x);

bank regs {
    register r0 size 4 @0x0000 {
        parameter allocate = false;
        parameter configuration = "none";
        method write(val) {
            log "info": "Fibonacci(%d) = %d.", val, fib(val);
        }

        method read() -> (value) {
            // Must be implemented to compile            
        }
       
    }
}

header %{
int fib(int x);
%}

footer %{
int fib(int x) {
    SIM_LOG_INFO(1, mydev, 0, "Generating Fibonacci for %d", x); 
    if (x < 2) return 1;
    else return fib(x-1) + fib(x-2);
}
%}

I want to log from an embedded C function.

2 Answers2

2

I solved this by passing the Simics conf_object_t pointer along to C. Just like implied in the question.

So you would use:

int fib(conf_object_t *mydev, int x) {
    SIM_LOG_INFO(1, mydev, 0, "Generating Fibonacci for %d", x); 
}

And

        method write(val) {
            log "info": "Fibonacci(%d) = %d.", val, fib(dev.obj,val);
        }
jakobengblom2
  • 5,531
  • 2
  • 25
  • 33
  • I didn't realize **dev** was a global variable. Is there a list of these available globals documented anywhere? – kbrunham-intel Feb 08 '23 at 21:36
  • 2
    it's not a global variable in the C sense, you can think of most identifiers in object scope as members of the device instance. If you are accustomed to C++ scoping, then you can think of `device simple_embedded;` as approximation of a block `class simple_embedded { }` surrounding the whole device, where DML `dev` approximates C++'s `this`. The analogy is severely lacking, because DML subobjects like banks and registers are a kind of quasi-singletons that have no direct equivalent in C++. – Erik Carstensen Feb 09 '23 at 10:09
1

Jakob's answer is the right one if your purpose is to offload some computations to C code (which makes sense in many situations, like when functionality is implemented by a lib).

However, if you just want a way to pass a callback to an API that asks for a function pointer, then it is easier to keep the implementation within DML and use a method reference, like:

method init() {
    SIM_add_notifier(obj, trigger_fib_notifier_type, obj, &trigger_fib,
                     &dev.regs.r0.val);
}

method trigger_fib(conf_object_t *_, lang_void *aux) {
    value = *cast(aux, uint64 *);
    local int result = fib(value);
    log info: "result: %d", result;
}

method fib(int x) -> (int) {
    log info: "Generating Fibonacci for %d", x; 
    if (x < 2) return 1;
    else return fib(x-1) + fib(x-2);
}
Erik Carstensen
  • 634
  • 4
  • 14