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
.