Skip to content

Commit

Permalink
[core] add support for int array
Browse files Browse the repository at this point in the history
  • Loading branch information
Kuree committed Dec 28, 2023
1 parent 6b01904 commit 53b68d5
Show file tree
Hide file tree
Showing 13 changed files with 194 additions and 25 deletions.
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
[submodule "extern/pybind11"]
path = pysv/extern/pybind11
url = https://github.com/pybind/pybind11
[submodule "pysv/extern/vlstd"]
path = pysv/extern/vlstd
url = https://github.com/Kuree/vlstd
3 changes: 3 additions & 0 deletions pysv/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ endif()

target_link_libraries(${TARGET} PRIVATE pybind11::embed)

# include the sv lib directory
target_include_directories(${TARGET} PRIVATE ${DPI_HEADER_DIR})

set(SHARED_LIB_EXT ".so")

add_definitions(-DPYTHON_LIBRARY="${PYTHON_LIBRARY}")
Expand Down
72 changes: 65 additions & 7 deletions pysv/codegen.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,16 @@ def __should_include_local_object(func_defs):
return False


def __should_include_buffer_impl(func_defs):
func_defs = __get_func_defs(func_defs)
for func_def in func_defs:
func_def = __get_func_def(func_def)
for input_type in func_def.arg_types.values():
if input_type == DataType.IntArray:
return True
return False


def __get_conda_path():
result = ""
if is_conda():
Expand All @@ -58,6 +68,45 @@ def __get_conda_path():
return result


def __is_array(t: DataType):
return t == DataType.IntArray

def __get_dpi_data_type(t: DataType):
if t == DataType.Bit:
return "bit"
elif t == DataType.Byte:
return "byte"
elif t == DataType.ShortInt:
return "shortint"
elif t == DataType.Int:
return "int"
elif t == DataType.LongInt:
return "longint"
elif t == DataType.UByte:
return "byte unsigned"
elif t == DataType.UShortInt:
return "shortint unsigned"
elif t == DataType.UInt:
return "int unsigned"
elif t == DataType.ULongInt:
return "longint unsigned"
elif t == DataType.Object:
return "chandle"
elif t == DataType.String:
return "string"
elif t == DataType.Float:
return "shortreal"
elif t == DataType.Double:
return "real"
# only for return type
elif t == DataType.Void:
return "void"
# the only array type supported
elif t == DataType.IntArray:
return "int"
raise ValueError("Unknown type")


def generate_dpi_signature(func_def: Union[Function, DPIFunctionCall],
pretty_print=True, is_class=False, is_function_class_wrapper=False,
ref_ctor_name=""):
Expand All @@ -78,14 +127,14 @@ def generate_dpi_signature(func_def: Union[Function, DPIFunctionCall],
else:
arg_type_str = __PYSV_OBJECT_BASE
else:
arg_type_str = arg_type.value
args.append("input {0} {1}".format(arg_type_str, arg_name))
arg_type_str = __get_dpi_data_type(arg_type)
args.append("input {0} {1}{2}".format(arg_type_str, arg_name, "[]" if __is_array(arg_type) else ""))
# notice that we generate output at the end
for arg_name in func_def.output_names:
# we only allow primitive data types for return reference type
arg_type = func_def.arg_types[arg_name]
arg_type_str = arg_type.value
args.append("output {0} {1}".format(arg_type_str, arg_name))
arg_type_str = __get_dpi_data_type(arg_type)
args.append("output {0} {1}{2}".format(arg_type_str, arg_name, "[]" if __is_array(arg_type) else ""))

# additional signature for ref ctor
if is_class and len(ref_ctor_name) > 0 and func_def.is_init:
Expand All @@ -104,7 +153,7 @@ def generate_dpi_signature(func_def: Union[Function, DPIFunctionCall],
else:
return_type_str = __PYSV_OBJECT_BASE
else:
return_type_str = func_def.return_type.value
return_type_str = __get_dpi_data_type(func_def.return_type)

if is_class:
if func_def.is_init:
Expand Down Expand Up @@ -268,6 +317,9 @@ def get_c_type_str(data_type: DataType): # pragma: no cover
return "double"
elif data_type == DataType.Void:
return "void"
elif data_type == DataType.IntArray:
# dpi open aray type
return "svOpenArrayHandle"
else:
raise ValueError(data_type)

Expand Down Expand Up @@ -359,6 +411,8 @@ def generate_local_variables(func_def: Union[Function, DPIFunctionCall]):
arg_type = func_def.arg_types[n]
if arg_type == DataType.Object and not (idx == 0 and func_def.parent_class is not None):
s = __INDENTATION + __GET_LOCAL_OBJECT + '("{0}", {1}, locals);\n'.format(str_name, n)
elif __is_array(arg_type):
s = __INDENTATION + 'locals["{0}"] = to_buffer({1});\n'.format(str_name, n)
else:
s = __INDENTATION + 'locals["{0}"] = {1};\n'.format(str_name, n)
result += s
Expand Down Expand Up @@ -486,7 +540,7 @@ def generate_sys_path_values(pretty_print=True):


def generate_bootstrap_code(pretty_print=True, add_sys_path=True, add_class=True, add_imports=True,
add_local_object=True):
add_local_object=True, add_buffer_impl=False):
result = __get_code_snippet("include_header.hh")
result += __get_code_snippet("runtime_values.cc")

Expand All @@ -505,6 +559,8 @@ def generate_bootstrap_code(pretty_print=True, add_sys_path=True, add_class=True
result += __get_code_snippet("import_global.cc")
if add_local_object:
result += __get_code_snippet("get_local_object.cc")
if add_buffer_impl:
result += __get_code_snippet("buffer_impl.cc")

return result

Expand Down Expand Up @@ -544,8 +600,10 @@ def generate_pybind_code(func_defs: List[Union[type, DPIFunctionCall]], pretty_p
add_sys_path = should_add_sys_path(func_defs)
add_imports = __has_imports(func_defs)
add_local_object = __should_include_local_object(func_defs)
add_buffer_impl = __should_include_buffer_impl(func_defs)
result = generate_bootstrap_code(pretty_print, add_sys_path=add_sys_path, add_class=add_class,
add_imports=add_imports, add_local_object=add_local_object) + "\n"
add_imports=add_imports, add_local_object=add_local_object,
add_buffer_impl=add_buffer_impl) + "\n"
# generate extern C block
result += 'extern "C" {\n'
code_blocks = []
Expand Down
3 changes: 3 additions & 0 deletions pysv/compile.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ def compile_lib(func_defs, cwd, lib_name="pysv", pretty_print=True, release_buil
# need to copy stuff over
root_dir = os.path.dirname(__file__)
pybind_path = os.path.join(root_dir, "extern", "pybind11")
vlstd_path = os.path.join(root_dir, "extern", "vlstd")
assert os.path.isdir(pybind_path)
# copy that to cwd if it doesn't exist
pybind_path_dst = os.path.join(cwd, "pybind11")
Expand Down Expand Up @@ -58,6 +59,8 @@ def compile_lib(func_defs, cwd, lib_name="pysv", pretty_print=True, release_buil
else:
build_type = "Debug"
cmake_args.append("-DCMAKE_BUILD_TYPE=" + build_type)
# add header definition
cmake_args.append("-DDPI_HEADER_DIR=" + vlstd_path)
subprocess.check_call(["cmake"] + cmake_args + [".."],
cwd=build_dir)
# built it!
Expand Down
1 change: 1 addition & 0 deletions pysv/extern/vlstd
Submodule vlstd added at c15fc0
1 change: 1 addition & 0 deletions pysv/function.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ def __init__(self, return_type: Union[DataType, type, Reference] = DataType.Int,
self.output_names.append(arg_name)
self.arg_types[arg_name] = arg_type
assert isinstance(self.return_type, DataType), "Return type has to be of " + DataType.__name__
assert self.return_type != DataType.IntArray, "Returning an array is not supported"
if imports is None:
self.imports = _inspect_frame()
else:
Expand Down
34 changes: 34 additions & 0 deletions pysv/snippets/buffer_impl.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
#include "svdpi.h"

py::memoryview to_buffer(const svOpenArrayHandle array_handle) {
ssize_t element_size = sizeof(int32_t);
void *base_ptr = nullptr;
std::vector<ssize_t> sizes = {};
std::vector<ssize_t> strides = {};

// we try to query the underlying representation
base_ptr = svGetArrayPtr(array_handle);
if (!base_ptr) {
throw std::runtime_error("Array type does not have native C representation");
}
auto dim = svDimensions(array_handle);
for (auto i = 0; i < dim; i++) {
auto s = svSize(array_handle, i);
sizes.emplace_back(s);
}
// assumes row major ordering
ssize_t stride = element_size;
strides = std::vector<ssize_t>(dim, element_size);
for (int i = 0; i < dim - 1; i++) {
stride *= sizes[i];
strides[i] = stride;
}

return py::memoryview::from_buffer(
base_ptr, /* Pointer to buffer */
element_size, /* Size of one scalar */
py::format_descriptor<int32_t>::value, /* Python struct-style format descriptor */
sizes, /* Buffer dimensions */
strides /* Strides (in bytes) for each index */
);
}
30 changes: 16 additions & 14 deletions pysv/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,23 @@


class DataType(enum.Enum):
Bit = "bit"
Byte = "byte"
ShortInt = "shortint"
Int = "int"
LongInt = "longint"
UByte = "byte unsigned"
UShortInt = "shortint unsigned"
UInt = "int unsigned"
ULongInt = "longint unsigned"
Object = "chandle"
String = "string"
Float = "shortreal"
Double = "real"
Bit = enum.auto()
Byte = enum.auto()
ShortInt = enum.auto()
Int = enum.auto()
LongInt = enum.auto()
UByte = enum.auto()
UShortInt = enum.auto()
UInt = enum.auto()
ULongInt = enum.auto()
Object = enum.auto()
String = enum.auto()
Float = enum.auto()
Double = enum.auto()
# only for return type
Void = "void"
Void = enum.auto()
# the only array type supported
IntArray = enum.auto()


class Reference:
Expand Down
4 changes: 2 additions & 2 deletions pysv/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,7 @@ def _set_lib_env(self):

def _run(self, args, cwd, env, blocking):
if blocking:
subprocess.check_call(args, cwd=cwd, env=env)
return subprocess.check_output(args, cwd=cwd, env=env)
else:
p = subprocess.Popen(args, cwd=cwd, env=env)
self.__process.append(p)
Expand Down Expand Up @@ -218,7 +218,7 @@ def run(self, blocking=True):
cwd=self.cwd, env=env)
# run the application
name = os.path.join("obj_dir", mk_file.replace(".mk", ""))
self._run([name], self.cwd, env, blocking)
return self._run([name], self.cwd, env, blocking)


class CadenceTester(Tester):
Expand Down
13 changes: 12 additions & 1 deletion tests/test_compile.py
Original file line number Diff line number Diff line change
Expand Up @@ -218,5 +218,16 @@ def func_output():
assert values[0] == 42 and values[1] == 43;


def test_buffer_int(temp):
@sv(return_type=DataType.Void, a=DataType.IntArray)
def foo(a):
a[0] = 2

# because we lack of dpi implementaiotn, we only test out
# and see if we can compile it
lib_file = compile_lib([foo], cwd=temp)
assert os.path.exists(lib_file)


if __name__ == "__main__":
test_float("temp")
test_buffer_int("temp")
25 changes: 24 additions & 1 deletion tests/test_sim.py
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,30 @@ def set_value():
tester.run()


@pytest.mark.skipif(not pysv.util.is_verilator_available(), reason="Verilator not available")
def test_verilator_array(get_vector_filename, temp):
@sv(a=DataType.IntArray)
def set_value(a):
print(a[2])
a[2] = 42

lib_path = compile_lib([set_value], cwd=temp)
header_file = os.path.join(os.path.abspath(temp), "test_verilator_array.hh")
generate_cxx_binding([set_value], filename=header_file)
sv_pkg = os.path.join(os.path.abspath(temp), "pysv_pkg.sv")
generate_sv_binding([set_value], filename=sv_pkg)

# we have three files
# the sv file, the driver file, and the header
sv_file = get_vector_filename("test_verilator_array.sv")
driver = get_vector_filename("test_verilator_array.cc")
# just run the verilator
tester = pysv.util.VerilatorTester(lib_path, sv_file, header_file, driver, cwd=temp)
out = tester.run().decode("ascii")
assert out == "2\n42\n"


if __name__ == "__main__":
from conftest import get_vector_filename_fn
test_verilator_return_reference(get_vector_filename_fn, "temp")
test_verilator_array(get_vector_filename_fn, "temp")

13 changes: 13 additions & 0 deletions tests/vectors/test_verilator_array.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#include "Vtest_verilator_array.h"
#include "test_verilator_array.hh"
#include <exception>
#include <random>
#include <iostream>

int main () {
Vtest_verilator_array vtop;
vtop.eval();

// tear down the runtime
pysv_finalize();
}
17 changes: 17 additions & 0 deletions tests/vectors/test_verilator_array.sv
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
`include "pysv_pkg.sv"

module test_verilator_array();

import pysv::*;

int a[3:0];

initial begin
for (int i = 0; i < 4; i++) begin
a[i] = 2;
end
set_value(a);
$display("%0d", a[2]);
end

endmodule

0 comments on commit 53b68d5

Please sign in to comment.