Object-Oriented Programming¶
pysv supports object-oriented programming. Here is a simply example on how to use it:
from pysv import sv, DataType, compile_lib
class Foo:
@sv()
def __init__(self, num):
self.num = num
@sv()
def bar(self, num):
return self.num + num
@sv(foo=Foo)
def foo_bar(foo, num):
return foo.bar(num)
lib_path = compile_lib([Foo, foo_bar], cwd="build")
We can use the class type directly in the compile_lib. pysv will inspect each methods and
export the methods decorated with @sv.
Note
If the class constructor does not have any argument except for self, we can omit the
@sv decorator since pysv will generate default constructor automatically. Nonetheless,
it doesn’t hurt to decorate the constructor.
To use it in the SystemVerilog, we need to first generate the SystemVerilog binding
from pysv import generate_sv_binding
generate_sv_binding([Foo, foo_bar], filename="pysv_pkg.sv")
Then we can use the class directly in a test bench, as shown below:
module top;
import pysv::*;
initial begin
Foo foo;
int res;
foo = new(41);
// functions that takes generated class object
res = foo_bar(foo, 1);
assert (res == 42);
// call class methods as well
res = foo.bar(-40);
assert (res == 1);
// need to finalize the pysv runtime
pysv_finalize();
end
endmodule
Save the test bench file as top.sv and we can run the example with Xcelium
with the following command:
xrun pysv_pkg.sv top.sv -sv_lib build/libpysv.so
The simulation will finish without triggering the assertion error.
We can open the generated SystemVerilog binding file to see the actual SystemVerilog class generated:
class PySVObject;
chandle pysv_ptr;
endclass
typedef class Foo;
class Foo extends PySVObject;
function new(input int num,
input chandle ptr=null);
if (ptr == null) begin
pysv_ptr = Foo_pysv_init(num);
end
else begin
pysv_ptr = ptr;
end
endfunction
function int bar(input int num);
return Foo_bar(pysv_ptr, num);
endfunction
function void destroy();
Foo_destroy(pysv_ptr);
endfunction
endclass
Notice that every SystemVerilog wrapper class is inherited from the base class
PySVObject, which has an C pointer to the actual Python object. To allow
wrapper objects being created with the pointer only, we added additional
argument to the constructor, since SystemVerilog does not support function
overloading.
For the function foo_bar where the argument is of type Foo, the
function is generated as follows, with proper function signature.
function int foo_bar(input Foo foo,
input int num);
return foo_bar_(foo.pysv_ptr, num);
endfunction
For cases where you need to duck-type the Python objects, you can set the argument
or type to DataType.Object. With that, PySVObject type will be used in
the signature, which avoids illegal downcast in SystemVerilog.
Warning
The current implementation assumes certain ordering of class object creation. If you want to create a class inside a function and the class constructor hasn’t been called in the SystemVerilog/C++ code yet, you will get a name error. This limitation only happens if the class is defined in the same file as the function. It should not be an issue if the class is imported from other modules, which could be a workaround.
We will address this issue in the future releases.
To avoid memory leak from SystemVerilog’s garbage collection, we provide a
“destructor” function called destory(). You need to manually call this
method before the object goes out of scope, since SystemVerilog does not
support automatic destructor function.
The process to generate C++ binding is similar. You can use generate_cxx_binding
as following:
generate_cxx_binding([Foo, foo_bar], filename="pysv.hh")
The generated code follows the same structure as the SystemVerilog’s. Here is the class definition:
class PySVObject {
public:
PySVObject() = default;
PySVObject(void* ptr): pysv_ptr(ptr) {}
PySVObject(const PySVObject &obj) : pysv_ptr(obj.pysv_ptr) {}
virtual ~PySVObject() = default;
void *pysv_ptr = nullptr;
};
class Foo : public PySVObject {
public:
Foo(int32_t num);
int32_t bar(int32_t num);
~Foo() override;
inline Foo(void *ptr): PySVObject(ptr) {}
};
Unlike SystemVerilog, we have overloaded class constructor to accommodate different usage scenarios.