├── .github └── workflows │ └── ci.yml ├── .gitignore ├── .vscode └── settings.json ├── Dockerfile ├── LICENSE ├── README.md ├── bootstrap.sh ├── doc ├── ChangeLog.md ├── Doxyfile ├── Makefile ├── PyConvenienceAPI.md ├── macro_api.md └── source │ ├── _imgs │ ├── UVM_cocotb_sync.png │ ├── UVM_python_ref_model.png │ ├── UVM_python_test.png │ ├── pyhdl_library_stack.png │ └── usecase_sv_seq_python_impl.png │ ├── cmdref.rst │ ├── conf.py │ ├── index.rst │ ├── overview.rst │ ├── py_api.rst │ ├── quickstart.rst │ ├── sim_integ.rst │ └── sv_api.rst ├── examples ├── .gitignore ├── call │ ├── cocotb │ │ └── call_sv_bfm │ │ │ ├── Makefile │ │ │ ├── README.md │ │ │ ├── call_sv_bfm.py │ │ │ ├── call_sv_bfm.sv │ │ │ ├── clean.sh │ │ │ ├── init_venv.sh │ │ │ ├── requirements.txt │ │ │ ├── runit_mti.sh │ │ │ ├── runit_vlt.sh │ │ │ └── runit_xcm.sh │ └── dpi │ │ ├── call_sv_bfm │ │ ├── call_sv_bfm.py │ │ ├── call_sv_bfm.sv │ │ ├── clean.sh │ │ ├── init_venv.sh │ │ ├── requirements.txt │ │ ├── runit_mti.sh │ │ ├── runit_vcs.sh │ │ ├── runit_vlt.sh │ │ ├── runit_xcm.sh │ │ └── wb_init_bfm.sv │ │ ├── sv2py2sv_call │ │ ├── clean.sh │ │ ├── runit.sh │ │ ├── runit_mti.sh │ │ ├── sv2py2sv_call.py │ │ └── sv2py2sv_call.sv │ │ ├── sv2py2sv_tc_call │ │ ├── clean.sh │ │ ├── runit.sh │ │ ├── runit_mti.sh │ │ ├── sv2py2sv_tc_call.py │ │ └── sv2py2sv_tc_call.sv │ │ ├── sv2py_call │ │ ├── clean.sh │ │ ├── runit_mti.sh │ │ ├── runit_vlt.sh │ │ ├── sv2py_call.py │ │ └── sv2py_call.sv │ │ └── sv2pyext_call │ │ ├── runit.sh │ │ ├── sv2py_call.py │ │ └── sv2py_call.sv ├── pi │ ├── dpi │ │ └── call_python │ │ │ ├── call_python.py │ │ │ ├── call_python.sv │ │ │ ├── clean.sh │ │ │ ├── runit.sh │ │ │ └── runit_mti.sh │ └── vpi │ │ ├── call_proto │ │ ├── call_proto.py │ │ ├── call_proto.sv │ │ ├── cast.py │ │ ├── clean.sh │ │ ├── runit_iverilog.sh │ │ └── runit_mti.sh │ │ └── call_python │ │ ├── call_python.py │ │ ├── call_python.sv │ │ ├── clean.sh │ │ ├── runit_iverilog.sh │ │ └── runit_mti.sh └── tlm │ ├── req_rsp_loopback │ ├── .gitignore │ ├── clean.sh │ ├── req_rsp_loopback.py │ ├── req_rsp_loopback.sv │ ├── runit.sh │ ├── runit_icarus.sh │ └── runit_mti.sh │ └── reqrsp_loopback │ ├── .gitignore │ ├── clean.sh │ ├── reqrsp_loopback.py │ ├── reqrsp_loopback.sv │ ├── runit_icarus.sh │ └── runit_mti.sh ├── ivpm.yaml ├── scripts ├── build_linux.sh ├── gen_py_if.py ├── gen_vpi_if.py └── vpi_user.h ├── setup.py ├── src ├── entry.c └── hdl_if │ ├── __init__.py │ ├── __main__.py │ ├── backend.py │ ├── call │ ├── call_proxy.py │ └── hdl_call_endpoint.py │ ├── cmd │ ├── cmd_api_gen_sv.py │ ├── cmd_ifc_gen_sv.py │ ├── cmd_ifc_gen_types.py │ ├── cmd_libs.py │ └── cmd_share.py │ ├── decorators.py │ ├── hdl_obj_rgy.py │ ├── hdl_services.py │ ├── impl │ ├── __init__.py │ ├── call │ │ ├── __init__.py │ │ ├── api_decorator_impl.py │ │ ├── api_def.py │ │ ├── api_def_rgy.py │ │ ├── call_proxy_dpi.py │ │ ├── context.py │ │ ├── ctor.py │ │ ├── gen_sv_class.py │ │ ├── hdl_call_endpoint_dpi.py │ │ ├── imp_func_impl.py │ │ ├── imp_task_impl.py │ │ ├── method_decorator_impl.py │ │ └── method_def.py │ ├── dpi │ │ ├── __init__.py │ │ └── hdl_services_dpi.py │ ├── pi │ │ ├── __init__.py │ │ ├── backend_asyncio.py │ │ └── backend_cocotb.py │ ├── tlm │ │ ├── __init__.py │ │ ├── gen_ifc_sv.py │ │ ├── gen_ifc_types.py │ │ ├── interface_decorator_impl_base.py │ │ ├── model_info_tlm_if.py │ │ ├── req_fifo_decorator_impl.py │ │ ├── req_rsp_fifo_decorator_impl.py │ │ ├── rsp_fifo_decorator_impl.py │ │ ├── stream.py │ │ ├── stream_req.py │ │ ├── stream_req_rsp.py │ │ ├── stream_rsp.py │ │ ├── tlm_interface_decorator_impl.py │ │ └── type_info_tlm_if.py │ └── vpi │ │ ├── __init__.py │ │ ├── api.py │ │ ├── call_api.py │ │ ├── call_proxy_vpi.py │ │ ├── pkg_tf.py │ │ ├── pytf.py │ │ ├── tlm_api.py │ │ └── util.py │ ├── pi │ └── __init__.py │ ├── pkginfo.py │ ├── share │ ├── dpi │ │ ├── py_builtins.svh │ │ ├── py_ctxt.svh │ │ ├── py_dict.svh │ │ ├── py_iter.svh │ │ ├── py_list.svh │ │ ├── py_object.svh │ │ ├── py_tuple.svh │ │ ├── py_utils.svh │ │ ├── pyhdl_if.sv │ │ ├── pyhdl_if_call_api.svh │ │ ├── pyhdl_if_call_dpi.svh │ │ ├── pyhdl_if_call_init.svh │ │ ├── pyhdl_if_icall_api.svh │ │ ├── pyhdl_if_init.svh │ │ ├── pyhdl_if_pi_dpi.svh │ │ ├── pyhdl_if_taskcall_closure.svh │ │ ├── pyhdl_if_time_cb.svh │ │ ├── pyhdl_if_tlm_init.svh │ │ ├── pyhdl_if_uvm.sv │ │ ├── tlm_hdl2hvl_fifo.sv │ │ └── tlm_hvl2hdl_fifo.sv │ ├── pyhdl_if.core │ ├── pyhdl_if_req_fifo.sv │ ├── pyhdl_if_reqrsp_fifo.sv │ ├── pyhdl_if_rsp_fifo.sv │ └── vpi │ │ ├── tlm_hdl2hvl_fifo.sv │ │ └── tlm_hvl2hdl_fifo.sv │ └── tlm │ ├── __init__.py │ ├── stream_rgy.py │ ├── tlm_ifc.py │ ├── tlm_ifc_rgy.py │ └── tlm_method.py └── tests ├── __init__.py ├── smoke ├── __init__.py ├── my_module.py ├── run.tcl ├── runit.sh ├── runit_mti.sh ├── smoke.core ├── test_smoke.py └── top.sv ├── sys ├── __init__.py ├── dpi │ ├── data │ │ ├── api_gen_1.py │ │ ├── api_gen_1.sv │ │ ├── api_gen_ext_1.py │ │ ├── api_gen_ext_1.sv │ │ ├── api_gen_uvm_1.py │ │ ├── api_gen_uvm_1.sv │ │ ├── async_call.py │ │ ├── async_call.sv │ │ ├── reqrsp_fifo_loopback.py │ │ ├── reqrsp_fifo_loopback.sv │ │ ├── smoke.py │ │ ├── smoke.sv │ │ ├── test_load.py │ │ └── test_load.sv │ ├── test_dpi.py │ ├── test_dpi_if.py │ └── vpi │ │ ├── data │ │ └── smoke.sv │ │ └── test_vpi.py ├── dpi_vpi │ ├── data │ │ ├── load.py │ │ └── test_load.sv │ └── test_dpi_vpi_if.py ├── hdl │ ├── data │ │ ├── hdl2hvl_fifo.sv │ │ └── hvl2hdl_fifo.sv │ └── test_fifos.py └── vpi │ ├── data │ ├── load.py │ └── test_load.sv │ └── test_vpi_if.py ├── unit ├── __init__.py ├── data │ ├── py_api │ │ ├── a_plus_b.py │ │ ├── a_plus_b.sv │ │ ├── data1.json │ │ ├── data1.sv │ │ ├── py_data1.py │ │ ├── smoke_test.py │ │ └── test_smoke.sv │ ├── test_smoke │ │ ├── call_sv_bfm.py │ │ ├── call_sv_bfm.sv │ │ └── wb_init_bfm.sv │ ├── test_smoke_str │ │ ├── call_sv_bfm.py │ │ ├── call_sv_bfm.sv │ │ └── wb_init_bfm.sv │ └── vpi_py_if │ │ └── vpi_py_if_smoke.v ├── disabled_test_decorators.py ├── disabled_test_generators.py ├── disabled_test_tlm_if_gen.py ├── disabled_test_tlm_if_spec.py ├── test_base.py ├── test_py_api.py ├── test_smoke.py └── test_vpi_py_if.py └── util.py /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "python.defaultInterpreterPath": "${workspaceFolder}/packages/python/bin/python3", 3 | "python.testing.pytestArgs": [ 4 | "-s", 5 | "./tests/unit", 6 | ], 7 | "python.testing.pytestEnabled": true, 8 | "python.testing.unittestEnabled": false, 9 | 10 | "search.useIgnoreFiles": true, 11 | "search.useGlobalIgnoreFiles": true 12 | } 13 | 14 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # Use the official Python image as the base 2 | FROM python@sha256:942374a9ed2353df11065502733c29c7f655a2b6bd03e0e2a3dd2086fdb1044c 3 | 4 | # Set the working directory inside the container 5 | WORKDIR /work/ 6 | 7 | # Clone the Verilator repository 8 | RUN git clone https://github.com/verilator/verilator.git 9 | 10 | # Change the working directory to the Verilator repository 11 | WORKDIR /work/ 12 | 13 | RUN apt-get update && \ 14 | apt-get install -y help2man flex bison ccache && \ 15 | apt-get install -y libgoogle-perftools-dev numactl perl-doc && \ 16 | cd verilator && \ 17 | git checkout 37a400209809d389eb8eae0a6b6bee47fd4008c7 && \ 18 | # Build and install Verilator 19 | autoconf && ./configure && make && make install 20 | 21 | # Add Verilator to the environment variables 22 | ENV PATH="${PATH}:/work/verilator/bin" 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PyHDL-IF - A Python Interface to HDL Simulators 2 | 3 | ## Installing PyHDL-IF 4 | As an end user, the easiest way to install PyHDL-IF is via `pip`: 5 | 6 | ``` 7 | % pip install pyhdl-if 8 | ``` 9 | 10 | Binary wheels are currently available for Linux x86_64 platforms. 11 | 12 | ## Setting up a Development Environment 13 | 14 | PyHDL-IF uses [IVPM](https://fvutils.github.io/ivpm/) to manage dependencies. The 15 | `bootstrap.sh` creates a local Python virtual environment, installs IVPM, and 16 | required fetches packages. 17 | 18 | ``` 19 | % ./bootstrap.sh 20 | ``` 21 | 22 | ### Building PyHDL-IF 23 | For development builds, PyHDL-IF is built using the Python `setup.py` script. 24 | Run the `setup.py` script using the local Python virtual environment: 25 | 26 | ``` 27 | % ./packages/python/bin/python setup.py build_ext --inplace 28 | ``` 29 | 30 | ### Running Tests 31 | PyHDL-IF has a growing set of unit tests. These tests assume that you have a 32 | recent version of the [Verilator](https://github.com/verilator/verilator) 33 | SystemVerilog simulator installed. 34 | 35 | PyHDL-IF uses pytest for its tests. Run the suite of tests like this: 36 | 37 | ``` 38 | % PYTHONPATH=$(pwd)/src ./packages/python/bin/python3 -m pytest -s tests/unit 39 | ``` 40 | 41 | Note: this assumes running `bash` as the shell. 42 | 43 | By default, Verilator will be used as the simulator. You can select a different 44 | simulator using the `PYTEST_FV_HDLSIM' environment variable: 45 | - mti - Siemens Questa (Modeltech) 46 | - xcm - Cadence Xcelium 47 | - vcs - Synopsys VCS 48 | - vlt - Verilator 49 | 50 | ``` 51 | % PYTEST_FV_HDLSIM=vlt PYTHONPATH=$(pwd)/src ./packages/python/bin/python3 -m pytest -s tests/ 52 | ``` 53 | 54 | 55 | 56 | 57 | -------------------------------------------------------------------------------- /bootstrap.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | script_dir=$(dirname $(realpath $0)) 4 | 5 | if test -z ${PYTHON}; then 6 | PYTHON=$(which python3) 7 | fi 8 | 9 | 10 | if test ! -d ${script_dir}/packages; then 11 | echo "Setting up packages directory using ${PYTHON}" 12 | ${PYTHON} -m venv ${script_dir}/packages/python 13 | ${script_dir}/packages/python/bin/pip install ivpm 14 | if test $? -ne 0; then exit 1; fi 15 | ${script_dir}/packages/python/bin/ivpm update -a 16 | if test $? -ne 0; then exit 1; fi 17 | else 18 | echo "Packages directory already exists" 19 | fi -------------------------------------------------------------------------------- /doc/ChangeLog.md: -------------------------------------------------------------------------------- 1 | 2 | ## 0.0.2 3 | - (#25) - Correct issue passing string-type parameters 4 | 5 | 6 | -------------------------------------------------------------------------------- /doc/Makefile: -------------------------------------------------------------------------------- 1 | DOC_DIR:=$(dir $(abspath $(lastword $(MAKEFILE_LIST)))) 2 | PYHDL_IF_DIR:=$(abspath $(DOC_DIR)/..) 3 | 4 | # Minimal makefile for Sphinx documentation 5 | # 6 | 7 | # You can set these variables from the command line, and also 8 | # from the environment for the first two. 9 | SPHINXOPTS ?= 10 | SPHINXBUILD ?= sphinx-build 11 | SOURCEDIR = source 12 | BUILDDIR = build 13 | 14 | SV_DEPS:=$(shell find $(PYHDL_IF_DIR)/src/hdl_if/share/dpi -name '*.sv*') 15 | 16 | 17 | #sv_doc : sv_pp.sv 18 | # echo "test" 19 | 20 | pyhdl_if_pp.sv : $(SV_DEPS) 21 | touch uvm_macros.svh 22 | verilator -E --pp-comments +incdir+$(PYHDL_IF_DIR)/src/hdl_if/share/dpi \ 23 | +incdir+$(pwd) \ 24 | $(PYHDL_IF_DIR)/src/hdl_if/share/dpi/pyhdl_if.sv > $@ || rm -f $@ 25 | 26 | pyhdl_if_uvm_pp.sv : $(SV_DEPS) 27 | touch uvm_macros.svh 28 | verilator -E --pp-comments +incdir+$(PYHDL_IF_DIR)/src/hdl_if/share/dpi \ 29 | +incdir+$(pwd) \ 30 | $(PYHDL_IF_DIR)/src/hdl_if/share/dpi/pyhdl_if_uvm.sv > $@ || rm -f $@ 31 | 32 | pyhdl_if.cpp : pyhdl_if_pp.sv pyhdl_if_uvm_pp.sv 33 | # echo "namespace pyhdl_if {" > $@ 34 | perl $(PYHDL_IF_DIR)/packages/doxygen-filter-sv/filter/idv_doxyfilter_sv.pl \ 35 | pyhdl_if_pp.sv >> $@ || rm -f $@ 36 | # echo "} // namespace pyhdl_if" >> $@ 37 | # echo "namespace pyhdl_if_uvm {" >> $@ 38 | # perl $(PYHDL_IF_DIR)/packages/doxygen-filter-sv/filter/idv_doxyfilter_sv.pl \ 39 | # pyhdl_if_uvm_pp.sv >> $@ || rm -f $@ 40 | # echo "} // namespace pyhdl_if_uvm" >> $@ 41 | sed -i -e 's/automatic//g' $@ -e 's/\[\$$\]/[]/g' -e 's/longint unsigned/unsigned long long/g' 42 | 43 | pyhdl_if_doxy.d : pyhdl_if.cpp 44 | rm -rf pyhdl_if_doxy 45 | doxygen Doxyfile 46 | cd pyhdl_if_doxy/xml ; for file in *.xml; do sed -i -e 's/inline="yes"/inline="no"/g' $$file; done 47 | touch $@ 48 | 49 | # Put it first so that "make" without argument is like "make help". 50 | help: 51 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 52 | 53 | .PHONY: help Makefile 54 | 55 | # Catch-all target: route all unknown targets to Sphinx using the new 56 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). 57 | html: Makefile pyhdl_if_doxy.d 58 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 59 | 60 | clean : 61 | rm -rf pyhdl_if_doxy pyhdl_if_doxy.d pyhdl_if_pp.sv pyhdl_if_uvm_pp.sv uvm_macros.svh 62 | rm -rf pyhdl_if.cpp html build 63 | -------------------------------------------------------------------------------- /doc/PyConvenienceAPI.md: -------------------------------------------------------------------------------- 1 | 2 | - py_object 3 | - as_<> converters 4 | - to_<> terminal converters 5 | 6 | 7 | - py_dict 8 | - mk 9 | - mk_new 10 | - clear 11 | - contains 12 | - contains_str 13 | - copy 14 | - set_item 15 | - set_item_str 16 | - del_item 17 | - del_item_str 18 | - get_item 19 | - get_item_ref 20 | - get_item_str 21 | - get_item_str_ref 22 | - pop 23 | - pop_str 24 | - size 25 | 26 | - py_list 27 | - mk 28 | - mk_new 29 | - size 30 | - get_item 31 | - get_item_ref 32 | - set_item 33 | - insert 34 | - append 35 | - extend 36 | - clear 37 | - get_slice 38 | - set_slice 39 | - sort 40 | - reverse 41 | - as_tuple 42 | - to_tuple 43 | 44 | - py_tuple 45 | - mk 46 | - mk_new 47 | - size 48 | - get_item 49 | - set_item 50 | - get_slice 51 | - set_slice 52 | 53 | Use Cases 54 | - Develop a symmetric API in Python and SystemVerilog 55 | - Call Python from SystemVerilog 56 | - Call SystemVerilog from Python 57 | - Call an existing Python API using the Python API (abstracted or not) 58 | - Implement a SystemVerilog convenience wrapper for an existing Python class 59 | -------------------------------------------------------------------------------- /doc/macro_api.md: -------------------------------------------------------------------------------- 1 | 2 | - define Python API with SystemVerilog macros 3 | - Implement with Python API 4 | 5 | ```python 6 | class my_class: 7 | def my_method(self, a, b, c) -> int: 8 | pass 9 | ``` 10 | 11 | ```systemverilog 12 | class my_api extends base; 13 | function new(); 14 | super.new("my_module", "my_class"); 15 | endfunction 16 | 17 | `hdl_if_long_fn(my_method, "%0d %0d %0s", (int a, int b, string c)) 18 | 19 | endclass 20 | 21 | ``` -------------------------------------------------------------------------------- /doc/source/_imgs/UVM_cocotb_sync.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvutils/pyhdl-if/15c3e1bcfc56f0506f7ac7808109d0ba061a2378/doc/source/_imgs/UVM_cocotb_sync.png -------------------------------------------------------------------------------- /doc/source/_imgs/UVM_python_ref_model.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvutils/pyhdl-if/15c3e1bcfc56f0506f7ac7808109d0ba061a2378/doc/source/_imgs/UVM_python_ref_model.png -------------------------------------------------------------------------------- /doc/source/_imgs/UVM_python_test.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvutils/pyhdl-if/15c3e1bcfc56f0506f7ac7808109d0ba061a2378/doc/source/_imgs/UVM_python_test.png -------------------------------------------------------------------------------- /doc/source/_imgs/pyhdl_library_stack.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvutils/pyhdl-if/15c3e1bcfc56f0506f7ac7808109d0ba061a2378/doc/source/_imgs/pyhdl_library_stack.png -------------------------------------------------------------------------------- /doc/source/_imgs/usecase_sv_seq_python_impl.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvutils/pyhdl-if/15c3e1bcfc56f0506f7ac7808109d0ba061a2378/doc/source/_imgs/usecase_sv_seq_python_impl.png -------------------------------------------------------------------------------- /doc/source/cmdref.rst: -------------------------------------------------------------------------------- 1 | ################# 2 | Command Reference 3 | ################# 4 | 5 | .. argparse:: 6 | :module: hdl_if.__main__ 7 | :func: getparser 8 | -------------------------------------------------------------------------------- /doc/source/conf.py: -------------------------------------------------------------------------------- 1 | # Configuration file for the Sphinx documentation builder. 2 | # 3 | # This file only contains a selection of the most common options. For a full 4 | # list see the documentation: 5 | # https://www.sphinx-doc.org/en/master/usage/configuration.html 6 | 7 | # -- Path setup -------------------------------------------------------------- 8 | 9 | # If extensions (or modules to document with autodoc) are in another directory, 10 | # add these directories to sys.path here. If the directory is relative to the 11 | # documentation root, use os.path.abspath to make it absolute, like shown here. 12 | # 13 | import os 14 | import sys 15 | # sys.path.insert(0, os.path.abspath('.')) 16 | 17 | source_dir = os.path.dirname(os.path.abspath(__file__)) 18 | doc_dir = os.path.dirname(source_dir) 19 | pyhdl_if_dir = os.path.dirname(doc_dir) 20 | 21 | sys.path.insert(0, os.path.join(pyhdl_if_dir, "src")) 22 | 23 | # -- Project information ----------------------------------------------------- 24 | 25 | project = 'PyHDL-IF' 26 | copyright = '2024, Matthew Ballance' 27 | author = 'Matthew Ballance' 28 | 29 | 30 | # -- General configuration --------------------------------------------------- 31 | 32 | # Add any Sphinx extension module names here, as strings. They can be 33 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom 34 | # ones. 35 | extensions = [ 36 | # 'sphinx.ext.ingmath', 37 | 'sphinx.ext.todo', 38 | 'breathe', 39 | 'sphinx_rtd_theme', 40 | 'sphinxarg.ext' 41 | ] 42 | 43 | breathe_projects = { 44 | "pyhdl_if": os.path.join(doc_dir, "pyhdl_if_doxy", "xml") 45 | } 46 | breathe_default_project = "pyhdl_if" 47 | breathe_implementation_filename_extensions = [] 48 | 49 | # Add any paths that contain templates here, relative to this directory. 50 | templates_path = ['_templates'] 51 | 52 | # List of patterns, relative to source directory, that match files and 53 | # directories to ignore when looking for source files. 54 | # This pattern also affects html_static_path and html_extra_path. 55 | exclude_patterns = [] 56 | 57 | 58 | # -- Options for HTML output ------------------------------------------------- 59 | 60 | # The theme to use for HTML and HTML Help pages. See the documentation for 61 | # a list of builtin themes. 62 | # 63 | html_theme = 'sphinx_rtd_theme' 64 | 65 | # Add any paths that contain custom static files (such as style sheets) here, 66 | # relative to this directory. They are copied after the builtin static files, 67 | # so a file named "default.css" will overwrite the builtin "default.css". 68 | html_static_path = ['_static'] 69 | -------------------------------------------------------------------------------- /doc/source/index.rst: -------------------------------------------------------------------------------- 1 | .. PyHDL-IF documentation master file, created by 2 | sphinx-quickstart on Sat Apr 6 19:07:21 2024. 3 | You can adapt this file completely to your liking, but it should at least 4 | contain the root `toctree` directive. 5 | 6 | PyHDL-IF Documentation 7 | ========================= 8 | PyHDL-IF implements a procedural interface between Python and 9 | various HDL simulator APIs. The library focuses on simplifying 10 | the task of implementing interactions between HDL and Python at a 11 | variety of abstraction levels. 12 | 13 | .. toctree:: 14 | :maxdepth: 2 15 | :caption: Contents: 16 | 17 | quickstart 18 | overview 19 | sim_integ 20 | cmdref 21 | py_api 22 | sv_api 23 | 24 | 25 | Indices and tables 26 | ================== 27 | 28 | * :ref:`genindex` 29 | * :ref:`modindex` 30 | * :ref:`search` 31 | -------------------------------------------------------------------------------- /doc/source/py_api.rst: -------------------------------------------------------------------------------- 1 | ########## 2 | Python API 3 | ########## 4 | 5 | -------------------------------------------------------------------------------- /doc/source/quickstart.rst: -------------------------------------------------------------------------------- 1 | ########## 2 | Quickstart 3 | ########## 4 | 5 | Installing PyHDL-IF 6 | =================== 7 | 8 | It's easiest to install PyHDL-IF as a binary package from PyPi: 9 | 10 | .. code:: shell 11 | 12 | % pip install pyhdl-if 13 | 14 | Installing PyHDL-IF from Source 15 | ------------------------------- 16 | 17 | Installing PyHDL-IF from source requires the installation of three 18 | Python packages that are only used during the build process: 19 | 20 | .. code:: shell 21 | 22 | % pip install cxxheaderparser pcpp ivpm 23 | 24 | Once these pre-requisites are installed, the package can be built 25 | using the following command: 26 | 27 | .. code:: shell 28 | 29 | % python3 setup.py build_ext --inplace 30 | 31 | -------------------------------------------------------------------------------- /doc/source/sim_integ.rst: -------------------------------------------------------------------------------- 1 | ##################### 2 | Simulator Integration 3 | ##################### 4 | 5 | SystemVerilog (DPI) 6 | =================== 7 | 8 | Source 9 | ****** 10 | The PyHDL-IF SystemVerilog package must be compiled prior to 11 | compilation of code that uses PyHDL-IF artifacts. If you use 12 | a FuseSoC-based build system, simply place a dependency on 13 | `fvutils::pyhdl-if`. 14 | 15 | If you directly specify sources: 16 | 17 | - Obtain the `share` directory by calling: 18 | 19 | .. code:: shell 20 | 21 | % python3 -c 'import hdl_if; print(hdl_if.share())' 22 | # OR 23 | % python3 -m hdl_if share 24 | 25 | - Specify `${share}/dpi` as an include directory 26 | - Specify `${share}/dpi/pyhdl_if.sv` as a source file 27 | 28 | DPI Shared Library 29 | ****************** 30 | Two shared libraries must be specified as DPI libraries to the simulator: 31 | 32 | - PyHDL-IF entrypoint 33 | - Python 34 | 35 | Obtain the paths to these libraries by running: 36 | 37 | .. code:: shell 38 | 39 | % python3 -c 'import hdl_if; print(" ".join(hdl_if.libs("dpi")))' 40 | # OR 41 | % python3 -m hdl_if libs --type dpi 42 | 43 | Specify these as DPI libraries to your simulator using its preferred mechanism. 44 | 45 | Verilog (VPI) 46 | ============= 47 | TBD 48 | 49 | VHDL (VHPI) 50 | =========== 51 | TBD 52 | 53 | VHDL (FLI) 54 | ========== 55 | TBD 56 | 57 | Loading the Python Library 58 | ************************** 59 | 60 | When the PyHDL-IF library is initialized, it will try to load the Python 61 | shared library and locate symbols for simulator-provided APIs. 62 | 63 | By default, the following process is followed: 64 | 65 | * If ${LIBPYTHON_LOC} is set, this shared library is loaded and Python symbols 66 | are obtained from it. 67 | * Python symbols will be obtained from the simulator's global namespace 68 | if Python symbols are available in the simulator's global namespace. 69 | * Next, shared libraries loaded by the simulator process are checked to specified 70 | if they provide Python symbols. 71 | * Next, the available Python interpreter is used to discover where its shared 72 | library is installed. 73 | 74 | LIBPYTHON_LOC 75 | ============= 76 | The environment variable `LIBPYTHON_LOC` can be set to specify the location of the 77 | Python shared library. Note that a full path must be specified. 78 | 79 | PYHDL_IF_PYTHON 80 | =============== 81 | The environment variable `PYHDL_IF_PYTHON` can be set to specify the Python 82 | interpreter to probe for configuration information. -------------------------------------------------------------------------------- /doc/source/sv_api.rst: -------------------------------------------------------------------------------- 1 | ################# 2 | SystemVerilog API 3 | ################# 4 | 5 | The SystemVerilog API provides two levels of Python API access: 6 | 7 | - Direct access to the Python C API 8 | - An abstracted class-based convenience API 9 | 10 | Class-Based API 11 | =============== 12 | .. doxygenclass:: py_object 13 | :members: 14 | 15 | .. doxygenclass:: py_dict 16 | :members: 17 | 18 | .. doxygenclass:: py_list 19 | :members: 20 | 21 | .. doxygenclass:: py_tuple 22 | :members: 23 | 24 | Utility Methods 25 | --------------- 26 | 27 | .. doxygenfunction:: py_from_int 28 | 29 | .. doxygenfunction:: py_from_uint 30 | 31 | .. doxygenfunction:: py_from_long 32 | 33 | .. doxygenfunction:: py_from_ulong 34 | 35 | .. doxygenfunction:: py_from_str 36 | 37 | .. doxygenfunction:: py_import 38 | 39 | .. doxygenfunction:: py_call_builtin 40 | 41 | .. doxygenfunction:: py_gil_enter 42 | 43 | .. doxygenfunction:: py_gil_leave 44 | 45 | 46 | 47 | Direct API 48 | ========== 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /examples/.gitignore: -------------------------------------------------------------------------------- 1 | 2 | MyC.svh 3 | -------------------------------------------------------------------------------- /examples/call/cocotb/call_sv_bfm/Makefile: -------------------------------------------------------------------------------- 1 | TOPLEVEL_LANG ?= verilog 2 | SIM ?= verilator 3 | 4 | PWD=$(shell pwd) 5 | 6 | HDL_IF_ENTRY := $(shell python -c "import hdl_if; print(hdl_if.get_entry())") 7 | HDL_IF_SHARE := $(shell python -c "import hdl_if; print(hdl_if.share())") 8 | 9 | VERILOG_SOURCES += $(HDL_IF_SHARE)/dpi/pyhdl_if.sv 10 | VERILOG_SOURCES += $(PWD)/call_sv_bfm.sv 11 | 12 | TOPLEVEL := call_sv_bfm 13 | MODULE := call_sv_bfm 14 | 15 | # Questa 16 | VLOG_ARGS += -sv +incdir+${HDL_IF_SHARE}/dpi 17 | VSIM_ARGS += -sv_lib $(basename $(HDL_IF_ENTRY)) 18 | 19 | # Verilator 20 | EXTRA_ARGS_verilator=--timing +incdir+${HDL_IF_SHARE}/dpi --vpi -Wno-fatal -LDFLAGS -export-dynamic -LDFLAGS ${HDL_IF_ENTRY} 21 | 22 | # Xcelium 23 | EXTRA_ARGS_xcelium=-sv_lib $(HDL_IF_ENTRY) -sv +incdir+${HDL_IF_SHARE}/dpi 24 | 25 | EXTRA_ARGS += $(EXTRA_ARGS_$(SIM)) 26 | 27 | CUSTOM_COMPILE_DEPS=hdl_call_if_api.svh 28 | 29 | include $(shell cocotb-config --makefiles)/Makefile.sim 30 | 31 | # needed for interface 32 | # TODO: should depend on call_sv_bfm.sv 33 | hdl_call_if_api.svh: 34 | python -m hdl_if api-gen-sv -m call_sv_bfm 35 | -------------------------------------------------------------------------------- /examples/call/cocotb/call_sv_bfm/README.md: -------------------------------------------------------------------------------- 1 | # Example: cocotb/call_sv_bfm 2 | 3 | This example highlights interoperability between cocotb and PyHDL-IF. The Python testbench (call_sv_bfm.py) 4 | interacts with clock and reset signals using cocotb, then interacts via tasks with a bus functional model (BFM) 5 | implemented in SystemVerilog. 6 | 7 | ## How to Run 8 | 1. Download docker and build the image using the dockerfile in the project root 9 | ``` 10 | docker build -t local_test . # Run this in proj root dir 11 | ``` 12 | 13 | 2. Get an interactive terminal to the docker container and mount our work area so changes persist when the container shuts down 14 | ``` 15 | docker run -it -v "$(pwd)":/work/pyhdl-if local_test /bin/bash # run this in proj root dir 16 | ``` 17 | 18 | 3. `cd` to example directory inside the docker container, initialize the venv and activate it 19 | ``` 20 | cd pyhdl-if/examples/call/cocotb/call_sv_bfm/ 21 | ./init_venv.sh 22 | source example_venv/bin/activate 23 | ``` 24 | 25 | 4. Run the test 26 | ``` 27 | make 28 | ``` 29 | -------------------------------------------------------------------------------- /examples/call/cocotb/call_sv_bfm/call_sv_bfm.py: -------------------------------------------------------------------------------- 1 | 2 | import cocotb 3 | from cocotb.triggers import Timer, RisingEdge 4 | import ctypes as ct 5 | import hdl_if as hif 6 | 7 | @hif.api 8 | class WishboneInitiator(object): 9 | 10 | @hif.imp 11 | async def write(self, addr : ct.c_uint32, data : ct.c_uint32): 12 | pass 13 | 14 | @hif.imp 15 | async def read(self, addr : ct.c_uint32) -> ct.c_uint32: 16 | pass 17 | 18 | @cocotb.test() 19 | async def entry(dut): 20 | print("entry") 21 | await Timer(1, 'ns') 22 | print("post-timeout") 23 | rgy = hif.HdlObjRgy.inst() 24 | init_bfm = rgy.findObj(r".*\.init_bfm", regex=True) 25 | 26 | # Wait for reset 27 | clk_ev = RisingEdge(dut.clk) 28 | 29 | while (True): 30 | await clk_ev 31 | print("reset: %d" % dut.reset) 32 | if dut.reset == 0: 33 | break 34 | 35 | for i in range(64): 36 | wr_val = (i+1) 37 | print(f'[Py] writing: {wr_val}') 38 | await init_bfm.write(0x8000_0000+(4*i), wr_val) 39 | rd = await init_bfm.read(0x8000_0000+(4*i)) 40 | print(f'[Py] readback: {rd}') 41 | assert wr_val == rd 42 | 43 | for i in range(64): 44 | await init_bfm.read(0x8000_0000+(4*i)) 45 | 46 | pass 47 | -------------------------------------------------------------------------------- /examples/call/cocotb/call_sv_bfm/clean.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | rm -rf __pycache__ obj_dir work transcript hdl_call_if_api.svh example_venv *.vstf results.xml 4 | -------------------------------------------------------------------------------- /examples/call/cocotb/call_sv_bfm/init_venv.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | example_dir=$(dirname $(realpath $0)) 4 | rundir=`pwd` 5 | 6 | pyhdl_if_dir=$(cd ${example_dir}/../../../../; pwd) 7 | 8 | 9 | interp_bindir=$rundir/example_venv/bin 10 | interp=$interp_bindir/python 11 | if test ! -d $rundir/example_venv; then 12 | echo "Creating Python virtual environment" 13 | python3 -m venv $rundir/example_venv 14 | $interp -m pip install --upgrade pip 15 | $interp -m pip install pcpp cxxheaderparser ivpm 16 | $interp ${pyhdl_if_dir}/scripts/gen_py_if.py # TODO: might be wrong place for this, but was needed after before install 17 | $interp -m pip install -e ${pyhdl_if_dir} cocotb 18 | $interp -m pip install -r $example_dir/requirements.txt 19 | fi 20 | -------------------------------------------------------------------------------- /examples/call/cocotb/call_sv_bfm/requirements.txt: -------------------------------------------------------------------------------- 1 | 2 | pyhdl-if 3 | cocotb 4 | -------------------------------------------------------------------------------- /examples/call/cocotb/call_sv_bfm/runit_mti.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | example_dir=$(dirname $(realpath $0)) 4 | proj_dir=$(cd ${example_dir}/../../../.. ; pwd) 5 | rundir=`pwd` 6 | 7 | use_project_venv=0 8 | 9 | if test $use_project_venv -eq 1; then 10 | interp_bindir=${proj_dir}/packages/python/bin 11 | interp=$interp_bindir/python 12 | export PYTHONPATH=${proj_dir}/src:${example_dir} 13 | else 14 | interp_bindir=$rundir/example_venv/bin 15 | interp=$interp_bindir/python 16 | if test ! -d $rundir/example_venv; then 17 | echo "Creating Python virtual environment" 18 | python3 -m venv $rundir/example_venv 19 | $interp -m pip install --upgrade pip 20 | $interp -m pip install -r $example_dir/requirements.txt 21 | fi 22 | export PYTHONPATH=${example_dir} 23 | fi 24 | #export PYTHONPATH=${proj_dir}/src:${example_dir}:${PYTHONPATH} 25 | 26 | source ${interp_bindir}/activate 27 | 28 | 29 | 30 | cocotb_libdir=$(${interp_bindir}/cocotb-config --lib-dir) 31 | libpython=$(${interp_bindir}/cocotb-config --libpython) 32 | 33 | hdl_if_libs=$(${interp} -c "import hdl_if ; print(' '.join(hdl_if.libs('vpi')))") 34 | hdl_if_share=$(${interp} -c "import hdl_if ; print(hdl_if.share())") 35 | 36 | echo "hdl_if_libs=${hdl_if_libs}" 37 | echo "libpython=${libpython}" 38 | 39 | vsim_args="" 40 | for lib in ${hdl_if_libs}; do 41 | lib=$(echo $lib | sed -e 's/\.so//') 42 | vsim_args="$vsim_args -sv_lib ${lib}" 43 | done 44 | 45 | # Generate the Wrapper API 46 | ${interp} -m hdl_if api-gen-sv -m call_sv_bfm 47 | if test $? -ne 0; then exit 1; fi 48 | 49 | vlib work 50 | vlog -sv \ 51 | +incdir+${hdl_if_share}/dpi \ 52 | ${hdl_if_share}/dpi/pyhdl_if.sv \ 53 | ${example_dir}/call_sv_bfm.sv 54 | if test $? -ne 0; then exit 1; fi 55 | 56 | #export PATH=${interp_bindir}:${PATH} 57 | 58 | # This appears to 59 | export LD_LIBRARY_PATH=$(dirname ${libpython}):${LD_LIBRARY_PATH} 60 | export MODULE=call_sv_bfm 61 | 62 | echo "PYTHON: `which python3`" 63 | 64 | #valgrind="-valgrind --tool=memcheck" 65 | 66 | vsim -c -do "run -a; quit -f" call_sv_bfm -dpioutoftheblue 1 \ 67 | ${valgrind} \ 68 | -pli ${cocotb_libdir}/libcocotbvpi_modelsim.so \ 69 | ${vsim_args} 70 | #vsim -c -do "run -a; quit -f" call_sv_bfm \ 71 | # -valgrind --tool=memcheck \ 72 | # ${vsim_args} 73 | 74 | 75 | 76 | -------------------------------------------------------------------------------- /examples/call/cocotb/call_sv_bfm/runit_vlt.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | example_dir=$(dirname $(realpath $0)) 4 | rundir=`pwd` 5 | 6 | interp_bindir=$rundir/example_venv/bin 7 | interp=$interp_bindir/python 8 | if test ! -d $rundir/example_venv; then 9 | echo "Creating Python virtual environment" 10 | python3 -m venv $rundir/example_venv 11 | $interp -m pip install --upgrade pip 12 | $interp -m pip install -r $example_dir/requirements.txt 13 | fi 14 | 15 | cocotb_libdir=$(${interp_bindir}/cocotb-config --lib-dir) 16 | libpython=$(${interp_bindir}/cocotb-config --libpython) 17 | 18 | hdl_if_libs=$(${interp} -c "import hdl_if ; print(' '.join(hdl_if.libs('dpi')))") 19 | hdl_if_share=$(${interp} -c "import hdl_if ; print(hdl_if.share())") 20 | 21 | echo "hdl_if_libs=${hdl_if_libs}" 22 | 23 | vlt_args="" 24 | for lib in ${hdl_if_libs}; do 25 | # dir=$(dirname $lib) 26 | # lib=$(echo $(basename $lib) | sed -e 's/\.so//' -e 's/^lib//g') 27 | vlt_args="$vlt_args -LDFLAGS $lib" 28 | done 29 | 30 | # Generate the Wrapper API 31 | PYTHONPATH=`pwd` ${interp} -m hdl_if api-gen-sv -m call_sv_bfm 32 | if test $? -ne 0; then exit 1; fi 33 | 34 | echo "[RUN CMD] verilator --binary --vpi -Wno-fatal -LDFLAGS -export-dynamic \ 35 | +incdir+${hdl_if_share}/dpi \ 36 | ${hdl_if_share}/dpi/pyhdl_if.sv \ 37 | $example_dir/call_sv_bfm.sv ${vlt_args}" 38 | 39 | verilator --binary --vpi -Wno-fatal -LDFLAGS -export-dynamic \ 40 | +incdir+${hdl_if_share}/dpi \ 41 | ${hdl_if_share}/dpi/pyhdl_if.sv \ 42 | $example_dir/call_sv_bfm.sv ${vlt_args} 43 | if test $? -ne 0; then exit 1; fi 44 | 45 | export PATH=${interp_bindir}:${PATH} 46 | export PYTHONPATH=$example_dir:${PYTHONPATH} 47 | export LD_LIBRARY_PATH=$(dirname ${libpython}):${LD_LIBRARY_PATH} 48 | export MODULE=call_sv_bfm 49 | 50 | ./obj_dir/Vpyhdl_if 51 | -------------------------------------------------------------------------------- /examples/call/cocotb/call_sv_bfm/runit_xcm.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | example_dir=$(dirname $(realpath $0)) 4 | proj_dir=$(cd ${example_dir}/../../../.. ; pwd) 5 | rundir=`pwd` 6 | 7 | use_project_venv=1 8 | 9 | if test $use_project_venv -eq 1; then 10 | interp_bindir=${proj_dir}/packages/python/bin 11 | interp=$interp_bindir/python 12 | export PYTHONPATH=${proj_dir}/src:${example_dir} 13 | else 14 | interp_bindir=$rundir/example_venv/bin 15 | interp=$interp_bindir/python 16 | if test ! -d $rundir/example_venv; then 17 | echo "Creating Python virtual environment" 18 | python3 -m venv $rundir/example_venv 19 | $interp -m pip install --upgrade pip 20 | $interp -m pip install -r $example_dir/requirements.txt 21 | fi 22 | export PYTHONPATH=${example_dir} 23 | fi 24 | 25 | source ${interp_bindir}/activate 26 | 27 | 28 | cocotb_libdir=$(${interp_bindir}/cocotb-config --lib-dir) 29 | libpython=$(${interp_bindir}/cocotb-config --libpython) 30 | 31 | hdl_if_libs=$(${interp} -c "import hdl_if ; print(' '.join(hdl_if.libs('vpi')))") 32 | hdl_if_share=$(${interp} -c "import hdl_if ; print(hdl_if.share())") 33 | 34 | echo "hdl_if_libs=${hdl_if_libs}" 35 | echo "libpython=${libpython}" 36 | 37 | xmsim_args="" 38 | for lib in ${hdl_if_libs}; do 39 | lib=$(echo $lib | sed -e 's/\.so//') 40 | xmsim_args="$xmsim_args -sv_lib ${lib}" 41 | done 42 | 43 | # Generate the Wrapper API 44 | ${interp} -m hdl_if api-gen-sv -m call_sv_bfm 45 | if test $? -ne 0; then exit 1; fi 46 | 47 | xmvlog -64bit -sv +define+VERILATOR \ 48 | +incdir+${hdl_if_share}/dpi \ 49 | ${hdl_if_share}/dpi/pyhdl_if.sv \ 50 | ${example_dir}/call_sv_bfm.sv 51 | if test $? -ne 0; then exit 1; fi 52 | 53 | xmelab -64bit -snap call_sv_bfm:snap call_sv_bfm \ 54 | -access +rwc -createdebugdb 55 | 56 | # This appears to 57 | export LD_LIBRARY_PATH=$(dirname ${libpython}):${LD_LIBRARY_PATH} 58 | export MODULE=call_sv_bfm 59 | 60 | echo "PYTHON: `which python3`" 61 | 62 | #valgrind="-valgrind --tool=memcheck" 63 | 64 | xmsim -64bit call_sv_bfm:snap \ 65 | -loadvpi ${cocotb_libdir}/libcocotbvpi_ius.so:vlog_startup_routines_bootstrap \ 66 | ${xmsim_args} 67 | 68 | 69 | 70 | 71 | -------------------------------------------------------------------------------- /examples/call/dpi/call_sv_bfm/call_sv_bfm.py: -------------------------------------------------------------------------------- 1 | 2 | import ctypes as ct 3 | import hdl_if as hif 4 | 5 | @hif.api 6 | class WishboneInitiator(object): 7 | 8 | @hif.imp 9 | async def write(self, addr : ct.c_uint32, data : ct.c_uint32): 10 | pass 11 | 12 | @hif.imp 13 | async def read(self, addr : ct.c_uint32) -> ct.c_uint32: 14 | pass 15 | 16 | @hif.api 17 | class Test(object): 18 | 19 | @hif.exp 20 | async def run(self, bfm : ct.py_object): 21 | print("run") 22 | 23 | for i in range(64): 24 | wr_val = (i+1) 25 | print(f'[Py] writing: {wr_val}') 26 | await bfm.write(0x8000_0000+(4*i), wr_val) 27 | rd = await bfm.read(0x8000_0000+(4*i)) 28 | print(f'[Py] readback: {rd}') 29 | assert wr_val == rd 30 | 31 | for i in range(64): 32 | await bfm.read(0x8000_0000+(4*i)) 33 | 34 | pass 35 | -------------------------------------------------------------------------------- /examples/call/dpi/call_sv_bfm/call_sv_bfm.sv: -------------------------------------------------------------------------------- 1 | 2 | module call_sv_bfm; 3 | import pyhdl_if::*; 4 | import call_sv_bfm_pkg::*; 5 | 6 | reg clk = 0; 7 | reg reset = 1; 8 | initial begin 9 | 10 | clk = 0; 11 | forever begin 12 | #10ns; 13 | clk = ~clk; 14 | end 15 | end 16 | 17 | wire[31:0] dat_r, dat_w; 18 | wire[31:0] adr; 19 | wire stb, cyc, we; 20 | reg ack_r; 21 | wire ack = (ack_r && cyc && stb); 22 | 23 | always @(posedge clk or reset) begin 24 | if (reset) begin 25 | ack_r <= 1'b0; 26 | end else begin 27 | ack_r <= (stb & cyc); 28 | end 29 | end 30 | 31 | assign dat_r = dat_w; 32 | 33 | WishboneInitiatorBFM init_bfm( 34 | .clock(clk), 35 | .reset(reset), 36 | .adr(adr), 37 | .dat_r(dat_r), 38 | .dat_w(dat_w), 39 | .stb(stb), 40 | .cyc(cyc), 41 | .ack(ack), 42 | .we(we) 43 | ); 44 | 45 | initial begin 46 | automatic Test test; 47 | 48 | pyhdl_if_start(); 49 | 50 | #50ns; 51 | reset = 0; 52 | 53 | // Create an instance of the Test class and run 54 | $display("%0t --> run", $time); 55 | test = new(); 56 | test.run(init_bfm.m_api_obj.m_obj); 57 | $display("%0t <-- run", $time); 58 | $finish; 59 | end 60 | 61 | endmodule 62 | -------------------------------------------------------------------------------- /examples/call/dpi/call_sv_bfm/clean.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | rm -rf __pycache__ obj_dir work transcript hdl_call_if_api.svh example_venv *.vstf results.xml 4 | rm -rf *.vcd call_sv_bfm_pkg.sv 5 | -------------------------------------------------------------------------------- /examples/call/dpi/call_sv_bfm/init_venv.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | example_dir=$(dirname $(realpath $0)) 4 | rundir=`pwd` 5 | 6 | pyhdl_if_dir=$(cd ${example_dir}/../../../../; pwd) 7 | 8 | 9 | interp_bindir=$rundir/example_venv/bin 10 | interp=$interp_bindir/python 11 | if test ! -d $rundir/example_venv; then 12 | echo "Creating Python virtual environment" 13 | python3 -m venv $rundir/example_venv 14 | $interp -m pip install --upgrade pip 15 | $interp -m pip install pcpp cxxheaderparser ivpm 16 | $interp ${pyhdl_if_dir}/scripts/gen_py_if.py # TODO: might be wrong place for this, but was needed after before install 17 | $interp -m pip install -e ${pyhdl_if_dir} cocotb 18 | $interp -m pip install -r $example_dir/requirements.txt 19 | fi 20 | -------------------------------------------------------------------------------- /examples/call/dpi/call_sv_bfm/requirements.txt: -------------------------------------------------------------------------------- 1 | 2 | pyhdl-if 3 | cocotb 4 | -------------------------------------------------------------------------------- /examples/call/dpi/call_sv_bfm/runit_mti.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -x 2 | 3 | example_dir=$(dirname $(realpath $0)) 4 | proj_dir=$(cd ${example_dir}/../../../.. ; pwd) 5 | rundir=`pwd` 6 | 7 | use_project_venv=0 8 | 9 | if test $use_project_venv -eq 1; then 10 | interp_bindir=${proj_dir}/packages/python/bin 11 | interp=$interp_bindir/python 12 | export PYTHONPATH=${proj_dir}/src:${example_dir} 13 | else 14 | interp_bindir=$rundir/example_venv/bin 15 | interp=$interp_bindir/python 16 | if test ! -d $rundir/example_venv; then 17 | echo "Creating Python virtual environment" 18 | python3 -m venv $rundir/example_venv 19 | $interp -m pip install --upgrade pip 20 | $interp -m pip install -r $example_dir/requirements.txt 21 | fi 22 | export PYTHONPATH=${example_dir} 23 | fi 24 | #export PYTHONPATH=${proj_dir}/src:${example_dir}:${PYTHONPATH} 25 | 26 | source ${interp_bindir}/activate 27 | 28 | hdl_if_libs=$(${interp} -c "import hdl_if ; print(' '.join(hdl_if.libs()))") 29 | hdl_if_share=$(${interp} -c "import hdl_if ; print(hdl_if.share())") 30 | 31 | echo "hdl_if_libs=${hdl_if_libs}" 32 | echo "libpython=${libpython}" 33 | 34 | vsim_args="" 35 | for lib in ${hdl_if_libs}; do 36 | lib=$(echo $lib | sed -e 's/\.so//') 37 | vsim_args="$vsim_args -sv_lib ${lib}" 38 | done 39 | 40 | # Generate the Wrapper API 41 | ${interp} -m hdl_if api-gen-sv -m call_sv_bfm \ 42 | -p call_sv_bfm_pkg -o call_sv_bfm_pkg.sv 43 | if test $? -ne 0; then exit 1; fi 44 | 45 | vlib work 46 | vlog -sv \ 47 | +incdir+${hdl_if_share}/dpi \ 48 | ${hdl_if_share}/dpi/pyhdl_if.sv \ 49 | ${example_dir}/call_sv_bfm_pkg.sv \ 50 | ${example_dir}/wb_init_bfm.sv \ 51 | ${example_dir}/call_sv_bfm.sv 52 | if test $? -ne 0; then exit 1; fi 53 | 54 | #valgrind="-valgrind --tool=memcheck" 55 | 56 | vsim -64 -c -do "run -a; quit -f" call_sv_bfm -dpioutoftheblue 1 \ 57 | ${valgrind} \ 58 | ${vsim_args} 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /examples/call/dpi/call_sv_bfm/runit_vcs.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | example_dir=$(dirname $(realpath $0)) 4 | rundir=`pwd` 5 | 6 | if test -z $PYTHON; then 7 | interp_bindir=$rundir/example_venv/bin 8 | interp=$interp_bindir/python 9 | if test ! -d $rundir/example_venv; then 10 | echo "Creating Python virtual environment" 11 | zoltar-python -m venv $rundir/example_venv 12 | $interp -m pip install --upgrade pip 13 | $interp -m pip install -r $example_dir/requirements.txt 14 | fi 15 | else 16 | interp=${PYTHON} 17 | fi 18 | 19 | #cocotb_libdir=$(${interp_bindir}/cocotb-config --lib-dir) 20 | #libpython=$(${interp_bindir}/cocotb-config --libpython) 21 | 22 | hdl_if_libs=$(${interp} -c "import hdl_if ; print(' '.join(hdl_if.libs()))") 23 | hdl_if_share=$(${interp} -c "import hdl_if ; print(hdl_if.share())") 24 | 25 | echo "hdl_if_libs=${hdl_if_libs}" 26 | 27 | simv_args="" 28 | for lib in ${hdl_if_libs}; do 29 | dir=$(dirname $lib) 30 | # lib=$(echo $(basename $lib) | sed -e 's/\.so//' -e 's/^lib//g') 31 | lib=$(echo $(basename $lib) | sed -e 's/\.so//') 32 | simv_args="$simv_args -sv_lib $dir/$lib" 33 | done 34 | 35 | # Generate the Wrapper API 36 | PYTHONPATH=$(pwd):${PYTHONPATH} ${interp} -m hdl_if api-gen-sv -m call_sv_bfm \ 37 | -p call_sv_bfm_pkg -o call_sv_bfm_pkg.sv 38 | if test $? -ne 0; then exit 1; fi 39 | 40 | echo "[RUN CMD] verilator --binary -Wno-fatal -LDFLAGS -export-dynamic \ 41 | +incdir+${hdl_if_share}/dpi \ 42 | ${hdl_if_share}/dpi/pyhdl_if.sv \ 43 | $example_dir/call_sv_bfm.sv ${vlt_args}" 44 | 45 | vcs -full64 -sverilog -timescale=1ns/1ns \ 46 | +incdir+${hdl_if_share}/dpi \ 47 | ${hdl_if_share}/dpi/pyhdl_if.sv \ 48 | ${example_dir}/call_sv_bfm_pkg.sv \ 49 | ${example_dir}/wb_init_bfm.sv \ 50 | ${example_dir}/call_sv_bfm.sv 51 | if test $? -ne 0; then exit 1; fi 52 | 53 | export PATH=${interp_bindir}:${PATH} 54 | export PYTHONPATH=$example_dir:${PYTHONPATH} 55 | export LD_LIBRARY_PATH=$(dirname ${libpython}):${LD_LIBRARY_PATH} 56 | export MODULE=call_sv_bfm 57 | 58 | ./simv ${simv_args} 59 | 60 | 61 | -------------------------------------------------------------------------------- /examples/call/dpi/call_sv_bfm/runit_vlt.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | example_dir=$(dirname $(realpath $0)) 4 | rundir=`pwd` 5 | 6 | if test -z $PYTHON; then 7 | interp_bindir=$rundir/example_venv/bin 8 | interp=$interp_bindir/python 9 | if test ! -d $rundir/example_venv; then 10 | echo "Creating Python virtual environment" 11 | python3 -m venv $rundir/example_venv 12 | $interp -m pip install --upgrade pip 13 | $interp -m pip install -r $example_dir/requirements.txt 14 | fi 15 | else 16 | interp=${PYTHON} 17 | fi 18 | 19 | #cocotb_libdir=$(${interp_bindir}/cocotb-config --lib-dir) 20 | #libpython=$(${interp_bindir}/cocotb-config --libpython) 21 | 22 | hdl_if_libs=$(${interp} -c "import hdl_if ; print(' '.join(hdl_if.libs()))") 23 | hdl_if_share=$(${interp} -c "import hdl_if ; print(hdl_if.share())") 24 | 25 | echo "hdl_if_libs=${hdl_if_libs}" 26 | 27 | vlt_args="" 28 | for lib in ${hdl_if_libs}; do 29 | # dir=$(dirname $lib) 30 | # lib=$(echo $(basename $lib) | sed -e 's/\.so//' -e 's/^lib//g') 31 | vlt_args="$vlt_args -LDFLAGS $lib" 32 | done 33 | 34 | # Generate the Wrapper API 35 | PYTHONPATH=$(pwd):${PYTHONPATH} ${interp} -m hdl_if api-gen-sv -m call_sv_bfm \ 36 | -p call_sv_bfm_pkg -o call_sv_bfm_pkg.sv 37 | if test $? -ne 0; then exit 1; fi 38 | 39 | echo "[RUN CMD] verilator --binary -Wno-fatal -LDFLAGS -export-dynamic \ 40 | +incdir+${hdl_if_share}/dpi \ 41 | ${hdl_if_share}/dpi/pyhdl_if.sv \ 42 | $example_dir/call_sv_bfm.sv ${vlt_args}" 43 | 44 | verilator --binary -Wno-fatal -LDFLAGS -export-dynamic \ 45 | +incdir+${hdl_if_share}/dpi \ 46 | ${hdl_if_share}/dpi/pyhdl_if.sv \ 47 | ${example_dir}/call_sv_bfm_pkg.sv \ 48 | ${example_dir}/wb_init_bfm.sv \ 49 | ${example_dir}/call_sv_bfm.sv ${vlt_args} 50 | if test $? -ne 0; then exit 1; fi 51 | 52 | export PATH=${interp_bindir}:${PATH} 53 | export PYTHONPATH=$example_dir:${PYTHONPATH} 54 | export LD_LIBRARY_PATH=$(dirname ${libpython}):${LD_LIBRARY_PATH} 55 | export MODULE=call_sv_bfm 56 | 57 | ./obj_dir/Vpyhdl_if 58 | -------------------------------------------------------------------------------- /examples/call/dpi/call_sv_bfm/runit_xcm.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | example_dir=$(dirname $(realpath $0)) 4 | proj_dir=$(cd ${example_dir}/../../../.. ; pwd) 5 | rundir=`pwd` 6 | 7 | use_project_venv=1 8 | 9 | if test $use_project_venv -eq 1; then 10 | interp_bindir=${proj_dir}/packages/python/bin 11 | interp=$interp_bindir/python 12 | export PYTHONPATH=${proj_dir}/src:${example_dir} 13 | else 14 | interp_bindir=$rundir/example_venv/bin 15 | interp=$interp_bindir/python 16 | if test ! -d $rundir/example_venv; then 17 | echo "Creating Python virtual environment" 18 | python3 -m venv $rundir/example_venv 19 | $interp -m pip install --upgrade pip 20 | $interp -m pip install -r $example_dir/requirements.txt 21 | fi 22 | export PYTHONPATH=${example_dir} 23 | fi 24 | 25 | source ${interp_bindir}/activate 26 | 27 | hdl_if_libs=$(${interp} -c "import hdl_if ; print(' '.join(hdl_if.libs()))") 28 | hdl_if_share=$(${interp} -c "import hdl_if ; print(hdl_if.share())") 29 | 30 | echo "hdl_if_libs=${hdl_if_libs}" 31 | echo "libpython=${libpython}" 32 | 33 | xmsim_args="" 34 | for lib in ${hdl_if_libs}; do 35 | lib=$(echo $lib | sed -e 's/\.so//') 36 | xmsim_args="$xmsim_args -sv_lib ${lib}" 37 | done 38 | 39 | # Generate the Wrapper API 40 | ${interp} -m hdl_if api-gen-sv -m call_sv_bfm \ 41 | -p call_sv_bfm_pkg -o call_sv_bfm_pkg.sv 42 | if test $? -ne 0; then exit 1; fi 43 | 44 | xmvlog -64bit -sv \ 45 | +incdir+${hdl_if_share}/dpi \ 46 | ${hdl_if_share}/dpi/pyhdl_if.sv \ 47 | ${example_dir}/call_sv_bfm_pkg.sv 48 | ${example_dir}/wb_init_bfm.sv 49 | ${example_dir}/call_sv_bfm.sv 50 | if test $? -ne 0; then exit 1; fi 51 | 52 | xmelab -64bit -snap call_sv_bfm:snap call_sv_bfm -createdebugdb 53 | 54 | xmsim -64bit call_sv_bfm:snap ${xmsim_args} 55 | 56 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /examples/call/dpi/sv2py2sv_call/clean.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | rm -rf __pycache__ obj_dir work transcript MyC.svh 4 | -------------------------------------------------------------------------------- /examples/call/dpi/sv2py2sv_call/runit.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | script_dir=$(dirname $(realpath $0)) 4 | proj_dir=$(cd ../../../../ ; pwd) 5 | export PYTHONPATH=${proj_dir}/python:${script_dir}:${PYTHONPATH} 6 | 7 | share=$(python3 -c 'import hdl_if; print(hdl_if.share())') 8 | libs=$(python3 -c 'import hdl_if; print(" ".join(hdl_if.libs()))') 9 | python_libdir=$(python3 -c 'import sysconfig, os; print(os.path.join(sysconfig.get_config_var("installed_platbase"), "lib"))') 10 | 11 | python3 -m hdl_if api-gen-sv -m sv2py2sv_call -o MyC.svh 12 | if test $? -ne 0; then exit 1; fi 13 | 14 | verilator --binary \ 15 | +incdir+${share}/dpi \ 16 | ${share}/dpi/pyhdl_if.sv \ 17 | sv2py2sv_call.sv -top sv2py2sv_call \ 18 | ${libs} -LDFLAGS -Wl,--export-dynamic -LDFLAGS -Wl,-rpath,${python_libdir} 19 | if test $? -ne 0; then exit 1; fi 20 | 21 | ./obj_dir/Vsv2py2sv_call 22 | 23 | -------------------------------------------------------------------------------- /examples/call/dpi/sv2py2sv_call/runit_mti.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | script_dir=$(dirname $(realpath $0)) 4 | proj_dir=$(cd ${script_dir}/../../../.. ; pwd) 5 | export PYTHONPATH=${proj_dir}/python:${script_dir}:${PYTHONPATH} 6 | 7 | share=$(python3 -c 'import hdl_if; print(hdl_if.share())') 8 | libs=$(python3 -c 'import hdl_if; print(" ".join(hdl_if.libs()))') 9 | python_libdir=$(python3 -c 'import sysconfig, os; print(os.path.join(sysconfig.get_config_var("installed_platbase"), "lib"))') 10 | 11 | python3 -m hdl_if api-gen-sv -m sv2py2sv_call -o MyC.svh 12 | if test $? -ne 0; then exit 1; fi 13 | 14 | flags="" 15 | 16 | for lib in $libs; do 17 | lib=`echo $lib | sed -e 's/\.so//g'` 18 | flags="$flags -sv_lib $lib" 19 | done 20 | 21 | echo "flags: ${flags}" 22 | 23 | vlib work 24 | vlog -sv \ 25 | +incdir+${share}/dpi \ 26 | ${share}/dpi/pyhdl_if.sv \ 27 | sv2py2sv_call.sv 28 | if test $? -ne 0; then exit 1; fi 29 | 30 | vsim -c -do "run -a; quit -f" sv2py2sv_call ${flags} 31 | if test $? -ne 0; then exit 1; fi 32 | 33 | 34 | -------------------------------------------------------------------------------- /examples/call/dpi/sv2py2sv_call/sv2py2sv_call.py: -------------------------------------------------------------------------------- 1 | import ctypes 2 | import hdl_if as hif 3 | 4 | @hif.api 5 | class MyC(object): 6 | 7 | @hif.expfunc 8 | def countones(self, vi : ctypes.c_int) -> ctypes.c_int: 9 | ret = 0 10 | v = vi 11 | while v: 12 | ret += 1 if (v&1) else 0 13 | v >>= 1 14 | return ret * self.getscale(vi) 15 | 16 | @hif.impfunc 17 | def getscale(self, v : ctypes.c_int) -> ctypes.c_int: 18 | pass 19 | 20 | -------------------------------------------------------------------------------- /examples/call/dpi/sv2py2sv_call/sv2py2sv_call.sv: -------------------------------------------------------------------------------- 1 | package pkg; 2 | import pyhdl_if::*; 3 | `include "MyC.svh" 4 | 5 | class MyCImpl extends MyC; 6 | virtual function int getscale(int v); 7 | $display("getscale: %0d", v); 8 | return v+1; 9 | endfunction 10 | endclass 11 | 12 | endpackage 13 | 14 | module sv2py2sv_call; 15 | import pyhdl_if::*; 16 | import pkg::*; 17 | 18 | initial begin 19 | automatic MyCImpl c = new(); 20 | 21 | for (int i=0; i<256; i++) begin 22 | automatic int res = c.countones(i); 23 | $display("res: %0d %0d", i, res); 24 | end 25 | end 26 | endmodule 27 | 28 | 29 | -------------------------------------------------------------------------------- /examples/call/dpi/sv2py2sv_tc_call/clean.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | rm -rf __pycache__ obj_dir work transcript MyC.svh 4 | -------------------------------------------------------------------------------- /examples/call/dpi/sv2py2sv_tc_call/runit.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | script_dir=$(dirname $(realpath $0)) 4 | proj_dir=$(cd ../../../.. ; pwd) 5 | export PYTHONPATH=${proj_dir}/python:${script_dir}:${PYTHONPATH} 6 | 7 | share=$(python3 -c 'import hdl_if; print(hdl_if.share())') 8 | libs=$(python3 -c 'import hdl_if; print(" ".join(hdl_if.libs()))') 9 | python_libdir=$(python3 -c 'import sysconfig, os; print(os.path.join(sysconfig.get_config_var("installed_platbase"), "lib"))') 10 | 11 | python3 -m hdl_if api-gen-sv -m sv2py2sv_tc_call -o MyC.svh 12 | if test $? -ne 0; then exit 1; fi 13 | 14 | echo "share_call: '${share_call}'" 15 | 16 | verilator --binary \ 17 | +incdir+${share}/dpi \ 18 | ${share}/dpi/pyhdl_if.sv \ 19 | sv2py2sv_tc_call.sv -top sv2py2sv_tc_call \ 20 | ${libs} -LDFLAGS -Wl,--export-dynamic -LDFLAGS -Wl,-rpath,${python_libdir} 21 | if test $? -ne 0; then exit 1; fi 22 | 23 | time ./obj_dir/Vsv2py2sv_tc_call 24 | 25 | -------------------------------------------------------------------------------- /examples/call/dpi/sv2py2sv_tc_call/runit_mti.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | script_dir=$(dirname $(realpath $0)) 4 | proj_dir=$(cd ../../../.. ; pwd) 5 | export PYTHONPATH=${proj_dir}/python:${script_dir}:${PYTHONPATH} 6 | 7 | share=$(python3 -c 'import hdl_if; print(hdl_if.share())') 8 | libs=$(python3 -c 'import hdl_if; print(" ".join(hdl_if.libs()))') 9 | python_libdir=$(python3 -c 'import sysconfig, os; print(os.path.join(sysconfig.get_config_var("installed_platbase"), "lib"))') 10 | 11 | echo "libs: ${libs}" 12 | 13 | vsim_args="" 14 | for lib in $libs; do 15 | lib=`echo $lib | sed -e 's/\.so//g'` 16 | vsim_args="$vsim_args -sv_lib $lib" 17 | done 18 | 19 | python3 -m hdl_if api-gen-sv -m sv2py2sv_tc_call -o MyC.svh 20 | if test $? -ne 0; then exit 1; fi 21 | 22 | echo "share_call: '${share_call}'" 23 | 24 | vlib work 25 | vlog -sv \ 26 | +incdir+${share}/dpi \ 27 | ${share}/dpi/pyhdl_if.sv \ 28 | sv2py2sv_tc_call.sv 29 | if test $? -ne 0; then exit 1; fi 30 | 31 | time vsim -c -do "run -a; quit -f" sv2py2sv_tc_call ${vsim_args} 32 | if test $? -ne 0; then exit 1; fi 33 | 34 | 35 | -------------------------------------------------------------------------------- /examples/call/dpi/sv2py2sv_tc_call/sv2py2sv_tc_call.py: -------------------------------------------------------------------------------- 1 | import ctypes 2 | import hdl_if as hif 3 | 4 | @hif.api 5 | class MyC(object): 6 | 7 | @hif.exptask 8 | async def body(self): 9 | exp = [0]*100 10 | for i in range(100): 11 | await self.write(4*i, exp[i]) 12 | for i in range(100): 13 | val = await self.read(i) 14 | 15 | @hif.imptask 16 | async def read(self, addr : ctypes.c_uint) -> ctypes.c_uint: 17 | pass 18 | 19 | @hif.imptask 20 | async def write(self, addr : ctypes.c_uint, data : ctypes.c_uint): 21 | pass 22 | 23 | 24 | -------------------------------------------------------------------------------- /examples/call/dpi/sv2py2sv_tc_call/sv2py2sv_tc_call.sv: -------------------------------------------------------------------------------- 1 | package pkg; 2 | import pyhdl_if::*; 3 | `include "MyC.svh" 4 | 5 | class MyCImpl extends MyC; 6 | virtual task read( 7 | output int unsigned retval, 8 | input int unsigned addr); 9 | #1ns; 10 | $display("read: 0x%08h", addr); 11 | retval = addr+1; 12 | endtask 13 | 14 | virtual task write( 15 | input int unsigned addr, 16 | input int unsigned data); 17 | #1ns; 18 | $display("write: %0d %0d",addr, data); 19 | endtask 20 | endclass 21 | 22 | class SeqImpl extends MyC; 23 | 24 | virtual task read( 25 | output int unsigned retval, 26 | input int unsigned addr); 27 | // ... 28 | endtask 29 | 30 | virtual task write( 31 | input int unsigned addr, 32 | input int unsigned data); 33 | // ... 34 | endtask 35 | endclass 36 | 37 | endpackage 38 | 39 | module sv2py2sv_tc_call; 40 | import pyhdl_if::*; 41 | import pkg::*; 42 | 43 | initial begin 44 | automatic MyCImpl c = new(); 45 | 46 | pyhdl_pi_if_start(); 47 | 48 | c.body(); 49 | end 50 | 51 | endmodule 52 | 53 | 54 | -------------------------------------------------------------------------------- /examples/call/dpi/sv2py_call/clean.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | rm -rf __pycache__ obj_dir work transcript MyC.svh 4 | -------------------------------------------------------------------------------- /examples/call/dpi/sv2py_call/runit_mti.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | script_dir=$(dirname $(realpath $0)) 4 | proj_dir=$(cd ${script_dir}/../../../../ ; pwd) 5 | export PYTHONPATH=${proj_dir}/python:${script_dir}:${PYTHONPATH} 6 | 7 | echo "PYTHONPATH=${PYTHONPATH}" 8 | 9 | share=$(python3 -c 'import hdl_if; print(hdl_if.share())') 10 | libs=$(python3 -c 'import hdl_if; print(" ".join(hdl_if.libs()))') 11 | python_libdir=$(python3 -c 'import sysconfig, os; print(os.path.join(sysconfig.get_config_var("installed_platbase"), "lib"))') 12 | 13 | python3 -m hdl_if api-gen-sv -m sv2py_call -o MyC.svh 14 | if test $? -ne 0; then exit 1; fi 15 | 16 | echo "share: '${share}'" 17 | echo "python_libdir: ${python_libdir}" 18 | echo "libs: ${libs}" 19 | 20 | vsim_args="" 21 | for lib in ${libs}; do 22 | lib=$(echo $lib | sed -e 's/.so//g') 23 | vsim_args="${vsim_args} -sv_lib ${lib}" 24 | done 25 | 26 | vlib work 27 | 28 | vlog -sv \ 29 | +incdir+${share}/dpi \ 30 | ${share}/dpi/pyhdl_if.sv \ 31 | sv2py_call.sv 32 | if test $? -ne 0; then exit 1; fi 33 | 34 | vopt -o sv2py_call_opt sv2py_call 35 | if test $? -ne 0; then exit 1; fi 36 | 37 | vsim -batch -do "run -a; quit -f" sv2py_call_opt ${vsim_args} 38 | if test $? -ne 0; then exit 1; fi 39 | 40 | 41 | -------------------------------------------------------------------------------- /examples/call/dpi/sv2py_call/runit_vlt.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | script_dir=$(dirname $(realpath $0)) 4 | proj_dir=$(cd ${script_dir}/../../../../ ; pwd) 5 | export PYTHONPATH=${proj_dir}/python:${script_dir}:${PYTHONPATH} 6 | 7 | echo "PYTHONPATH=${PYTHONPATH}" 8 | 9 | share=$(python3 -c 'import hdl_if; print(hdl_if.share())') 10 | libs=$(python3 -c 'import hdl_if; print(" ".join(hdl_if.libs()))') 11 | python_libdir=$(python3 -c 'import sysconfig, os; print(os.path.join(sysconfig.get_config_var("installed_platbase"), "lib"))') 12 | 13 | python3 -m hdl_if api-gen-sv -m sv2py_call -o MyC.svh 14 | if test $? -ne 0; then exit 1; fi 15 | 16 | echo "share: '${share}'" 17 | echo "python_libdir: ${python_libdir}" 18 | echo "libs: ${libs}" 19 | 20 | verilator --binary -Wno-fatal \ 21 | +incdir+${share}/dpi \ 22 | ${share}/dpi/pyhdl_if.sv \ 23 | sv2py_call.sv -top sv2py_call \ 24 | ${libs} -LDFLAGS -Wl,--export-dynamic -LDFLAGS -Wl,-rpath,${python_libdir} 25 | if test $? -ne 0; then exit 1; fi 26 | 27 | ./obj_dir/Vsv2py_call 28 | 29 | -------------------------------------------------------------------------------- /examples/call/dpi/sv2py_call/sv2py_call.py: -------------------------------------------------------------------------------- 1 | import ctypes 2 | import hdl_if as hif 3 | 4 | @hif.api 5 | class MyC(object): 6 | 7 | @hif.exp 8 | def countones(self, v : ctypes.c_int) -> ctypes.c_int: 9 | ret = 0 10 | while v: 11 | ret += 1 if (v&1) else 0 12 | v >>= 1 13 | return ret 14 | 15 | -------------------------------------------------------------------------------- /examples/call/dpi/sv2py_call/sv2py_call.sv: -------------------------------------------------------------------------------- 1 | package pkg; 2 | import pyhdl_if::*; 3 | `include "MyC.svh" 4 | 5 | endpackage 6 | 7 | module sv2py_call; 8 | import pyhdl_if::*; 9 | import pkg::*; 10 | 11 | initial begin 12 | automatic MyC c = new(); 13 | 14 | for (int i=0; i<256; i++) begin 15 | automatic int res = c.countones(i); 16 | $display("res: %0d %0d", i, res); 17 | end 18 | end 19 | endmodule 20 | 21 | 22 | -------------------------------------------------------------------------------- /examples/call/dpi/sv2pyext_call/runit.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | script_dir=$(dirname $(realpath $0)) 4 | proj_dir=$(cd ${script_dir}/../../../.. ; pwd) 5 | export PYTHONPATH=${proj_dir}/python:${script_dir}:${PYTHONPATH} 6 | 7 | share=$(python3 -c 'import hdl_if; print(hdl_if.share())') 8 | libs=$(python3 -c 'import hdl_if; print(" ".join(hdl_if.libs()))') 9 | python_libdir=$(python3 -c 'import sysconfig, os; print(os.path.join(sysconfig.get_config_var("installed_platbase"), "lib"))') 10 | 11 | python3 -m hdl_if api-gen-sv -m sv2py_call -o MyC.svh 12 | if test $? -ne 0; then exit 1; fi 13 | 14 | echo "share_call: '${share_call}'" 15 | 16 | verilator --binary \ 17 | +incdir+${share}/dpi \ 18 | ${share}/dpi/pyhdl_if.sv \ 19 | sv2py_call.sv -top sv2py_call \ 20 | ${libs} -LDFLAGS -Wl,--export-dynamic -LDFLAGS -Wl,-rpath,${python_libdir} 21 | if test $? -ne 0; then exit 1; fi 22 | 23 | ./obj_dir/Vsv2py_call 24 | 25 | -------------------------------------------------------------------------------- /examples/call/dpi/sv2pyext_call/sv2py_call.py: -------------------------------------------------------------------------------- 1 | import ctypes 2 | import hdl_if as hif 3 | 4 | @hif.api 5 | class MyC(object): 6 | 7 | @hif.expfunc 8 | def countones(self, v : ctypes.c_int) -> ctypes.c_int: 9 | ret = 0 10 | while v: 11 | ret += 1 if (v&1) else 0 12 | v >>= 1 13 | return ret 14 | 15 | class MyCExt(MyC): 16 | 17 | def countones(self, v): 18 | print("MyCExt.countones", flush=True) 19 | return super().countones(v) + 5 20 | -------------------------------------------------------------------------------- /examples/call/dpi/sv2pyext_call/sv2py_call.sv: -------------------------------------------------------------------------------- 1 | package pkg; 2 | import pyhdl_if::*; 3 | `include "MyC.svh" 4 | 5 | endpackage 6 | 7 | module sv2py_call; 8 | import pyhdl_if::*; 9 | import pkg::*; 10 | 11 | initial begin 12 | // Create an instance of the extended class, then wrap 13 | // it with the wrapper 14 | automatic MyC_wrap c = new(MyC_wrap#()::create_pyobj(.clsname("MyCExt"))); 15 | 16 | for (int i=0; i<256; i++) begin 17 | int res = c.countones(i); 18 | $display("res: %0d %0d", i, res); 19 | end 20 | end 21 | endmodule 22 | 23 | 24 | -------------------------------------------------------------------------------- /examples/pi/dpi/call_python/call_python.py: -------------------------------------------------------------------------------- 1 | 2 | def countones(v): 3 | ones = 0 4 | 5 | while v: 6 | if v & 1: 7 | ones += 1 8 | v >>= 1 9 | 10 | return ones 11 | 12 | -------------------------------------------------------------------------------- /examples/pi/dpi/call_python/call_python.sv: -------------------------------------------------------------------------------- 1 | 2 | module call_python(); 3 | import pyhdl_if::*; 4 | 5 | initial begin 6 | automatic PyObject call_python_m = PyImport_ImportModule("call_python"); 7 | automatic PyObject countones = PyObject_GetAttrString(call_python_m, "countones"); 8 | 9 | begin 10 | for (int i=0; i<256; i++) begin 11 | automatic PyObject res_o, args; 12 | automatic int res; 13 | 14 | args = PyTuple_New(1); 15 | void'(PyTuple_SetItem(args, 0, PyLong_FromLong(i))); 16 | res_o = PyObject_Call(countones, args, null); 17 | res = PyLong_AsLong(res_o); 18 | if (res != $countones(i)) begin 19 | $display("Error: "); 20 | end else begin 21 | $display("Pass: %0d == %0d", res, $countones(i)); 22 | end 23 | end 24 | end 25 | 26 | $finish; 27 | end 28 | endmodule 29 | -------------------------------------------------------------------------------- /examples/pi/dpi/call_python/clean.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | rm -rf obj_dir __pycache__ 4 | 5 | -------------------------------------------------------------------------------- /examples/pi/dpi/call_python/runit.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | script_dir=$(dirname $(realpath $0)) 4 | pyhdl_if_dir=$(cd $script_dir/../../../.. ; pwd) 5 | 6 | export PYTHONPATH=$pyhdl_if_dir/python:${script_dir}:${PYTHONPATH} 7 | 8 | share=$(python3 -c 'import hdl_if; print(hdl_if.share())') 9 | libs=$(python3 -c 'import hdl_if; print(" ".join(hdl_if.libs()))') 10 | 11 | echo "libs: ${libs}" 12 | 13 | verilator --binary \ 14 | +incdir+${share}/dpi \ 15 | ${share}/dpi/pyhdl_if.sv \ 16 | call_python.sv \ 17 | -top call_python ${libs} -LDFLAGS -Wl,--export-dynamic 18 | if test $? -ne 0; then exit 1; fi 19 | 20 | ./obj_dir/Vcall_python 21 | 22 | -------------------------------------------------------------------------------- /examples/pi/dpi/call_python/runit_mti.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | script_dir=$(dirname $(realpath $0)) 4 | pyhdl_if_dir=$(cd $script_dir/../../../.. ; pwd) 5 | 6 | export PYTHONPATH=$pyhdl_if_dir/src:${script_dir}:${PYTHONPATH} 7 | 8 | share=$(python3 -c 'import hdl_if; print(hdl_if.share())') 9 | libs=$(python3 -c 'import hdl_if; print(" ".join(hdl_if.libs("vpi")))') 10 | 11 | echo "libs: ${libs}" 12 | 13 | vsim_args="" 14 | for lib in ${libs}; do 15 | lib=$(echo $lib | sed -e 's/.so//g') 16 | vsim_args="$vsim_args -sv_lib ${lib}" 17 | done 18 | 19 | vlib work 20 | vlog -sv \ 21 | +incdir+${share}/dpi \ 22 | ${share}/dpi/pyhdl_if.sv \ 23 | call_python.sv 24 | if test $? -ne 0; then exit 1; fi 25 | 26 | vsim -c -do "run -a; quit -f" call_python ${vsim_args} 27 | if test $? -ne 0; then exit 1; fi 28 | 29 | 30 | -------------------------------------------------------------------------------- /examples/pi/vpi/call_proto/call_proto.py: -------------------------------------------------------------------------------- 1 | import hdl_if as hif 2 | 3 | def countones(v): 4 | ones = 0 5 | print("countones: v=%d" % v, flush=True) 6 | 7 | while v: 8 | if v & 1: 9 | ones += 1 10 | v >>= 1 11 | 12 | return ones 13 | 14 | async def main(call_if): 15 | print("main", flush=True) 16 | 17 | print("--> invoked", flush=True) 18 | res = await call_if._proxy.invoke_hdl_t("put", ()) 19 | print("<-- invoked (%s)" % str(res), flush=True) 20 | 21 | print("--> invoked", flush=True) 22 | await call_if._proxy.invoke_hdl_t("put", ()) 23 | print("<-- invoked", flush=True) 24 | 25 | print("--> invoked", flush=True) 26 | await call_if._proxy.invoke_hdl_t("put", ()) 27 | print("<-- invoked", flush=True) 28 | pass 29 | 30 | @hif.api 31 | class CallIF(object): 32 | pass 33 | 34 | def init(call_if): 35 | from hdl_if.backend import Backend 36 | print("init", flush=True) 37 | be = Backend.inst() 38 | be.mkTask(main(call_if)) 39 | 40 | def info(obj): 41 | print("info: %s" % str(obj), flush=True) 42 | print("type: %s" % str(type(obj))) 43 | for f in dir(obj): 44 | print("Field: %s" % f, flush=True) 45 | print("id: %d" % obj.id, flush=True) 46 | -------------------------------------------------------------------------------- /examples/pi/vpi/call_proto/cast.py: -------------------------------------------------------------------------------- 1 | import ctypes 2 | 3 | class MyC(object): 4 | def doit(self): 5 | print("Hello from doit") 6 | pass 7 | 8 | obj = MyC() 9 | 10 | py_obj = ctypes.py_object(obj).value 11 | #i_obj = ctypes.cast(obj, ctypes.c_void_p).value 12 | i_obj = id(py_obj) 13 | 14 | print("i_obj: 0x%08x" % i_obj) 15 | 16 | obj2 = ctypes.cast(i_obj, ctypes.py_object).value 17 | #py_obj2 = ctypes.py_object(py_obj2_v) 18 | 19 | #print("py_obj2: %s" % str(py_obj2)) 20 | print("obj2: %s" % str(obj2)) 21 | obj2.doit() 22 | 23 | -------------------------------------------------------------------------------- /examples/pi/vpi/call_proto/clean.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | rm -rf obj_dir __pycache__ call_python *.vstf transcript work 4 | 5 | -------------------------------------------------------------------------------- /examples/pi/vpi/call_proto/runit_iverilog.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | script_dir=$(dirname $(realpath $0)) 4 | pyhdl_if_dir=$(cd $script_dir/../../../.. ; pwd) 5 | 6 | export PYTHONPATH=$pyhdl_if_dir/python:${script_dir}:${PYTHONPATH} 7 | 8 | share=$(python3 -c 'import hdl_if; print(hdl_if.share())') 9 | libs=$(python3 -c 'import hdl_if; print(" ".join(hdl_if.libs("vpi")))') 10 | 11 | vvp_args="" 12 | for l in $libs; do 13 | vvp_args="$vvp_args -m $l" 14 | done 15 | 16 | echo "libs: ${libs}" 17 | 18 | iverilog -g2005-sv call_proto.sv -s call_proto -o call_proto.vvp 19 | if test $? -ne 0; then exit 1; fi 20 | 21 | #valgrind --tool=memcheck vvp $vvp_args call_proto 22 | vvp $vvp_args call_proto.vvp 23 | 24 | -------------------------------------------------------------------------------- /examples/pi/vpi/call_proto/runit_mti.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -x 2 | 3 | script_dir=$(dirname $(realpath $0)) 4 | pyhdl_if_dir=$(cd $script_dir/../../../.. ; pwd) 5 | 6 | export PYTHONPATH=$pyhdl_if_dir/python:${script_dir}:${PYTHONPATH} 7 | 8 | share=$(python3 -c 'import hdl_if; print(hdl_if.share())') 9 | libs=$(python3 -c 'import hdl_if; print(" ".join(hdl_if.libs("vpi")))') 10 | 11 | vsim_args="" 12 | for l in $libs; do 13 | vsim_args="$vsim_args -pli $l" 14 | done 15 | 16 | echo "vsim_args: ${vsim_args}" 17 | 18 | vlib work 19 | vlog -sv call_proto.sv 20 | if test $? -ne 0; then exit 1; fi 21 | 22 | vsim -batch -do "run -a; quit -f" $vsim_args call_proto 23 | if test $? -ne 0; then exit 1; fi 24 | 25 | 26 | -------------------------------------------------------------------------------- /examples/pi/vpi/call_python/call_python.py: -------------------------------------------------------------------------------- 1 | 2 | def countones(v): 3 | ones = 0 4 | print("countones: v=%d" % v, flush=True) 5 | 6 | while v: 7 | if v & 1: 8 | ones += 1 9 | v >>= 1 10 | 11 | return ones 12 | 13 | -------------------------------------------------------------------------------- /examples/pi/vpi/call_python/call_python.sv: -------------------------------------------------------------------------------- 1 | 2 | module call_python(); 3 | 4 | initial begin 5 | static reg[63:0] call_python_m; 6 | static reg[63:0] countones; 7 | static int i; 8 | 9 | call_python_m = $PyImport_ImportModule("call_python"); 10 | $display("call_python_m: %08h", call_python_m); 11 | countones = $PyObject_GetAttrString(call_python_m, "countones"); 12 | $display("countones: %08h", countones); 13 | 14 | begin : vars 15 | static reg[63:0] res_o; 16 | static int res; 17 | static reg[63:0] args, lval; 18 | 19 | $display("countones: %08h", countones); 20 | 21 | for (i=0; i<256; i=i+1) begin 22 | args = $PyTuple_New(1); 23 | res = $PyTuple_SetItem(args, 0, $PyLong_FromLong(i)); 24 | $display("res: %0d", res); 25 | res_o = $PyObject_Call(countones, args, 0); 26 | $display("res_o: %08h", res_o); 27 | res = $PyLong_AsLong(res_o); 28 | if (res != $countones(i)) begin 29 | $display("Error: res=%0d countones=%0d", res, $countones(i)); 30 | end else begin 31 | $display("Pass: %0d == %0d", res, $countones(i)); 32 | end 33 | end 34 | end 35 | 36 | $finish; 37 | end 38 | endmodule 39 | -------------------------------------------------------------------------------- /examples/pi/vpi/call_python/clean.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | rm -rf obj_dir __pycache__ call_python 4 | 5 | -------------------------------------------------------------------------------- /examples/pi/vpi/call_python/runit_iverilog.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -x 2 | 3 | script_dir=$(dirname $(realpath $0)) 4 | pyhdl_if_dir=$(cd $script_dir/../../../.. ; pwd) 5 | 6 | export PYTHONPATH=$pyhdl_if_dir/python:${script_dir}:${PYTHONPATH} 7 | 8 | share=$(python3 -c 'import hdl_if; print(hdl_if.share())') 9 | libs=$(python3 -c 'import hdl_if; print(" ".join(hdl_if.libs("vpi")))') 10 | 11 | vvp_args="" 12 | for l in $libs; do 13 | vvp_args="$vvp_args -m $l" 14 | done 15 | 16 | echo "libs: ${libs}" 17 | 18 | iverilog -g2005-sv call_python.sv -s call_python -o call_python 19 | if test $? -ne 0; then exit 1; fi 20 | 21 | gdb --args vvp $vvp_args call_python 22 | 23 | -------------------------------------------------------------------------------- /examples/pi/vpi/call_python/runit_mti.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -x 2 | 3 | script_dir=$(dirname $(realpath $0)) 4 | pyhdl_if_dir=$(cd $script_dir/../../../.. ; pwd) 5 | 6 | export PYTHONPATH=$pyhdl_if_dir/python:${script_dir}:${PYTHONPATH} 7 | 8 | share=$(python3 -c 'import hdl_if; print(hdl_if.share())') 9 | libs=$(python3 -c 'import hdl_if; print(" ".join(hdl_if.libs("vpi")))') 10 | 11 | vsim_args="" 12 | for l in $libs; do 13 | vsim_args="$vsim_args -pli $l" 14 | done 15 | 16 | echo "vsim_args: ${vsim_args}" 17 | 18 | vlib work 19 | vlog -sv call_python.sv 20 | if test $? -ne 0; then exit 1; fi 21 | 22 | vsim -batch -do "run -a; quit -f" $vsim_args call_python 23 | if test $? -ne 0; then exit 1; fi 24 | 25 | 26 | -------------------------------------------------------------------------------- /examples/tlm/req_rsp_loopback/.gitignore: -------------------------------------------------------------------------------- 1 | ReqRspLoopback.sv 2 | 3 | -------------------------------------------------------------------------------- /examples/tlm/req_rsp_loopback/clean.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | rm -rf __pycache__ obj_dir work transcript MyC.svh *.vcd ReqRspLoopback.sv 4 | -------------------------------------------------------------------------------- /examples/tlm/req_rsp_loopback/req_rsp_loopback.py: -------------------------------------------------------------------------------- 1 | import ctypes 2 | import hdl_if as hif 3 | 4 | @hif.tlm_if 5 | class ReqRspLoopback(object): 6 | 7 | class ReqData(ctypes.Structure): 8 | _fields_ = [ 9 | ('data', ctypes.c_uint), 10 | ] 11 | 12 | class RspData(ctypes.Structure): 13 | _fields_ = [ 14 | ('data', ctypes.c_uint), 15 | ] 16 | 17 | @hif.req_fifo 18 | def req(self, t : ReqData): 19 | pass 20 | 21 | @hif.rsp_fifo 22 | def rsp(self) -> RspData: 23 | pass 24 | 25 | # req2 = ReqRspLoopback.ReqData.from_buffer_copy(bytes(req)) 26 | 27 | async def main(): 28 | from hdl_if.tlm import StreamRgy 29 | rgy = StreamRgy.inst() 30 | 31 | await rgy.initialize() 32 | 33 | ifc_name = rgy.get_interfaces()[0] 34 | print("ifc: %s" % ifc_name) 35 | 36 | ifc = ReqRspLoopback() 37 | await rgy.connect_if(ifc, ifc_name, False) 38 | 39 | req = ReqRspLoopback.ReqData() 40 | req.data = 0x03020100 41 | 42 | for _ in range(16): 43 | print("--> req", flush=True) 44 | await ifc.req(req) 45 | print("<-- req", flush=True) 46 | 47 | rsp = await ifc.rsp() 48 | print("rsp: 0x%08x" % rsp.data) 49 | print("done", flush=True) 50 | 51 | req.data += 1 52 | pass 53 | 54 | def init(): 55 | print("init", flush=True) 56 | 57 | backend = hif.Backend.inst() 58 | 59 | backend.mkTask(main()) 60 | 61 | -------------------------------------------------------------------------------- /examples/tlm/req_rsp_loopback/runit.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | script_dir=$(dirname $(realpath $0)) 4 | proj_dir=$(cd ../../.. ; pwd) 5 | export PYTHONPATH=${proj_dir}/python:${script_dir}:${PYTHONPATH} 6 | 7 | share=$(python3 -c 'import hdl_if; print(hdl_if.share())') 8 | libs=$(python3 -c 'import hdl_if; print(" ".join(hdl_if.libs()))') 9 | python_libdir=$(python3 -c 'import sysconfig, os; print(os.path.join(sysconfig.get_config_var("installed_platbase"), "lib"))') 10 | 11 | python3 -m hdl_if ifc-gen-sv -m req_rsp_loopback ReqRspLoopback -o ReqRspLoopback.sv 12 | if test $? -ne 0; then exit 1; fi 13 | 14 | echo "share_call: '${share_call}'" 15 | 16 | verilator --binary \ 17 | +incdir+${share}/dpi \ 18 | ${share}/dpi/pyhdl_if.sv \ 19 | ${share}/dpi/tlm_hdl2hvl_fifo.sv \ 20 | ${share}/dpi/tlm_hvl2hdl_fifo.sv \ 21 | ReqRspLoopback.sv \ 22 | req_rsp_loopback.sv -top req_rsp_loopback \ 23 | ${libs} -LDFLAGS -Wl,--export-dynamic -LDFLAGS -Wl,-rpath,${python_libdir} 24 | if test $? -ne 0; then exit 1; fi 25 | 26 | #PYTHONMALLOC=malloc valgrind --tool=memcheck ./obj_dir/Vreq_rsp_loopback 27 | time ./obj_dir/Vreq_rsp_loopback 28 | #gdb --args ./obj_dir/Vreq_rsp_loopback 29 | 30 | -------------------------------------------------------------------------------- /examples/tlm/req_rsp_loopback/runit_icarus.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | script_dir=$(dirname $(realpath $0)) 4 | proj_dir=$(cd ../../.. ; pwd) 5 | export PYTHONPATH=${proj_dir}/python:${script_dir}:${PYTHONPATH} 6 | 7 | share=$(python3 -c 'import hdl_if; print(hdl_if.share())') 8 | libs=$(python3 -c 'import hdl_if; print(" ".join(hdl_if.libs("vpi")))') 9 | python_libdir=$(python3 -c 'import sysconfig, os; print(os.path.join(sysconfig.get_config_var("installed_platbase"), "lib"))') 10 | 11 | python3 -m hdl_if ifc-gen-sv -m req_rsp_loopback ReqRspLoopback -o ReqRspLoopback.sv 12 | if test $? -ne 0; then exit 1; fi 13 | 14 | vvp_args="" 15 | for l in $libs; do 16 | vvp_args="$vvp_args -m $l" 17 | done 18 | 19 | echo "share_call: '${share_call}'" 20 | 21 | iverilog \ 22 | ${share}/tlm_hdl2hvl_fifo.sv \ 23 | ${share}/tlm_hvl2hdl_fifo.sv \ 24 | ReqRspLoopback.sv \ 25 | req_rsp_loopback.sv -s req_rsp_loopback -o req_rsp_loopback.vvp 26 | if test $? -ne 0; then exit 1; fi 27 | 28 | #gdb --args vvp ${vvp_args} req_rsp_loopback.vvp 29 | valgrind --tool=memcheck vvp ${vvp_args} req_rsp_loopback.vvp 30 | 31 | 32 | -------------------------------------------------------------------------------- /examples/tlm/req_rsp_loopback/runit_mti.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | script_dir=$(dirname $(realpath $0)) 4 | proj_dir=$(cd ../../.. ; pwd) 5 | export PYTHONPATH=${proj_dir}/python:${script_dir}:${PYTHONPATH} 6 | 7 | share=$(python3 -c 'import hdl_if; print(hdl_if.share())') 8 | libs=$(python3 -c 'import hdl_if; print(" ".join(hdl_if.libs()))') 9 | python_libdir=$(python3 -c 'import sysconfig, os; print(os.path.join(sysconfig.get_config_var("installed_platbase"), "lib"))') 10 | 11 | python3 -m hdl_if ifc-gen-sv -m req_rsp_loopback ReqRspLoopback -o ReqRspLoopback.sv 12 | if test $? -ne 0; then exit 1; fi 13 | 14 | echo "share_call: '${share_call}'" 15 | 16 | vsim_args="" 17 | for lib in $libs; do 18 | lib=`echo $lib | sed -e 's/\.so//g'` 19 | vsim_args="$vsim_args -sv_lib $lib" 20 | done 21 | 22 | vlib work 23 | vlog -sv \ 24 | +incdir+${share}/dpi \ 25 | ${share}/dpi/pyhdl_if.sv \ 26 | ${share}/dpi/tlm_hdl2hvl_fifo.sv \ 27 | ${share}/dpi/tlm_hvl2hdl_fifo.sv \ 28 | ReqRspLoopback.sv \ 29 | req_rsp_loopback.sv 30 | time vsim -c -do "run -a; quit -f" req_rsp_loopback ${vsim_args} 31 | if test $? -ne 0; then exit 1; fi 32 | 33 | 34 | -------------------------------------------------------------------------------- /examples/tlm/reqrsp_loopback/.gitignore: -------------------------------------------------------------------------------- 1 | ReqRspLoopback.sv 2 | 3 | -------------------------------------------------------------------------------- /examples/tlm/reqrsp_loopback/clean.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | rm -rf __pycache__ obj_dir work transcript MyC.svh *.vcd ReqRspLoopback.sv *.vvp 4 | -------------------------------------------------------------------------------- /examples/tlm/reqrsp_loopback/reqrsp_loopback.py: -------------------------------------------------------------------------------- 1 | import ctypes 2 | import hdl_if as hif 3 | 4 | @hif.tlm_if 5 | class ReqRspLoopback(object): 6 | 7 | class ReqData(ctypes.Structure): 8 | _fields_ = [ 9 | ('data', ctypes.c_uint), 10 | ] 11 | 12 | class RspData(ctypes.Structure): 13 | _fields_ = [ 14 | ('data', ctypes.c_uint), 15 | ] 16 | 17 | @hif.reqrsp_fifo 18 | def req(self, t : ReqData) -> RspData: 19 | pass 20 | 21 | 22 | # req2 = ReqRspLoopback.ReqData.from_buffer_copy(bytes(req)) 23 | 24 | async def main(): 25 | from hdl_if.tlm import StreamRgy 26 | rgy = StreamRgy.inst() 27 | 28 | await rgy.initialize() 29 | 30 | ifc_name = rgy.get_interfaces()[0] 31 | print("ifc: %s" % ifc_name) 32 | 33 | ifc = ReqRspLoopback() 34 | await rgy.connect_if(ifc, ifc_name, False) 35 | 36 | req = ReqRspLoopback.ReqData() 37 | req.data = 0x03020100 38 | 39 | for _ in range(16): 40 | print("--> req", flush=True) 41 | resp = await ifc.req(req) 42 | print("<-- req", flush=True) 43 | 44 | print("done", flush=True) 45 | 46 | req.data += 1 47 | pass 48 | 49 | def init(): 50 | print("init", flush=True) 51 | 52 | backend = hif.Backend.inst() 53 | 54 | backend.mkTask(main()) 55 | 56 | -------------------------------------------------------------------------------- /examples/tlm/reqrsp_loopback/runit_icarus.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | script_dir=$(dirname $(realpath $0)) 4 | proj_dir=$(cd ../../.. ; pwd) 5 | export PYTHONPATH=${proj_dir}/src:${script_dir}:${PYTHONPATH} 6 | 7 | share=$(python3 -c 'import hdl_if; print(hdl_if.share())') 8 | libs=$(python3 -c 'import hdl_if; print(" ".join(hdl_if.libs("vpi")))') 9 | python_libdir=$(python3 -c 'import sysconfig, os; print(os.path.join(sysconfig.get_config_var("installed_platbase"), "lib"))') 10 | 11 | python3 -m hdl_if ifc-gen-sv -m reqrsp_loopback ReqRspLoopback -o ReqRspLoopback.sv 12 | if test $? -ne 0; then exit 1; fi 13 | 14 | vvp_args="" 15 | for l in $libs; do 16 | vvp_args="$vvp_args -m $l" 17 | done 18 | 19 | echo "share_call: '${share_call}'" 20 | 21 | iverilog \ 22 | ${share}/pyhdl_if_req_fifo.sv \ 23 | ${share}/pyhdl_if_rsp_fifo.sv \ 24 | ${share}/pyhdl_if_reqrsp_fifo.sv \ 25 | ReqRspLoopback.sv \ 26 | reqrsp_loopback.sv -s reqrsp_loopback -o reqrsp_loopback.vvp 27 | if test $? -ne 0; then exit 1; fi 28 | 29 | #gdb --args vvp ${vvp_args} req_rsp_loopback.vvp 30 | valgrind --tool=memcheck vvp ${vvp_args} reqrsp_loopback.vvp 31 | 32 | 33 | -------------------------------------------------------------------------------- /examples/tlm/reqrsp_loopback/runit_mti.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | script_dir=$(dirname $(realpath $0)) 4 | proj_dir=$(cd ../../.. ; pwd) 5 | export PYTHONPATH=${proj_dir}/src:${script_dir}:${PYTHONPATH} 6 | 7 | share=$(python3 -c 'import hdl_if; print(hdl_if.share())') 8 | libs=$(python3 -c 'import hdl_if; print(" ".join(hdl_if.libs()))') 9 | python_libdir=$(python3 -c 'import sysconfig, os; print(os.path.join(sysconfig.get_config_var("installed_platbase"), "lib"))') 10 | 11 | python3 -m hdl_if ifc-gen-sv -m reqrsp_loopback ReqRspLoopback -o ReqRspLoopback.sv 12 | if test $? -ne 0; then exit 1; fi 13 | 14 | echo "share_call: '${share_call}'" 15 | 16 | vsim_args="" 17 | for lib in $libs; do 18 | lib=`echo $lib | sed -e 's/\.so//g'` 19 | vsim_args="$vsim_args -sv_lib $lib" 20 | done 21 | 22 | vlib work 23 | vlog -sv \ 24 | +incdir+${share}/dpi \ 25 | ${share}/dpi/pyhdl_if.sv \ 26 | ${share}/pyhdl_if_req_fifo.sv \ 27 | ${share}/pyhdl_if_reqrsp_fifo.sv \ 28 | ${share}/pyhdl_if_rsp_fifo.sv \ 29 | ReqRspLoopback.sv \ 30 | reqrsp_loopback.sv 31 | vopt -o reqrsp_loopback_opt -debug reqrsp_loopback 32 | time vsim -c -do "run -a; quit -f" reqrsp_loopback_opt ${vsim_args} 33 | if test $? -ne 0; then exit 1; fi 34 | 35 | 36 | -------------------------------------------------------------------------------- /ivpm.yaml: -------------------------------------------------------------------------------- 1 | 2 | package: 3 | name: pyhdl-if 4 | 5 | # setup-deps: 6 | #- ninja 7 | #- cython 8 | 9 | dep-sets: 10 | 11 | - name: default 12 | deps: 13 | - name: pytest 14 | src: pypi 15 | - name: pytypeworks 16 | src: pypi 17 | - name: pyvsc-dataclasses 18 | src: pypi 19 | - name: fusesoc 20 | src: pypi 21 | 22 | - name: default-dev 23 | deps: 24 | - name: pytest 25 | src: pypi 26 | - name: pytest-fv 27 | url: https://github.com/fvutils/pytest-fv.git 28 | - name: pytypeworks 29 | url: https://github.com/mballance-utils/pytypeworks.git 30 | - name: pyvsc-dataclasses 31 | url: https://github.com/vsc-tools/pyvsc-dataclasses.git 32 | - name: fusesoc 33 | url: https://github.com/olofk/fusesoc.git 34 | - name: iverilog 35 | url: https://github.com/pss-hands-on/iverilog-bin/releases/download/v12.0/iverilog-maylinux2014-12.0.tar.gz 36 | - name: verilator 37 | url: https://github.com/pss-hands-on/verilator-bin/releases/download/v5.030/verilator-ubuntu-x64-5.030.tar.gz 38 | - name: pcpp 39 | src: pypi 40 | - name: cxxheaderparser 41 | src: pypi 42 | - name: sphinx 43 | src: pypi 44 | - name: breathe 45 | src: pypi 46 | - name: sphinx-rtd-theme 47 | src: pypi 48 | - name: cairosvg 49 | src: pypi 50 | - name: sphinx-argparse 51 | src: pypi 52 | - name: packaging 53 | src: pypi 54 | - name: cocotb 55 | src: pypi 56 | - name: doxygen-filter-sv 57 | url: https://github.com/SeanOBoyle/DoxygenFilterSystemVerilog.git 58 | 59 | env: 60 | - name: PATH 61 | path-prepend: 62 | - ${IVPM_PACKAGES}/verilator/bin 63 | - ${IVPM_PACKAGES}/iverilog/bin 64 | - name: PYTHONPATH 65 | path-prepend: ${IVPM_PROJECT}/src 66 | 67 | paths: 68 | project: 69 | lib-dirs: 70 | - src/hdl_if/share 71 | 72 | -------------------------------------------------------------------------------- /scripts/build_linux.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh -x 2 | 3 | echo "BUILD_NUM=${BUILD_NUM}" >> src/hdl_if/__build_num__.py 4 | ${IVPM_PYTHON} -m pip install ivpm cython 5 | if test $? -ne 0; then exit 1; fi 6 | 7 | ${IVPM_PYTHON} -m ivpm update -a 8 | if test $? -ne 0; then exit 1; fi 9 | 10 | PYTHON=./packages/python/bin/python 11 | ${PYTHON} -m pip install twine auditwheel ninja wheel cython 12 | if test $? -ne 0; then exit 1; fi 13 | 14 | ${PYTHON} setup.py bdist_wheel 15 | if test $? -ne 0; then exit 1; fi 16 | 17 | for whl in dist/*.whl; do 18 | ${PYTHON} -m auditwheel repair $whl 19 | if test $? -ne 0; then exit 1; fi 20 | rm $whl 21 | done -------------------------------------------------------------------------------- /src/hdl_if/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | import ctypes 3 | import os 4 | import platform 5 | 6 | from .hdl_services import HdlServices 7 | from .backend import Backend 8 | from .decorators import * 9 | from .hdl_obj_rgy import * 10 | 11 | # class s_vpi_vlog_info(ctypes.Structure): 12 | # _fields_ = [ 13 | # ('argc', ctypes.c_int32), 14 | # ('argv', ctypes.POINTER(ctypes.c_char_p)), 15 | # ('product', ctypes.c_char_p), 16 | # ('version', ctypes.c_char_p) 17 | # ] 18 | 19 | 20 | # print("hdl_if.init", flush=True) 21 | 22 | # def init(): 23 | # print("::init", flush=True) 24 | 25 | # try: 26 | # exe = ctypes.cdll.LoadLibrary(None) 27 | 28 | # exe.vpi_get_vlog_info.restype = ctypes.c_int 29 | # exe.vpi_get_vlog_info.argtypes = [ 30 | # ctypes.POINTER(s_vpi_vlog_info) 31 | # ] 32 | 33 | # info = s_vpi_vlog_info() 34 | # exe.vpi_get_vlog_info(ctypes.pointer(info)) 35 | 36 | # print("argc: %d" % info.argc) 37 | 38 | # except Exception as e: 39 | # print("Exception: %s" % str(e)) 40 | 41 | def vpi_init(): 42 | import hdl_if.vpi as vpi 43 | print("::vpi_init") 44 | vpi.vpi_init() 45 | 46 | # def dpi_init(scope): 47 | # import hdl_if.dpi as dpi 48 | # print("::dpi_init") 49 | 50 | # dpi.dpi_init(scope) 51 | 52 | def get_entry(): 53 | hdl_pi_if_dir = os.path.dirname(os.path.abspath(__file__)) 54 | libpref = "lib" 55 | dllext = ".so" 56 | if platform.system() == "Windows": 57 | libpref = "" 58 | dllext = ".dll" 59 | elif platform.system() == "Darwin": 60 | libpref = "lib" 61 | dllext = ".dylib" 62 | 63 | ext = os.path.join(hdl_pi_if_dir, "%spyhdl_if%s" % (libpref, dllext)) 64 | if os.path.isfile(ext): 65 | return ext 66 | else: 67 | raise Exception("Library path %s doesn't exist" % ext) 68 | 69 | def root(): 70 | pkg_dir = os.path.dirname(os.path.abspath(__file__)) 71 | return os.path.join(pkg_dir) 72 | 73 | def share(): 74 | pkg_dir = os.path.dirname(os.path.abspath(__file__)) 75 | return os.path.join(pkg_dir, "share") 76 | 77 | def libs(kind="dpi"): 78 | import sys 79 | import sysconfig 80 | 81 | lib_filename = get_entry() 82 | 83 | ret = [ lib_filename ] 84 | 85 | return ret 86 | 87 | @property 88 | def backend() -> Backend: 89 | return Backend.inst() 90 | -------------------------------------------------------------------------------- /src/hdl_if/backend.py: -------------------------------------------------------------------------------- 1 | #**************************************************************************** 2 | #* backend.py 3 | #* 4 | #* Copyright 2023 Matthew Ballance and Contributors 5 | #* 6 | #* Licensed under the Apache License, Version 2.0 (the "License"); you may 7 | #* not use this file except in compliance with the License. 8 | #* You may obtain a copy of the License at: 9 | #* 10 | #* http://www.apache.org/licenses/LICENSE-2.0 11 | #* 12 | #* Unless required by applicable law or agreed to in writing, software 13 | #* distributed under the License is distributed on an "AS IS" BASIS, 14 | #* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | #* See the License for the specific language governing permissions and 16 | #* limitations under the License. 17 | #* 18 | #* Created on: 19 | #* Author: 20 | #* 21 | #**************************************************************************** 22 | 23 | class Backend(object): 24 | 25 | _inst = None 26 | 27 | def __init__(self): 28 | pass 29 | 30 | def mkEvent(self): 31 | raise NotImplementedError("mkEvent (class %s)" % str(type(self))) 32 | 33 | def mkLock(self): 34 | raise NotImplementedError("mkLock (class %s)" % str(type(self))) 35 | 36 | def mkSemaphore(self): 37 | raise NotImplementedError("mkSemaphore (class %s)" % str(type(self))) 38 | 39 | def mkTask(self, coro): 40 | raise NotImplementedError("mkTask (class %s)" % str(type(self))) 41 | 42 | def idle(self): 43 | #raise NotImplementedError("idle (class %s)" % str(type(self))) 44 | print("idle", flush=True) 45 | 46 | def callCallback(self, cb): 47 | raise NotImplementedError("callCallback (class %s)" % str(type(self))) 48 | 49 | @classmethod 50 | def inst(cls, inst=None): 51 | if inst is not None: 52 | cls._inst = inst 53 | elif cls._inst is None: 54 | import sys 55 | if "cocotb" in sys.modules.keys(): 56 | from .impl.pi.backend_cocotb import BackendCocotb 57 | return BackendCocotb() 58 | else: 59 | from .impl.pi.backend_asyncio import BackendAsyncio 60 | return BackendAsyncio() 61 | 62 | return cls._inst 63 | 64 | -------------------------------------------------------------------------------- /src/hdl_if/call/call_proxy.py: -------------------------------------------------------------------------------- 1 | #**************************************************************************** 2 | #* call_proxy.py 3 | #* 4 | #* Copyright 2023 Matthew Ballance and Contributors 5 | #* 6 | #* Licensed under the Apache License, Version 2.0 (the "License"); you may 7 | #* not use this file except in compliance with the License. 8 | #* You may obtain a copy of the License at: 9 | #* 10 | #* http://www.apache.org/licenses/LICENSE-2.0 11 | #* 12 | #* Unless required by applicable law or agreed to in writing, software 13 | #* distributed under the License is distributed on an "AS IS" BASIS, 14 | #* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | #* See the License for the specific language governing permissions and 16 | #* limitations under the License. 17 | #* 18 | #* Created on: 19 | #* Author: 20 | #* 21 | #**************************************************************************** 22 | 23 | class CallProxy(object): 24 | 25 | def __init__(self, obj): 26 | self.target = obj 27 | pass 28 | 29 | async def invoke_hdl_t( 30 | self, 31 | method_name : str, 32 | args : tuple): 33 | raise NotImplementedError("CallProxy.invoke_hdl_t: %s" % str(type(self))) 34 | 35 | def invoke_hdl_f( 36 | self, 37 | method_name : str, 38 | args : tuple): 39 | raise NotImplementedError("CallProxy.invoke_hdl_f: %s" % str(type(self))) 40 | 41 | def response_hdl_t( 42 | self, 43 | call_id : int, 44 | result : object): 45 | """Called by the HDL environment to respond to a task call""" 46 | raise NotImplementedError("CallProxy.response_hdl_t: %s" % str(type(self))) 47 | 48 | def invoke_py_f( 49 | self, 50 | method_name : str, 51 | args : tuple) -> object: 52 | """Called by the HDL environment to invoke a Python function""" 53 | m = getattr(self.target, method_name, None) 54 | 55 | if m is None: 56 | raise Exception("Failed to find method %s" % method_name) 57 | 58 | return m(*args) 59 | 60 | def invoke_py_t( 61 | self, 62 | method_name : str, 63 | args : tuple) -> int: 64 | pass 65 | 66 | def response_py_t( 67 | self, 68 | call_id : int, 69 | result : object): 70 | raise NotImplementedError("CallProxy.response_py_t: %s" % str(type(self))) 71 | 72 | -------------------------------------------------------------------------------- /src/hdl_if/cmd/cmd_api_gen_sv.py: -------------------------------------------------------------------------------- 1 | #**************************************************************************** 2 | #* cmd_api_gen_sv.py 3 | #* 4 | #* Copyright 2023 Matthew Ballance and Contributors 5 | #* 6 | #* Licensed under the Apache License, Version 2.0 (the "License"); you may 7 | #* not use this file except in compliance with the License. 8 | #* You may obtain a copy of the License at: 9 | #* 10 | #* http://www.apache.org/licenses/LICENSE-2.0 11 | #* 12 | #* Unless required by applicable law or agreed to in writing, software 13 | #* distributed under the License is distributed on an "AS IS" BASIS, 14 | #* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | #* See the License for the specific language governing permissions and 16 | #* limitations under the License. 17 | #* 18 | #* Created on: 19 | #* Author: 20 | #* 21 | #**************************************************************************** 22 | import importlib 23 | import os 24 | from hdl_if.impl.call.gen_sv_class import GenSVClass 25 | from hdl_if.impl.call.api_def_rgy import ApiDefRgy 26 | 27 | class CmdApiGenSV(object): 28 | 29 | def __init__(self): 30 | pass 31 | 32 | def __call__(self, args): 33 | 34 | # First, load up the specified modules 35 | if not hasattr(args, "module") or args.module is None or len(args.module) == 0: 36 | raise Exception("Must specify modules to load") 37 | 38 | for m in args.module: 39 | try: 40 | importlib.import_module(m) 41 | except ImportError as e: 42 | raise Exception("Failed to import module \"%s\": %s" % ( 43 | m, str(e))) 44 | 45 | apis = ApiDefRgy.inst().getApis() 46 | 47 | if len(apis) == 0: 48 | raise Exception("No APIs defined") 49 | 50 | if os.path.dirname(args.output) != "" and not os.path.isdir(os.path.dirname(args.output)): 51 | os.makedirs(os.path.dirname(args.output)) 52 | 53 | with open(args.output, "w") as fp: 54 | gen = GenSVClass(fp, uvm=args.uvm) 55 | 56 | if args.package is not None: 57 | gen.println("package %s;" % args.package) 58 | gen.inc_ind() 59 | 60 | for api in apis: 61 | gen.gen(api) 62 | 63 | if args.package is not None: 64 | gen.dec_ind() 65 | gen.println("endpackage") 66 | 67 | pass 68 | 69 | -------------------------------------------------------------------------------- /src/hdl_if/cmd/cmd_ifc_gen_sv.py: -------------------------------------------------------------------------------- 1 | #**************************************************************************** 2 | #* cmd_ifc_gen_sv.py 3 | #* 4 | #* Copyright 2023 Matthew Ballance and Contributors 5 | #* 6 | #* Licensed under the Apache License, Version 2.0 (the "License"); you may 7 | #* not use this file except in compliance with the License. 8 | #* You may obtain a copy of the License at: 9 | #* 10 | #* http://www.apache.org/licenses/LICENSE-2.0 11 | #* 12 | #* Unless required by applicable law or agreed to in writing, software 13 | #* distributed under the License is distributed on an "AS IS" BASIS, 14 | #* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | #* See the License for the specific language governing permissions and 16 | #* limitations under the License. 17 | #* 18 | #* Created on: 19 | #* Author: 20 | #* 21 | #**************************************************************************** 22 | import importlib 23 | import os 24 | import traceback 25 | from hdl_if.impl.tlm.gen_ifc_sv import GenIfcSv 26 | from hdl_if.tlm.tlm_ifc_rgy import TlmIfcRgy 27 | 28 | class CmdIfcGenSv(object): 29 | 30 | def __init__(self): 31 | pass 32 | 33 | def __call__(self, args): 34 | # First, load up the specified modules 35 | if not hasattr(args, "module") or args.module is None or len(args.module) == 0: 36 | raise Exception("Must specify modules to load") 37 | 38 | for m in args.module: 39 | try: 40 | importlib.import_module(m) 41 | except ImportError as e: 42 | traceback.print_exception(e) 43 | raise Exception("Failed to import module \"%s\": %s" % ( 44 | m, str(e))) 45 | 46 | rgy = TlmIfcRgy.inst() 47 | 48 | if len(rgy.getTlmIfcs()) == 0: 49 | raise Exception("No interfaces defined") 50 | 51 | if args.output is None: 52 | raise Exception("Must specify output path") 53 | 54 | if os.path.dirname(args.output) != "" and not os.path.isdir(os.path.dirname(args.output)): 55 | os.makedirs(os.path.dirname(args.output)) 56 | 57 | with open(args.output, "w") as fp: 58 | gen = GenIfcSv(args.style in ("vl", "verilog")) 59 | 60 | for ifc in rgy.getTlmIfcs(): 61 | gen.gen_ifc_module(ifc, fp) 62 | 63 | 64 | pass 65 | 66 | -------------------------------------------------------------------------------- /src/hdl_if/cmd/cmd_ifc_gen_types.py: -------------------------------------------------------------------------------- 1 | #**************************************************************************** 2 | #* cmd_ifc_gen_types.py 3 | #* 4 | #* Copyright 2023 Matthew Ballance and Contributors 5 | #* 6 | #* Licensed under the Apache License, Version 2.0 (the "License"); you may 7 | #* not use this file except in compliance with the License. 8 | #* You may obtain a copy of the License at: 9 | #* 10 | #* http://www.apache.org/licenses/LICENSE-2.0 11 | #* 12 | #* Unless required by applicable law or agreed to in writing, software 13 | #* distributed under the License is distributed on an "AS IS" BASIS, 14 | #* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | #* See the License for the specific language governing permissions and 16 | #* limitations under the License. 17 | #* 18 | #* Created on: 19 | #* Author: 20 | #* 21 | #**************************************************************************** 22 | import importlib 23 | import os 24 | import traceback 25 | from hdl_if.impl.tlm.gen_ifc_types import GenIfcTypes 26 | from hdl_if.tlm.tlm_ifc_rgy import TlmIfcRgy 27 | 28 | class CmdIfcGenTypes(object): 29 | 30 | def __init__(self): 31 | pass 32 | 33 | def __call__(self, args): 34 | # First, load up the specified modules 35 | if not hasattr(args, "module") or args.module is None or len(args.module) == 0: 36 | raise Exception("Must specify modules to load") 37 | 38 | for m in args.module: 39 | try: 40 | importlib.import_module(m) 41 | except ImportError as e: 42 | traceback.print_exception(e) 43 | raise Exception("Failed to import module \"%s\": %s" % ( 44 | m, str(e))) 45 | 46 | rgy = TlmIfcRgy.inst() 47 | 48 | if len(rgy.getTlmIfcs()) == 0: 49 | raise Exception("No interfaces defined") 50 | 51 | if args.output is None: 52 | raise Exception("Must specify output path") 53 | 54 | if os.path.dirname(args.output) != "" and not os.path.isdir(os.path.dirname(args.output)): 55 | os.makedirs(os.path.dirname(args.output)) 56 | 57 | with open(args.output, "w") as fp: 58 | gen = GenIfcTypes() 59 | 60 | for ifc in rgy.getTlmIfcs(): 61 | gen.gen_ifc_module(ifc, fp) 62 | 63 | 64 | pass 65 | 66 | -------------------------------------------------------------------------------- /src/hdl_if/cmd/cmd_libs.py: -------------------------------------------------------------------------------- 1 | 2 | class CmdLibs(object): 3 | 4 | def __call__(self, args): 5 | from hdl_if import libs 6 | print(" ".join(libs())) 7 | -------------------------------------------------------------------------------- /src/hdl_if/cmd/cmd_share.py: -------------------------------------------------------------------------------- 1 | 2 | class CmdShare(object): 3 | 4 | def __call__(self, args): 5 | from hdl_if import share 6 | print(share()) 7 | 8 | -------------------------------------------------------------------------------- /src/hdl_if/hdl_services.py: -------------------------------------------------------------------------------- 1 | #**************************************************************************** 2 | #* hdl_services.py 3 | #* 4 | #* Copyright 2023 Matthew Ballance and Contributors 5 | #* 6 | #* Licensed under the Apache License, Version 2.0 (the "License"); you may 7 | #* not use this file except in compliance with the License. 8 | #* You may obtain a copy of the License at: 9 | #* 10 | #* http://www.apache.org/licenses/LICENSE-2.0 11 | #* 12 | #* Unless required by applicable law or agreed to in writing, software 13 | #* distributed under the License is distributed on an "AS IS" BASIS, 14 | #* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | #* See the License for the specific language governing permissions and 16 | #* limitations under the License. 17 | #* 18 | #* Created on: 19 | #* Author: 20 | #* 21 | #**************************************************************************** 22 | from typing import Dict, List 23 | 24 | class HdlServices(object): 25 | 26 | _impl : List['HdlServices'] = [] 27 | _impl_m : Dict[str, 'HdlServices'] = {} 28 | 29 | def __init__(self, name): 30 | self.name = name 31 | 32 | def registerTimeCB(self, cb : callable, time_ps : int) -> object: 33 | raise NotImplementedError("registerTimeCB for %s" % str(type(self))) 34 | 35 | @classmethod 36 | def registerServices(cls, services): 37 | cls._impl.append(services) 38 | cls._impl_m[services.name] = services 39 | 40 | @classmethod 41 | def inst(cls, name=None): 42 | if len(cls._impl) == 0: 43 | raise Exception("No services registered yet") 44 | if name is None: 45 | return cls._impl[0] 46 | else: 47 | return cls._impl_m[name] 48 | 49 | -------------------------------------------------------------------------------- /src/hdl_if/impl/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | def get_none(): 3 | return None 4 | 5 | def mkTask(coro): 6 | from hdl_if.backend import Backend 7 | be = Backend.inst() 8 | print("-- mkTask", flush=True) 9 | task = be.mkTask(coro) 10 | 11 | be.idle() 12 | 13 | return task 14 | -------------------------------------------------------------------------------- /src/hdl_if/impl/call/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/hdl_if/impl/call/api_decorator_impl.py: -------------------------------------------------------------------------------- 1 | #**************************************************************************** 2 | #* api_decorator_impl.py 3 | #* 4 | #* Copyright 2023 Matthew Ballance and Contributors 5 | #* 6 | #* Licensed under the Apache License, Version 2.0 (the "License"); you may 7 | #* not use this file except in compliance with the License. 8 | #* You may obtain a copy of the License at: 9 | #* 10 | #* http://www.apache.org/licenses/LICENSE-2.0 11 | #* 12 | #* Unless required by applicable law or agreed to in writing, software 13 | #* distributed under the License is distributed on an "AS IS" BASIS, 14 | #* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | #* See the License for the specific language governing permissions and 16 | #* limitations under the License. 17 | #* 18 | #* Created on: 19 | #* Author: 20 | #* 21 | #**************************************************************************** 22 | import inspect 23 | from typing import get_type_hints 24 | from .ctor import Ctor 25 | from .api_def import ApiDef 26 | from .api_def_rgy import ApiDefRgy 27 | 28 | class ApiDecoratorImpl(object): 29 | 30 | def __init__(self, args=None, kwargs=None): 31 | pass 32 | 33 | def __call__(self, T): 34 | ctor = Ctor.inst() 35 | rgy = ApiDefRgy.inst() 36 | 37 | # print(inspect.getargs(T.__init__)) 38 | init_params = [] 39 | init_m = getattr(T, "__init__") 40 | if hasattr(init_m, "__code__"): 41 | init_co = init_m.__code__ 42 | 43 | type_hints = get_type_hints(init_m) 44 | for i in range(1,init_co.co_argcount): 45 | name = init_co.co_varnames[i] 46 | if name not in type_hints.keys(): 47 | raise Exception("parameter %s is missing a type specification" % name) 48 | init_params.append((name, type_hints[name])) 49 | 50 | fullname = T.__module__ + "." + T.__qualname__ 51 | ad = ApiDef( 52 | fullname, 53 | init_params, 54 | ctor.getMethodDefs()) 55 | rgy.addApiDef(ad) 56 | 57 | return T 58 | 59 | 60 | -------------------------------------------------------------------------------- /src/hdl_if/impl/call/api_def.py: -------------------------------------------------------------------------------- 1 | #**************************************************************************** 2 | #* api_def.py 3 | #* 4 | #* Copyright 2023 Matthew Ballance and Contributors 5 | #* 6 | #* Licensed under the Apache License, Version 2.0 (the "License"); you may 7 | #* not use this file except in compliance with the License. 8 | #* You may obtain a copy of the License at: 9 | #* 10 | #* http://www.apache.org/licenses/LICENSE-2.0 11 | #* 12 | #* Unless required by applicable law or agreed to in writing, software 13 | #* distributed under the License is distributed on an "AS IS" BASIS, 14 | #* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | #* See the License for the specific language governing permissions and 16 | #* limitations under the License. 17 | #* 18 | #* Created on: 19 | #* Author: 20 | #* 21 | #**************************************************************************** 22 | 23 | class ApiDef(object): 24 | 25 | def __init__(self, fullname, init_params, methods): 26 | self._fullname = fullname 27 | self._init_params = init_params 28 | self._methods = methods 29 | 30 | @property 31 | def fullname(self): 32 | return self._fullname 33 | 34 | @property 35 | def name(self): 36 | last_dot = self._fullname.rfind('.') 37 | if last_dot != -1: 38 | return self._fullname[last_dot+1:] 39 | else: 40 | return self._fullname 41 | 42 | @property 43 | def init_params(self): 44 | return self._init_params 45 | 46 | @property 47 | def methods(self): 48 | return self._methods 49 | 50 | -------------------------------------------------------------------------------- /src/hdl_if/impl/call/api_def_rgy.py: -------------------------------------------------------------------------------- 1 | #**************************************************************************** 2 | #* api_def_rgy.py 3 | #* 4 | #* Copyright 2023 Matthew Ballance and Contributors 5 | #* 6 | #* Licensed under the Apache License, Version 2.0 (the "License"); you may 7 | #* not use this file except in compliance with the License. 8 | #* You may obtain a copy of the License at: 9 | #* 10 | #* http://www.apache.org/licenses/LICENSE-2.0 11 | #* 12 | #* Unless required by applicable law or agreed to in writing, software 13 | #* distributed under the License is distributed on an "AS IS" BASIS, 14 | #* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | #* See the License for the specific language governing permissions and 16 | #* limitations under the License. 17 | #* 18 | #* Created on: 19 | #* Author: 20 | #* 21 | #**************************************************************************** 22 | from typing import List, Dict 23 | from .api_def import ApiDef 24 | 25 | class ApiDefRgy(object): 26 | 27 | _inst = None 28 | 29 | def __init__(self): 30 | self.api_m : Dict[str, ApiDef] = {} 31 | self.api_l : List[ApiDef] = [] 32 | pass 33 | 34 | def addApiDef(self, ad): 35 | self.api_m[ad.name] = ad 36 | self.api_l.append(ad) 37 | 38 | def getApis(self): 39 | return self.api_l 40 | 41 | @classmethod 42 | def init(cls): 43 | cls._inst = None 44 | 45 | @classmethod 46 | def inst(cls): 47 | if cls._inst is None: 48 | cls._inst = ApiDefRgy() 49 | return cls._inst 50 | 51 | 52 | -------------------------------------------------------------------------------- /src/hdl_if/impl/call/call_proxy_dpi.py: -------------------------------------------------------------------------------- 1 | #**************************************************************************** 2 | #* call_proxy_dpi.py 3 | #* 4 | #* Copyright 2023 Matthew Ballance and Contributors 5 | #* 6 | #* Licensed under the Apache License, Version 2.0 (the "License"); you may 7 | #* not use this file except in compliance with the License. 8 | #* You may obtain a copy of the License at: 9 | #* 10 | #* http://www.apache.org/licenses/LICENSE-2.0 11 | #* 12 | #* Unless required by applicable law or agreed to in writing, software 13 | #* distributed under the License is distributed on an "AS IS" BASIS, 14 | #* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | #* See the License for the specific language governing permissions and 16 | #* limitations under the License. 17 | #* 18 | #* Created on: 19 | #* Author: 20 | #* 21 | #**************************************************************************** 22 | from hdl_if.call.call_proxy import CallProxy 23 | 24 | class CallProxyDPI(CallProxy): 25 | 26 | def __init__(self, target, obj_id, ep): 27 | self.target = target 28 | self.obj_id = obj_id 29 | self.ep = ep 30 | pass 31 | 32 | def invoke_hdl_f( 33 | self, 34 | method_name : str, 35 | args : tuple): 36 | return self.ep.invoke_hdl_f( 37 | self.obj_id, 38 | method_name, 39 | args) 40 | pass 41 | 42 | async def invoke_hdl_t( 43 | self, 44 | method_name, 45 | args): 46 | from hdl_if.backend import Backend 47 | be = Backend.inst(); 48 | evt = be.mkEvent() 49 | 50 | self.ep.invoke_hdl_t(self.obj_id, evt, method_name, args) 51 | res = await evt.wait() 52 | 53 | return res 54 | 55 | async def invoke_py_t_wrap( 56 | self, 57 | sem_id, 58 | m, 59 | args): 60 | res = await m(*args) 61 | self.ep.response_py_t(sem_id, res) 62 | 63 | def invoke_py_t( 64 | self, 65 | sem_id, 66 | method_name, 67 | args): 68 | from hdl_if.backend import Backend 69 | be = Backend.inst() 70 | 71 | m = getattr(self.target, method_name, None) 72 | 73 | if m is None: 74 | print("Error: failed to find method %s" % method_name, flush=True) 75 | 76 | be.mkTask(self.invoke_py_t_wrap(sem_id, m, args)) 77 | be.idle() 78 | -------------------------------------------------------------------------------- /src/hdl_if/impl/call/context.py: -------------------------------------------------------------------------------- 1 | #**************************************************************************** 2 | #* context.py 3 | #* 4 | #* Copyright 2023 Matthew Ballance and Contributors 5 | #* 6 | #* Licensed under the Apache License, Version 2.0 (the "License"); you may 7 | #* not use this file except in compliance with the License. 8 | #* You may obtain a copy of the License at: 9 | #* 10 | #* http://www.apache.org/licenses/LICENSE-2.0 11 | #* 12 | #* Unless required by applicable law or agreed to in writing, software 13 | #* distributed under the License is distributed on an "AS IS" BASIS, 14 | #* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | #* See the License for the specific language governing permissions and 16 | #* limitations under the License. 17 | #* 18 | #* Created on: 19 | #* Author: 20 | #* 21 | #**************************************************************************** 22 | from hdl_if.call.hdl_call_endpoint import HdlCallEndpoint 23 | 24 | class Context(object): 25 | _inst = None 26 | 27 | def __init__(self): 28 | self._endpoints = [] 29 | self._object_rgy_type_m = {} 30 | self._object_rgy_name_m = {} 31 | pass 32 | 33 | def addEndpoint(self, ep : HdlCallEndpoint): 34 | self._endpoints.append(ep) 35 | 36 | def getObjectsByType(self, typename): 37 | pass 38 | 39 | @classmethod 40 | def inst(cls): 41 | if cls._inst is None: 42 | cls._inst = Context() 43 | return cls._inst 44 | 45 | 46 | -------------------------------------------------------------------------------- /src/hdl_if/impl/call/ctor.py: -------------------------------------------------------------------------------- 1 | #**************************************************************************** 2 | #* ctor.py 3 | #* 4 | #* Copyright 2023 Matthew Ballance and Contributors 5 | #* 6 | #* Licensed under the Apache License, Version 2.0 (the "License"); you may 7 | #* not use this file except in compliance with the License. 8 | #* You may obtain a copy of the License at: 9 | #* 10 | #* http://www.apache.org/licenses/LICENSE-2.0 11 | #* 12 | #* Unless required by applicable law or agreed to in writing, software 13 | #* distributed under the License is distributed on an "AS IS" BASIS, 14 | #* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | #* See the License for the specific language governing permissions and 16 | #* limitations under the License. 17 | #* 18 | #* Created on: 19 | #* Author: 20 | #* 21 | #**************************************************************************** 22 | from typing import List 23 | from .api_def import ApiDef 24 | from .method_def import MethodDef 25 | 26 | class Ctor(object): 27 | 28 | _inst = None 29 | 30 | def __init__(self): 31 | self.method_l : List[MethodDef] = [] 32 | 33 | def addMethodDef(self, md): 34 | self.method_l.append(md) 35 | 36 | def getMethodDefs(self) -> List[MethodDef]: 37 | ret = self.method_l.copy() 38 | self.method_l.clear() 39 | return ret 40 | 41 | @classmethod 42 | def init(cls): 43 | cls._inst = None 44 | 45 | @classmethod 46 | def inst(cls): 47 | if cls._inst is None: 48 | cls._inst = Ctor() 49 | return cls._inst 50 | 51 | -------------------------------------------------------------------------------- /src/hdl_if/impl/call/imp_func_impl.py: -------------------------------------------------------------------------------- 1 | #**************************************************************************** 2 | #* imp_func_impl.py 3 | #* 4 | #* Copyright 2023 Matthew Ballance and Contributors 5 | #* 6 | #* Licensed under the Apache License, Version 2.0 (the "License"); you may 7 | #* not use this file except in compliance with the License. 8 | #* You may obtain a copy of the License at: 9 | #* 10 | #* http://www.apache.org/licenses/LICENSE-2.0 11 | #* 12 | #* Unless required by applicable law or agreed to in writing, software 13 | #* distributed under the License is distributed on an "AS IS" BASIS, 14 | #* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | #* See the License for the specific language governing permissions and 16 | #* limitations under the License. 17 | #* 18 | #* Created on: 19 | #* Author: 20 | #* 21 | #**************************************************************************** 22 | from hdl_if.call.call_proxy import CallProxy 23 | 24 | class ImpFuncImpl(object): 25 | 26 | def __init__(self, md): 27 | self._md = md 28 | pass 29 | 30 | def __call__(self, api_self, *args, **kwargs): 31 | if not hasattr(api_self, "_proxy") or getattr(api_self, "_proxy") is None: 32 | raise Exception("Class is not bound to an HDL object") 33 | 34 | if len(kwargs) != 0: 35 | raise Exception("Only positional arguments are supported") 36 | 37 | proxy : CallProxy = getattr(api_self, "_proxy") 38 | 39 | print("args: %s" % str(args), flush=True) 40 | return proxy.invoke_hdl_f( 41 | self._md.name, 42 | args 43 | ) 44 | -------------------------------------------------------------------------------- /src/hdl_if/impl/call/imp_task_impl.py: -------------------------------------------------------------------------------- 1 | #**************************************************************************** 2 | #* imp_task_impl.py 3 | #* 4 | #* Copyright 2023 Matthew Ballance and Contributors 5 | #* 6 | #* Licensed under the Apache License, Version 2.0 (the "License"); you may 7 | #* not use this file except in compliance with the License. 8 | #* You may obtain a copy of the License at: 9 | #* 10 | #* http://www.apache.org/licenses/LICENSE-2.0 11 | #* 12 | #* Unless required by applicable law or agreed to in writing, software 13 | #* distributed under the License is distributed on an "AS IS" BASIS, 14 | #* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | #* See the License for the specific language governing permissions and 16 | #* limitations under the License. 17 | #* 18 | #* Created on: 19 | #* Author: 20 | #* 21 | #**************************************************************************** 22 | from hdl_if.call.call_proxy import CallProxy 23 | from .method_def import MethodDef 24 | 25 | class ImpTaskImpl(object): 26 | 27 | def __init__(self, md : MethodDef): 28 | self._md = md 29 | pass 30 | 31 | async def __call__(self, api_self, *args, **kwargs): 32 | if not hasattr(api_self, "_proxy") or getattr(api_self, "_proxy") is None: 33 | raise Exception("Class is not bound to an HDL object") 34 | 35 | if len(kwargs) != 0: 36 | raise Exception("Only positional arguments are supported") 37 | 38 | proxy : CallProxy = getattr(api_self, "_proxy") 39 | return await proxy.invoke_hdl_t( 40 | self._md.name, 41 | args 42 | ) 43 | 44 | -------------------------------------------------------------------------------- /src/hdl_if/impl/call/method_def.py: -------------------------------------------------------------------------------- 1 | #**************************************************************************** 2 | #* method_def.py 3 | #* 4 | #* Copyright 2023 Matthew Ballance and Contributors 5 | #* 6 | #* Licensed under the Apache License, Version 2.0 (the "License"); you may 7 | #* not use this file except in compliance with the License. 8 | #* You may obtain a copy of the License at: 9 | #* 10 | #* http://www.apache.org/licenses/LICENSE-2.0 11 | #* 12 | #* Unless required by applicable law or agreed to in writing, software 13 | #* distributed under the License is distributed on an "AS IS" BASIS, 14 | #* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | #* See the License for the specific language governing permissions and 16 | #* limitations under the License. 17 | #* 18 | #* Created on: 19 | #* Author: 20 | #* 21 | #**************************************************************************** 22 | from typing import List, Tuple 23 | from enum import Enum, auto 24 | 25 | class MethodKind(Enum): 26 | ImpTask = auto() 27 | ExpTask = auto() 28 | ImpFunc = auto() 29 | ExpFunc = auto() 30 | Imp = auto() 31 | Exp = auto() 32 | 33 | class MethodDef(object): 34 | 35 | def __init__(self, 36 | kind : MethodKind, 37 | T, 38 | name : str, 39 | rtype, 40 | params : List[Tuple[str, object]]): 41 | self._kind = kind 42 | self._name = name 43 | self._rtype = rtype 44 | self._params = params 45 | pass 46 | 47 | @property 48 | def name(self): 49 | return self._name 50 | 51 | @property 52 | def kind(self): 53 | return self._kind 54 | 55 | @property 56 | def rtype(self): 57 | return self._rtype 58 | 59 | @property 60 | def params(self): 61 | return self._params 62 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /src/hdl_if/impl/dpi/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | from hdl_if.hdl_services import HdlServices 3 | from .hdl_services_dpi import HdlServicesDpi 4 | 5 | services = None 6 | 7 | 8 | def dpi_init(): 9 | global services 10 | from hdl_if.impl.call.context import Context 11 | from hdl_if.impl.call.hdl_call_endpoint_dpi import HdlCallEndpointDPI 12 | 13 | print("dpi_init", flush=True) 14 | 15 | ep = None 16 | try: 17 | services = HdlServicesDpi() 18 | HdlServices.registerServices(services) 19 | 20 | ctxt = Context.inst() 21 | 22 | ep = HdlCallEndpointDPI(services.getPkgScope()) 23 | ctxt.addEndpoint(ep) 24 | except Exception as e: 25 | print("Exception: %s" % str(e), flush=True) 26 | 27 | return ep 28 | 29 | -------------------------------------------------------------------------------- /src/hdl_if/impl/dpi/hdl_services_dpi.py: -------------------------------------------------------------------------------- 1 | #**************************************************************************** 2 | #* hdl_services_dpi.py 3 | #* 4 | #* Copyright 2023 Matthew Ballance and Contributors 5 | #* 6 | #* Licensed under the Apache License, Version 2.0 (the "License"); you may 7 | #* not use this file except in compliance with the License. 8 | #* You may obtain a copy of the License at: 9 | #* 10 | #* http://www.apache.org/licenses/LICENSE-2.0 11 | #* 12 | #* Unless required by applicable law or agreed to in writing, software 13 | #* distributed under the License is distributed on an "AS IS" BASIS, 14 | #* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | #* See the License for the specific language governing permissions and 16 | #* limitations under the License. 17 | #* 18 | #* Created on: 19 | #* Author: 20 | #* 21 | #**************************************************************************** 22 | import ctypes 23 | from hdl_if.hdl_services import HdlServices 24 | 25 | class HdlServicesDpi(HdlServices): 26 | 27 | def __init__(self, scope=None): 28 | super().__init__("dpi") 29 | self._scope = scope 30 | 31 | try: 32 | lib = ctypes.cdll.LoadLibrary(None) 33 | self._svGetScope = getattr(lib, "svGetScope") 34 | self._svGetScope.restype = ctypes.c_void_p 35 | 36 | self._svSetScope = getattr(lib, "svSetScope") 37 | self._svSetScope.argtypes = [ctypes.c_void_p] 38 | 39 | self._svGetNameFromScope = getattr(lib, "svGetNameFromScope") 40 | self._svGetNameFromScope.restype = ctypes.c_char_p 41 | self._svGetNameFromScope.argtypes = [ctypes.c_void_p] 42 | 43 | self._RegisterTimeCB = getattr(lib, "pyhdl_pi_if_RegisterTimeCB"); 44 | self._RegisterTimeCB.restype = ctypes.c_int 45 | self._RegisterTimeCB.argtypes = [ctypes.py_object, ctypes.c_uint64] 46 | except Exception as e: 47 | print("DPI Exception: %s" % str(e), flush=True) 48 | 49 | if self._scope == None: 50 | self._scope = self.svGetScope() 51 | 52 | def getPkgScope(self): 53 | return self._scope 54 | 55 | def svGetScope(self): 56 | return self._svGetScope() 57 | 58 | def svSetScope(self, scope): 59 | self._svSetScope(scope) 60 | 61 | def registerTimeCB(self, cb : callable, time_ps : int) -> object: 62 | self._svSetScope(self._scope) 63 | cb_id = self._RegisterTimeCB(cb, time_ps) 64 | return cb_id 65 | -------------------------------------------------------------------------------- /src/hdl_if/impl/pi/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvutils/pyhdl-if/15c3e1bcfc56f0506f7ac7808109d0ba061a2378/src/hdl_if/impl/pi/__init__.py -------------------------------------------------------------------------------- /src/hdl_if/impl/pi/backend_asyncio.py: -------------------------------------------------------------------------------- 1 | #**************************************************************************** 2 | #* backend_asyncio.py 3 | #* 4 | #* Copyright 2023 Matthew Ballance and Contributors 5 | #* 6 | #* Licensed under the Apache License, Version 2.0 (the "License"); you may 7 | #* not use this file except in compliance with the License. 8 | #* You may obtain a copy of the License at: 9 | #* 10 | #* http://www.apache.org/licenses/LICENSE-2.0 11 | #* 12 | #* Unless required by applicable law or agreed to in writing, software 13 | #* distributed under the License is distributed on an "AS IS" BASIS, 14 | #* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | #* See the License for the specific language governing permissions and 16 | #* limitations under the License. 17 | #* 18 | #* Created on: 19 | #* Author: 20 | #* 21 | #**************************************************************************** 22 | import asyncio 23 | from hdl_if.backend import Backend 24 | 25 | class Event(asyncio.Event): 26 | def __init__(self): 27 | super().__init__() 28 | self.val = None 29 | 30 | def set(self, val) -> None: 31 | be = Backend.inst(); 32 | self.val = val 33 | super().set() 34 | be.idle() 35 | 36 | async def wait(self): 37 | await super().wait() 38 | return self.val 39 | 40 | class BackendAsyncio(Backend): 41 | 42 | def __init__(self): 43 | super().__init__() 44 | self._loop = asyncio.get_event_loop() 45 | pass 46 | 47 | def mkTask(self, coro): 48 | print("mkTask", flush=True) 49 | return asyncio.ensure_future(coro) 50 | 51 | def mkEvent(self): 52 | return Event() 53 | 54 | def mkLock(self): 55 | return asyncio.Lock() 56 | 57 | def mkSemaphore(self): 58 | return asyncio.Semaphore() 59 | 60 | def __soon_cb(self): 61 | self._loop.stop() 62 | 63 | def idle(self): 64 | self._loop.call_soon(self.__soon_cb) 65 | self._loop.run_forever() 66 | 67 | def callCallback(self, cb): 68 | cb() 69 | self.idle() 70 | 71 | -------------------------------------------------------------------------------- /src/hdl_if/impl/pi/backend_cocotb.py: -------------------------------------------------------------------------------- 1 | #**************************************************************************** 2 | #* backend_cocotb.py 3 | #* 4 | #* Copyright 2023 Matthew Ballance and Contributors 5 | #* 6 | #* Licensed under the Apache License, Version 2.0 (the "License"); you may 7 | #* not use this file except in compliance with the License. 8 | #* You may obtain a copy of the License at: 9 | #* 10 | #* http://www.apache.org/licenses/LICENSE-2.0 11 | #* 12 | #* Unless required by applicable law or agreed to in writing, software 13 | #* distributed under the License is distributed on an "AS IS" BASIS, 14 | #* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | #* See the License for the specific language governing permissions and 16 | #* limitations under the License. 17 | #* 18 | #* Created on: 19 | #* Author: 20 | #* 21 | #**************************************************************************** 22 | from hdl_if.backend import Backend 23 | 24 | class BackendCocotb(Backend): 25 | 26 | def __init__(self): 27 | super().__init__() 28 | pass 29 | 30 | def mkEvent(self): 31 | from cocotb.triggers import Event 32 | return Event() 33 | 34 | def mkLock(self): 35 | from cocotb.triggers import Lock 36 | return Lock() 37 | 38 | def mkSemaphore(self): 39 | raise NotImplementedError("mkSemaphore (class %s)" % str(type(self))) 40 | 41 | def mkTask(self, coro): 42 | import cocotb 43 | return cocotb.create_task(coro) 44 | 45 | -------------------------------------------------------------------------------- /src/hdl_if/impl/tlm/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | from .stream import * 3 | from .stream_req import * 4 | from .stream_rsp import * 5 | from .stream_req_rsp import * 6 | 7 | -------------------------------------------------------------------------------- /src/hdl_if/impl/tlm/interface_decorator_impl_base.py: -------------------------------------------------------------------------------- 1 | #**************************************************************************** 2 | #* interface_decorator_impl_base.py 3 | #* 4 | #* Copyright 2023 Matthew Ballance and Contributors 5 | #* 6 | #* Licensed under the Apache License, Version 2.0 (the "License"); you may 7 | #* not use this file except in compliance with the License. 8 | #* You may obtain a copy of the License at: 9 | #* 10 | #* http://www.apache.org/licenses/LICENSE-2.0 11 | #* 12 | #* Unless required by applicable law or agreed to in writing, software 13 | #* distributed under the License is distributed on an "AS IS" BASIS, 14 | #* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | #* See the License for the specific language governing permissions and 16 | #* limitations under the License. 17 | #* 18 | #* Created on: 19 | #* Author: 20 | #* 21 | #**************************************************************************** 22 | import typeworks 23 | 24 | class InterfaceDecoratorImplBase(typeworks.MethodDecoratorBase): 25 | 26 | def __init__(self, args, kwargs): 27 | super().__init__(args, kwargs) 28 | 29 | def get_category(self): 30 | from hdl_if.tlm.tlm_method import TlmMethod 31 | return TlmMethod 32 | 33 | -------------------------------------------------------------------------------- /src/hdl_if/impl/tlm/model_info_tlm_if.py: -------------------------------------------------------------------------------- 1 | #**************************************************************************** 2 | #* model_info_tlm_if.py 3 | #* 4 | #* Copyright 2023 Matthew Ballance and Contributors 5 | #* 6 | #* Licensed under the Apache License, Version 2.0 (the "License"); you may 7 | #* not use this file except in compliance with the License. 8 | #* You may obtain a copy of the License at: 9 | #* 10 | #* http://www.apache.org/licenses/LICENSE-2.0 11 | #* 12 | #* Unless required by applicable law or agreed to in writing, software 13 | #* distributed under the License is distributed on an "AS IS" BASIS, 14 | #* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | #* See the License for the specific language governing permissions and 16 | #* limitations under the License. 17 | #* 18 | #* Created on: 19 | #* Author: 20 | #* 21 | #**************************************************************************** 22 | 23 | class ModelInfoTlmIF(object): 24 | 25 | def __init__(self): 26 | self._if_m = {} 27 | 28 | -------------------------------------------------------------------------------- /src/hdl_if/impl/tlm/req_fifo_decorator_impl.py: -------------------------------------------------------------------------------- 1 | #**************************************************************************** 2 | #* req_fifo_decorator_impl.py 3 | #* 4 | #* Copyright 2023 Matthew Ballance and Contributors 5 | #* 6 | #* Licensed under the Apache License, Version 2.0 (the "License"); you may 7 | #* not use this file except in compliance with the License. 8 | #* You may obtain a copy of the License at: 9 | #* 10 | #* http://www.apache.org/licenses/LICENSE-2.0 11 | #* 12 | #* Unless required by applicable law or agreed to in writing, software 13 | #* distributed under the License is distributed on an "AS IS" BASIS, 14 | #* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | #* See the License for the specific language governing permissions and 16 | #* limitations under the License. 17 | #* 18 | #* Created on: 19 | #* Author: 20 | #* 21 | #**************************************************************************** 22 | from .interface_decorator_impl_base import InterfaceDecoratorImplBase 23 | 24 | class ReqFifoDecoratorImpl(InterfaceDecoratorImplBase): 25 | 26 | def __init__(self, args, kwargs): 27 | super().__init__(args, kwargs) 28 | self.proxy = None 29 | 30 | def decorate(self, T): 31 | import ctypes 32 | from hdl_if.tlm.tlm_method import TlmMethod, TlmMethodKind 33 | 34 | is_method, rtype, params = self.get_signature() 35 | 36 | if rtype is not None: 37 | raise Exception("Request-FIFO method may not have a return type") 38 | if len(params) != 1: 39 | raise Exception("Request-FIFO method must have exactly one data parameter (%d)" % len(params)) 40 | 41 | ptype = params[0][1] 42 | 43 | if hasattr(ptype, "_fields_"): 44 | pass 45 | else: 46 | raise Exception("FIFO data-parameter type (%s) must derived from ctypes.Structure" % str(type(ptype))) 47 | 48 | self.proxy = TlmMethod(T.__name__, TlmMethodKind.Req, ptype, None) 49 | 50 | async def closure(self, obj): 51 | model = self._model 52 | if T.__name__ not in model._if_m.keys(): 53 | raise Exception("Method %s is unbound" % T.__name__) 54 | ifc = model._if_m[T.__name__] 55 | await ifc.put(obj) 56 | return closure 57 | 58 | def register(self, T, Tp): 59 | super().register(T, self.proxy) 60 | 61 | -------------------------------------------------------------------------------- /src/hdl_if/impl/tlm/rsp_fifo_decorator_impl.py: -------------------------------------------------------------------------------- 1 | #**************************************************************************** 2 | #* rsp_fifo_decorator_impl.py 3 | #* 4 | #* Copyright 2023 Matthew Ballance and Contributors 5 | #* 6 | #* Licensed under the Apache License, Version 2.0 (the "License"); you may 7 | #* not use this file except in compliance with the License. 8 | #* You may obtain a copy of the License at: 9 | #* 10 | #* http://www.apache.org/licenses/LICENSE-2.0 11 | #* 12 | #* Unless required by applicable law or agreed to in writing, software 13 | #* distributed under the License is distributed on an "AS IS" BASIS, 14 | #* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | #* See the License for the specific language governing permissions and 16 | #* limitations under the License. 17 | #* 18 | #* Created on: 19 | #* Author: 20 | #* 21 | #**************************************************************************** 22 | import ctypes 23 | from .interface_decorator_impl_base import InterfaceDecoratorImplBase 24 | 25 | class RspFifoDecoratorImpl(InterfaceDecoratorImplBase): 26 | 27 | def __init__(self, args, kwargs): 28 | super().__init__(args, kwargs) 29 | self.proxy = None 30 | 31 | def decorate(self, T): 32 | from hdl_if.tlm.tlm_method import TlmMethod, TlmMethodKind 33 | 34 | is_method, rtype, params = self.get_signature() 35 | 36 | if rtype is None: 37 | raise Exception("Response FIFO must specify a return type") 38 | if len(params) > 0: 39 | raise Exception("Respoonse FIFO must not have parameters") 40 | 41 | if not hasattr(rtype, "_fields_"): 42 | raise Exception("FIFO response type must derive from ctypes.Structure") 43 | 44 | self.proxy = TlmMethod(T.__name__, TlmMethodKind.Rsp, None, rtype) 45 | 46 | async def closure(self): 47 | model = self._model 48 | if T.__name__ not in model._if_m.keys(): 49 | raise Exception("Method %s is unbound" % T.__name__) 50 | ifc = model._if_m[T.__name__] 51 | ival = await ifc.get() 52 | sz = ctypes.sizeof(rtype) 53 | bval = bytearray(sz) 54 | 55 | for i in range(sz): 56 | bval[i] = (ival & 0xFF) 57 | ival >>= 8 58 | 59 | obj = rtype.from_buffer_copy(bval) 60 | 61 | return obj 62 | return closure 63 | 64 | def register(self, T, Tp): 65 | super().register(T, self.proxy) 66 | 67 | -------------------------------------------------------------------------------- /src/hdl_if/impl/tlm/stream.py: -------------------------------------------------------------------------------- 1 | #**************************************************************************** 2 | #* stream.py 3 | #* 4 | #* Copyright 2023 Matthew Ballance and Contributors 5 | #* 6 | #* Licensed under the Apache License, Version 2.0 (the "License"); you may 7 | #* not use this file except in compliance with the License. 8 | #* You may obtain a copy of the License at: 9 | #* 10 | #* http://www.apache.org/licenses/LICENSE-2.0 11 | #* 12 | #* Unless required by applicable law or agreed to in writing, software 13 | #* distributed under the License is distributed on an "AS IS" BASIS, 14 | #* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | #* See the License for the specific language governing permissions and 16 | #* limitations under the License. 17 | #* 18 | #* Created on: 19 | #* Author: 20 | #* 21 | #**************************************************************************** 22 | from hdl_if.call.call_proxy import CallProxy 23 | from enum import Enum, auto 24 | 25 | class StreamKind(Enum): 26 | Req = auto() 27 | Rsp = auto() 28 | ReqRsp = auto() 29 | 30 | class Stream(object): 31 | 32 | def __init__(self, kind, name): 33 | self.kind = kind 34 | if name.endswith(".fifo_reg"): 35 | name = name[:name.rfind(".")] 36 | self.fullname = name 37 | self.name = name[name.rfind('.')+1:] 38 | 39 | @property 40 | def proxy(self) -> CallProxy: 41 | return getattr(self, "_proxy") 42 | 43 | -------------------------------------------------------------------------------- /src/hdl_if/impl/tlm/stream_req.py: -------------------------------------------------------------------------------- 1 | #**************************************************************************** 2 | #* stream_req.py 3 | #* 4 | #* Copyright 2023 Matthew Ballance and Contributors 5 | #* 6 | #* Licensed under the Apache License, Version 2.0 (the "License"); you may 7 | #* not use this file except in compliance with the License. 8 | #* You may obtain a copy of the License at: 9 | #* 10 | #* http://www.apache.org/licenses/LICENSE-2.0 11 | #* 12 | #* Unless required by applicable law or agreed to in writing, software 13 | #* distributed under the License is distributed on an "AS IS" BASIS, 14 | #* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | #* See the License for the specific language governing permissions and 16 | #* limitations under the License. 17 | #* 18 | #* Created on: 19 | #* Author: 20 | #* 21 | #**************************************************************************** 22 | from .stream import Stream, StreamKind 23 | 24 | class StreamReq(Stream): 25 | 26 | def __init__(self, name): 27 | super().__init__(StreamKind.Req, name) 28 | 29 | async def put(self, obj): 30 | dat_arr = bytes(obj) 31 | data = 0 32 | for d in reversed(dat_arr): 33 | data <<= 8 34 | data |= d 35 | 36 | # TODO: Check if this is a valid object 37 | print("put::data 0x%08x" % data, flush=True) 38 | await self.proxy.invoke_hdl_t( 39 | "put", 40 | (data,) 41 | ) 42 | 43 | -------------------------------------------------------------------------------- /src/hdl_if/impl/tlm/stream_req_rsp.py: -------------------------------------------------------------------------------- 1 | #**************************************************************************** 2 | #* stream_req_rsp.py 3 | #* 4 | #* Copyright 2023 Matthew Ballance and Contributors 5 | #* 6 | #* Licensed under the Apache License, Version 2.0 (the "License"); you may 7 | #* not use this file except in compliance with the License. 8 | #* You may obtain a copy of the License at: 9 | #* 10 | #* http://www.apache.org/licenses/LICENSE-2.0 11 | #* 12 | #* Unless required by applicable law or agreed to in writing, software 13 | #* distributed under the License is distributed on an "AS IS" BASIS, 14 | #* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | #* See the License for the specific language governing permissions and 16 | #* limitations under the License. 17 | #* 18 | #* Created on: 19 | #* Author: 20 | #* 21 | #**************************************************************************** 22 | from .stream import Stream, StreamKind 23 | 24 | class StreamReqRsp(Stream): 25 | 26 | def __init__(self, name): 27 | super().__init__(StreamKind.ReqRsp, name) 28 | 29 | async def put(self, obj): 30 | dat_arr = bytes(obj) 31 | data = 0 32 | for d in reversed(dat_arr): 33 | data <<= 8 34 | data |= d 35 | 36 | # TODO: Check if this is a valid object 37 | print("put::data 0x%08x" % data, flush=True) 38 | await self.proxy.invoke_hdl_t( 39 | "put", 40 | (data,)) 41 | 42 | async def get(self) -> int: 43 | intval = await self.proxy.invoke_hdl_t("get", ()) 44 | return intval 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /src/hdl_if/impl/tlm/stream_rsp.py: -------------------------------------------------------------------------------- 1 | #**************************************************************************** 2 | #* stream_rsp.py 3 | #* 4 | #* Copyright 2023 Matthew Ballance and Contributors 5 | #* 6 | #* Licensed under the Apache License, Version 2.0 (the "License"); you may 7 | #* not use this file except in compliance with the License. 8 | #* You may obtain a copy of the License at: 9 | #* 10 | #* http://www.apache.org/licenses/LICENSE-2.0 11 | #* 12 | #* Unless required by applicable law or agreed to in writing, software 13 | #* distributed under the License is distributed on an "AS IS" BASIS, 14 | #* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | #* See the License for the specific language governing permissions and 16 | #* limitations under the License. 17 | #* 18 | #* Created on: 19 | #* Author: 20 | #* 21 | #**************************************************************************** 22 | from .stream import Stream, StreamKind 23 | 24 | class StreamRsp(Stream): 25 | 26 | def __init__(self, name): 27 | super().__init__(StreamKind.Rsp, name) 28 | 29 | async def get(self) -> int: 30 | intval = await self.proxy.invoke_hdl_t("get", ()) 31 | return intval 32 | 33 | -------------------------------------------------------------------------------- /src/hdl_if/impl/tlm/tlm_interface_decorator_impl.py: -------------------------------------------------------------------------------- 1 | #**************************************************************************** 2 | #* tlm_interface_decorator_impl.py 3 | #* 4 | #* Copyright 2023 Matthew Ballance and Contributors 5 | #* 6 | #* Licensed under the Apache License, Version 2.0 (the "License"); you may 7 | #* not use this file except in compliance with the License. 8 | #* You may obtain a copy of the License at: 9 | #* 10 | #* http://www.apache.org/licenses/LICENSE-2.0 11 | #* 12 | #* Unless required by applicable law or agreed to in writing, software 13 | #* distributed under the License is distributed on an "AS IS" BASIS, 14 | #* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | #* See the License for the specific language governing permissions and 16 | #* limitations under the License. 17 | #* 18 | #* Created on: 19 | #* Author: 20 | #* 21 | #**************************************************************************** 22 | import typeworks 23 | from hdl_if.tlm.tlm_ifc import TlmIfc 24 | from hdl_if.tlm.tlm_ifc_rgy import TlmIfcRgy 25 | from .type_info_tlm_if import TypeInfoTlmIF 26 | 27 | class TlmInterfaceDecoratorImpl(typeworks.ClsDecoratorBase): 28 | 29 | def __init__(self, args, kwargs): 30 | super().__init__(args, kwargs) 31 | self.tlm_ifc = None 32 | pass 33 | 34 | def get_type_category(self): 35 | return TlmIfc 36 | 37 | def pre_decorate(self, T): 38 | from hdl_if.tlm.tlm_method import TlmMethod 39 | tlm_if_ti = TypeInfoTlmIF.get(self.get_typeinfo()) 40 | self.tlm_ifc = TlmIfc(tlm_if_ti, T.__qualname__) 41 | 42 | # TODO: collect registered TLM methods from base classes 43 | if_methods = typeworks.DeclRgy.pop_decl(TlmMethod) 44 | 45 | print("if_methods: %d" % len(if_methods)) 46 | 47 | tlm_if_ti._if_method_l.extend(if_methods) 48 | self.tlm_ifc._if_method_l.extend(if_methods) 49 | 50 | super().pre_decorate(T) 51 | 52 | def post_decorate(self, T, Tp): 53 | tlm_if_ti = TypeInfoTlmIF.get(self.get_typeinfo()) 54 | super().post_decorate(T, Tp) 55 | 56 | tlm_if_ti._base_init = Tp.__init__ 57 | 58 | TypeInfoTlmIF.addMethods(Tp) 59 | 60 | # TODO: Add Methods 61 | 62 | def pre_register(self): 63 | TlmIfcRgy.inst().addTlmIf(self.tlm_ifc) 64 | super().pre_register() 65 | 66 | 67 | 68 | 69 | -------------------------------------------------------------------------------- /src/hdl_if/impl/tlm/type_info_tlm_if.py: -------------------------------------------------------------------------------- 1 | #**************************************************************************** 2 | #* type_info_tlm_if.py 3 | #* 4 | #* Copyright 2023 Matthew Ballance and Contributors 5 | #* 6 | #* Licensed under the Apache License, Version 2.0 (the "License"); you may 7 | #* not use this file except in compliance with the License. 8 | #* You may obtain a copy of the License at: 9 | #* 10 | #* http://www.apache.org/licenses/LICENSE-2.0 11 | #* 12 | #* Unless required by applicable law or agreed to in writing, software 13 | #* distributed under the License is distributed on an "AS IS" BASIS, 14 | #* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | #* See the License for the specific language governing permissions and 16 | #* limitations under the License. 17 | #* 18 | #* Created on: 19 | #* Author: 20 | #* 21 | #**************************************************************************** 22 | import typeworks 23 | from typing import List 24 | from hdl_if.tlm.tlm_method import TlmMethod 25 | from .model_info_tlm_if import ModelInfoTlmIF 26 | 27 | class TypeInfoTlmIF(object): 28 | ATTR_NAME = "_tlm_if_typeinfo" 29 | 30 | def __init__(self, info : typeworks.TypeInfo): 31 | self._base_init = None 32 | self._if_method_l : List[TlmMethod] = [] 33 | pass 34 | 35 | @staticmethod 36 | def init(self, *args, **kwargs): 37 | print("init %s" % str(self)) 38 | tlm_if_ti = TypeInfoTlmIF.get(typeworks.TypeInfo.get(type(self))) 39 | self._model = ModelInfoTlmIF() 40 | tlm_if_ti._base_init(self, *args, **kwargs) 41 | 42 | @classmethod 43 | def addMethods(cls, T): 44 | T.__init__ = cls.init 45 | 46 | @staticmethod 47 | def get(info) -> 'TypeInfoTlmIF': 48 | if not hasattr(info, TypeInfoTlmIF.ATTR_NAME): 49 | from hdl_if.tlm.tlm_ifc import TlmIfc 50 | setattr(info, TypeInfoTlmIF.ATTR_NAME, TlmIfc(info, info.T.__qualname__)) 51 | return getattr(info, TypeInfoTlmIF.ATTR_NAME) 52 | 53 | -------------------------------------------------------------------------------- /src/hdl_if/impl/vpi/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | import ctypes 3 | import importlib 4 | 5 | try: 6 | import hdl_if.impl.vpi.api as api 7 | from hdl_if.impl.vpi.api import * 8 | except Exception as e: 9 | print("Exception(vpi::init::1): %s" % str(e), flush=True) 10 | 11 | 12 | func_t = ctypes.CFUNCTYPE(ctypes.c_int, ctypes.POINTER(ctypes.c_byte)) 13 | def my_task(cb): 14 | print("my_task", flush=True) 15 | sys = api.vpi_handle(api.vpiSysTfCall, 0) 16 | print("sys: %s" % str(sys)) 17 | return 0 18 | 19 | my_task_h = func_t(my_task) 20 | 21 | def vpi_init(): 22 | import ctypes 23 | import hdl_if.impl.vpi.call_api as call_api 24 | import hdl_if.impl.vpi.tlm_api as tlm_api 25 | import hdl_if.impl.vpi.pkg_tf as pkg_tf 26 | print("::vpi_init") 27 | 28 | exe = ctypes.cdll.LoadLibrary(None) 29 | 30 | if hasattr(exe, "vpi_get"): 31 | print("Found VPI") 32 | try: 33 | api.load(exe) 34 | except Exception as e: 35 | print("Exception: %s" % str(e), flush=True) 36 | else: 37 | print("Failed to find VPI") 38 | 39 | info = api.t_vpi_vlog_info() 40 | api.vpi_get_vlog_info(ctypes.pointer(info)) 41 | print("product: %s %s" % (info.product.decode(), info.version.decode())) 42 | 43 | try: 44 | pkg_tf.register() 45 | call_api.register_vpi_tf() 46 | tlm_api.register_vpi_tf() 47 | except Exception as e: 48 | print("Exception: %s" % str(e)) 49 | 50 | for i in range(info.argc): 51 | arg = info.argv[i] 52 | arg = arg.decode() 53 | print("arg: %s" % arg, flush=True) 54 | if arg.startswith("+pyhdl-pi-if.mod="): 55 | mod = arg[len("+pyhdl-pi-if.mod="):] 56 | 57 | try: 58 | imp = importlib.import_module(mod, None) 59 | if hasattr(imp, "vlog_startup"): 60 | try: 61 | imp.vlog_startup() 62 | except Exception as e: 63 | print("Error: vlog_startup in module %s failed: %s" % ( 64 | mod, 65 | str(e))) 66 | else: 67 | print("Error: module %s doesn't have 'vlog_startup' method" % mod) 68 | except Exception as e: 69 | print("Failed to load module %s" % mod) 70 | 71 | 72 | -------------------------------------------------------------------------------- /src/hdl_if/impl/vpi/pkg_tf.py: -------------------------------------------------------------------------------- 1 | import hdl_if.impl.vpi.api as api 2 | from .util import vpi_set_val_obj 3 | import ctypes 4 | 5 | tf_func_t = ctypes.CFUNCTYPE(ctypes.c_int, ctypes.POINTER(ctypes.c_byte)) 6 | 7 | def hdl_if_None(ud): 8 | print("None", flush=True) 9 | tf_h = api.vpi_handle(api.vpiSysTfCall, 0) 10 | vpi_set_val_obj(tf_h, None) 11 | 12 | return 0 13 | hdl_if_None_tf = api.t_vpi_systf_data() 14 | hdl_if_None_fp = tf_func_t(hdl_if_None) 15 | 16 | def hdl_if_idle(ud): 17 | from hdl_if.backend import Backend 18 | print("idle", flush=True) 19 | be = Backend.inst() 20 | be.idle() 21 | return 0 22 | hdl_if_idle_tf = api.t_vpi_systf_data() 23 | hdl_if_idle_fp = tf_func_t(hdl_if_idle) 24 | 25 | 26 | 27 | def register(): 28 | global hdl_if_None_tf, hdl_if_None_fp 29 | global hdl_if_idle_tf, hdl_if_idle_fp 30 | 31 | hdl_if_None_tf.tfname = "$pyhdl_if_None".encode() 32 | hdl_if_None_tf.calltf = hdl_if_None_fp 33 | hdl_if_None_tf.type = api.vpiSysFunc 34 | hdl_if_None_tf.sysfunctype = api.vpiSizedFunc 35 | api.vpi_register_systf(ctypes.byref(hdl_if_None_tf)) 36 | 37 | hdl_if_idle_tf.tfname = "$pyhdl_if_idle".encode() 38 | hdl_if_idle_tf.calltf = hdl_if_idle_fp 39 | hdl_if_idle_tf.type = api.vpiSysTask 40 | api.vpi_register_systf(ctypes.byref(hdl_if_idle_tf)) 41 | -------------------------------------------------------------------------------- /src/hdl_if/pi/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvutils/pyhdl-if/15c3e1bcfc56f0506f7ac7808109d0ba061a2378/src/hdl_if/pi/__init__.py -------------------------------------------------------------------------------- /src/hdl_if/pkginfo.py: -------------------------------------------------------------------------------- 1 | #**************************************************************************** 2 | #* pkginfo.py 3 | #* 4 | #* Copyright 2023 Matthew Ballance and Contributors 5 | #* 6 | #* Licensed under the Apache License, Version 2.0 (the "License"); you may 7 | #* not use this file except in compliance with the License. 8 | #* You may obtain a copy of the License at: 9 | #* 10 | #* http://www.apache.org/licenses/LICENSE-2.0 11 | #* 12 | #* Unless required by applicable law or agreed to in writing, software 13 | #* distributed under the License is distributed on an "AS IS" BASIS, 14 | #* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | #* See the License for the specific language governing permissions and 16 | #* limitations under the License. 17 | #* 18 | #* Created on: 19 | #* Author: 20 | #* 21 | #**************************************************************************** 22 | import os 23 | import sys 24 | import sysconfig 25 | import ivpm 26 | 27 | class PkgInfo(ivpm.PkgInfo): 28 | 29 | def __init__(self): 30 | super().__init__("pyhdl-if") 31 | pass 32 | 33 | def getPaths(self, kind): 34 | if kind == "lib-dirs": 35 | pkg_dir = os.path.dirname(os.path.abspath(__file__)) 36 | return [os.path.join(pkg_dir, "share")] 37 | else: 38 | return [] 39 | 40 | def getLibDirs(self, kind=None): 41 | if kind is not None: 42 | return [sysconfig.get_config_var("LIBDIR")] 43 | 44 | def getLibs(self, kind=None): 45 | if kind is not None: 46 | pkg_dir = os.path.dirname(os.path.abspath(__file__)) 47 | 48 | lib = None 49 | for f in os.listdir(pkg_dir): 50 | if f.endswith(self.dllext) and f.startswith("entry"): 51 | lib = os.path.join(pkg_dir, f) 52 | break 53 | 54 | if lib is None: 55 | raise Exception("Failed to find pyhdl-pi-if library") 56 | 57 | lib_filename = lib 58 | 59 | ret = [ lib_filename ] 60 | if kind != "vpi": 61 | exe_dir = os.path.dirname(sys.executable) 62 | python_dir = os.path.dirname(exe_dir) 63 | python_dir = sysconfig.get_config_var("installed_platbase") 64 | python_libdir = os.path.join(python_dir, 'lib') 65 | 66 | ret.append(os.path.join(python_libdir, sysconfig.get_config_var("LDLIBRARY"))) 67 | 68 | return ret 69 | 70 | -------------------------------------------------------------------------------- /src/hdl_if/share/dpi/py_builtins.svh: -------------------------------------------------------------------------------- 1 | 2 | class py_builtins; 3 | static py_object m_builtins; 4 | 5 | static function py_object get_builtins(); 6 | if (m_builtins == null) begin 7 | m_builtins 8 | end 9 | return m_builtins; 10 | endfunction 11 | 12 | static function py_object call(string name, py_tuple args, py_dict kwargs=null); 13 | py_object builtins = 14 | endfunction 15 | endclass -------------------------------------------------------------------------------- /src/hdl_if/share/dpi/py_ctxt.svh: -------------------------------------------------------------------------------- 1 | 2 | typedef class py_object; 3 | 4 | class py_ctxt; 5 | py_object objects[$]; 6 | 7 | function void dispose(); 8 | foreach (objects[i]) begin 9 | objects[i].dispose(); 10 | end 11 | endfunction 12 | 13 | virtual function py_object add(py_object obj); 14 | objects.push_back(obj); 15 | return obj; 16 | endfunction 17 | 18 | endclass 19 | -------------------------------------------------------------------------------- /src/hdl_if/share/dpi/py_dict.svh: -------------------------------------------------------------------------------- 1 | 2 | class py_dict extends py_object; 3 | function new (PyObject obj); 4 | super.new(obj); 5 | endfunction 6 | 7 | /** 8 | * Constructs a py_dict wrapper around an existing object 9 | */ 10 | static function py_dict mk(py_object obj, py_ctxt ctxt=null); 11 | py_dict ret; 12 | if (obj != null) begin 13 | ret = new(obj.obj); 14 | if (ctxt != null) begin 15 | void'(ctxt.add(ret)); 16 | end 17 | end 18 | return ret; 19 | endfunction 20 | 21 | typedef py_object pair_t[2]; 22 | 23 | static function py_dict mk_init(pair_t init[$]); 24 | py_dict ret = new(PyDict_New()); 25 | // foreach (init[i]) begin 26 | // void'(PyDict_SetItem( 27 | // ret.obj, 28 | // init[i][0].obj, 29 | // init[i][1].obj)); 30 | // end 31 | return ret; 32 | endfunction 33 | 34 | /** 35 | * Obtains a list of keys 36 | */ 37 | function py_list keys(); 38 | py_list ret = new(PyDict_Keys(obj)); 39 | return ret; 40 | endfunction 41 | 42 | // typedef py_object dict_t[string]; 43 | 44 | // static function py_dict mk_init(dict_t init); 45 | // return null; 46 | // endfunction 47 | 48 | endclass -------------------------------------------------------------------------------- /src/hdl_if/share/dpi/py_iter.svh: -------------------------------------------------------------------------------- 1 | 2 | class py_iter; 3 | PyObject obj; 4 | PyObject item; 5 | 6 | function new(PyObject obj); 7 | this.obj = obj; 8 | endfunction 9 | 10 | function bit valid(); 11 | if (item == null) begin 12 | item = PyIter_Next(this.obj); 13 | end 14 | return (item != null); 15 | endfunction 16 | 17 | function py_object next(); 18 | py_object ret; 19 | if (item == null) begin 20 | item = PyIter_Next(this.obj); 21 | end 22 | if (item != null) begin 23 | ret = new(item); 24 | item = null; 25 | end 26 | return ret; 27 | endfunction 28 | 29 | endclass -------------------------------------------------------------------------------- /src/hdl_if/share/dpi/py_list.svh: -------------------------------------------------------------------------------- 1 | 2 | class py_list extends py_object; 3 | 4 | function new(PyObject obj); 5 | super.new(obj); 6 | endfunction 7 | 8 | /** 9 | * Returns the number of elements in the list 10 | */ 11 | function int size(); 12 | return int'(PyList_Size(obj)); 13 | endfunction 14 | 15 | /** 16 | * Gets the item at the specified list index 17 | */ 18 | function py_object get_item(int idx); 19 | py_object ret = new(PyList_GetItem(obj, longint'(idx))); 20 | return ret; 21 | endfunction 22 | 23 | /** 24 | * Appends a new element to the list 25 | */ 26 | function void append(py_object obj); 27 | void'(PyList_Append(this.obj, obj.steal())); 28 | endfunction 29 | 30 | /** 31 | * Creates a new list object that wraps an existing object 32 | */ 33 | static function py_list mk(py_object obj); 34 | py_list ret = new(obj.obj); 35 | return ret; 36 | endfunction 37 | 38 | /** 39 | * Creates a new list object from an initial list of items 40 | */ 41 | static function py_list mk_init(py_object objs[$]); 42 | py_list ret = new(PyList_New(longint'(objs.size()))); 43 | foreach (objs[i]) begin 44 | void'(PyList_Append(ret.obj, objs[i].steal())); 45 | end 46 | return ret; 47 | endfunction 48 | 49 | endclass 50 | -------------------------------------------------------------------------------- /src/hdl_if/share/dpi/py_tuple.svh: -------------------------------------------------------------------------------- 1 | 2 | class py_tuple extends py_object; 3 | 4 | function new(PyObject obj); 5 | super.new(obj); 6 | endfunction 7 | 8 | /** 9 | * Sets the specified tuple element 10 | */ 11 | function void set_item(int idx, py_object obj); 12 | void'(PyTuple_SetItem(this.obj, longint'(idx), obj.steal())); 13 | endfunction 14 | 15 | /** 16 | * Gets the specified tuple element 17 | */ 18 | function py_object get_item(int idx); 19 | py_object ret = new(PyTuple_GetItem(this.obj, longint'(idx))); 20 | return ret; 21 | endfunction 22 | 23 | /** 24 | * Creates a new tuple by wrapping an existing object 25 | */ 26 | static function py_tuple mk(py_object obj); 27 | py_tuple ret = new(obj.obj); 28 | return ret; 29 | endfunction 30 | 31 | /** 32 | * Creates a new empty tuple of the specified size 33 | */ 34 | static function py_tuple mk_new_sz(int sz, py_ctxt ctxt=null); 35 | PyObject tuple = PyTuple_New(longint'(sz)); 36 | py_tuple ret = new(tuple); 37 | 38 | if (ctxt != null) begin 39 | void'(ctxt.add(ret)); 40 | end 41 | 42 | return ret; 43 | endfunction 44 | 45 | /** 46 | * Creates a new tuple from the specified elements 47 | */ 48 | static function py_tuple mk_init(py_object elems[$], py_ctxt ctxt=null); 49 | PyObject tuple = PyTuple_New(longint'(elems.size())); 50 | py_tuple ret = new(tuple); 51 | foreach (elems[i]) begin 52 | void'(PyTuple_SetItem(tuple, longint'(i), elems[i].steal())); 53 | end 54 | 55 | if (ctxt != null) begin 56 | void'(ctxt.add(ret)); 57 | end 58 | 59 | return ret; 60 | endfunction 61 | 62 | endclass -------------------------------------------------------------------------------- /src/hdl_if/share/dpi/pyhdl_if_call_dpi.svh: -------------------------------------------------------------------------------- 1 | 2 | function PyObject pyhdl_call_if_invoke_hdl_f( 3 | int obj_id, 4 | string method_name, 5 | PyObject args); 6 | PyObject ret; 7 | ret = __objects[obj_id].invokeFunc(method_name, args); 8 | return ret; 9 | endfunction 10 | export "DPI-C" function pyhdl_call_if_invoke_hdl_f; 11 | 12 | function void pyhdl_call_if_invoke_hdl_t( 13 | int obj_id, 14 | PyObject evt_obj, 15 | string method_name, 16 | PyObject args); 17 | TaskCallClosure closure; 18 | 19 | closure = new( 20 | __objects[obj_id], 21 | evt_obj, 22 | method_name, 23 | args); 24 | 25 | pyhdl_pi_if_queue_runnable(closure); 26 | endfunction 27 | export "DPI-C" function pyhdl_call_if_invoke_hdl_t; 28 | 29 | function void pyhdl_call_if_response_py_t( 30 | int sem_id, 31 | PyObject res); 32 | pyhdl_if_setSem(sem_id, res); 33 | endfunction 34 | export "DPI-C" function pyhdl_call_if_response_py_t; 35 | -------------------------------------------------------------------------------- /src/hdl_if/share/dpi/pyhdl_if_call_init.svh: -------------------------------------------------------------------------------- 1 | 2 | function automatic bit __pyhdl_if_call_init(); 3 | // PyObject __sv_init = null; 4 | // PyObject res; 5 | 6 | // // Register ourselves with the Python side 7 | // __hdl_call_if = pyhdl_pi_if_HandleErr(PyImport_ImportModule("hdl_call_if")); 8 | // if (__hdl_call_if == null) begin 9 | // $display("Internal Error: Failed to find Python package hdl_call_if"); 10 | // $finish; 11 | // end 12 | // __sv_init = pyhdl_pi_if_HandleErr(PyObject_GetAttrString(__hdl_call_if, "__sv_init")); 13 | // if (__sv_init == null) begin 14 | // $display("Internal Error: Failed to find __sv_init method"); 15 | // $finish; 16 | // end 17 | // Py_IncRef(__sv_init); 18 | // begin 19 | // PyObject args = PyTuple_New(1); 20 | // PyObject scope = PyLong_FromVoidPtr(svGetScope()); 21 | // int ret; 22 | 23 | // Py_IncRef(scope); 24 | // ret = PyTuple_SetItem(args, 0, scope); 25 | // res = PyObject_Call(__sv_init, args, null); 26 | 27 | // if (res == null) begin 28 | // $display("Internal Error: Failed to run __sv_init"); 29 | // $finish; 30 | // end 31 | // __ep_h = res; 32 | // end 33 | return 1; 34 | endfunction 35 | -------------------------------------------------------------------------------- /src/hdl_if/share/dpi/pyhdl_if_icall_api.svh: -------------------------------------------------------------------------------- 1 | /** 2 | * pyhdl_icall_api.sv 3 | * 4 | * Copyright 2024 Matthew Ballance and Contributors 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); you may 7 | * not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at: 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | * Created on: 19 | * Author: 20 | */ 21 | interface class ICallApi; 22 | 23 | pure virtual function PyObject invokeFunc( 24 | string method, 25 | PyObject args); 26 | 27 | pure virtual task invokeTask( 28 | output PyObject retval, 29 | input string method, 30 | input PyObject args); 31 | 32 | endclass -------------------------------------------------------------------------------- /src/hdl_if/share/dpi/pyhdl_if_init.svh: -------------------------------------------------------------------------------- 1 | 2 | function automatic bit __pyhdl_if_init(); 3 | bit ret = 1; 4 | PyObject hdl_if_impl, hdl_if_impl_dpi, dpi_init, get_none, args, res; 5 | PyGILState_STATE state; 6 | 7 | if (pyhdl_if_dpi_entry() != 1) begin 8 | $display("Fatal: Failed to initialize pyhdl-pi-if DPI interface"); 9 | $finish; 10 | return 0; 11 | end 12 | 13 | Py_Initialize(); 14 | 15 | state = PyGILState_Ensure(); 16 | 17 | __hdl_pi_if = pyhdl_pi_if_HandleErr(PyImport_ImportModule("hdl_if")); 18 | if (__hdl_pi_if == null) begin 19 | PyErr_Print(); 20 | $display("Fatal: failed to load hdl_if"); 21 | $finish; 22 | end 23 | 24 | hdl_if_impl = pyhdl_pi_if_HandleErr(PyImport_ImportModule("hdl_if.impl")); 25 | if (hdl_if_impl == null) begin 26 | $display("Fatal: failed to load hdl_if.impl"); 27 | $finish; 28 | end 29 | 30 | hdl_if_impl_dpi = pyhdl_pi_if_HandleErr(PyImport_ImportModule("hdl_if.impl.dpi")); 31 | if (hdl_if_impl_dpi == null) begin 32 | $display("Fatal: failed to load hdl_if.impl.dpi"); 33 | $finish; 34 | end 35 | dpi_init = PyObject_GetAttrString(hdl_if_impl_dpi, "dpi_init"); 36 | if (dpi_init == null) begin 37 | ret = 0; 38 | $display("Fatal: failed to find dpi_init in hdl_if.impl.dpi"); 39 | $finish; 40 | end 41 | 42 | args = PyTuple_New(0); 43 | 44 | __ep_h = pyhdl_pi_if_HandleErr(PyObject_Call(dpi_init, args, null)); 45 | if (__ep_h == null) begin 46 | $display("Fatal: failed to initialize hdl_pi_if package"); 47 | $finish; 48 | end 49 | 50 | get_none = PyObject_GetAttrString(hdl_if_impl, "get_none"); 51 | args = PyTuple_New(0); 52 | None = PyObject_Call(get_none, args, null); 53 | 54 | __mkTask = PyObject_GetAttrString(hdl_if_impl, "mkTask"); 55 | 56 | ret &= __pyhdl_if_call_init(); 57 | 58 | ret &= __pyhdl_if_tlm_init(); 59 | 60 | PyGILState_Release(state); 61 | 62 | return ret; 63 | endfunction -------------------------------------------------------------------------------- /src/hdl_if/share/dpi/pyhdl_if_pi_dpi.svh: -------------------------------------------------------------------------------- 1 | 2 | function automatic int pyhdl_pi_if_RegisterTimeCB( 3 | PyObject target, 4 | longint unsigned time_ps); 5 | PyHdlDpiTimeCB cb; 6 | int cb_id = -1; 7 | foreach (prv_time_cb[i]) begin 8 | if (prv_time_cb[i] == null) begin 9 | cb_id = i; 10 | break; 11 | end 12 | end 13 | 14 | if (cb_id == -1) begin 15 | cb_id = prv_time_cb.size(); 16 | prv_time_cb.push_back(null); 17 | end 18 | 19 | cb = new(target, cb_id, time_ps); 20 | 21 | pyhdl_pi_if_queue_runnable(cb); 22 | 23 | return cb_id; 24 | endfunction 25 | export "DPI-C" function pyhdl_pi_if_RegisterTimeCB; 26 | -------------------------------------------------------------------------------- /src/hdl_if/share/dpi/pyhdl_if_taskcall_closure.svh: -------------------------------------------------------------------------------- 1 | 2 | class TaskCallClosure implements PyHdlPiRunnable; 3 | ICallApi m_obj; 4 | PyObject m_evt_obj; 5 | string m_method_name; 6 | PyObject m_args; 7 | 8 | function new( 9 | ICallApi obj, 10 | PyObject evt_obj, 11 | string method_name, 12 | PyObject args); 13 | m_obj = obj; 14 | m_evt_obj = evt_obj; 15 | m_method_name = method_name; 16 | m_args = args; 17 | endfunction 18 | 19 | virtual task run(); 20 | PyObject args, res, evt_obj_set; 21 | PyGILState_STATE state = PyGILState_Ensure(); 22 | 23 | evt_obj_set = PyObject_GetAttrString(m_evt_obj, "set"); 24 | args = PyTuple_New(1); 25 | 26 | m_obj.invokeTask(res, m_method_name, m_args); 27 | 28 | if (res == null) begin 29 | res = None; 30 | end 31 | 32 | void'(PyTuple_SetItem(args, 0, res)); 33 | res = PyObject_Call(evt_obj_set, args, null); 34 | 35 | if (res == null) begin 36 | $display("Internal Error: Failed to trigger pyhdl_call_if event"); 37 | $finish; 38 | end 39 | 40 | PyGILState_Release(state); 41 | endtask 42 | endclass -------------------------------------------------------------------------------- /src/hdl_if/share/dpi/pyhdl_if_time_cb.svh: -------------------------------------------------------------------------------- 1 | 2 | class PyHdlDpiTimeCB implements PyHdlPiRunnable; 3 | PyObject m_target; 4 | int m_cb_id; 5 | longint unsigned m_time_ps; 6 | 7 | function new( 8 | PyObject target, 9 | int cb_id, 10 | longint unsigned time_ps); 11 | m_target = target; 12 | Py_IncRef(m_target); 13 | m_cb_id = cb_id; 14 | m_time_ps = time_ps; 15 | endfunction 16 | 17 | virtual task run(); 18 | PyObject backend, args, callCallback; 19 | #(m_time_ps * 1ps); 20 | if (m_cb_id != -1) begin 21 | backend = pyhdl_pi_if_getBackend(); 22 | args = PyTuple_New(1); 23 | callCallback = PyObject_GetAttrString(backend, "callCallback"); 24 | void'(PyTuple_SetItem(args, 0, m_target)); 25 | if (pyhdl_pi_if_HandleErr(PyObject_Call(callCallback, args, null)) == null) begin 26 | $display("Fatal Error: failed to invoke timed callback"); 27 | $finish; 28 | end 29 | prv_time_cb[m_cb_id] = null; 30 | end 31 | endtask 32 | endclass -------------------------------------------------------------------------------- /src/hdl_if/share/dpi/pyhdl_if_tlm_init.svh: -------------------------------------------------------------------------------- 1 | 2 | function automatic bit __pyhdl_if_tlm_init(); 3 | PyObject args, stream_rgy_t, inst_m; 4 | PyObject hdl_if_tlm, hdl_if_impl_tlm; 5 | 6 | hdl_if_tlm = pyhdl_pi_if_HandleErr(PyImport_ImportModule("hdl_if.tlm")); 7 | hdl_if_impl_tlm = pyhdl_pi_if_HandleErr(PyImport_ImportModule("hdl_if.impl.tlm")); 8 | 9 | if (hdl_if_tlm == null) begin 10 | $display("Fatal Error: Failed to load hdl_if.tlm package"); 11 | $finish; 12 | return 0; 13 | end 14 | 15 | stream_req_t = pyhdl_pi_if_HandleErr(PyObject_GetAttrString(hdl_if_impl_tlm, "StreamReq")); 16 | if (stream_req_t == null) begin 17 | $display("Fatal Error: Failed to find StreamReq class in hdl_if.impl.tlm"); 18 | $finish; 19 | return 0; 20 | end 21 | stream_rsp_t = pyhdl_pi_if_HandleErr(PyObject_GetAttrString(hdl_if_impl_tlm, "StreamRsp")); 22 | if (stream_rsp_t == null) begin 23 | $display("Fatal Error: Failed to find StreamRsp class in hdl_if.impl.impl"); 24 | $finish; 25 | return 0; 26 | end 27 | 28 | stream_rgy_t = pyhdl_pi_if_HandleErr(PyObject_GetAttrString(hdl_if_tlm, "StreamRgy")); 29 | inst_m = pyhdl_pi_if_HandleErr(PyObject_GetAttrString(stream_rgy_t, "inst")); 30 | 31 | args = PyTuple_New(0); 32 | stream_rgy = pyhdl_pi_if_HandleErr(PyObject_Call(inst_m, args, null)); 33 | 34 | register_stream = pyhdl_pi_if_HandleErr(PyObject_GetAttrString(stream_rgy, "register_stream")); 35 | 36 | return 1; 37 | endfunction 38 | -------------------------------------------------------------------------------- /src/hdl_if/share/dpi/pyhdl_if_uvm.sv: -------------------------------------------------------------------------------- 1 | `include "uvm_macros.svh" 2 | 3 | package pyhdl_if_uvm; 4 | import pyhdl_if::*; 5 | import uvm_pkg::*; 6 | 7 | 8 | endpackage 9 | -------------------------------------------------------------------------------- /src/hdl_if/share/pyhdl_if.core: -------------------------------------------------------------------------------- 1 | CAPI=2: 2 | 3 | name: fvutils::pyhdl-if 4 | 5 | filesets: 6 | pkg: 7 | file_type: "systemVerilogSource" 8 | files: 9 | - 'dpi/pyhdl_if.sv': 10 | include_path: "dpi" 11 | fifos: 12 | file_type: "verilogSource" 13 | files: 14 | - 'pyhdl_if_req_fifo.sv' 15 | - 'pyhdl_if_rsp_fifo.sv' 16 | - 'pyhdl_if_reqrsp_fifo.sv' 17 | 18 | 19 | targets: 20 | default: 21 | filesets: 22 | - pkg 23 | - fifos 24 | 25 | -------------------------------------------------------------------------------- /src/hdl_if/tlm/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | import sys 3 | print("PATH: %s" % str(sys.path), flush=True) 4 | 5 | from .stream_rgy import * -------------------------------------------------------------------------------- /src/hdl_if/tlm/tlm_ifc.py: -------------------------------------------------------------------------------- 1 | #**************************************************************************** 2 | #* tlm_ifc.py 3 | #* 4 | #* Copyright 2023 Matthew Ballance and Contributors 5 | #* 6 | #* Licensed under the Apache License, Version 2.0 (the "License"); you may 7 | #* not use this file except in compliance with the License. 8 | #* You may obtain a copy of the License at: 9 | #* 10 | #* http://www.apache.org/licenses/LICENSE-2.0 11 | #* 12 | #* Unless required by applicable law or agreed to in writing, software 13 | #* distributed under the License is distributed on an "AS IS" BASIS, 14 | #* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | #* See the License for the specific language governing permissions and 16 | #* limitations under the License. 17 | #* 18 | #* Created on: 19 | #* Author: 20 | #* 21 | #**************************************************************************** 22 | from hdl_if.impl.tlm.type_info_tlm_if import TypeInfoTlmIF 23 | 24 | class TlmIfc(TypeInfoTlmIF): 25 | 26 | def __init__(self, ti, qname): 27 | super().__init__(ti) 28 | self.ti = ti 29 | self.qname = qname 30 | self.name = qname 31 | ld = self.name.rfind('.') 32 | if ld != -1: 33 | self.name = self.name[ld+1:] 34 | 35 | def getMethods(self): 36 | return self._if_method_l 37 | 38 | @staticmethod 39 | def get(T) -> 'TlmIfc': 40 | """Return type-info for a """ 41 | from typeworks.impl.typeinfo import TypeInfo 42 | return TypeInfoTlmIF.get(TypeInfo.get(T)) 43 | 44 | -------------------------------------------------------------------------------- /src/hdl_if/tlm/tlm_ifc_rgy.py: -------------------------------------------------------------------------------- 1 | #**************************************************************************** 2 | #* tlm_ifc_rgy.py 3 | #* 4 | #* Copyright 2023 Matthew Ballance and Contributors 5 | #* 6 | #* Licensed under the Apache License, Version 2.0 (the "License"); you may 7 | #* not use this file except in compliance with the License. 8 | #* You may obtain a copy of the License at: 9 | #* 10 | #* http://www.apache.org/licenses/LICENSE-2.0 11 | #* 12 | #* Unless required by applicable law or agreed to in writing, software 13 | #* distributed under the License is distributed on an "AS IS" BASIS, 14 | #* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | #* See the License for the specific language governing permissions and 16 | #* limitations under the License. 17 | #* 18 | #* Created on: 19 | #* Author: 20 | #* 21 | #**************************************************************************** 22 | import typeworks 23 | 24 | class TlmIfcRgy(object): 25 | 26 | _inst = None 27 | 28 | def __init__(self): 29 | self.tlm_ifcs = [] 30 | 31 | def addTlmIf(self, tlm_ifc): 32 | self.tlm_ifcs.append(tlm_ifc) 33 | 34 | def getTlmIfcs(self): 35 | return self.tlm_ifcs 36 | 37 | @classmethod 38 | def inst(cls): 39 | if cls._inst is None: 40 | cls._inst = TlmIfcRgy() 41 | return cls._inst 42 | 43 | @classmethod 44 | def reset(cls): 45 | cls._inst = None 46 | -------------------------------------------------------------------------------- /src/hdl_if/tlm/tlm_method.py: -------------------------------------------------------------------------------- 1 | #**************************************************************************** 2 | #* tlm_method.py 3 | #* 4 | #* Copyright 2023 Matthew Ballance and Contributors 5 | #* 6 | #* Licensed under the Apache License, Version 2.0 (the "License"); you may 7 | #* not use this file except in compliance with the License. 8 | #* You may obtain a copy of the License at: 9 | #* 10 | #* http://www.apache.org/licenses/LICENSE-2.0 11 | #* 12 | #* Unless required by applicable law or agreed to in writing, software 13 | #* distributed under the License is distributed on an "AS IS" BASIS, 14 | #* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | #* See the License for the specific language governing permissions and 16 | #* limitations under the License. 17 | #* 18 | #* Created on: 19 | #* Author: 20 | #* 21 | #**************************************************************************** 22 | from enum import Enum, auto 23 | 24 | class TlmMethodKind(Enum): 25 | Req = auto() 26 | Rsp = auto() 27 | ReqRsp = auto() 28 | 29 | 30 | class TlmMethod(object): 31 | 32 | def __init__(self, name, kind, t1, t2): 33 | self.name = name 34 | self.kind = kind 35 | self.t1 = t1 36 | self.t2 = t2 37 | 38 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /tests/smoke/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvutils/pyhdl-if/15c3e1bcfc56f0506f7ac7808109d0ba061a2378/tests/smoke/__init__.py -------------------------------------------------------------------------------- /tests/smoke/my_module.py: -------------------------------------------------------------------------------- 1 | import ctypes 2 | import hdl_call_if.svdpi as svdpi 3 | print("My Module", flush=True) 4 | 5 | def thread_1(lib, scope): 6 | try: 7 | print("--> thread_1 %s scope=0x%08x" % (str(lib), scope), flush=True) 8 | print("--> svSetScope", flush=True) 9 | top = svdpi.svGetScopeFromName("top".encode()) 10 | # print("top=0x%08x scope=0x%08x" % (int(top), scope), flush=True) 11 | svdpi.svSetScope(top) 12 | print("<-- svSetScope", flush=True) 13 | lib.timed_task() 14 | print("<-- thread_1 %s" % str(lib), flush=True) 15 | except Exception as e: 16 | print("Exception: %s" % str(e), flush=True) 17 | 18 | def thread_2(): 19 | print("thread_2", flush=True) 20 | 21 | _closure = None 22 | 23 | def my_method(top_scope): 24 | global _closure 25 | print("my_method %s" % str(top_scope), flush=True) 26 | try: 27 | lib = ctypes.CDLL(None) 28 | except Exception as e: 29 | print("Exception: %s" % str(e), flush=True) 30 | 31 | print("lib: %s" % str(lib), flush=True) 32 | 33 | try: 34 | svGetScope = lib.svGetScope 35 | except Exception as e: 36 | print("Exception: %s" % str(e), flush=True) 37 | scope = svdpi.svGetScope() 38 | print("scope: %s" % str(scope), flush=True) 39 | 40 | _closure = lambda : thread_1(lib, top_scope) 41 | lib.pyhdl_queue_runnable(ctypes.py_object(_closure)) 42 | # lib.pyhdl_queue_runnable(ctypes.py_object(thread_2)) 43 | 44 | 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /tests/smoke/run.tcl: -------------------------------------------------------------------------------- 1 | run -all 2 | exit 3 | -------------------------------------------------------------------------------- /tests/smoke/runit.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | script_dir=$(dirname $(realpath $0)) 4 | 5 | verilator --binary -sv \ 6 | +incdir+../../src/hdl_call_if/share/sv/ \ 7 | ../../src/hdl_call_if/share/sv/pyhdl_call_if.sv \ 8 | top.sv \ 9 | -LDFLAGS "-Wl,-L,/project/tools/python/3.12.0/lib -Wl,-rpath,/project/tools/python/3.12.0/lib -Wl,-lpython3.12 -rdynamic" 10 | 11 | #GDB="gdb --args" 12 | export LD_PRELOAD=/project/tools/python/3.12.0/lib/libpython3.12.so 13 | #PYTHONPATH=${script_dir}/../../python ${GDB} ./obj_dir/Vpyhdl_call_if 14 | PYTHONPATH=${script_dir}:${script_dir}/../../python ${GDB} ./obj_dir/Vpyhdl_call_if 15 | 16 | -------------------------------------------------------------------------------- /tests/smoke/runit_mti.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | script_dir=$(dirname $(realpath $0)) 4 | 5 | vlib work 6 | vlog -sv \ 7 | +incdir+../../src/hdl_call_if/share/sv/ \ 8 | ../../src/hdl_call_if/share/sv/pyhdl_call_if.sv \ 9 | top.sv 10 | 11 | export LD_LIBRARY_PATH=/project/tools/python/3.12.0/lib 12 | export PYTHONPATH=${script_dir}:${script_dir}/../../python 13 | export LD_PRELOAD=/project/tools/python/3.12.0/lib/libpython3.12.so 14 | 15 | vsim -trace_dpi 1 -c -do "run -a; quit -f" top \ 16 | -sv_lib /project/tools/python/3.12.0/lib/libpython3.12 17 | 18 | #GDB="gdb --args" 19 | #PYTHONPATH=${script_dir}:${script_dir}/../../python ${GDB} ./obj_dir/Vpyhdl_call_if 20 | #PYTHONPATH=${script_dir}/../../python ${GDB} ./obj_dir/Vpyhdl_call_if 21 | 22 | -------------------------------------------------------------------------------- /tests/smoke/smoke.core: -------------------------------------------------------------------------------- 1 | CAPI=2: 2 | 3 | name: smoke 4 | 5 | filesets: 6 | hvl: 7 | files: 8 | - top.sv 9 | file_type: systemVerilogSource 10 | depend: 11 | - hdl_call_if 12 | 13 | targets: 14 | default: 15 | filesets: 16 | - hvl 17 | -------------------------------------------------------------------------------- /tests/smoke/test_smoke.py: -------------------------------------------------------------------------------- 1 | #**************************************************************************** 2 | #* test_smoke.py 3 | #* 4 | #* Copyright 2023 Matthew Ballance and Contributors 5 | #* 6 | #* Licensed under the Apache License, Version 2.0 (the "License"); you may 7 | #* not use this file except in compliance with the License. 8 | #* You may obtain a copy of the License at: 9 | #* 10 | #* http://www.apache.org/licenses/LICENSE-2.0 11 | #* 12 | #* Unless required by applicable law or agreed to in writing, software 13 | #* distributed under the License is distributed on an "AS IS" BASIS, 14 | #* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | #* See the License for the specific language governing permissions and 16 | #* limitations under the License. 17 | #* 18 | #* Created on: 19 | #* Author: 20 | #* 21 | #**************************************************************************** 22 | import os 23 | import pytest 24 | import sysconfig 25 | from pytest_fv import * 26 | from ..util import Util 27 | 28 | def test_smoke(request): 29 | utils = Util(request) 30 | 31 | testdir = os.path.dirname(os.path.abspath(__file__)) 32 | pyhdl_dir = os.path.abspath(os.path.join(testdir, "../..")) 33 | 34 | fs = FuseSoc() 35 | fs.add_library(os.path.join(pyhdl_dir, "packages/uvmf-core/uvmf_base_pkg")) 36 | fs.add_library(os.path.join(pyhdl_dir, "src/hdl_call_if/share/sv")) 37 | fs.add_library(testdir) 38 | 39 | sim = HdlSim.create(testdir, "vlt") 40 | sim.addFiles(fs.getFiles("smoke")) 41 | sim.top.add("top") 42 | 43 | libs = sysconfig.get_config_var("LIBS") 44 | libdir = sysconfig.get_config_var("LIBDIR") 45 | libpython = sysconfig.get_config_var("LDLIBRARY") 46 | 47 | # LDLIBRARY 48 | # LIBDIR 49 | sim.dpi_libs.append(os.path.join(libdir, libpython)) 50 | 51 | sim.build() 52 | 53 | # for key,val in sysconfig.get_config_vars().items(): 54 | # print("Elem: %s = %s" % (key, val)) 55 | 56 | 57 | args = sim.mkRunArgs(testdir) 58 | 59 | sim.run(args) 60 | -------------------------------------------------------------------------------- /tests/smoke/top.sv: -------------------------------------------------------------------------------- 1 | 2 | module top; 3 | import pyhdl_call_if::*; 4 | 5 | task automatic timed_task(); 6 | $display("--> timed_task"); 7 | repeat (20) begin 8 | $display("-- tick"); 9 | #1ns; 10 | end 11 | $display("<-- timed_task"); 12 | endtask 13 | export "DPI-C" task timed_task; 14 | import "DPI-C" context function longint unsigned svGetScope(); 15 | 16 | longint unsigned scope = svGetScope(); 17 | 18 | initial begin 19 | PyObject test_smoke; 20 | PyObject args; 21 | PyObject my_method; 22 | longint unsigned our_scope1, our_scope2; 23 | 24 | our_scope1 = svGetScope(); 25 | 26 | pyhdl_start(); 27 | 28 | our_scope2 = svGetCallerScope(); 29 | $display("CallerScope: %08h %08h %08h", scope, our_scope1, our_scope2); 30 | 31 | test_smoke = PyImport_ImportModule("my_module"); 32 | if (test_smoke == null) begin 33 | $display("Fatal: failed to load module"); 34 | $finish; 35 | end else begin 36 | my_method = PyObject_GetAttrString(test_smoke, "my_method"); 37 | void'(PyObject_Call(my_method, pyhdl_mkTuple('{ 38 | PyLong_FromUnsignedLongLong(scope) 39 | }), null)); 40 | end 41 | repeat (100) begin 42 | #1ns; 43 | end 44 | $finish; 45 | end 46 | 47 | endmodule 48 | -------------------------------------------------------------------------------- /tests/sys/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvutils/pyhdl-if/15c3e1bcfc56f0506f7ac7808109d0ba061a2378/tests/sys/__init__.py -------------------------------------------------------------------------------- /tests/sys/dpi/data/api_gen_1.py: -------------------------------------------------------------------------------- 1 | import ctypes 2 | import hdl_call_if as hci 3 | 4 | @hci.api 5 | class MyC(object): 6 | 7 | @hci.expfunc 8 | def get_py_val(self, i : ctypes.c_int) -> ctypes.c_int: 9 | print("get_py_val: %d" % i, flush=True) 10 | return i + 5 11 | pass 12 | 13 | 14 | -------------------------------------------------------------------------------- /tests/sys/dpi/data/api_gen_1.sv: -------------------------------------------------------------------------------- 1 | 2 | package api_pkg; 3 | import pyhdl_dpi_if::*; 4 | import pyhdl_call_if::*; 5 | 6 | `include "api_gen_1.svh" 7 | 8 | endpackage 9 | 10 | module api_gen_1; 11 | import api_pkg::*; 12 | 13 | initial begin 14 | automatic MyC c = new(); 15 | $display("val: %0d", c.get_py_val(20)); 16 | $finish; 17 | end 18 | 19 | endmodule 20 | -------------------------------------------------------------------------------- /tests/sys/dpi/data/api_gen_ext_1.py: -------------------------------------------------------------------------------- 1 | import ctypes 2 | import hdl_call_if as hci 3 | 4 | @hci.api 5 | class MyC(object): 6 | 7 | @hci.expfunc 8 | def get_py_val(self, i : ctypes.c_int) -> ctypes.c_int: 9 | print("get_py_val: %d" % i, flush=True) 10 | return i + 5 11 | 12 | class MyCExt(MyC): 13 | 14 | def get_py_val(self, i): 15 | print("get_py_val(ext): %d" % i, flush=True) 16 | return i+8 17 | 18 | 19 | -------------------------------------------------------------------------------- /tests/sys/dpi/data/api_gen_ext_1.sv: -------------------------------------------------------------------------------- 1 | 2 | package api_pkg; 3 | import pyhdl_dpi_if::*; 4 | import pyhdl_call_if::*; 5 | 6 | `include "api_gen_ext_1.svh" 7 | 8 | endpackage 9 | 10 | module api_gen_ext_1; 11 | import api_pkg::*; 12 | import pyhdl_dpi_if::*; 13 | 14 | initial begin 15 | automatic PyObject obj = pyhdl_pi_if_NewClass("api_gen_ext_1", "MyCExt", {}); 16 | automatic MyC_wrap c = new(); 17 | 18 | // Connect our SV and Python classes 19 | c.init(obj); 20 | 21 | $display("val: %0d", c.get_py_val(20)); 22 | $finish; 23 | end 24 | 25 | endmodule 26 | -------------------------------------------------------------------------------- /tests/sys/dpi/data/api_gen_uvm_1.py: -------------------------------------------------------------------------------- 1 | import ctypes 2 | import hdl_call_if as hci 3 | 4 | @hci.api 5 | class MyC(object): 6 | 7 | @hci.expfunc 8 | def get_py_val(self, i : ctypes.c_int) -> ctypes.c_int: 9 | print("get_py_val: %d" % i, flush=True) 10 | return i + 5 11 | pass 12 | 13 | 14 | -------------------------------------------------------------------------------- /tests/sys/dpi/data/api_gen_uvm_1.sv: -------------------------------------------------------------------------------- 1 | `include "uvm_macros.svh" 2 | 3 | package api_pkg; 4 | import uvm_pkg::*; 5 | import pyhdl_dpi_if::*; 6 | import pyhdl_call_if::*; 7 | 8 | `include "api_gen_uvm_1.svh" 9 | 10 | endpackage 11 | 12 | module api_gen_uvm_1; 13 | import uvm_pkg::*; 14 | import api_pkg::*; 15 | 16 | class MyCls extends MyC #(.CREATE(0)); 17 | `uvm_object_utils(MyCls) 18 | 19 | function new(string name="MyCls"); 20 | super.new(name); 21 | endfunction 22 | 23 | function void startup(); 24 | m_obj = create_pyobj(); 25 | init(m_obj); 26 | endfunction 27 | 28 | endclass 29 | 30 | initial begin 31 | automatic MyCls c = new(); 32 | c.startup(); 33 | $display("val: %0d", c.get_py_val(20)); 34 | $finish; 35 | end 36 | 37 | endmodule 38 | -------------------------------------------------------------------------------- /tests/sys/dpi/data/async_call.py: -------------------------------------------------------------------------------- 1 | import hdl_call_if as hci 2 | 3 | class MyC(object): 4 | 5 | def __init__(self): 6 | print("MyC::__init__", flush=True) 7 | 8 | def inbound(self): 9 | print("inbound", flush=True) 10 | 11 | if hasattr(self, "__proxy"): 12 | print("has", flush=True) 13 | proxy = getattr(self, "__proxy") 14 | else: 15 | print("doesn't have", flush=True) 16 | 17 | try: 18 | ret = proxy.invoke_hdl_f("outbound", ()) 19 | except Exception as e: 20 | print("Exception(inbound): %s" % str(e), flush=True) 21 | 22 | return ret 23 | 24 | async def inbound_t(self): 25 | print("--> inbound_t"); 26 | 27 | if hasattr(self, "__proxy"): 28 | print("has", flush=True) 29 | proxy = getattr(self, "__proxy") 30 | else: 31 | print("doesn't have", flush=True) 32 | 33 | for i in range(10): 34 | await proxy.invoke_hdl_t( 35 | "outbound_t", 36 | () 37 | ) 38 | # await self.outbound_t() 39 | 40 | print("<-- inbound_t"); 41 | 42 | 43 | -------------------------------------------------------------------------------- /tests/sys/dpi/data/reqrsp_fifo_loopback.py: -------------------------------------------------------------------------------- 1 | 2 | import traceback 3 | import hdl_tlm_if as hti 4 | import vsc_dataclasses as vdc 5 | 6 | @hti.tlm_if 7 | class reqrsp_fifo(object): 8 | 9 | @vdc.randclass 10 | class Req(object): 11 | data : vdc.bit_t[8] 12 | data2 : vdc.bit_t[8] 13 | 14 | @vdc.randclass 15 | class Rsp(object): 16 | data : vdc.bit_t[8] 17 | data2 : vdc.bit_t[8] 18 | 19 | @hti.req_fifo 20 | async def req(self, req : Req): 21 | pass 22 | 23 | @hti.rsp_fifo 24 | async def rsp(self, rsp : Rsp): 25 | pass 26 | 27 | evt = None 28 | def callback(): 29 | global evt 30 | print("callback", flush=True) 31 | evt.set() 32 | 33 | # Test entry-point 34 | async def run(): 35 | global evt 36 | from hdl_pi_if.hdl_services import HdlServices 37 | from hdl_pi_if.backend import Backend 38 | 39 | print("run", flush=True) 40 | reqrsp_if = reqrsp_fifo() 41 | 42 | await hti.initialize() 43 | 44 | ifcs = hti.get_interfaces() 45 | 46 | print("ifcs: %s" % str(ifcs), flush=True) 47 | 48 | try: 49 | services = HdlServices.inst() 50 | services.registerTimeCB(callback, 0) 51 | 52 | be = Backend.inst() 53 | evt = be.mkEvent() 54 | 55 | print("--> waiting", flush=True) 56 | await evt.wait() 57 | print("<-- waiting", flush=True) 58 | 59 | await hti.connect_if(reqrsp_if, ifcs[0], False) 60 | 61 | except Exception as e: 62 | print("Exception: %s" % str(e), flush=True) 63 | traceback.print_exc() 64 | 65 | 66 | try: 67 | for i in range(20): 68 | req = reqrsp_fifo.Req() 69 | req.data = i; 70 | req.data2 = i+1; 71 | await reqrsp_if.req(req) 72 | rsp = reqrsp_fifo.Rsp() 73 | await reqrsp_if.rsp(rsp) 74 | print("rsp: data=%08x data2=%08x" % (rsp.data, rsp.data2)) 75 | 76 | except Exception as e: 77 | print("Exception: %s" % str(e)) 78 | traceback.print_exc() 79 | 80 | 81 | -------------------------------------------------------------------------------- /tests/sys/dpi/data/smoke.py: -------------------------------------------------------------------------------- 1 | 2 | class Smoke(object): 3 | 4 | def sum(self, a, b): 5 | return a + b 6 | -------------------------------------------------------------------------------- /tests/sys/dpi/data/smoke.sv: -------------------------------------------------------------------------------- 1 | 2 | module smoke; 3 | import pyhdl_call_if::*; 4 | import pyhdl_dpi_if::*; 5 | 6 | class MyC; 7 | PyObject m_obj; 8 | function new(); 9 | PyObject smoke_obj, smoke_c; 10 | PyObject args, obj_h; 11 | 12 | if ((smoke_obj = PyImport_ImportModule("smoke")) == null) begin 13 | $display("Fatal: failed to import smoke"); 14 | $finish; 15 | end 16 | if ((smoke_c = PyObject_GetAttrString(smoke_obj, "Smoke")) == null) begin 17 | $display("Fatal: failed to find Smoke"); 18 | $finish; 19 | end 20 | 21 | args = PyTuple_New(0); 22 | Py_IncRef(smoke_c); 23 | m_obj = pyhdl_call_if_new(smoke_c, args); 24 | endfunction 25 | 26 | function int sum(int a, int b); 27 | PyObject args = PyTuple_New(2); 28 | PyObject func = PyObject_GetAttrString(m_obj, "sum"); 29 | PyObject res; 30 | int ret; 31 | 32 | PyTuple_SetItem(args, 0, PyLong_FromLong(a)); 33 | PyTuple_SetItem(args, 1, PyLong_FromLong(b)); 34 | 35 | res = PyObject_Call(func, args, null); 36 | 37 | ret = PyLong_AsLong(res); 38 | 39 | pyhdl_pi_if_idle(); 40 | 41 | return ret; 42 | endfunction 43 | endclass 44 | 45 | initial begin 46 | automatic MyC ci = new(); 47 | 48 | $display("smoke"); 49 | $display("1+2 = %0d", ci.sum(1, 2)); 50 | end 51 | 52 | endmodule 53 | -------------------------------------------------------------------------------- /tests/sys/dpi/data/test_load.py: -------------------------------------------------------------------------------- 1 | 2 | def init(): 3 | print("Hello from test_load", flush=True) 4 | pass -------------------------------------------------------------------------------- /tests/sys/dpi/data/test_load.sv: -------------------------------------------------------------------------------- 1 | 2 | module test_load; 3 | import pyhdl_dpi_if::*; 4 | 5 | initial begin 6 | automatic PyObject test_load_mod, init_m; 7 | automatic PyObject init_args, res; 8 | $display("Hello"); 9 | 10 | test_load_mod = PyImport_ImportModule("test_load"); 11 | if (test_load_mod == null) begin 12 | $display("Fail: failed to load 'test_load' module"); 13 | $finish; 14 | end 15 | 16 | init_m = PyObject_GetAttrString(test_load_mod, "init"); 17 | if (init_m == null) begin 18 | $display("Fail: failed to get 'init' module"); 19 | $finish; 20 | end 21 | 22 | init_args = PyTuple_New(0); 23 | 24 | res = PyObject_Call(init_m, init_args, null); 25 | 26 | Py_DecRef(init_args); 27 | Py_DecRef(res); 28 | 29 | $finish; 30 | end 31 | endmodule 32 | -------------------------------------------------------------------------------- /tests/sys/dpi/test_dpi.py: -------------------------------------------------------------------------------- 1 | import os 2 | import pytest 3 | import pytest_fv as ptv 4 | from pytest_fv.fixtures import * 5 | import sys 6 | 7 | def test_reqrsp_fifo_loopback(dirconfig): 8 | from ivpm import PkgInfoRgy 9 | 10 | tests_dir = os.path.dirname(os.path.abspath(__file__)) 11 | data_dir = os.path.join(tests_dir, "data") 12 | proj_dir = os.path.abspath(os.path.join(tests_dir, "../../..")) 13 | 14 | flow = ptv.FlowSim(dirconfig, "mti") 15 | flow.sim.debug = True 16 | 17 | flow.addFileset("sim", ptv.FSVlnv("pyhdl-if::tlm")) 18 | flow.addFileset("sim", ptv.FSPaths([ 19 | os.path.join(data_dir, "reqrsp_fifo_loopback.sv")], "verilogSource")) 20 | flow.sim.addIncdir(dirconfig.builddir()) 21 | flow.sim.top.add("reqrsp_fifo_loopback") 22 | 23 | pi_if_pkg = PkgInfoRgy.inst().getPkg("pyhdl-call-if") 24 | flow.sim.dpi_libs.extend(PkgInfoRgy.inst().getLibs("dpi")) 25 | flow.sim.addLibDirs(PkgInfoRgy.inst().getLibDirs("dpi")) 26 | # flow.sim.setenv("PYTHONHOME", os.path.join(proj_dir, "packages/python")) 27 | flow.sim.append_pathenv("PYTHONPATH", os.path.join(proj_dir, "src")) 28 | flow.sim.append_pathenv("PYTHONPATH", os.path.join(proj_dir, "packages/pyhdl-pi-if/python")) 29 | flow.sim.append_pathenv("PYTHONPATH", os.path.join(proj_dir, "packages/pyhdl-call-if/src")) 30 | flow.sim.append_pathenv("PYTHONPATH", os.path.join(proj_dir, "packages/pyvsc-dataclasses/src")) 31 | flow.sim.append_pathenv("PYTHONPATH", os.path.join(proj_dir, "packages/pytypeworks/src")) 32 | flow.sim.append_pathenv("PYTHONPATH", data_dir) 33 | 34 | args = flow.sim.mkRunArgs(dirconfig.rundir()) 35 | flow.addTaskToPhase("run.main", flow.sim.mkRunTask(args)) 36 | 37 | flow.run_all() 38 | 39 | pass -------------------------------------------------------------------------------- /tests/sys/dpi/test_dpi_if.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | dpi_vpi_dir = os.path.dirname(os.path.abspath(__file__)) 4 | proj_dir = os.path.abspath(os.path.join(dpi_vpi_dir, "../../..")) 5 | sys.path.append(os.path.join(proj_dir, "python")) 6 | 7 | import asyncio 8 | import pytest 9 | import pytest_fv as ptv 10 | import sysconfig 11 | from pytest_fv.fixtures import * 12 | import hdl_pi_if 13 | 14 | def test_load(dirconfig): 15 | dpi_vpi_dir = os.path.dirname(os.path.abspath(__file__)) 16 | dpi_vpi_data_dir = os.path.join(dpi_vpi_dir, "data") 17 | proj_dir = os.path.abspath(os.path.join(dpi_vpi_dir, "../../..")) 18 | 19 | 20 | flow = ptv.FlowSim(dirconfig, "xsm") 21 | flow.addFileset("sim", ptv.FSPaths([ 22 | os.path.join(proj_dir, "python/hdl_pi_if/share/dpi/pyhdl_dpi_if.sv"), 23 | os.path.join(dpi_vpi_data_dir, "test_load.sv") 24 | ], 25 | "systemVerilogSource", 26 | incs=[ 27 | os.path.join(proj_dir, "python/hdl_pi_if/share/dpi") 28 | ])) 29 | 30 | from hdl_pi_if.pkginfo import PkgInfo 31 | pkginfo = PkgInfo() 32 | flow.sim.dpi_libs.extend(pkginfo.getLibs("dpi")) 33 | print("dpi_libs: %s" % str(flow.sim.dpi_libs)) 34 | flow.sim.top.add("test_load") 35 | 36 | args = flow.sim.mkRunArgs(dirconfig.rundir()) 37 | args.env["PYTHONPATH"] = os.path.join(proj_dir, "python") + ":" + dpi_vpi_data_dir 38 | # args.env["LD_LIBRARY_PATH"] = "/project/tools/python/3.12.0/lib" 39 | # args.env["LD_PRELOAD"] = "/project/tools/python/3.12.0/lib/libpython3.12.so" 40 | flow.addTaskToPhase("run.main", flow.sim.mkRunTask(args)) 41 | 42 | flow.run_all() 43 | -------------------------------------------------------------------------------- /tests/sys/dpi/vpi/data/smoke.sv: -------------------------------------------------------------------------------- 1 | 2 | module smoke; 3 | 4 | initial begin 5 | $display("Hello World"); 6 | $finish; 7 | end 8 | 9 | endmodule -------------------------------------------------------------------------------- /tests/sys/dpi_vpi/data/load.py: -------------------------------------------------------------------------------- 1 | import ctypes 2 | import hdl_pi_if.vpi.api as vpi 3 | 4 | pkg_hndl = None 5 | exp_func = None 6 | 7 | def my_task(p): 8 | global pkg_hndl 9 | global exp_func 10 | print("my_task", flush=True) 11 | import hdl_pi_if.dpi as dpi 12 | 13 | if exp_func is None: 14 | lib = ctypes.cdll.LoadLibrary(None) 15 | exp_func = getattr(lib, "exp_func", None) 16 | print("exp_func=%s" % str(exp_func)) 17 | 18 | print("-1- Get scope: %s" % str(dpi.svGetScope()), flush=True) 19 | print(" Name: %s" % dpi.svGetNameFromScope(dpi.svGetScope()).decode()) 20 | print("-2- Set scope to %s" % str(pkg_hndl)) 21 | dpi.svSetScope(pkg_hndl) 22 | print("-3- Get scope: %s" % str(dpi.svGetScope()), flush=True) 23 | exp_func() 24 | print("TODO: Invoke dpi-export function") 25 | 26 | return 0 27 | my_task_t = None 28 | my_task_h = None 29 | 30 | 31 | def dpi_init(p): 32 | import hdl_pi_if.dpi as dpi 33 | global pkg_hndl 34 | # pkg_hndl = dpi.svGetScope() 35 | pkg_hndl = p 36 | print("dpi_init: %s" % str(pkg_hndl), flush=True) 37 | print("Name: %s" % dpi.svGetNameFromScope(pkg_hndl).decode()) 38 | 39 | def vlog_startup(): 40 | global my_task_t, my_task_h 41 | print("vlog_startup") 42 | 43 | my_task_t = ctypes.CFUNCTYPE(ctypes.c_int, ctypes.POINTER(ctypes.c_byte)) 44 | my_task_h = my_task_t(my_task) 45 | try: 46 | systf_i = vpi.t_vpi_systf_data() 47 | systf_i.tfname = "$my_task".encode() 48 | systf_i.type = vpi.vpiSysTask 49 | print("my_task_h: %s" % str(my_task_h), flush=True) 50 | systf_i.calltf = my_task_h 51 | # systf_i.compiletf = None 52 | # systf_i.sizetf = None 53 | systf_i.userdata = None 54 | print("vpi_register_systf=%s" % str(vpi.vpi_register_systf), flush=True) 55 | ret = vpi.vpi_register_systf(ctypes.pointer(systf_i)) 56 | print("ret: %s" % str(ret), flush=True) 57 | except Exception as e: 58 | print("Exception: %s" % str(e), flush=True) 59 | -------------------------------------------------------------------------------- /tests/sys/dpi_vpi/data/test_load.sv: -------------------------------------------------------------------------------- 1 | 2 | package sv_pkg; 3 | import pyhdl_dpi_if::*; 4 | function automatic void exp_func(); 5 | $display("exp_func"); 6 | endfunction 7 | export "DPI-C" function exp_func; 8 | 9 | import "DPI-C" context function chandle svGetScope(); 10 | import "DPI-C" context function string svGetNameFromScope(chandle s); 11 | 12 | 13 | function automatic bit __do_init(); 14 | automatic PyObject test_load_mod, init_m; 15 | automatic PyObject init_args, res; 16 | $display("Hello scope=%p (%0s)", svGetScope(), svGetNameFromScope(svGetScope())); 17 | $display("Hello scope=%p", svGetScope()); 18 | 19 | test_load_mod = PyImport_ImportModule("load"); 20 | if (test_load_mod == null) begin 21 | $display("Fail: failed to load 'load' module"); 22 | $finish; 23 | end 24 | 25 | init_m = PyObject_GetAttrString(test_load_mod, "dpi_init"); 26 | if (init_m == null) begin 27 | $display("Fail: failed to get 'init' module"); 28 | $finish; 29 | end 30 | 31 | init_args = PyTuple_New(1); 32 | PyTuple_SetItem(init_args, 0, PyLong_FromVoidPtr(svGetScope())); 33 | 34 | res = PyObject_Call(init_m, init_args, null); 35 | 36 | Py_DecRef(init_args); 37 | Py_DecRef(res); 38 | 39 | return 1; 40 | endfunction 41 | bit __is_init = __do_init(); 42 | endpackage 43 | 44 | 45 | module test_load; 46 | import pyhdl_dpi_if::*; 47 | import sv_pkg::*; 48 | 49 | initial begin 50 | #10ns; 51 | 52 | $my_task(); 53 | 54 | $finish; 55 | end 56 | endmodule 57 | -------------------------------------------------------------------------------- /tests/sys/dpi_vpi/test_dpi_vpi_if.py: -------------------------------------------------------------------------------- 1 | import os 2 | import asyncio 3 | import pytest 4 | import pytest_fv as ptv 5 | import sysconfig 6 | from pytest_fv.fixtures import * 7 | import hdl_pi_if 8 | 9 | def test_load(dirconfig): 10 | dpi_vpi_dir = os.path.dirname(os.path.abspath(__file__)) 11 | dpi_vpi_data_dir = os.path.join(dpi_vpi_dir, "data") 12 | proj_dir = os.path.abspath(os.path.join(dpi_vpi_dir, "../../..")) 13 | 14 | flow = ptv.FlowSim(dirconfig, "mti") 15 | flow.addFileset("sim", ptv.FSPaths([ 16 | os.path.join(proj_dir, "python/hdl_pi_if/share/dpi/pyhdl_dpi_if.sv"), 17 | os.path.join(dpi_vpi_data_dir, "test_load.sv") 18 | ], 19 | "systemVerilogSource", 20 | incs=[ 21 | os.path.join(proj_dir, "python/hdl_pi_if/share/dpi") 22 | ])) 23 | flow.sim.dpi_libs.append(hdl_pi_if.get_entry()) 24 | flow.sim.pli_libs.append(hdl_pi_if.get_entry()) 25 | flow.sim.top.add("test_load") 26 | 27 | args = flow.sim.mkRunArgs(dirconfig.rundir()) 28 | args.env["PYTHONPATH"] = os.path.join(proj_dir, "python") + ":" + dpi_vpi_data_dir 29 | args.env["LD_LIBRARY_PATH"] = "/project/tools/python/3.12.0/lib" 30 | args.plusargs.append("pyhdl-pi-if.mod=load") 31 | # args.env["LD_PRELOAD"] = "/project/tools/python/3.12.0/lib/libpython3.12.so" 32 | flow.addTaskToPhase("run.main", flow.sim.mkRunTask(args)) 33 | 34 | flow.run_all() 35 | -------------------------------------------------------------------------------- /tests/sys/hdl/data/hdl2hvl_fifo.sv: -------------------------------------------------------------------------------- 1 | 2 | module hdl2hvl_fifo; 3 | reg clock = 0; 4 | reg reset = 1; 5 | 6 | initial begin 7 | forever begin 8 | #10ns; 9 | clock <= ~clock; 10 | end 11 | end 12 | 13 | initial begin 14 | repeat (20) begin 15 | @(posedge clock); 16 | end 17 | reset = 0; 18 | end 19 | 20 | wire ready; 21 | reg valid = 1; 22 | reg[31:0] data = 1; 23 | 24 | always @(posedge clock) begin 25 | $display("clock: reset=%0d", reset); 26 | if (ready && valid) begin 27 | $display("Write data: 0x%08h", data); 28 | data <= data + 1; 29 | end 30 | end 31 | 32 | tlm_hdl2hvl_fifo #( 33 | .Tdepth(4) 34 | ) u_fifo ( 35 | .clock(clock), 36 | .reset(reset), 37 | .ready(ready), 38 | .valid(valid), 39 | .dat_i(data) 40 | ); 41 | 42 | initial begin 43 | automatic int i, tdat; 44 | while (reset == 1) begin 45 | @(posedge clock); 46 | end 47 | for (i=0; i<20; i=i+1) begin 48 | u_fifo.get(tdat); 49 | $display("Read data: 0x%08h", tdat); 50 | end 51 | $finish; 52 | end 53 | endmodule 54 | -------------------------------------------------------------------------------- /tests/sys/hdl/data/hvl2hdl_fifo.sv: -------------------------------------------------------------------------------- 1 | 2 | module hvl2hdl_fifo; 3 | reg clock = 0; 4 | reg reset = 1; 5 | 6 | initial begin 7 | forever begin 8 | #10ns; 9 | clock <= ~clock; 10 | end 11 | end 12 | 13 | initial begin 14 | repeat (20) begin 15 | @(posedge clock); 16 | end 17 | reset = 0; 18 | end 19 | 20 | reg ready = 1; 21 | wire valid; 22 | wire[31:0] data; 23 | 24 | always @(posedge clock) begin 25 | $display("clock: reset=%0d", reset); 26 | if (ready && valid) begin 27 | $display("data: 0x%08h", data); 28 | end 29 | end 30 | 31 | tlm_hvl2hdl_fifo #( 32 | .Tdepth(4) 33 | ) u_fifo ( 34 | .clock(clock), 35 | .reset(reset), 36 | .ready(ready), 37 | .valid(valid), 38 | .dat_o(data) 39 | ); 40 | 41 | initial begin 42 | automatic int i; 43 | while (reset == 1) begin 44 | @(posedge clock); 45 | end 46 | for (i=1; i<=20; i=i+1) begin 47 | u_fifo.put(i); 48 | end 49 | $finish; 50 | end 51 | endmodule 52 | -------------------------------------------------------------------------------- /tests/sys/vpi/data/load.py: -------------------------------------------------------------------------------- 1 | 2 | print("load", flush=True) 3 | 4 | 5 | 6 | 7 | def vlog_startup(): 8 | print("vlog_startup", flush=True) 9 | -------------------------------------------------------------------------------- /tests/sys/vpi/data/test_load.sv: -------------------------------------------------------------------------------- 1 | 2 | module test_load; 3 | 4 | initial begin : local 5 | reg[63:0] imp; 6 | $display("Hello World"); 7 | // $my_task; 8 | $Py_Initialize(); 9 | imp = $PyImport_ImportModule("load"); 10 | $display("imp: %0d", imp); 11 | 12 | end 13 | 14 | endmodule 15 | 16 | -------------------------------------------------------------------------------- /tests/sys/vpi/test_vpi_if.py: -------------------------------------------------------------------------------- 1 | import os 2 | import asyncio 3 | import pytest 4 | import pytest_fv as ptv 5 | from pytest_fv.fixtures import * 6 | import hdl_pi_if 7 | 8 | def test_load(dirconfig): 9 | vpi_dir = os.path.dirname(os.path.abspath(__file__)) 10 | vpi_data_dir = os.path.join(vpi_dir, "data") 11 | proj_dir = os.path.abspath(os.path.join(vpi_dir, "../../..")) 12 | 13 | flow = ptv.FlowSim(dirconfig, "ivl") 14 | flow.addFileset("sim", ptv.FSPaths([ 15 | os.path.join(vpi_data_dir, "test_load.sv") 16 | ], 17 | "verilogSource")) 18 | flow.sim.top.add("test_load") 19 | flow.sim.pli_libs.append(hdl_pi_if.get_entry()) 20 | flow.sim.plusargs.append("pyhdl-pi-if.mod=load") 21 | 22 | args = flow.sim.mkRunArgs(dirconfig.rundir()) 23 | args.env["PYTHONPATH"] = os.path.join(proj_dir, "python") + ":" + vpi_data_dir 24 | args.env["LD_LIBRARY_PATH"] = "/project/tools/python/3.12.0/lib" 25 | flow.addTaskToPhase("run.main", flow.sim.mkRunTask(args)) 26 | 27 | loop = asyncio.new_event_loop() 28 | asyncio.set_event_loop(loop) 29 | loop.run_until_complete(flow.run()) 30 | 31 | pass 32 | 33 | -------------------------------------------------------------------------------- /tests/unit/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvutils/pyhdl-if/15c3e1bcfc56f0506f7ac7808109d0ba061a2378/tests/unit/__init__.py -------------------------------------------------------------------------------- /tests/unit/data/py_api/a_plus_b.py: -------------------------------------------------------------------------------- 1 | 2 | def a_plus_b(a, b): 3 | return a + b; 4 | -------------------------------------------------------------------------------- /tests/unit/data/py_api/a_plus_b.sv: -------------------------------------------------------------------------------- 1 | 2 | module a_plus_b; 3 | import pyhdl_if::*; 4 | 5 | initial begin 6 | py_object a_plus_b, foo; 7 | int ret; 8 | 9 | a_plus_b = py_import("a_plus_b"); 10 | foo = a_plus_b.get_attr("a_plus_b"); 11 | 12 | ret = foo.call(py_tuple::mk_init('{ 13 | py_from_int(5), py_from_int(13)})).to_int(); 14 | // ret = foo.call('{py_from_long(5), py_from_long(13)}).to_int(); 15 | 16 | begin 17 | int fp = $fopen("status.txt", "w"); 18 | if (ret == 18) begin 19 | $fwrite(fp, "PASS:\n"); 20 | end else begin 21 | $fwrite(fp, "FAIL: %0d != 18\n", ret); 22 | end 23 | $fclose(fp); 24 | end 25 | $finish; 26 | end 27 | 28 | endmodule 29 | -------------------------------------------------------------------------------- /tests/unit/data/py_api/data1.json: -------------------------------------------------------------------------------- 1 | { 2 | "a" : { 3 | "b" : [1, 2, 3, 4], 4 | "c" : [5, 6, 7, 8] 5 | }, 6 | "b" : { 7 | "b" : [9, 10, 11, 12], 8 | "c" : [13, 14, 15, 16] 9 | } 10 | } -------------------------------------------------------------------------------- /tests/unit/data/py_api/data1.sv: -------------------------------------------------------------------------------- 1 | 2 | module data1; 3 | import pyhdl_if::*; 4 | 5 | initial begin 6 | automatic string datafile; 7 | automatic py_object json, data_fp, data_s; 8 | automatic py_dict data; 9 | automatic py_list keys; 10 | automatic py_ctxt ctxt = new(); 11 | 12 | if (!$value$plusargs("data=%s", datafile)) begin 13 | $display("Error: no datafile specified"); 14 | $finish; 15 | end 16 | 17 | py_gil_enter(); 18 | 19 | // Import Python's 'json' package 20 | json = ctxt.add(py_import("json")); 21 | 22 | // Open and read the specified data file 23 | data_fp = ctxt.add(py_call_builtin( 24 | "open", py_tuple::mk_init('{ 25 | py_from_str(datafile), 26 | py_from_str("r")}))); 27 | data_s = ctxt.add(data_fp.call_attr("read")); 28 | data_fp.call_attr("close"); 29 | 30 | // Parse the data 31 | data = py_dict::mk(json.call_attr("loads", py_tuple::mk_init('{data_s})), ctxt); 32 | 33 | // Get the list of keys 34 | keys = data.keys(); 35 | 36 | // Iterate based on the list size 37 | for (int i=0; i ct.c_uint32: 14 | pass 15 | 16 | @hif.api 17 | class Test(object): 18 | 19 | @hif.exp 20 | async def run(self, bfm : ct.py_object): 21 | errors = 0 22 | print("run") 23 | 24 | for i in range(64): 25 | wr_val = (i+1) 26 | print(f'[Py] writing: {wr_val}') 27 | await bfm.write(0x8000_0000+(4*i), wr_val) 28 | rd = await bfm.read(0x8000_0000+(4*i)) 29 | print(f'[Py] readback: {rd}') 30 | if wr_val != rd: 31 | errors += 1 32 | 33 | with open("status.txt", "w") as fp: 34 | fp.write("%s: %d errors\n" % (("PASS" if errors==0 else "FAIL"), errors)) 35 | 36 | -------------------------------------------------------------------------------- /tests/unit/data/test_smoke/call_sv_bfm.sv: -------------------------------------------------------------------------------- 1 | 2 | module call_sv_bfm; 3 | import pyhdl_if::*; 4 | import call_sv_bfm_pkg::*; 5 | 6 | reg clk = 0; 7 | reg reset = 1; 8 | initial begin 9 | 10 | clk = 0; 11 | forever begin 12 | #10ns; 13 | clk = ~clk; 14 | end 15 | end 16 | 17 | wire[31:0] dat_r, dat_w; 18 | wire[31:0] adr; 19 | wire stb, cyc, we; 20 | reg ack_r; 21 | wire ack = (ack_r && cyc && stb); 22 | 23 | always @(posedge clk or reset) begin 24 | if (reset) begin 25 | ack_r <= 1'b0; 26 | end else begin 27 | ack_r <= (stb & cyc); 28 | end 29 | end 30 | 31 | assign dat_r = dat_w; 32 | 33 | WishboneInitiatorBFM init_bfm( 34 | .clock(clk), 35 | .reset(reset), 36 | .adr(adr), 37 | .dat_r(dat_r), 38 | .dat_w(dat_w), 39 | .stb(stb), 40 | .cyc(cyc), 41 | .ack(ack), 42 | .we(we) 43 | ); 44 | 45 | initial begin 46 | automatic Test test; 47 | 48 | pyhdl_if_start(); 49 | 50 | #50ns; 51 | reset = 0; 52 | 53 | // Create an instance of the Test class and run 54 | $display("%0t --> run", $time); 55 | test = new(); 56 | test.run(init_bfm.m_api_obj.m_obj); 57 | $display("%0t <-- run", $time); 58 | $finish; 59 | end 60 | 61 | endmodule 62 | -------------------------------------------------------------------------------- /tests/unit/data/test_smoke_str/call_sv_bfm.py: -------------------------------------------------------------------------------- 1 | 2 | import ctypes as ct 3 | import hdl_if as hif 4 | 5 | @hif.api 6 | class WishboneInitiator(object): 7 | 8 | @hif.imp 9 | async def is_eq_hello(self, val : str) -> ct.c_bool: 10 | pass 11 | 12 | @hif.imp 13 | async def write(self, addr : ct.c_uint32, data : ct.c_uint32): 14 | pass 15 | 16 | @hif.imp 17 | async def read(self, addr : ct.c_uint32) -> ct.c_uint32: 18 | pass 19 | 20 | @hif.api 21 | class Test(object): 22 | 23 | @hif.exp 24 | async def run(self, bfm : ct.py_object): 25 | errors = 0 26 | print("run") 27 | 28 | for i in range(64): 29 | wr_val = (i+1) 30 | print(f'[Py] writing: {wr_val}') 31 | await bfm.write(0x8000_0000+(4*i), wr_val) 32 | rd = await bfm.read(0x8000_0000+(4*i)) 33 | print(f'[Py] readback: {rd}') 34 | if wr_val != rd: 35 | errors += 1 36 | 37 | is_eq_1 = await bfm.is_eq_hello("hello") 38 | is_eq_2 = await bfm.is_eq_hello("goodbye") 39 | print("is_eq_1: %s" % is_eq_1, flush=True) 40 | print("is_eq_2: %s" % is_eq_2, flush=True) 41 | 42 | if not is_eq_1: 43 | errors += 1 44 | 45 | if is_eq_2: 46 | errors += 1 47 | 48 | with open("status.txt", "w") as fp: 49 | fp.write("%s: %d errors\n" % (("PASS" if errors==0 else "FAIL"), errors)) 50 | 51 | -------------------------------------------------------------------------------- /tests/unit/data/test_smoke_str/call_sv_bfm.sv: -------------------------------------------------------------------------------- 1 | 2 | module call_sv_bfm; 3 | import pyhdl_if::*; 4 | import call_sv_bfm_pkg::*; 5 | 6 | reg clk = 0; 7 | reg reset = 1; 8 | initial begin 9 | 10 | clk = 0; 11 | forever begin 12 | #10ns; 13 | clk = ~clk; 14 | end 15 | end 16 | 17 | wire[31:0] dat_r, dat_w; 18 | wire[31:0] adr; 19 | wire stb, cyc, we; 20 | reg ack_r; 21 | wire ack = (ack_r && cyc && stb); 22 | 23 | always @(posedge clk or reset) begin 24 | if (reset) begin 25 | ack_r <= 1'b0; 26 | end else begin 27 | ack_r <= (stb & cyc); 28 | end 29 | end 30 | 31 | assign dat_r = dat_w; 32 | 33 | WishboneInitiatorBFM init_bfm( 34 | .clock(clk), 35 | .reset(reset), 36 | .adr(adr), 37 | .dat_r(dat_r), 38 | .dat_w(dat_w), 39 | .stb(stb), 40 | .cyc(cyc), 41 | .ack(ack), 42 | .we(we) 43 | ); 44 | 45 | initial begin 46 | automatic Test test; 47 | 48 | pyhdl_if_start(); 49 | 50 | #50ns; 51 | reset = 0; 52 | 53 | // Create an instance of the Test class and run 54 | $display("%0t --> run", $time); 55 | test = new(); 56 | test.run(init_bfm.m_api_obj.m_obj); 57 | $display("%0t <-- run", $time); 58 | $finish; 59 | end 60 | 61 | endmodule 62 | -------------------------------------------------------------------------------- /tests/unit/data/vpi_py_if/vpi_py_if_smoke.v: -------------------------------------------------------------------------------- 1 | 2 | module vpi_py_if_smoke; 3 | 4 | initial begin 5 | $display("Hello World"); 6 | $finish; 7 | end 8 | 9 | endmodule 10 | -------------------------------------------------------------------------------- /tests/unit/disabled_test_decorators.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | import ctypes 3 | import os 4 | import pytest 5 | import sys 6 | 7 | tests_unit_dir = os.path.dirname(os.path.abspath(__file__)) 8 | proj_src_dir = os.path.abspath(os.path.join(tests_unit_dir, "../../src")) 9 | print("proj_src_dir: %s" % proj_src_dir) 10 | sys.path.insert(0, proj_src_dir) 11 | 12 | import hdl_call_if as hci 13 | 14 | @pytest.fixture 15 | def decorator_init(): 16 | from hdl_call_if.impl.ctor import Ctor 17 | from hdl_call_if.impl.api_def_rgy import ApiDefRgy 18 | print("setup") 19 | Ctor.init() 20 | ApiDefRgy.init() 21 | yield 1 22 | print("teardown") 23 | Ctor.init() 24 | ApiDefRgy.init() 25 | 26 | pass 27 | 28 | def test_single_api(decorator_init): 29 | from hdl_call_if.impl.api_def_rgy import ApiDefRgy 30 | 31 | @hci.api 32 | class API(object): 33 | 34 | @hci.impfunc 35 | def callout(self, 36 | a : ctypes.c_int, 37 | b : ctypes.c_int, 38 | c : ctypes.c_int): 39 | pass 40 | 41 | @hci.imptask 42 | async def callout_t(self, a : ctypes.c_int): 43 | pass 44 | 45 | 46 | a = API() 47 | # a.callout(1, 2, 3) 48 | # asyncio.new_event_loop().run_until_complete(a.callout_t(2)) 49 | 50 | rgy = ApiDefRgy.inst() 51 | apis = rgy.getApis() 52 | assert len(apis) == 1 53 | assert apis[0].name == "API" 54 | assert len(apis[0].methods) == 2 55 | 56 | 57 | pass -------------------------------------------------------------------------------- /tests/unit/disabled_test_generators.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | import ctypes 3 | import io 4 | import os 5 | import pytest 6 | import sys 7 | 8 | tests_unit_dir = os.path.dirname(os.path.abspath(__file__)) 9 | proj_src_dir = os.path.abspath(os.path.join(tests_unit_dir, "../../src")) 10 | print("proj_src_dir: %s" % proj_src_dir) 11 | sys.path.insert(0, proj_src_dir) 12 | 13 | import hdl_call_if as hci 14 | 15 | @pytest.fixture 16 | def decorator_init(): 17 | from hdl_call_if.impl.ctor import Ctor 18 | from hdl_call_if.impl.api_def_rgy import ApiDefRgy 19 | print("setup") 20 | Ctor.init() 21 | ApiDefRgy.init() 22 | yield 1 23 | print("teardown") 24 | Ctor.init() 25 | ApiDefRgy.init() 26 | 27 | pass 28 | 29 | def test_single_api(decorator_init): 30 | from hdl_call_if.impl.api_def_rgy import ApiDefRgy 31 | 32 | @hci.api 33 | class API(object): 34 | 35 | def __init__(self, a : ctypes.c_int, b : ctypes.c_int): 36 | pass 37 | 38 | @hci.impfunc 39 | def callout(self, 40 | a : ctypes.c_int, 41 | b : ctypes.c_int, 42 | c : ctypes.c_int): 43 | pass 44 | 45 | @hci.imptask 46 | async def callout_t(self, a : ctypes.c_int): 47 | pass 48 | 49 | 50 | # a = API() 51 | # a.callout(1, 2, 3) 52 | # asyncio.new_event_loop().run_until_complete(a.callout_t(2)) 53 | 54 | rgy = ApiDefRgy.inst() 55 | apis = rgy.getApis() 56 | assert len(apis) == 1 57 | assert apis[0].name == "API" 58 | assert len(apis[0].methods) == 2 59 | 60 | from hdl_call_if.impl.gen_sv_class import GenSVClass 61 | out = io.StringIO() 62 | gen = GenSVClass(out) 63 | gen.gen(apis[0]) 64 | 65 | print("Result:\n%s\n" % out.getvalue()) 66 | 67 | -------------------------------------------------------------------------------- /tests/unit/disabled_test_tlm_if_gen.py: -------------------------------------------------------------------------------- 1 | 2 | import os 3 | import sys 4 | import pytest 5 | 6 | @pytest.fixture 7 | def init_test(): 8 | import vsc_dataclasses as vdc 9 | unit_tests_dir = os.path.dirname(os.path.abspath(__file__)) 10 | proj_dir = os.path.abspath(os.path.join(unit_tests_dir, "../..")) 11 | proj_src_dir = os.path.join(proj_dir, "src") 12 | if proj_src_dir not in sys.path: 13 | sys.path.append(proj_src_dir) 14 | yield 5 15 | 16 | # TODO: cleanup 17 | 18 | def test_smoke(init_test): 19 | import hdl_tlm_if as hti 20 | from hdl_tlm_if.impl.gen_ifc_sv import GenIfcSv 21 | from hdl_tlm_if.tlm_ifc import TlmIfc 22 | import vsc_dataclasses as vdc 23 | import hdl_tlm_if as hti 24 | 25 | @hti.tlm_if 26 | class TwoChannelIF(object): 27 | 28 | @vdc.randclass 29 | class Req(object): 30 | data : vdc.bit_t[8] 31 | data2 : vdc.bit_t[8] 32 | 33 | @vdc.randclass 34 | class Rsp(object): 35 | data : vdc.bit_t[12] 36 | data2 : vdc.bit_t[20] 37 | 38 | @hti.req_fifo 39 | async def req(self, pkt : Req): 40 | pass 41 | 42 | @hti.rsp_fifo 43 | async def rsp(self, pkt : Rsp): 44 | pass 45 | 46 | ti = TlmIfc.get(TwoChannelIF) 47 | print("ti: %s" % str(ti)) 48 | 49 | gen = GenIfcSv() 50 | gen.gen_ifc_module(ti, sys.stdout) 51 | 52 | 53 | -------------------------------------------------------------------------------- /tests/unit/disabled_test_tlm_if_spec.py: -------------------------------------------------------------------------------- 1 | 2 | 3 | def test_param_if(): 4 | import os 5 | import sys 6 | import vsc_dataclasses as vdc 7 | unit_tests_dir = os.path.dirname(os.path.abspath(__file__)) 8 | proj_dir = os.path.abspath(os.path.join(unit_tests_dir, "../..")) 9 | sys.path.append(os.path.join(proj_dir, "src")) 10 | 11 | import vsc_dataclasses as vdc 12 | import hdl_tlm_if as hti 13 | 14 | @hti.tlm_if 15 | class BfmIF(object): 16 | P1 : int = 2 17 | P2 : int = 3 18 | P3 : int = P2 19 | ADDR_WIDTH : vdc.param[int] = 20 20 | 21 | @vdc.randclass 22 | class AddrWriteReq(object): 23 | data : vdc.bit_t[8] 24 | data2 : vdc.bit_t[8] 25 | def __init__(self): 26 | pass 27 | 28 | @vdc.randclass 29 | class WriteRsp(object): 30 | data : vdc.bit_t[32] 31 | pass 32 | 33 | @hti.req_fifo 34 | async def write_addr_req(self, req : AddrWriteReq): 35 | pass 36 | 37 | @hti.rsp_fifo 38 | async def write_rsp(self, rsp : WriteRsp): 39 | pass 40 | 41 | bfm_if = BfmIF() 42 | 43 | req = BfmIF.AddrWriteReq() 44 | 45 | print("Fields: %s" % str(req.fields())) 46 | print("Size: %d" % req.bitsize()) 47 | 48 | for i in range(16): 49 | req.data = i 50 | req.data2 = i+1 51 | print("Value: 0x%08x" % req.intval()) 52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /tests/unit/test_base.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | 4 | hdl_if_dir = os.path.join( 5 | os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))), 6 | "src") 7 | 8 | #sys.path.insert(0, hdl_if_dir) 9 | -------------------------------------------------------------------------------- /tests/unit/test_py_api.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import pytest 4 | import pytest_fv as pfv 5 | from pytest_fv.fixtures import * 6 | from .test_base import * 7 | 8 | import hdl_if 9 | 10 | SKIP_HDLSIM = ('ivl',) 11 | 12 | data_dir = os.path.join( 13 | os.path.dirname(os.path.abspath(__file__)), 14 | "data") 15 | test_py_api_data_dir = os.path.join(data_dir, "py_api") 16 | 17 | def _test_file(dirconfig, name, plusargs=None): 18 | flow = pfv.FlowSim(dirconfig) 19 | 20 | flow.fs.add_library(hdl_if.share()) 21 | flow.sim.addFileset(pfv.FSVlnv("fvutils::pyhdl-if", "systemVerilogSource")) 22 | 23 | flow.sim.addFileset(pfv.FSPaths( 24 | test_py_api_data_dir, ["%s.sv" % name], "systemVerilogSource")) 25 | 26 | flow.sim.dpi_libs.append(hdl_if.get_entry()) 27 | flow.sim.top.add(name) 28 | 29 | run_args = flow.sim.mkRunArgs(dirconfig.rundir()) 30 | print("rundir: %s" % dirconfig.rundir()) 31 | run_args.prepend_pathenv("PYTHONPATH", test_py_api_data_dir) 32 | print("Adding PYTHONPATH=%s" % test_py_api_data_dir) 33 | if plusargs is not None: 34 | run_args.plusargs.extend(plusargs) 35 | flow.addTaskToPhase("run.main", flow.sim.mkRunTask(run_args)) 36 | 37 | if dirconfig.config.getHdlSim() in SKIP_HDLSIM: 38 | pytest.skip("Unsupported simulator %s" % dirconfig.config.getHdlSim()) 39 | else: 40 | flow.run_all() 41 | 42 | with open(os.path.join(dirconfig.rundir(), "status.txt"), "r") as fp: 43 | status = fp.read().strip() 44 | 45 | assert status.startswith("PASS:") 46 | 47 | def test_smoke(dirconfig): 48 | _test_file(dirconfig, "test_smoke") 49 | 50 | def test_a_plus_b(dirconfig): 51 | _test_file(dirconfig, "a_plus_b") 52 | 53 | #@pytest.mark.skip("Needs more investigation") 54 | def test_data1(dirconfig): 55 | _test_file(dirconfig, "data1", plusargs=[ 56 | 'data=%s' % os.path.join(test_py_api_data_dir, "data1.json")]) -------------------------------------------------------------------------------- /tests/unit/test_vpi_py_if.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import pytest 4 | import pytest_fv as pfv 5 | from pytest_fv.fixtures import * 6 | 7 | from .test_base import * 8 | 9 | print("path: %s" % str(sys.path)) 10 | 11 | import hdl_if 12 | 13 | data_dir = os.path.join( 14 | os.path.dirname(os.path.abspath(__file__)), 15 | "data") 16 | test_vpi_py_if_data_dir = os.path.join(data_dir, "vpi_py_if") 17 | 18 | SKIP_HDLSIM = ('xsm', 'vlt') 19 | 20 | 21 | def test_smoke(dirconfig : pfv.DirConfig): 22 | flow = pfv.FlowSim(dirconfig) 23 | 24 | # flow.fs.add_library(hdl_if.share()) 25 | # flow.sim.addFileset(pfv.FSVlnv("fvutils::pyhdl-if", "systemVerilogSource")) 26 | 27 | # flow.sim.addFileset(pfv.FSPaths( 28 | # dirconfig.builddir(), 29 | # ["call_sv_bfm_pkg.sv"], 30 | # "systemVerilogSource")) 31 | 32 | flow.sim.addFileset(pfv.FSPaths( 33 | test_vpi_py_if_data_dir, 34 | ["vpi_py_if_smoke.v"], 35 | "verilogSource")) 36 | 37 | flow.sim.pli_libs.append(hdl_if.get_entry()) 38 | flow.sim.top.add("vpi_py_if_smoke") 39 | 40 | global hdl_if_dir 41 | run_args = flow.sim.mkRunArgs(dirconfig.rundir()) 42 | # run_args.prepend_pathenv("PYTHONPATH", hdl_if_dir) 43 | flow.addTaskToPhase("run.main", flow.sim.mkRunTask(run_args)) 44 | 45 | if dirconfig.config.getHdlSim() in SKIP_HDLSIM: 46 | pytest.skip("Unsupported simulator %s" % dirconfig.config.getHdlSim()) 47 | else: 48 | flow.run_all() -------------------------------------------------------------------------------- /tests/util.py: -------------------------------------------------------------------------------- 1 | #**************************************************************************** 2 | #* util.py 3 | #* 4 | #* Copyright 2023 Matthew Ballance and Contributors 5 | #* 6 | #* Licensed under the Apache License, Version 2.0 (the "License"); you may 7 | #* not use this file except in compliance with the License. 8 | #* You may obtain a copy of the License at: 9 | #* 10 | #* http://www.apache.org/licenses/LICENSE-2.0 11 | #* 12 | #* Unless required by applicable law or agreed to in writing, software 13 | #* distributed under the License is distributed on an "AS IS" BASIS, 14 | #* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | #* See the License for the specific language governing permissions and 16 | #* limitations under the License. 17 | #* 18 | #* Created on: 19 | #* Author: 20 | #* 21 | #**************************************************************************** 22 | 23 | class Util(object): 24 | 25 | def __init__(self, request): 26 | pass 27 | 28 | --------------------------------------------------------------------------------