From 9e102854a3991b9d2a52dc7798ec3e0db2350fdd Mon Sep 17 00:00:00 2001 From: Matthew Ballance Date: Thu, 5 Jun 2025 04:03:09 +0000 Subject: [PATCH 1/5] Test updates to support dfm Signed-off-by: Matthew Ballance --- ivpm.yaml | 12 ++-- setup.py | 5 +- src/hdl_if/dfm/__ext__.py | 14 +++++ src/hdl_if/dfm/__init__.py | 0 src/hdl_if/dfm/api_gen_sv.py | 46 ++++++++++++++++ src/hdl_if/dfm/dpi_lib.py | 16 ++++++ src/hdl_if/dfm/flow.dv | 38 +++++++++++++ src/hdl_if/dfm/sv_pkg.py | 27 +++++++++ tests/unit/__init__.py | 18 ++++++ tests/unit/test_py_api.py | 66 +++++++++++----------- tests/unit/test_smoke.py | 103 ++++++++++++++++++++++------------- tests/unit/test_tlm_dpi.py | 0 tests/unit/test_vpi_py_if.py | 5 +- 13 files changed, 270 insertions(+), 80 deletions(-) create mode 100644 src/hdl_if/dfm/__ext__.py create mode 100644 src/hdl_if/dfm/__init__.py create mode 100644 src/hdl_if/dfm/api_gen_sv.py create mode 100644 src/hdl_if/dfm/dpi_lib.py create mode 100644 src/hdl_if/dfm/flow.dv create mode 100644 src/hdl_if/dfm/sv_pkg.py create mode 100644 tests/unit/test_tlm_dpi.py diff --git a/ivpm.yaml b/ivpm.yaml index d8db6de..6aab9fc 100644 --- a/ivpm.yaml +++ b/ivpm.yaml @@ -23,14 +23,18 @@ package: deps: - name: pytest src: pypi - - name: pytest-fv - url: https://github.com/fvutils/pytest-fv.git + - name: cython + src: pypi + - name: setuptools_scm + src: pypi + - name: pytest-dfm + url: https://github.com/dv-flow/pytest-dfm.git + - name: dv-flow-libhdlsim + url: https://github.com/dv-flow/dv-flow-libhdlsim.git - name: pytypeworks url: https://github.com/mballance-utils/pytypeworks.git - name: pyvsc-dataclasses url: https://github.com/vsc-tools/pyvsc-dataclasses.git - - name: fusesoc - url: https://github.com/olofk/fusesoc.git - name: iverilog url: https://github.com/pss-hands-on/iverilog-bin/releases/download/v12.0/iverilog-maylinux2014-12.0.tar.gz - name: verilator diff --git a/setup.py b/setup.py index 89ddb27..0911782 100644 --- a/setup.py +++ b/setup.py @@ -105,7 +105,10 @@ ], 'ivpm.pkginfo': [ 'pyhdl-if = hdl_if.pkginfo:PkgInfo' - ] + ], + 'dv_flow.mgr': [ + 'pyhdl-if = hdl_if.dfm.__ext__' + ], }, ext_modules=[ ext ] ) diff --git a/src/hdl_if/dfm/__ext__.py b/src/hdl_if/dfm/__ext__.py new file mode 100644 index 0000000..c0c3c58 --- /dev/null +++ b/src/hdl_if/dfm/__ext__.py @@ -0,0 +1,14 @@ + + +print("__ext__") + +def dfm_packages(): + """Returns the DFM packages""" + import os + + dfm_dir = os.path.dirname(os.path.abspath(__file__)) + + return { + 'pyhdl-if': os.path.join(dfm_dir, "flow.dv"), + } + diff --git a/src/hdl_if/dfm/__init__.py b/src/hdl_if/dfm/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/hdl_if/dfm/api_gen_sv.py b/src/hdl_if/dfm/api_gen_sv.py new file mode 100644 index 0000000..cf7667b --- /dev/null +++ b/src/hdl_if/dfm/api_gen_sv.py @@ -0,0 +1,46 @@ +import os +import sys +from dv_flow.mgr import TaskRunCtxt, TaskDataInput, TaskDataResult, FileSet + +async def APIGenSV(runner, input) -> None: + + env = runner.env.copy() + + if input.params.pythonpath: + for path in input.params.pythonpath: + if "PYTHONPATH" not in env: + env["PYTHONPATH"] = path + else: + env["PYTHONPATH"] += os.pathsep + path + + status = 0 + + cmd = [ + sys.executable, + '-m', 'hdl_if', + 'api-gen-sv' + ] + + for m in input.params.modules: + cmd.extend(['-m', m]) + + if input.params.pkgname: + cmd.extend(['--package', input.params.pkgname]) + + filename = input.params.filename + + cmd.extend(['-o', os.path.join(input.rundir, filename)]) + + status |= await runner.exec(cmd, env=env) + + output = [] + output.append(FileSet( + basedir=input.rundir, + files=[filename], + filetype="systemVerilogSource" + )) + + return TaskDataResult( + status=status, + output=output + ) \ No newline at end of file diff --git a/src/hdl_if/dfm/dpi_lib.py b/src/hdl_if/dfm/dpi_lib.py new file mode 100644 index 0000000..826947f --- /dev/null +++ b/src/hdl_if/dfm/dpi_lib.py @@ -0,0 +1,16 @@ +import os +from dv_flow.mgr import TaskDataInput, TaskRunCtxt, TaskDataResult, FileSet + +async def DpiLib(ctxt : TaskRunCtxt, input : TaskDataInput) -> TaskDataResult: + from hdl_if import get_entry + entry = get_entry() + + return TaskDataResult( + status=0, + output=[ + FileSet( + filetype="systemVerilogDPI", + basedir=os.path.dirname(entry), + files=[os.path.basename(entry)]) + ] + ) diff --git a/src/hdl_if/dfm/flow.dv b/src/hdl_if/dfm/flow.dv new file mode 100644 index 0000000..53f218a --- /dev/null +++ b/src/hdl_if/dfm/flow.dv @@ -0,0 +1,38 @@ +package: + name: pyhdl-if + + tasks: + - name: DpiLib + desc: PyHDL-IF DPI Library + shell: pytask + run: hdl_if.dfm.dpi_lib.DpiLib + + - name: SvPkg + desc: PyHDL-IF SystemVerilog Package + shell: pytask + run: hdl_if.dfm.sv_pkg.SvPkg + + - name: APIGenSV + desc: Generates a SystemVerilog API from the Python API + with: + inline: + desc: Run the generator in-line + type: bool + value: False + filename: + desc: name of the output file + type: str + value: api.sv + pkgname: + desc: Of the package + type: str + value: "" + pythonpath: + desc: Extra PYTHONPATH to use + type: list + modules: + desc: List of Python modules to load + type: list + + shell: pytask + run: hdl_if.dfm.api_gen_sv.APIGenSV \ No newline at end of file diff --git a/src/hdl_if/dfm/sv_pkg.py b/src/hdl_if/dfm/sv_pkg.py new file mode 100644 index 0000000..93d8b16 --- /dev/null +++ b/src/hdl_if/dfm/sv_pkg.py @@ -0,0 +1,27 @@ +import os +from dv_flow.mgr import TaskRunCtxt, TaskDataInput, TaskDataResult, FileSet + +async def SvPkg(ctxt: TaskRunCtxt, input: TaskDataInput) -> TaskDataResult: + """ + Task to return the SystemVerilog package file for the HDL interface. + + Args: + ctxt (TaskRunCtxt): The task run context. + input (TaskDataInput): The task input data. + + Returns: + TaskDataResult: The result containing the package file. + """ + from hdl_if import share + sharedir = share() + + return TaskDataResult( + status=0, + output=[ + FileSet( + filetype="systemVerilogSource", + basedir=os.path.join(sharedir, "dpi"), + files=["pyhdl_if.sv"] + ) + ] + ) \ No newline at end of file diff --git a/tests/unit/__init__.py b/tests/unit/__init__.py index e69de29..ee75544 100644 --- a/tests/unit/__init__.py +++ b/tests/unit/__init__.py @@ -0,0 +1,18 @@ +import os +import pytest + +@pytest.fixture(scope="session") +def hdl_if_env(): + unit_tests_dir = os.path.dirname(os.path.abspath(__file__)) + hdl_if_dir = os.path.abspath( + os.path.join(unit_tests_dir, "..", "..", "src")) + + env = os.environ.copy() + if "PYTHONPATH" not in env: + env["PYTHONPATH"] = hdl_if_dir + else: + env["PYTHONPATH"] += os.pathsep + hdl_if_dir + + return env + + diff --git a/tests/unit/test_py_api.py b/tests/unit/test_py_api.py index ed8d3b0..cf10ffb 100644 --- a/tests/unit/test_py_api.py +++ b/tests/unit/test_py_api.py @@ -1,56 +1,54 @@ import os import sys import pytest -import pytest_fv as pfv -from pytest_fv.fixtures import * from .test_base import * - -import hdl_if - -SKIP_HDLSIM = ('ivl',) +from dv_flow.libhdlsim.pytest import hdlsim_dvflow, HdlSimDvFlow +from . import hdl_if_env data_dir = os.path.join( os.path.dirname(os.path.abspath(__file__)), "data") test_py_api_data_dir = os.path.join(data_dir, "py_api") -def _test_file(dirconfig, name, plusargs=None): - flow = pfv.FlowSim(dirconfig) +def _test_file(hdlsim_dvflow : HdlSimDvFlow, name, env, plusargs=None): + env["PYTHONPATH"] = test_py_api_data_dir + os.pathsep + env["PYTHONPATH"] + hdlsim_dvflow.setEnv(env) + + sim_img = hdlsim_dvflow.mkTask("hdlsim.%s.SimImage" % hdlsim_dvflow.sim) - flow.fs.add_library(hdl_if.share()) - flow.sim.addFileset(pfv.FSVlnv("fvutils::pyhdl-if", "systemVerilogSource")) + sv_src = hdlsim_dvflow.mkTask("std.FileSet", + base=test_py_api_data_dir, + include="%s.sv" % name, + type="systemVerilogSource") - flow.sim.addFileset(pfv.FSPaths( - test_py_api_data_dir, ["%s.sv" % name], "systemVerilogSource")) + dpi_lib = hdlsim_dvflow.mkTask("pyhdl-if.DpiLib") + sv_pkg = hdlsim_dvflow.mkTask("pyhdl-if.SvPkg") - flow.sim.dpi_libs.append(hdl_if.get_entry()) - flow.sim.top.add(name) + sim_img = hdlsim_dvflow.mkTask("hdlsim.%s.SimImage" % hdlsim_dvflow.sim, + top=[name], + needs=[sv_pkg, sv_src, dpi_lib]) + + sim_run = hdlsim_dvflow.mkTask("hdlsim.%s.SimRun" % hdlsim_dvflow.sim, + plusargs=plusargs if plusargs is not None else [], + needs=[sim_img]) + + status, out = hdlsim_dvflow.runTask(sim_run) - run_args = flow.sim.mkRunArgs(dirconfig.rundir()) - print("rundir: %s" % dirconfig.rundir()) - run_args.prepend_pathenv("PYTHONPATH", test_py_api_data_dir) - print("Adding PYTHONPATH=%s" % test_py_api_data_dir) - if plusargs is not None: - run_args.plusargs.extend(plusargs) - flow.addTaskToPhase("run.main", flow.sim.mkRunTask(run_args)) + assert status == 0 - if dirconfig.config.getHdlSim() in SKIP_HDLSIM: - pytest.skip("Unsupported simulator %s" % dirconfig.config.getHdlSim()) - else: - flow.run_all() + with open(os.path.join(sim_run.rundir, "status.txt"), "r") as fp: + status = fp.read().strip() - with open(os.path.join(dirconfig.rundir(), "status.txt"), "r") as fp: - status = fp.read().strip() + assert status.startswith("PASS:") - assert status.startswith("PASS:") -def test_smoke(dirconfig): - _test_file(dirconfig, "test_smoke") +def test_smoke(hdlsim_dvflow, hdl_if_env): + _test_file(hdlsim_dvflow, "test_smoke", env=hdl_if_env) -def test_a_plus_b(dirconfig): - _test_file(dirconfig, "a_plus_b") +def test_a_plus_b(hdlsim_dvflow, hdl_if_env): + _test_file(hdlsim_dvflow, "a_plus_b", env=hdl_if_env) #@pytest.mark.skip("Needs more investigation") -def test_data1(dirconfig): - _test_file(dirconfig, "data1", plusargs=[ +def test_data1(hdlsim_dvflow, hdl_if_env): + _test_file(hdlsim_dvflow, "data1", env=hdl_if_env, plusargs=[ 'data=%s' % os.path.join(test_py_api_data_dir, "data1.json")]) \ No newline at end of file diff --git a/tests/unit/test_smoke.py b/tests/unit/test_smoke.py index 79fabee..64f521b 100644 --- a/tests/unit/test_smoke.py +++ b/tests/unit/test_smoke.py @@ -1,14 +1,11 @@ + import os import sys import pytest -import pytest_fv as pfv -from pytest_fv.fixtures import * - from .test_base import * +from dv_flow.libhdlsim.pytest import hdlsim_dvflow, HdlSimDvFlow +from . import hdl_if_env -import hdl_if - -SKIP_HDLSIM = ('ivl',) data_dir = os.path.join( os.path.dirname(os.path.abspath(__file__)), @@ -16,49 +13,81 @@ test_smoke_data_dir = os.path.join(data_dir, "test_smoke") test_smoke_str_data_dir = os.path.join(data_dir, "test_smoke_str") - -def test_smoke(dirconfig : pfv.DirConfig): - flow = pfv.FlowSim(dirconfig) +def test_smoke(hdlsim_dvflow, hdl_if_env): + env = hdl_if_env + env["PYTHONPATH"] = test_smoke_data_dir + os.pathsep + env["PYTHONPATH"] + hdlsim_dvflow.setEnv(env) print("test_smoke_data_dir: %s" % test_smoke_data_dir, flush=True) - flow.addTaskToPhase("generate.main", pfv.TaskCmd("gen-api", - cmd=[sys.executable, "-m", "hdl_if", "api-gen-sv", "-m", "call_sv_bfm", - "--package", "call_sv_bfm_pkg", "-o", "call_sv_bfm_pkg.sv"], - env=[pfv.EnvAction.prepend_path("PYTHONPATH", test_smoke_data_dir)], - cwd=dirconfig.builddir() - )) - flow.fs.add_library(hdl_if.share()) - flow.sim.addFileset(pfv.FSVlnv("fvutils::pyhdl-if", "systemVerilogSource")) + hdl_if_pkg = hdlsim_dvflow.mkTask("pyhdl-if.SvPkg") + hdl_if_dpi = hdlsim_dvflow.mkTask("pyhdl-if.DpiLib") - flow.sim.addFileset(pfv.FSPaths( - dirconfig.builddir(), - ["call_sv_bfm_pkg.sv"], - "systemVerilogSource")) + gen_api = hdlsim_dvflow.mkTask( + "pyhdl-if.APIGenSV", + pkgname="call_sv_bfm_pkg", + filename="call_sv_bfm_pkg.sv", + modules=["call_sv_bfm"], + pythonpath=[test_smoke_data_dir]) + - flow.sim.addFileset(pfv.FSPaths( - test_smoke_data_dir, - ["wb_init_bfm.sv", "call_sv_bfm.sv"], - "systemVerilogSource")) +# status, out = hdlsim_dvflow.runTask(gen_api) + + # flow.addTaskToPhase("generate.main", pfv.TaskCmd("gen-api", + # cmd=[sys.executable, "-m", "hdl_if", "api-gen-sv", "-m", "call_sv_bfm", + # "--package", "call_sv_bfm_pkg", "-o", "call_sv_bfm_pkg.sv"], + # env=[pfv.EnvAction.prepend_path("PYTHONPATH", test_smoke_data_dir)], + # cwd=dirconfig.builddir() + # )) + # flow.fs.add_library(hdl_if.share()) + # flow.sim.addFileset(pfv.FSVlnv("fvutils::pyhdl-if", "systemVerilogSource")) + + # flow.sim.addFileset(pfv.FSPaths( + # dirconfig.builddir(), + # ["call_sv_bfm_pkg.sv"], + # "systemVerilogSource")) + + test_sv = hdlsim_dvflow.mkTask("std.FileSet", + base=test_smoke_data_dir, + include=["wb_init_bfm.sv", "call_sv_bfm.sv"], + type="systemVerilogSource") + # flow.sim.addFileset(pfv.FSPaths( + # test_smoke_data_dir, + # ["wb_init_bfm.sv", "call_sv_bfm.sv"], + # "systemVerilogSource")) - flow.sim.dpi_libs.append(hdl_if.get_entry()) - flow.sim.top.add("call_sv_bfm") + # flow.sim.dpi_libs.append(hdl_if.get_entry()) + # flow.sim.top.add("call_sv_bfm") - run_args = flow.sim.mkRunArgs(dirconfig.rundir()) - run_args.prepend_pathenv("PYTHONPATH", test_smoke_data_dir) - flow.addTaskToPhase("run.main", flow.sim.mkRunTask(run_args)) + sim_img = hdlsim_dvflow.mkTask("hdlsim.%s.SimImage" % hdlsim_dvflow.sim, + top=["call_sv_bfm"], + needs=[hdl_if_pkg, hdl_if_dpi, gen_api, test_sv]) + + sim_run = hdlsim_dvflow.mkTask( + "hdlsim.%s.SimRun" % hdlsim_dvflow.sim, + needs=[sim_img]) - if dirconfig.config.getHdlSim() in SKIP_HDLSIM: - pytest.skip("Unsupported simulator %s" % dirconfig.config.getHdlSim()) - else: - flow.run_all() + status, out = hdlsim_dvflow.runTask(sim_run) - with open(os.path.join(dirconfig.rundir(), "status.txt"), "r") as fp: - status = fp.read().strip() + assert status == 0 + + # run_args = flow.sim.mkRunArgs(dirconfig.rundir()) + # run_args.prepend_pathenv("PYTHONPATH", test_smoke_data_dir) + # flow.addTaskToPhase("run.main", flow.sim.mkRunTask(run_args)) + + with open(os.path.join(out.output[0].basedir, "status.txt"), "r") as fp: + status = fp.read().strip() assert status.startswith("PASS:") -def test_smoke_str(dirconfig : pfv.DirConfig): + # if dirconfig.config.getHdlSim() in SKIP_HDLSIM: + # pytest.skip("Unsupported simulator %s" % dirconfig.config.getHdlSim()) + # else: + # flow.run_all() + + +@pytest.mark.skip("Needs more investigation") +def test_smoke_str(dirconfig): flow = pfv.FlowSim(dirconfig) print("test_smoke_data_dir: %s" % test_smoke_str_data_dir, flush=True) diff --git a/tests/unit/test_tlm_dpi.py b/tests/unit/test_tlm_dpi.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/unit/test_vpi_py_if.py b/tests/unit/test_vpi_py_if.py index d4d7f78..1190ede 100644 --- a/tests/unit/test_vpi_py_if.py +++ b/tests/unit/test_vpi_py_if.py @@ -1,9 +1,6 @@ import os import sys import pytest -import pytest_fv as pfv -from pytest_fv.fixtures import * - from .test_base import * print("path: %s" % str(sys.path)) @@ -18,7 +15,7 @@ SKIP_HDLSIM = ('xsm', 'vlt') -def test_smoke(dirconfig : pfv.DirConfig): +def test_smoke(dirconfig): flow = pfv.FlowSim(dirconfig) # flow.fs.add_library(hdl_if.share()) From 2bd59f75ebefb81fd383532f20f52af28baec0fe Mon Sep 17 00:00:00 2001 From: Matthew Ballance Date: Fri, 6 Jun 2025 00:10:54 +0000 Subject: [PATCH 2/5] XX Signed-off-by: Matthew Ballance --- src/hdl_if/dfm/flow.dv | 5 ++++ src/hdl_if/dfm/vpi_lib.py | 16 ++++++++++++ tests/unit/__init__.py | 14 +++++++++++ tests/unit/test_py_api.py | 6 +++-- tests/unit/test_smoke.py | 3 ++- tests/unit/test_vpi_py_if.py | 47 +++++++++++++++++------------------- 6 files changed, 63 insertions(+), 28 deletions(-) create mode 100644 src/hdl_if/dfm/vpi_lib.py diff --git a/src/hdl_if/dfm/flow.dv b/src/hdl_if/dfm/flow.dv index 53f218a..c909f7d 100644 --- a/src/hdl_if/dfm/flow.dv +++ b/src/hdl_if/dfm/flow.dv @@ -6,6 +6,11 @@ package: desc: PyHDL-IF DPI Library shell: pytask run: hdl_if.dfm.dpi_lib.DpiLib + + - name: VpiLib + desc: PyHDL-IF VPI Library + shell: pytask + run: hdl_if.dfm.vpi_lib.VpiLib - name: SvPkg desc: PyHDL-IF SystemVerilog Package diff --git a/src/hdl_if/dfm/vpi_lib.py b/src/hdl_if/dfm/vpi_lib.py new file mode 100644 index 0000000..9c350a5 --- /dev/null +++ b/src/hdl_if/dfm/vpi_lib.py @@ -0,0 +1,16 @@ +import os +from dv_flow.mgr import TaskDataInput, TaskRunCtxt, TaskDataResult, FileSet + +async def VpiLib(ctxt : TaskRunCtxt, input : TaskDataInput) -> TaskDataResult: + from hdl_if import get_entry + entry = get_entry() + + return TaskDataResult( + status=0, + output=[ + FileSet( + filetype="verilogVPI", + basedir=os.path.dirname(entry), + files=[os.path.basename(entry)]) + ] + ) diff --git a/tests/unit/__init__.py b/tests/unit/__init__.py index ee75544..ce6ceef 100644 --- a/tests/unit/__init__.py +++ b/tests/unit/__init__.py @@ -1,5 +1,19 @@ import os import pytest +from dv_flow.libhdlsim.pytest import HdlSimDvFlow, hdlsim_available_sims + +def available_sims_dpi(incl=None, excl=None): + if excl is None: + # TODO: control via env var? + excl = ["xsm"] + return hdlsim_available_sims(incl, excl) + +def available_sims_vpi(incl=None, excl=None): + if excl is None: + # TODO: control via env var? + excl = ["vlt", "xsm"] + return hdlsim_available_sims(incl, excl) + @pytest.fixture(scope="session") def hdl_if_env(): diff --git a/tests/unit/test_py_api.py b/tests/unit/test_py_api.py index cf10ffb..ebde281 100644 --- a/tests/unit/test_py_api.py +++ b/tests/unit/test_py_api.py @@ -3,7 +3,7 @@ import pytest from .test_base import * from dv_flow.libhdlsim.pytest import hdlsim_dvflow, HdlSimDvFlow -from . import hdl_if_env +from . import hdl_if_env, available_sims_dpi data_dir = os.path.join( os.path.dirname(os.path.abspath(__file__)), @@ -42,13 +42,15 @@ def _test_file(hdlsim_dvflow : HdlSimDvFlow, name, env, plusargs=None): assert status.startswith("PASS:") +@pytest.mark.parametrize("hdlsim_dvflow", available_sims_dpi(), indirect=True) def test_smoke(hdlsim_dvflow, hdl_if_env): _test_file(hdlsim_dvflow, "test_smoke", env=hdl_if_env) +@pytest.mark.parametrize("hdlsim_dvflow", available_sims_dpi(), indirect=True) def test_a_plus_b(hdlsim_dvflow, hdl_if_env): _test_file(hdlsim_dvflow, "a_plus_b", env=hdl_if_env) -#@pytest.mark.skip("Needs more investigation") +@pytest.mark.parametrize("hdlsim_dvflow", available_sims_dpi(), indirect=True) def test_data1(hdlsim_dvflow, hdl_if_env): _test_file(hdlsim_dvflow, "data1", env=hdl_if_env, plusargs=[ 'data=%s' % os.path.join(test_py_api_data_dir, "data1.json")]) \ No newline at end of file diff --git a/tests/unit/test_smoke.py b/tests/unit/test_smoke.py index 64f521b..d5a83fc 100644 --- a/tests/unit/test_smoke.py +++ b/tests/unit/test_smoke.py @@ -4,7 +4,7 @@ import pytest from .test_base import * from dv_flow.libhdlsim.pytest import hdlsim_dvflow, HdlSimDvFlow -from . import hdl_if_env +from . import hdl_if_env, available_sims_dpi data_dir = os.path.join( @@ -13,6 +13,7 @@ test_smoke_data_dir = os.path.join(data_dir, "test_smoke") test_smoke_str_data_dir = os.path.join(data_dir, "test_smoke_str") +@pytest.mark.parametrize("hdlsim_dvflow", available_sims_dpi(), indirect=True) def test_smoke(hdlsim_dvflow, hdl_if_env): env = hdl_if_env env["PYTHONPATH"] = test_smoke_data_dir + os.pathsep + env["PYTHONPATH"] diff --git a/tests/unit/test_vpi_py_if.py b/tests/unit/test_vpi_py_if.py index 1190ede..0da1909 100644 --- a/tests/unit/test_vpi_py_if.py +++ b/tests/unit/test_vpi_py_if.py @@ -2,6 +2,8 @@ import sys import pytest from .test_base import * +from dv_flow.libhdlsim.pytest import hdlsim_dvflow, HdlSimDvFlow +from . import hdl_if_env, available_sims_vpi print("path: %s" % str(sys.path)) @@ -15,31 +17,26 @@ SKIP_HDLSIM = ('xsm', 'vlt') -def test_smoke(dirconfig): - flow = pfv.FlowSim(dirconfig) +@pytest.mark.parametrize("hdlsim_dvflow", available_sims_vpi(), indirect=True) +def test_smoke(hdlsim_dvflow : HdlSimDvFlow, hdl_if_env): + hdlsim_dvflow.setEnv(hdl_if_env) -# flow.fs.add_library(hdl_if.share()) -# flow.sim.addFileset(pfv.FSVlnv("fvutils::pyhdl-if", "systemVerilogSource")) - -# flow.sim.addFileset(pfv.FSPaths( -# dirconfig.builddir(), -# ["call_sv_bfm_pkg.sv"], -# "systemVerilogSource")) + vpi_py_if_smoke = hdlsim_dvflow.mkTask("std.FileSet", + base=test_vpi_py_if_data_dir, + include=["vpi_py_if_smoke.v"], + type="verilogSource") + + vpi_lib = hdlsim_dvflow.mkTask("pyhdl-if.VpiLib") - flow.sim.addFileset(pfv.FSPaths( - test_vpi_py_if_data_dir, - ["vpi_py_if_smoke.v"], - "verilogSource")) + sim_img = hdlsim_dvflow.mkTask( + "hdlsim.%s.SimImage" % hdlsim_dvflow.sim, + needs=[vpi_py_if_smoke, vpi_lib], + top=["vpi_py_if_smoke"]) - flow.sim.pli_libs.append(hdl_if.get_entry()) - flow.sim.top.add("vpi_py_if_smoke") - - global hdl_if_dir - run_args = flow.sim.mkRunArgs(dirconfig.rundir()) -# run_args.prepend_pathenv("PYTHONPATH", hdl_if_dir) - flow.addTaskToPhase("run.main", flow.sim.mkRunTask(run_args)) - - if dirconfig.config.getHdlSim() in SKIP_HDLSIM: - pytest.skip("Unsupported simulator %s" % dirconfig.config.getHdlSim()) - else: - flow.run_all() \ No newline at end of file + sim_run = hdlsim_dvflow.mkTask( + "hdlsim.%s.SimRun" % hdlsim_dvflow.sim, + needs=[sim_img]) + + status, out = hdlsim_dvflow.runTask(sim_run) + + assert status == 0 From 41c2541f6f50d6244cd5f7de608e6329bceeb96e Mon Sep 17 00:00:00 2001 From: Matthew Ballance Date: Fri, 6 Jun 2025 00:17:10 +0000 Subject: [PATCH 3/5] Update testing dependencies Signed-off-by: Matthew Ballance --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3a41d4a..ea89e12 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -13,7 +13,7 @@ jobs: - name: Test build run: | python3 -m venv py - ./py/bin/pip install ivpm pytest pytest-fv + ./py/bin/pip install ivpm pytest pytest-dfm dv-flow-libhdlsim ./py/bin/python3 -m ivpm update -a ./packages/python/bin/python3 setup.py bdist_wheel - name: Run tests From bffd44f9ab547a61ba99ad0b90f20dc90b26f166 Mon Sep 17 00:00:00 2001 From: Matthew Ballance Date: Sat, 7 Jun 2025 20:40:59 +0000 Subject: [PATCH 4/5] Update to support AMD Xilinx Xsim Signed-off-by: Matthew Ballance --- .github/workflows/ci.yml | 2 +- .gitignore | 1 + .../call/dpi/call_sv_bfm/requirements.txt | 4 +- ivpm.yaml | 2 + src/entry.c | 133 +++++++++++++++++- src/hdl_if/cmd/cmd_api_gen_sv.py | 1 + src/hdl_if/impl/call/call_proxy_dpi.py | 4 +- src/hdl_if/impl/call/gen_sv_class.py | 2 + src/hdl_if/impl/call/hdl_call_endpoint_dpi.py | 21 ++- src/hdl_if/impl/dpi/hdl_services_dpi.py | 1 + src/hdl_if/share/dpi/pyhdl_if.sv | 5 + src/hdl_if/share/dpi/pyhdl_if_call_dpi.svh | 7 +- src/hdl_if/share/dpi/pyhdl_if_init.svh | 12 ++ src/hdl_if/share/dpi/pyhdl_if_macros.svh | 24 ++++ .../share/dpi/pyhdl_if_taskcall_closure.svh | 5 + tests/unit/__init__.py | 7 +- tests/unit/test_smoke.py | 1 + 17 files changed, 218 insertions(+), 14 deletions(-) create mode 100644 src/hdl_if/share/dpi/pyhdl_if_macros.svh diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ea89e12..1e97fc9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -13,7 +13,7 @@ jobs: - name: Test build run: | python3 -m venv py - ./py/bin/pip install ivpm pytest pytest-dfm dv-flow-libhdlsim + ./py/bin/pip install -U ivpm pytest pytest-dfm dv-flow-libhdlsim ./py/bin/python3 -m ivpm update -a ./packages/python/bin/python3 setup.py bdist_wheel - name: Run tests diff --git a/.gitignore b/.gitignore index 6bb0613..c197f95 100644 --- a/.gitignore +++ b/.gitignore @@ -175,3 +175,4 @@ results.xml *.bk examples/call/dpi/call_sv_bfm/call_sv_bfm_pkg.sv +rundir/ diff --git a/examples/call/dpi/call_sv_bfm/requirements.txt b/examples/call/dpi/call_sv_bfm/requirements.txt index ddb1501..9ed5c3f 100644 --- a/examples/call/dpi/call_sv_bfm/requirements.txt +++ b/examples/call/dpi/call_sv_bfm/requirements.txt @@ -1,3 +1,5 @@ pyhdl-if -cocotb +dv-flow-mgr +dv-flow-libhdlsim + diff --git a/ivpm.yaml b/ivpm.yaml index 6aab9fc..3d40a1e 100644 --- a/ivpm.yaml +++ b/ivpm.yaml @@ -25,6 +25,8 @@ package: src: pypi - name: cython src: pypi + - name: build + src: pypi - name: setuptools_scm src: pypi - name: pytest-dfm diff --git a/src/entry.c b/src/entry.c index 4dd3a79..85bfef2 100644 --- a/src/entry.c +++ b/src/entry.c @@ -61,9 +61,29 @@ extern "C" { #endif static lib_h_t _python_lib = 0; - static lib_h_t init_python(); +/** + * The following ensures that this DPI library has a dependency + * on DPI-exported symbols. This ensures that Python is able + * to find the exports + */ +int pyhdl_pi_if_RegisterTimeCB(); +int pyhdl_call_if_invoke_hdl_f(void); +int pyhdl_call_if_invoke_hdl_t(void); +int pyhdl_call_if_response_py_t(void); + +void *funcs[] = { + &pyhdl_pi_if_RegisterTimeCB, + &pyhdl_call_if_invoke_hdl_f, + &pyhdl_call_if_invoke_hdl_t, + &pyhdl_call_if_response_py_t, +}; + +void *get_dpiexport_funcs() { + return funcs; +} + //typedef void *PyObject; /******************************************************************* @@ -234,7 +254,7 @@ lib_h_t find_loaded_lib(const char *sym) { char mapfile_path[256]; FILE *map_fp; - DEBUG("find_loaded_lib(linux) %s", sym); + DEBUG("--> find_loaded_lib(linux) %s", sym); // First, try loading the executable { @@ -244,6 +264,7 @@ lib_h_t find_loaded_lib(const char *sym) { DEBUG("returning %p", ret); return ret; } else { + DEBUG("didn't find symbol \"%s\" in the executable\n", sym); ret = 0; } } @@ -299,11 +320,14 @@ lib_h_t find_loaded_lib(const char *sym) { free(path_s); } + DEBUG("<-- find_loaded_lib(linux) %s %p", sym, ret); + return ret; } lib_h_t check_lib(const char *path, const char *sym) { lib_h_t ret = 0; + DEBUG("--> check_lib(%s, %s)", path, sym); lib_h_t lib = dlopen(path, RTLD_LAZY); if (lib) { void *sym_h = dlsym(lib, sym); @@ -314,11 +338,53 @@ lib_h_t check_lib(const char *path, const char *sym) { ret = lib; } } + DEBUG("<-- check_lib(%s, %s) %p", path, sym, ret); return ret; } #endif +char *clean_env(const char *name, const char *omit) { + const char *value = getenv(name); + char *new_value; + const char *cp, *cpn; + int first_entry = 1; + + if (!value || !value[0]) { + return 0; + } + + cp = value; + + new_value = (char *)malloc(strlen(name)+strlen(value)+2); + strcpy(new_value, name); + strcat(new_value, "="); + + while (cp && *cp) { + cpn = strchr(cp, ':'); + if (!cpn) { + cpn = cp + strlen(cp); + } + + // Check if this path starts with pythonhome + if (strncmp(cp, omit, strlen(omit)) != 0) { + if (!first_entry) { + strcat(new_value, ":"); + } + strncat(new_value, cp, cpn - cp); + first_entry = 0; + } + + if (*cpn == ':') { + cp = cpn + 1; + } else { + cp = NULL; + } + } + + return new_value; +} + #ifdef _WIN32 #else lib_h_t find_config_python_lib() { @@ -342,6 +408,62 @@ lib_h_t find_config_python_lib() { fprintf(stdout, "PyHDL-IF Note: Using Python interpreter \"%s\", specified by $PYHDL_IF_PYTHON\n", python); fflush(stdout); + const char *pythonhome = getenv("PYTHONHOME"); + if (pythonhome && pythonhome[0]) { + const char *pythonpath = getenv("PYTHONPATH"); + const char *ldlibrarypath = getenv("LD_LIBRARY_PATH"); + const char *path = getenv("PATH"); + + fprintf(stdout, "PyHDL-IF Note: Clearing PYTHONHOME and cleaning PYTHONPATH\n"); + fflush(stdout); + + { + char *new_pythonpath = clean_env("PYTHONPATH", pythonhome); + if (new_pythonpath) { + fprintf(stdout, "PyHDL-IF Note: Setting PYTHONPATH to \"%s\"\n", new_pythonpath); + putenv(new_pythonpath); + } + } + { + char *new_ldlibrarypath = clean_env("LD_LIBRARY_PATH", pythonhome); + if (new_ldlibrarypath) { + fprintf(stdout, "PyHDL-IF Note: Setting LD_LIBRARY_PATH to \"%s\"\n", new_ldlibrarypath); + putenv(new_ldlibrarypath); + } + } + + { + char *new_path = clean_env("PATH", pythonhome); + if (new_path) { + fprintf(stdout, "PyHDL-IF Note: Setting PATH to \"%s\"\n", new_path); + putenv(new_path); + } + } + + putenv(strdup("PYTHON=")); + putenv(strdup("PYTHONHOME=")); + } + + // Now, add the new Python interpreter to the PATH + { + char *python_dir = strdup(python); + char *slash = strrchr(python_dir, '/'); + char *new_path; + if (slash) { + *slash = 0; // Remove the filename + } + + DEBUG("Python directory: %s", python_dir); + new_path = (char *)malloc(strlen(python_dir)+strlen(getenv("PATH"))+16); + strcpy(new_path, "PATH="); + strcat(new_path, python_dir); + strcat(new_path, ":"); + strcat(new_path, getenv("PATH")); + DEBUG("New PATH: %s", new_path); + putenv(new_path); + free(python_dir); + } + } args[0] = python; @@ -350,8 +472,15 @@ lib_h_t find_config_python_lib() { args[3] = 0; { + int i; + extern char **environ; const char *ld_library_path = getenv("LD_LIBRARY_PATH"); DEBUG("LD_LIBRARY_PATH: %s", ld_library_path?ld_library_path:"null"); + + for (i=0; environ[i]; i++) { + DEBUG("environ[%d]: %s", i, environ[i]); + } + } (void)pipe(cout_pipe); diff --git a/src/hdl_if/cmd/cmd_api_gen_sv.py b/src/hdl_if/cmd/cmd_api_gen_sv.py index 645184c..087b655 100644 --- a/src/hdl_if/cmd/cmd_api_gen_sv.py +++ b/src/hdl_if/cmd/cmd_api_gen_sv.py @@ -54,6 +54,7 @@ def __call__(self, args): gen = GenSVClass(fp, uvm=args.uvm) if args.package is not None: + gen.println('`include "pyhdl_if_macros.svh"') gen.println("package %s;" % args.package) gen.inc_ind() diff --git a/src/hdl_if/impl/call/call_proxy_dpi.py b/src/hdl_if/impl/call/call_proxy_dpi.py index 18e8d88..133135f 100644 --- a/src/hdl_if/impl/call/call_proxy_dpi.py +++ b/src/hdl_if/impl/call/call_proxy_dpi.py @@ -46,8 +46,10 @@ async def invoke_hdl_t( from hdl_if.backend import Backend be = Backend.inst(); evt = be.mkEvent() +# param = (method_name,) + param = method_name - self.ep.invoke_hdl_t(self.obj_id, evt, method_name, args) + self.ep.invoke_hdl_t(self.obj_id, evt, param, args) res = await evt.wait() return res diff --git a/src/hdl_if/impl/call/gen_sv_class.py b/src/hdl_if/impl/call/gen_sv_class.py index 85fabb4..8c1845e 100644 --- a/src/hdl_if/impl/call/gen_sv_class.py +++ b/src/hdl_if/impl/call/gen_sv_class.py @@ -392,6 +392,7 @@ def gen_task_dispatch(self, api): self.dec_ind() self.println() self.inc_ind() + self.println('`PYHDL_IF_ENTER(("invokeTask"));'); self.println("retval = pyhdl_if::None;"); self.println() self.println("case (method)") @@ -440,6 +441,7 @@ def gen_task_dispatch(self, api): self.dec_ind() self.println("endcase") + self.println('`PYHDL_IF_LEAVE(("invokeTask"));'); self.dec_ind() self.println("endtask") diff --git a/src/hdl_if/impl/call/hdl_call_endpoint_dpi.py b/src/hdl_if/impl/call/hdl_call_endpoint_dpi.py index 4e3488e..a36bc77 100644 --- a/src/hdl_if/impl/call/hdl_call_endpoint_dpi.py +++ b/src/hdl_if/impl/call/hdl_call_endpoint_dpi.py @@ -30,16 +30,24 @@ def __init__(self, scope): self.scope = scope exe_l = ctypes.cdll.LoadLibrary(None) - if not hasattr(exe_l, "pyhdl_call_if_invoke_hdl_f"): - print("TODO: Try DPIExportLib") - exe_l = self._findDPIExportLib() + pyhdl_call_if_invoke_hdl_f = None + try: + pyhdl_call_if_invoke_hdl_f = getattr(exe_l, "pyhdl_call_if_invoke_hdl_f") + pyhdl_call_if_invoke_hdl_f.restype = ctypes.c_int + except Exception as e: + print("Exception(__init__): %s" % str(e), flush=True) +# pyhdl_call_if_invoke_f = None +# if not hasattr(exe_l, "pyhdl_call_if_invoke_hdl_f"): +# print("TODO: Try DPIExportLib") +# exe_l = self._findDPIExportLib() + try: # self.svSetScope = exe_l.svSetScope # self.svSetScope.restype = None # self.svSetScope.argtypes = ( # ctypes.c_void_p, # ) - self.pyhdl_call_if_invoke_hdl_f = exe_l.pyhdl_call_if_invoke_hdl_f + self.pyhdl_call_if_invoke_hdl_f = pyhdl_call_if_invoke_hdl_f self.pyhdl_call_if_invoke_hdl_f.restype = ctypes.py_object self.pyhdl_call_if_invoke_hdl_f.argtypes = ( ctypes.c_int, @@ -50,7 +58,8 @@ def __init__(self, scope): self.pyhdl_call_if_invoke_hdl_t.argtypes = ( ctypes.c_int, ctypes.py_object, - ctypes.c_char_p, +# ctypes.c_char_p, + ctypes.py_object, ctypes.py_object) self.pyhdl_call_if_response_py_t = exe_l.pyhdl_call_if_response_py_t self.pyhdl_call_if_response_py_t.restype = None @@ -80,7 +89,7 @@ def invoke_hdl_t(self, self.pyhdl_call_if_invoke_hdl_t( obj_id, evt_obj, - method_name.encode(), + method_name, args) def response_py_t(self, sem_id, res): diff --git a/src/hdl_if/impl/dpi/hdl_services_dpi.py b/src/hdl_if/impl/dpi/hdl_services_dpi.py index fbc9892..fd76838 100644 --- a/src/hdl_if/impl/dpi/hdl_services_dpi.py +++ b/src/hdl_if/impl/dpi/hdl_services_dpi.py @@ -20,6 +20,7 @@ #* #**************************************************************************** import ctypes +import os from hdl_if.hdl_services import HdlServices class HdlServicesDpi(HdlServices): diff --git a/src/hdl_if/share/dpi/pyhdl_if.sv b/src/hdl_if/share/dpi/pyhdl_if.sv index 6c96220..44af026 100644 --- a/src/hdl_if/share/dpi/pyhdl_if.sv +++ b/src/hdl_if/share/dpi/pyhdl_if.sv @@ -18,12 +18,17 @@ * Created on * Author: */ + + `include "pyhdl_if_macros.svh" + package pyhdl_if; `include "pyhdl_dpi_imports.svh" import "DPI-C" context function int pyhdl_if_dpi_entry(); + int pyhdl_if_debug = 0; + // Cached handles to Python objects PyObject None; PyObject __hdl_pi_if; diff --git a/src/hdl_if/share/dpi/pyhdl_if_call_dpi.svh b/src/hdl_if/share/dpi/pyhdl_if_call_dpi.svh index b68c695..8d401e1 100644 --- a/src/hdl_if/share/dpi/pyhdl_if_call_dpi.svh +++ b/src/hdl_if/share/dpi/pyhdl_if_call_dpi.svh @@ -4,7 +4,9 @@ function PyObject pyhdl_call_if_invoke_hdl_f( string method_name, PyObject args); PyObject ret; + `PYHDL_IF_ENTER(("pyhdl_call_if_invoke_hdl_f(obj_id=%0d, method_name=%s, args=%p)", obj_id, method_name, args)); ret = __objects[obj_id].invokeFunc(method_name, args); + `PYHDL_IF_LEAVE(("pyhdl_call_if_invoke_hdl_f")); return ret; endfunction export "DPI-C" function pyhdl_call_if_invoke_hdl_f; @@ -12,14 +14,15 @@ export "DPI-C" function pyhdl_call_if_invoke_hdl_f; function void pyhdl_call_if_invoke_hdl_t( int obj_id, PyObject evt_obj, - string method_name, + PyObject method_name, PyObject args); TaskCallClosure closure; + automatic string local_name = PyUnicode_AsUTF8(method_name); closure = new( __objects[obj_id], evt_obj, - method_name, + local_name, args); pyhdl_pi_if_queue_runnable(closure); diff --git a/src/hdl_if/share/dpi/pyhdl_if_init.svh b/src/hdl_if/share/dpi/pyhdl_if_init.svh index 9220e60..7c1d170 100644 --- a/src/hdl_if/share/dpi/pyhdl_if_init.svh +++ b/src/hdl_if/share/dpi/pyhdl_if_init.svh @@ -4,9 +4,20 @@ function automatic bit __pyhdl_if_init(); PyObject hdl_if_impl, hdl_if_impl_dpi, dpi_init, get_none, args, res; PyGILState_STATE state; + if ($value$plusargs("pyhdl_if_debug=%d", pyhdl_if_debug)) begin + if (pyhdl_if_debug > 0) begin + $display("PYHDL-IF: Debug mode enabled (%d)", pyhdl_if_debug); + end else begin + $display("PYHDL-IF: Debug mode disabled"); + end + end + + `PYHDL_IF_ENTER(("pyhdl_if_init")); + if (pyhdl_if_dpi_entry() != 1) begin $display("Fatal: Failed to initialize pyhdl-pi-if DPI interface"); $finish; + `PYHDL_IF_LEAVE(("pyhdl_if_init")); return 0; end @@ -59,5 +70,6 @@ function automatic bit __pyhdl_if_init(); PyGILState_Release(state); + `PYHDL_IF_LEAVE(("pyhdl_if_init %0d", ret)); return ret; endfunction \ No newline at end of file diff --git a/src/hdl_if/share/dpi/pyhdl_if_macros.svh b/src/hdl_if/share/dpi/pyhdl_if_macros.svh new file mode 100644 index 0000000..e72e20e --- /dev/null +++ b/src/hdl_if/share/dpi/pyhdl_if_macros.svh @@ -0,0 +1,24 @@ + +`ifndef INCLUDED_PYHDL_IF_MACROS_SVH +`define INCLUDED_PYHDL_IF_MACROS_SVH + +`define PYHDL_IF_DEBUG(x) \ + if (pyhdl_if::pyhdl_if_debug > 0) begin \ + $write("PyHDL-IF: "); \ + $display x ; \ + end + +`define PYHDL_IF_ENTER(x) \ + if (pyhdl_if::pyhdl_if_debug > 0) begin \ + $write("--> PyHDL-IF: "); \ + $display x ; \ + end + +`define PYHDL_IF_LEAVE(x) \ + if (pyhdl_if::pyhdl_if_debug > 0) begin \ + $write("<-- PyHDL-IF: "); \ + $display x ; \ + end + + +`endif /* INCLUDED_PYHDL_IF_MACROS_SVH */ diff --git a/src/hdl_if/share/dpi/pyhdl_if_taskcall_closure.svh b/src/hdl_if/share/dpi/pyhdl_if_taskcall_closure.svh index a03666f..d38d12a 100644 --- a/src/hdl_if/share/dpi/pyhdl_if_taskcall_closure.svh +++ b/src/hdl_if/share/dpi/pyhdl_if_taskcall_closure.svh @@ -10,15 +10,19 @@ class TaskCallClosure implements PyHdlPiRunnable; PyObject evt_obj, string method_name, PyObject args); + `PYHDL_IF_ENTER(("TaskCallClosure::new(method=%0s)", method_name)); m_obj = obj; m_evt_obj = evt_obj; m_method_name = method_name; + `PYHDL_IF_DEBUG(("m_method_name=%0s", m_method_name)); m_args = args; + `PYHDL_IF_LEAVE(("TaskCallClosure::new(method=%0s)", method_name)); endfunction virtual task run(); PyObject args, res, evt_obj_set; PyGILState_STATE state = PyGILState_Ensure(); + `PYHDL_IF_ENTER(("TaskCallClosure::run(method=%0s)", m_method_name)); evt_obj_set = PyObject_GetAttrString(m_evt_obj, "set"); args = PyTuple_New(1); @@ -38,5 +42,6 @@ class TaskCallClosure implements PyHdlPiRunnable; end PyGILState_Release(state); + `PYHDL_IF_LEAVE(("TaskCallClosure::run(method=%0s)", m_method_name)); endtask endclass \ No newline at end of file diff --git a/tests/unit/__init__.py b/tests/unit/__init__.py index ce6ceef..7f75e8c 100644 --- a/tests/unit/__init__.py +++ b/tests/unit/__init__.py @@ -1,11 +1,14 @@ import os import pytest +import sys + from dv_flow.libhdlsim.pytest import HdlSimDvFlow, hdlsim_available_sims def available_sims_dpi(incl=None, excl=None): if excl is None: # TODO: control via env var? - excl = ["xsm"] +# excl = ["xsm"] + pass return hdlsim_available_sims(incl, excl) def available_sims_vpi(incl=None, excl=None): @@ -27,6 +30,8 @@ def hdl_if_env(): else: env["PYTHONPATH"] += os.pathsep + hdl_if_dir + env["PYHDL_IF_PYTHON"] = sys.executable + return env diff --git a/tests/unit/test_smoke.py b/tests/unit/test_smoke.py index d5a83fc..c758da4 100644 --- a/tests/unit/test_smoke.py +++ b/tests/unit/test_smoke.py @@ -66,6 +66,7 @@ def test_smoke(hdlsim_dvflow, hdl_if_env): sim_run = hdlsim_dvflow.mkTask( "hdlsim.%s.SimRun" % hdlsim_dvflow.sim, + plusargs=["pyhdl_if_debug=1"], needs=[sim_img]) status, out = hdlsim_dvflow.runTask(sim_run) From 35febb03a8e38a0759a53b0a9bbf38f2da592e53 Mon Sep 17 00:00:00 2001 From: Matthew Ballance Date: Sat, 7 Jun 2025 22:07:43 +0000 Subject: [PATCH 5/5] Updates for 0.0.3 release Signed-off-by: Matthew Ballance --- doc/ChangeLog.md | 6 ++++++ setup.py | 2 +- src/hdl_if/impl/call/call_proxy_dpi.py | 1 - src/hdl_if/impl/call/gen_sv_class.py | 11 ++++++++--- src/hdl_if/share/dpi/pyhdl_if_icall_api.svh | 7 ++++--- src/hdl_if/share/dpi/pyhdl_if_taskcall_closure.svh | 2 +- 6 files changed, 20 insertions(+), 9 deletions(-) diff --git a/doc/ChangeLog.md b/doc/ChangeLog.md index c52d542..f501d42 100644 --- a/doc/ChangeLog.md +++ b/doc/ChangeLog.md @@ -1,4 +1,10 @@ +## 0.0.3 +- (#29) - Resolve issues with tasks and Python GIL +- (#22) - Resolve return value issue +- () - Add support for AMD Xilinx Xsim simulator +- () - Move tests over to use the DV Flow Manager PyTest extension + ## 0.0.2 - (#25) - Correct issue passing string-type parameters diff --git a/setup.py b/setup.py index 0911782..84c774f 100644 --- a/setup.py +++ b/setup.py @@ -6,7 +6,7 @@ import sysconfig from setuptools import Extension, setup, find_namespace_packages -version="0.0.2" +version="0.0.3" proj_dir = os.path.dirname(os.path.abspath(__file__)) pythondir = os.path.join(proj_dir, "src") diff --git a/src/hdl_if/impl/call/call_proxy_dpi.py b/src/hdl_if/impl/call/call_proxy_dpi.py index 133135f..8e66340 100644 --- a/src/hdl_if/impl/call/call_proxy_dpi.py +++ b/src/hdl_if/impl/call/call_proxy_dpi.py @@ -46,7 +46,6 @@ async def invoke_hdl_t( from hdl_if.backend import Backend be = Backend.inst(); evt = be.mkEvent() -# param = (method_name,) param = method_name self.ep.invoke_hdl_t(self.obj_id, evt, param, args) diff --git a/src/hdl_if/impl/call/gen_sv_class.py b/src/hdl_if/impl/call/gen_sv_class.py index 8c1845e..8c41e7a 100644 --- a/src/hdl_if/impl/call/gen_sv_class.py +++ b/src/hdl_if/impl/call/gen_sv_class.py @@ -386,9 +386,10 @@ def gen_tf_impl(self, m : MethodDef): def gen_task_dispatch(self, api): self.println("virtual task invokeTask(") self.inc_ind() - self.println("output pyhdl_if::PyObject retval,") - self.println("input string method,") - self.println("input pyhdl_if::PyObject args);") + self.println("output pyhdl_if::PyObject retval,") + self.println("inout pyhdl_if::PyGILState_STATE state,") + self.println("input string method,") + self.println("input pyhdl_if::PyObject args);") self.dec_ind() self.println() self.inc_ind() @@ -410,6 +411,7 @@ def gen_task_dispatch(self, api): self.py2sv_func(p[1]), i )) + self.println("pyhdl_if::PyGILState_Release(state); // Release the GIL before invoking the task") self.write("%s" % self._ind) self.write("%s" % m.name) if len(m.params) == 0: @@ -429,6 +431,9 @@ def gen_task_dispatch(self, api): "," if (i+1 < len(m.params)) else ");" )) self.dec_ind() + + self.println("state = pyhdl_if::PyGILState_Ensure(); // Reacquire the GIL after invoking the task") + if m.rtype is not None: self.println("retval = %s;" % self.sv2py_func(m.rtype, "__retval")) self.dec_ind() diff --git a/src/hdl_if/share/dpi/pyhdl_if_icall_api.svh b/src/hdl_if/share/dpi/pyhdl_if_icall_api.svh index 026188b..f707793 100644 --- a/src/hdl_if/share/dpi/pyhdl_if_icall_api.svh +++ b/src/hdl_if/share/dpi/pyhdl_if_icall_api.svh @@ -25,8 +25,9 @@ interface class ICallApi; PyObject args); pure virtual task invokeTask( - output PyObject retval, - input string method, - input PyObject args); + output PyObject retval, + inout PyGILState_STATE state, + input string method, + input PyObject args); endclass \ No newline at end of file diff --git a/src/hdl_if/share/dpi/pyhdl_if_taskcall_closure.svh b/src/hdl_if/share/dpi/pyhdl_if_taskcall_closure.svh index d38d12a..df45bac 100644 --- a/src/hdl_if/share/dpi/pyhdl_if_taskcall_closure.svh +++ b/src/hdl_if/share/dpi/pyhdl_if_taskcall_closure.svh @@ -27,7 +27,7 @@ class TaskCallClosure implements PyHdlPiRunnable; evt_obj_set = PyObject_GetAttrString(m_evt_obj, "set"); args = PyTuple_New(1); - m_obj.invokeTask(res, m_method_name, m_args); + m_obj.invokeTask(res, state, m_method_name, m_args); if (res == null) begin res = None;