Hide navigation sidebar
Hide table of contents sidebar
Skip to content
Back to top
View this page
Toggle table of contents sidebar

# Functions

Before proceeding with this section, make sure that you are already familiar with the basics of binding functions and classes, as explained in First steps and Object-oriented code. The following guide is applicable to both free and member functions, i.e. methods in Python.

## Return value policies

Python and SystemVerilog use fundamentally different ways of managing the memory and lifetime of objects managed by them. This can lead to issues when creating bindings for functions that return a non-trivial type. When non-trivial type returned SystemVerilog should manage the object life time and deallocate the memory occupied by the object with `py_object::delete()` when the object is no longer needed.

Return value policies are tricky, and it’s very important to get them right. Just to illustrate what can go wrong, consider the following simple example:

    //equivalent to: from decimal import Decimal
    py_object Decimal = py_module::import_("decimal").attr("Decimal").obtain();
    py_object decimal_exp = Decimal.attr("exp");

    // Compute the e^n for n=0..4
    for (int n = 0; n < 5; n++) begin
        py_object d = Decimal.call(py::int_(n));
        py::print(py::str_("Result: "), decimal_exp.call(d));
    end

What’s going on here? When `decimal_exp.call(d)` is called and the result object passed to `py::print()` function, the handler the result of `decimal_exp.call(d)` no longer available. Therefore it is not possible to access the result of `decimal_exp.call(d)` and deallocate the memory occupied by the object. The same issue occurs for the object `d`. The the `d.delete()` function not called to deallocate the memory occupied by the object during the loop. Non trivial type objects should be deallocated by calling the `py_object::delete()` function. The Python garbage collector will not deallocate the memory occupied by the this objects since the object reference count is not zero.

The proper way to write to the above code is to deallocate the memory occupied by the object `d` and the result of `decimal_exp.call(d)`.

    //equivalent to: from decimal import Decimal
    py_object Decimal = py_module::import_("decimal").attr("Decimal").obtain();
    py_object decimal_exp = Decimal.attr("exp");

    // Compute the e^n for n=0..4
    for (int n = 0; n < 5; n++) begin
        py_object d = Decimal.call(py::int_(n));
        py_object result = decimal_exp.call(d);
        py::print(py::str_("Result: "), result);
        result.delete();
        d.delete();
    end

## Python objects as arguments

PyStim exposes all major Python types using thin SystemVerilog wrapper classes. These wrapper classes can also be used as parameters of functions in bindings, which makes it possible to directly work with native Python types on the SystemVerilog side. For instance, the following statement iterates over a Python `dict`:

    function void print_dict(py::py_dict dict);
        /* Easily interact with Python types */
        iterator it = students_dict.get_iterator();
        while(it.has_next())begin
            py_pair p = it.next().cast_dict_pair();
            py_object key = p.get_key();
            py_object value = p.get_value();
            $display("Key: %s, Value: %s", key.cast_string().get_value(),
                    value.cast_uint().get_value());

        end
    endfunction

Note

Currently, the SystemVerilog `dict` wrapper class supports only keys of type `string`.

For more information on using Python objects in SystemVerilog, see Python SystemVerilog interface.

## Accepting *args and **kwargs

Python provides a useful mechanism to define functions that accept arbitrary numbers of arguments and keyword arguments:

    def generic(*args, **kwargs):
        ...  # do something with args and kwargs

To call the Python function with args and kwargs, use SystemVerilog named arguments mapping:

    py_tuple args = ...;
    py_dict kwargs = ...;
    ... // do something with args and kwargs
    some_attribute.call(.args(args), .kwargs(kwargs));

The class `py_args` derives from `py_tuple` and `py_kwargs` derives from `py_dict`.

You may also use just one or the other, and may combine these with other arguments. Note, however, that `args` and `kwargs` must always be named mapped arguments.

A demonstration of `args` and `kwargs` is available in `examples/misc/test_args_and_kwargs.sv`.