24 |
25 | # Interfacing VHDL and foreign languages with [VUnit](https://github.com/VUnit/vunit)
26 |
27 | Three main approaches are used to co-simulate (co-execute) VHDL sources along with software applications written in a different language (typically C/C++):
28 |
29 | - Verilog Procedural Interface (VPI), also known as Program Language Interface (PLI) 2.0.
30 | - VHDL Procedural Interface (VHPI), or specific implementations, such as Foreign Language Interface (FLI).
31 | - Generation of C/C++ models/sources through a transpiler.
32 |
33 | This repository aims to gather resources to use these techniques with VUnit. The content is organised in **bridges**, **examples** and **utils**:
34 |
35 | - Bridges contain VHDL packages, (optionally) matching C headers and Python classes. Each bridge provides the glue logic between a VHDL API (or another bridge) and some other API in VHDL and/or C (bindings).
36 | - Examples are working demo projects that use some utils and/or bridges. Dockerfiles are included in the examples that require additional FOSS tools.
37 | - Utils contains helper functions in Python (based on `ctypes`, `base64`, `numpy` and `Pillow`) which are useful for interacting with C-alike executables.
38 |
--------------------------------------------------------------------------------
/cosim/__init__.py:
--------------------------------------------------------------------------------
1 | """
2 | HDL co-simulation with VUnit
3 | """
4 |
--------------------------------------------------------------------------------
/cosim/utils.py:
--------------------------------------------------------------------------------
1 | """
2 | C/Python co-simulation utilities
3 | """
4 |
5 | # pylint:disable=wrong-import-order
6 |
7 | import sys
8 | from sys import platform
9 | from pathlib import Path
10 | from base64 import b64encode
11 | from io import BytesIO
12 | import ctypes
13 | import _ctypes # type: ignore
14 | import numpy # type: ignore
15 | from PIL import Image # type: ignore
16 |
17 |
18 | def dlopen(path):
19 | """
20 | Open/load a PIE binary or a shared library.
21 | """
22 | if not Path(path).is_file():
23 | print("Executable binary not found: " + path)
24 | sys.exit(1)
25 | try:
26 | return ctypes.CDLL(path)
27 | except OSError:
28 | print(
29 | "Loading executables dynamically seems not to be supported on this platform"
30 | )
31 | sys.exit(1)
32 |
33 |
34 | def dlclose(obj):
35 | """
36 | Close/unload a PIE binary or a shared library.
37 |
38 | :param obj: object returned by ctypes.CDLL when the resource was loaded
39 | """
40 | if platform == "win32":
41 | _ctypes.FreeLibrary(obj._handle) # pylint:disable=protected-access,no-member
42 | else:
43 | _ctypes.dlclose(obj._handle) # pylint:disable=protected-access,no-member
44 |
45 |
46 | def enc_args(args):
47 | """
48 | Convert args to a suitable format for a foreign C function.
49 |
50 | :param args: list of strings
51 | """
52 | xargs = (ctypes.POINTER(ctypes.c_char) * len(args))()
53 | for idx, arg in enumerate(args):
54 | xargs[idx] = ctypes.create_string_buffer(arg.encode("utf-8"))
55 | return xargs
56 |
57 |
58 | def byte_buf(lst):
59 | """
60 | Convert array to a string buffer (uint8_t* in C).
61 |
62 | :param lst: list of naturals range [0,255]
63 | """
64 | return ctypes.create_string_buffer(bytes(lst), len(lst))
65 |
66 |
67 | def int_buf(lst, bpw=4, signed=True):
68 | """
69 | Convert array to a string buffer (uint8_t* in C).
70 |
71 | :param lst: list of integers
72 | :param bpw: number of bytes per word/integer
73 | :param signed: whether to encode the numbers as signed
74 | """
75 | out = [None] * 4 * len(lst)
76 | for idx, val in enumerate(lst):
77 | out[idx * 4 : idx * 4 + 4] = (val).to_bytes(
78 | bpw, byteorder="little", signed=signed
79 | )
80 | return byte_buf(out)
81 |
82 |
83 | def read_byte_buf(buf):
84 | """
85 | Read byte/string buffer (uint8_t* in C) as a list of numbers.
86 | """
87 | return read_int_buf(buf, bpw=1, signed=False)
88 |
89 |
90 | def read_int_buf(buf, bpw=4, signed=True):
91 | """
92 | Read byte/string buffer as a list of numbers.
93 |
94 | :param buf: byte/string buffer (uint8_t* in C) to read from
95 | :param bpw: number of bytes per word/integer
96 | :param signed: whether to decode the numbers as signed
97 | """
98 | out = [None] * int(len(buf) / bpw)
99 | if bpw == 1:
100 | for idx, val in enumerate(buf):
101 | out[idx] = int.from_bytes(val, byteorder="little", signed=signed)
102 | else:
103 | for idx, _ in enumerate(out):
104 | out[idx] = int.from_bytes(
105 | buf[idx * bpw : idx * bpw + bpw], byteorder="little", signed=signed
106 | )
107 | return out
108 |
109 |
110 | def b64enc_int_list(lst, width, height):
111 | """
112 | Encode list of numbers as a base64 encoded PNG image.
113 |
114 | :param lst: list of pixel values (len = height x width) in row-major order
115 | :param width: spatial width
116 | :param height: spatial height
117 | """
118 | buf = BytesIO()
119 | Image.fromarray(
120 | numpy.array(numpy.reshape(lst, (height, width)), dtype=numpy.uint16)
121 | ).save(buf, format="PNG")
122 | return b64encode(buf.getvalue()).decode("utf8")
123 |
124 |
125 | def b64enc_list_of_int_lists(lst, width, height):
126 | """
127 | Convert list of lists of numbers to list of base64 encoded PNG images.
128 |
129 | :param lst: list of lists of pixel values (len = height x width) in row-major order
130 | :param width: spatial width
131 | :param height: spatial height
132 | """
133 | b64 = [[] for idx in range(len(lst))]
134 | for idx, val in enumerate(lst):
135 | b64[idx] = b64enc_int_list(val, width, height)
136 | return b64
137 |
--------------------------------------------------------------------------------
/cosim/vhpidirect/.gitignore:
--------------------------------------------------------------------------------
1 | /vhdl/*
--------------------------------------------------------------------------------
/cosim/vhpidirect/__init__.py:
--------------------------------------------------------------------------------
1 | """
2 | VHPIDIRECT VUnit co-simulation bridge
3 | """
4 |
5 | import re
6 | from os import linesep, makedirs
7 | from pathlib import Path
8 | from shutil import copyfile
9 | from vunit import ROOT as SROOT # type: ignore
10 |
11 | ROOT = Path(SROOT)
12 |
13 |
14 | class VHPIDIRECT:
15 | """
16 | VHPIDIRECT VUnit co-simulation bridge class
17 |
18 | :param srcs: optional alternative location of the sources
19 | """
20 |
21 | def __init__(self, srcs=None):
22 | self._srcs = Path(srcs) if srcs is not None else Path(__file__).parent
23 |
24 | def bridge(self, features=None):
25 | """
26 | Get lists of files to register the bridge with ``vu.add_builtins``
27 |
28 | :param features: struct,``{'string': , 'integer': }``, to provide
29 | bridges for the external VHDL API. Accepted values are
30 | ``True``, ``False``, ``None`` or ``['path/to/custom/file']``.
31 | """
32 | funcs = {
33 | "string": {
34 | "write_char : procedure": "write_char",
35 | "read_char : function": "read_char",
36 | "get_ptr : function": "get_string_ptr",
37 | },
38 | "integer_vector": {
39 | "write_integer : procedure": "write_integer",
40 | "read_integer : function": "read_integer",
41 | "get_ptr : function": "get_intvec_ptr",
42 | },
43 | }
44 |
45 | api = {"string": True, "integer_vector": True}
46 | for key in api:
47 | if key in features:
48 | val = features[key]
49 | if val is not True:
50 | api[key] = val
51 | else:
52 | dname = self._srcs / "vhdl" / ("%s.vhd" % key)
53 | api[key] = [str(dname)]
54 |
55 | try:
56 | makedirs(str(dname.parent), 0o755)
57 | except FileExistsError:
58 | pass
59 |
60 | attrs = linesep
61 | for fkey, fval in funcs[key].items():
62 | attrs += ' attribute foreign of %s is "VHPIDIRECT %s";%s' % (
63 | fkey,
64 | fval,
65 | linesep,
66 | )
67 |
68 | # Instead of providing separate VHDL sources, in this bridge we patch
69 | # the reference API definition sources from VUnit/vunit
70 | with (
71 | ROOT
72 | / "vunit"
73 | / "vhdl"
74 | / "data_types"
75 | / "src"
76 | / "api"
77 | / ("external_%s_pkg.vhd" % key)
78 | ).open() as sfptr:
79 | with dname.open("w") as dfptr:
80 | dfptr.write(
81 | re.sub("(end package;)", attrs + r"\1", sfptr.read())
82 | )
83 | return api
84 |
85 | @property
86 | def include(self):
87 | """
88 | Get path to include directories containing C sources/headers
89 | """
90 | return [str(self._srcs / "c")]
91 |
92 | def verscript(self, file=None):
93 | """
94 | Get argument to add a custom version-script file to GHDL
95 |
96 | :param file: provide a custom file instead of the one provided with the bridge
97 | """
98 | return "-Wl,-Wl,--version-script=%s" % (
99 | file if file is not None else str(self._srcs / "c" / "grt.ver")
100 | )
101 |
102 | @staticmethod
103 | def post_func(results):
104 | """
105 | Optional post-func to copy runtime args for each test/executable to output subdir 'cosim'
106 | """
107 | report = results.get_report()
108 | cosim_args_dir = Path(report.output_path) / "cosim"
109 |
110 | try:
111 | makedirs(str(cosim_args_dir))
112 | except FileExistsError:
113 | pass
114 |
115 | for key, item in report.tests.items():
116 | copyfile(
117 | str(Path(item.path) / "ghdl" / "args.json"),
118 | str(
119 | cosim_args_dir / ("%s.json" % re.search(r"lib\.(.+)\.all", key)[1])
120 | ),
121 | )
122 |
--------------------------------------------------------------------------------
/cosim/vhpidirect/c/grt.ver:
--------------------------------------------------------------------------------
1 | VHPIDIRECT {
2 | global:
3 | main;
4 | ghdl_main;
5 | read_char;
6 | write_char;
7 | read_integer;
8 | write_integer;
9 | set_string_ptr;
10 | get_string_ptr;
11 | set_intvec_ptr;
12 | get_intvec_ptr;
13 | local:
14 | *;
15 | };
16 |
--------------------------------------------------------------------------------
/cosim/vhpidirect/c/stubs.c:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 |
5 | void set_string_ptr(uint8_t id, uint8_t *p) {
6 | printf("ERROR set_string_ptr: THIS IS A STUB\n");
7 | exit(1);
8 | return;
9 | }
10 |
11 | uintptr_t get_string_ptr(uint8_t id) {
12 | printf("ERROR get_string_ptr: THIS IS A STUB\n");
13 | exit(1);
14 | return NULL;
15 | }
16 |
17 | void write_char( uint8_t id, uint32_t i, uint8_t v ) {
18 | printf("ERROR write_char: THIS IS A STUB\n");
19 | exit(1);
20 | return;
21 | }
22 |
23 | uint8_t read_char( uint8_t id, uint32_t i ) {
24 | printf("ERROR read_char: THIS IS A STUB\n");
25 | exit(1);
26 | return 0;
27 | }
28 |
29 | //---
30 |
31 | void set_intvec_ptr(uint8_t id, uint8_t *p) {
32 | printf("ERROR set_intvec_ptr: THIS IS A STUB\n");
33 | exit(1);
34 | return;
35 | }
36 |
37 | uintptr_t get_intvec_ptr(uint8_t id) {
38 | printf("ERROR get_intvec_ptr: THIS IS A STUB\n");
39 | exit(1);
40 | return NULL;
41 | }
42 |
43 | void write_integer(uint8_t id, uint32_t i, int32_t v) {
44 | printf("ERROR write_integer: THIS IS A STUB\n");
45 | exit(1);
46 | return;
47 | }
48 |
49 | int32_t read_integer(uint8_t id, uint32_t i) {
50 | printf("ERROR read_integer: THIS IS A STUB\n");
51 | exit(1);
52 | return 0;
53 | }
54 |
--------------------------------------------------------------------------------
/cosim/vhpidirect/c/vhpidirect_user.h:
--------------------------------------------------------------------------------
1 | #include
2 |
3 | extern int ghdl_main (int argc, char **argv);
4 |
5 | uint8_t *D[256];
6 |
7 | //---
8 |
9 | // External string/byte_vector through access (mode = extacc)
10 |
11 | void set_string_ptr(uint8_t id, uintptr_t p) {
12 | D[id] = (uint8_t*)p;
13 | }
14 |
15 | uintptr_t get_string_ptr(uint8_t id) {
16 | return (uintptr_t)D[id];
17 | }
18 |
19 | // External string/byte_vector through functions (mode = extfnc)
20 |
21 | void write_char(uint8_t id, uint32_t i, uint8_t v) {
22 | ((uint8_t*)D[id])[i] = v;
23 | }
24 |
25 | uint8_t read_char(uint8_t id, uint32_t i) {
26 | return ((uint8_t*)D[id])[i];
27 | }
28 |
29 | //---
30 |
31 | // External integer_vector through access (mode = extacc)
32 |
33 | void set_intvec_ptr(uint8_t id, uintptr_t p) {
34 | D[id] = (uint8_t*)p;
35 | }
36 |
37 | uintptr_t get_intvec_ptr(uint8_t id) {
38 | return (uintptr_t)D[id];
39 | }
40 |
41 | // External integer_vector through functions (mode = extfnc)
42 |
43 | void write_integer(uint8_t id, uint32_t i, int32_t v) {
44 | ((int32_t*)D[id])[i] = v;
45 | }
46 |
47 | int32_t read_integer(uint8_t id, uint32_t i) {
48 | return ((int32_t*)D[id])[i];
49 | }
50 |
--------------------------------------------------------------------------------
/docs/_static/VUnit_cosim_logo_420x420.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/VUnit/cosim/9ad3dfa20e34a269b2a4df8e2b18b934ab84dd72/docs/_static/VUnit_cosim_logo_420x420.png
--------------------------------------------------------------------------------
/docs/_static/vunit_cosim.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/VUnit/cosim/9ad3dfa20e34a269b2a4df8e2b18b934ab84dd72/docs/_static/vunit_cosim.ico
--------------------------------------------------------------------------------
/docs/_static/vunit_cosim.svg:
--------------------------------------------------------------------------------
1 |
2 |
70 |
--------------------------------------------------------------------------------
/docs/_templates/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/VUnit/cosim/9ad3dfa20e34a269b2a4df8e2b18b934ab84dd72/docs/_templates/.gitkeep
--------------------------------------------------------------------------------
/docs/bridges/index.rst:
--------------------------------------------------------------------------------
1 | .. _bridges:
2 |
3 | Bridges
4 | #######
5 |
6 | Bridges contain VHDL packages, matching C headers and/or Python classes. Each bridge provides the glue logic between a VHDL API and some other API in VHDL and/or C (bindings).
7 |
8 | .. NOTE:: `GHDL `_, the only free and open source simulator for VHDL supported by VUnit, features VPI and a limited subset of VHPI named VHPIDIRECT. Since co-simulation of VHDL and Python through VPI is already supported and documented by `cocotb `_, this project is mostly focused on VHPIDIRECT.
9 |
10 | Available modules:
11 |
12 | :ref:`bridges:vhpidirect`
13 | C bindings for VUnit's external VHDL API (see :ref:`vunit:data_types_library`).
14 |
15 | .. toctree::
16 | :maxdepth: 2
17 | :hidden:
18 |
19 | vhpidirect
20 |
--------------------------------------------------------------------------------
/docs/bridges/vhpidirect.rst:
--------------------------------------------------------------------------------
1 | .. _bridges:vhpidirect:
2 |
3 | VHPIDIRECT
4 | ##########
5 |
6 | This is a bridge between VUnit's external VHDL API and C, through GHDL's VHPIDIRECT
7 | features (see :ref:`ghdl:USING:Foreign`). A Python class (named ``VHPIDIRECT``) provides
8 | helper functions to (re)use a C API (``vhpidirect_user.h``, ``grt.ver`` and/or
9 | ``stubs.c``) with VUnit's ``add_builtins`` and ``set_sim_option("ghdl.elab_flags", ...)``.
10 | Examples of how to use this bridge are shown in :ref:`examples:vhpidirect`.
11 |
12 | .. NOTE:: In the latest stable release of GHDL (v0.36), mcode backend does not support
13 | VHPIDIRECT. Moreover, using LLVM or GCC backends is suggested, as these generate
14 | executable files and are supported in a wider range of platforms.
15 |
16 | .. NOTE:: For GHDL to generate PIE executable files, it needs to be configured with option
17 | ``--default-pic``. Moreover, loading ELF binaries dynamically is a *hackish* procedure
18 | that takes advantage of PIE ELF binaries and shared libraries having a common structure
19 | on GNU/Linux. However, this does not work on Windows (see `ghdl/ghdl#803 `_).
20 | As a result:
21 |
22 | * GHDL needs to be enhanced to allow building designs as shared libraries instead of
23 | executable binaries (see `ghdl/ghdl#800 `_).
24 | * or, build features in VUnit need to be extended to use ``ghdl --bind``,
25 | ``ghdl --list-link`` and gcc/clang in order to generate a ``*.dll`` on Windows.
26 | * or, a helper method need to be added to this class to use gcc/clang.
27 |
28 | Implementation details
29 | ======================
30 |
31 | ``vhpidirect_user.h`` is the C implementation of the interface. It uses an array of
32 | pointers (``uint8_t *D[256];``) to keep a reference to all the buffers that are shared
33 | between C and VHDL.
34 |
35 | .. NOTE:: Users that need to share more than 256 pointers, can and should include their
36 | own copy of this header file. Actually, in example :ref:`examples:copy` the provided
37 | header file is not used. Instead, functions are implemented in ``main.c``.
38 |
39 | ``stubs.c`` is to be used when multiple tests are handled in a single ``run.py`` file
40 | (i.e. by the same VUnit object), but some of them do NOT use external modes. Since
41 | builtins are compiled once only, every test (binary) needs to have placeholders for the C
42 | functions that VHDL expects to use. ``stubs.c`` provides *dummy* definitions of the C API.
43 | Note that these will produce errors if executed at runtime.
44 |
45 | ``grt.ver`` is to be used when the ELF file built with GHDL is to be loaded dynamically.
46 | By default, GHDL hides most of the global symbols. Providing this file as an elaboration
47 | option ensures that the functions of the C API are visible. Script ``corun.py`` in example
48 | :ref:`examples:buffer` shows how to dynamically load and execute the simulation from Python.
49 |
50 | extfnc mode
51 | -----------
52 |
53 | As explained in :ref:`vunit:data_types_library:external`, mode *extfnc* is expected to be
54 | used when data is not available in the same memory space as the VHDL simulation. For
55 | example, when libraries such as `gRPC `_, `ZeroMQ `_
56 | or `netpp `_ are used to co-execute simulations in
57 | remote targets. Hence, ``write_*/read_*`` function bodies provided in ``vhpidirect_user.h``
58 | are for testing purposes. In practice, developers willing to use this mode are expected
59 | to provide their own custom header file.
60 |
61 | Note that this is the difference between *extfnc* and *extacc*: with *extacc*, there is
62 | no explicit callback when VHDL modifies a value in a shared buffer. Conversely, with
63 | *extfnc*, VHDL cannot modify a value without a callback.
64 |
65 | Python interface
66 | ================
67 |
68 | .. autoclass:: cosim.vhpidirect.VHPIDIRECT()
69 |
--------------------------------------------------------------------------------
/docs/conf.py:
--------------------------------------------------------------------------------
1 | from pathlib import Path
2 |
3 | # -- Path setup --------------------------------------------------------------
4 |
5 | # If extensions (or modules to document with autodoc) are in another directory,
6 | # add these directories to sys.path here. If the directory is relative to the
7 | # documentation root, use os.path.abspath to make it absolute, like shown here.
8 | #
9 | # import os
10 | # import sys
11 | # sys.path.insert(0, os.path.abspath('.'))
12 |
13 |
14 | # -- Project information -----------------------------------------------------
15 |
16 | project = "VUnit cosim"
17 | copyright = "2019-2020, author"
18 | author = "author"
19 |
20 | version = ""
21 | release = ""
22 |
23 |
24 | # -- General configuration ---------------------------------------------------
25 |
26 | # Add any Sphinx extension module names here, as strings. They can be
27 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
28 | # ones.
29 | extensions = [
30 | "sphinx.ext.autodoc",
31 | # "sphinx.ext.extlinks",
32 | "sphinx.ext.intersphinx",
33 | # "sphinx.ext.todo",
34 | # "sphinxarg.ext", # Automatic argparse command line argument documentation
35 | ]
36 |
37 | autodoc_default_flags = ["members"]
38 |
39 | # Add any paths that contain templates here, relative to this directory.
40 | templates_path = ["_templates"]
41 |
42 | # List of patterns, relative to source directory, that match files and
43 | # directories to ignore when looking for source files.
44 | # This pattern also affects html_static_path and html_extra_path.
45 | exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"]
46 |
47 |
48 | # -- Options for HTML output -------------------------------------------------
49 |
50 | # The theme to use for HTML and HTML Help pages. See the documentation for
51 | # a list of builtin themes.
52 | #
53 | html_theme = "alabaster"
54 |
55 | # Add any paths that contain custom static files (such as style sheets) here,
56 | # relative to this directory. They are copied after the builtin static files,
57 | # so a file named "default.css" will overwrite the builtin "default.css".
58 | html_static_path = ["_static"]
59 |
60 | html_logo = str(Path(html_static_path[0]) / "VUnit_cosim_logo_420x420.png")
61 |
62 | html_favicon = str(Path(html_static_path[0]) / "vunit_cosim.ico")
63 |
64 |
65 | # -- Sphinx.Ext.InterSphinx --------------------------------------------------
66 |
67 | intersphinx_mapping = {
68 | "vunit": ("https://vunit.github.io", None),
69 | "ghdl": ("https://ghdl.rtfd.io/en/latest", None),
70 | }
71 |
--------------------------------------------------------------------------------
/docs/examples/buffer.rst:
--------------------------------------------------------------------------------
1 | .. _examples:buffer:
2 |
3 | Buffer
4 | ######
5 |
6 | This example shows how to use :class:`cosim.vhpidirect.VHPIDIRECT` (see :ref:`bridges:vhpidirect` to interact with VUnit's internal ``string_ptr``/``byte_vector_ptr`` and/or ``integer_vector_ptr``, from C and/or Python.
7 |
8 | * ``run.py``: supports a custom CLI option, ``--build``, that will set ``VU.set_sim_option("ghdl.elab_e", True)``.
9 |
10 | * :func:`cosim.vhpidirect.VHPIDIRECT.bridge` is used to enable external mode support for ``string_ptr`` and `integer_vector_ptr` (see :ref:`vunit:data_types_library`).
11 | * :func:`cosim.vhpidirect.VHPIDIRECT.include` is used to build C sources with the provided C API/headers.
12 | * :func:`cosim.vhpidirect.VHPIDIRECT.verscript` is used to provide a version script that matches the functions in the C API.
13 | * The ``post_func`` provided by :ref:`bridges:vhpidirect` (see :func:`cosim.vhpidirect.VHPIDIRECT.post_func`) is used to save in subdir `vunit_out/cosim` the CLI args that correspond to each testbench.
14 |
15 | * ``corun.py``: allows to dynamically load and run any of the simulation binaries built with ``run.py``. Given a testbench name, it finds the corresponding binary and arguments JSON file. Then, it is loaded dynamically and data buffers allocated in Python are shared with the simulation.
16 |
17 | Example session:
18 |
19 | .. CODE:: bash
20 |
21 | # python3 run.py --build -v
22 | ...
23 |
24 | # ls vunit_out/cosim/
25 | tb_external_byte_vector.json tb_external_integer_vector.json tb_external_string.json
26 |
27 | # python3 corun.py tb_external_byte_vector
28 | ...
29 |
30 | .. NOTE:: GHDL with mcode backend does NOT generate executable binaries. In order to use the two-step workflow, eithe LLVM or GCC backends need to be used.
31 |
32 | .. NOTE:: ``corun.py`` depends on :mod:`cosim.utils`, which requires some additional dependencies. File ``requirements.txt`` can be used to install all of them at once.
33 |
--------------------------------------------------------------------------------
/docs/examples/copy.rst:
--------------------------------------------------------------------------------
1 | .. _examples:copy:
2 |
3 | Copy
4 | ######
5 |
6 | This example shows the most basic usage of :class:`cosim.vhpidirect.VHPIDIRECT` (see :ref:`bridges:vhpidirect`) to interact with VUnit's internal ``string_ptr``/``byte_vector_ptr`` (see :ref:`vunit:data_types_library`) from C. :func:`cosim.vhpidirect.VHPIDIRECT.bridge` is used enable external mode support for ``string_ptr``. Two equivalent testbenches are provided: one uses ``string_ptr`` and the other one ``byte_vector_ptr``.
7 |
--------------------------------------------------------------------------------
/docs/examples/index.rst:
--------------------------------------------------------------------------------
1 | .. _examples:
2 |
3 | Examples
4 | ########
5 |
6 | .. NOTE:: Since this project is not distributed through PyPI yet, and because it depends
7 | on a specific branch of VUnit, it is suggested to clone both repositories as siblings,
8 | and to use PYTHONPATH. For example:
9 |
10 | .. CODE:: bash
11 |
12 | mkdir VUnit
13 | cd VUnit
14 | git clone -b cosim --recurse-submodules -j4 https://github.com/dbhi/vunit
15 | git clone https://github.com/vunit/cosim
16 | export PYTHONPATH="$(pwd)/vunit":"$(pwd)/cosim"
17 |
18 | .. _examples:vhpidirect:
19 |
20 | VHPIDIRECT
21 | ----------
22 |
23 | .. toctree::
24 | :maxdepth: 2
25 |
26 | copy
27 | buffer
28 |
--------------------------------------------------------------------------------
/docs/genindex.rst:
--------------------------------------------------------------------------------
1 | .. # This file is a placeholder and will be replaced
2 |
3 | Index
4 | #####
5 |
--------------------------------------------------------------------------------
/docs/index.rst:
--------------------------------------------------------------------------------
1 | .. centered:: |shieldVunit|_ |shieldCosim|_ |shieldGitter|_ |shieldTwitter|_
2 |
3 | .. |shieldVunit| image:: https://img.shields.io/badge/VUnit/vunit-0c479d.svg?longCache=true&style=flat-square&logo=github
4 | .. _shieldVunit: https://github.com/VUnit/vunit
5 |
6 | .. |shieldCosim| image:: https://img.shields.io/badge/VUnit/cosim-000000.svg?longCache=true&style=flat-square&logo=github&logoColor=fdbe00
7 | .. _shieldCosim: https://github.com/VUnit/cosim
8 |
9 | .. |shieldGitter| image:: https://img.shields.io/gitter/room/VUnit/vunit.svg?longCache=true&style=flat-square&logo=gitter&logoColor=4db797&color=4db797
10 | .. _shieldGitter: https://gitter.im/VUnit/vunit
11 |
12 | .. |shieldTwitter| image:: https://img.shields.io/twitter/follow/VUnitFramework.svg?longCache=true&style=flat-square&color=1DA1F2&label=%40VUnitFramework&logo=twitter&logoColor=fff
13 | .. _shieldTwitter: https://www.twitter.com/VUnitFramework
14 |
15 | VUnit cosim
16 | ===========
17 |
18 | **VUnit** is an open source unit testing framework for VHDL/SystemVerilog, which features the
19 | functionality needed to realise continuous and automated testing of HDL code.
20 | VUnit doesn't replace but rather complements traditional testing methodologies by
21 | supporting a *"test early and often"* approach through automation. :ref:`Read more `
22 |
23 | **VUnit cosim** follows the same philosophy to support co-simulation (co-execution) of VHDL along with
24 | software applications written in a different language (typically C/C++ or Python). The content of this module is organised in bridges, examples and utils:
25 |
26 | * :ref:`bridges`: glue logic between a :ref:`external VHDL API ` (or another bridge) and some other API in VHDL and/or C (bindings).
27 |
28 | * :ref:`examples`: working demo projects that use some utils and/or bridges.
29 |
30 | * :ref:`utils`: helper functions in Python (based on ctypes, base64, numpy and Pillow) which are useful for interacting with C-alike executables.
31 |
32 | .. toctree::
33 | :maxdepth: 3
34 | :hidden:
35 |
36 | bridges/index
37 | examples/index
38 | utils
39 | genindex
40 | py-modindex
41 |
--------------------------------------------------------------------------------
/docs/py-modindex.rst:
--------------------------------------------------------------------------------
1 | .. # This file is a placeholder and will be replaced
2 |
3 | Python Module Index
4 | ###################
5 |
--------------------------------------------------------------------------------
/docs/utils.rst:
--------------------------------------------------------------------------------
1 | .. _utils:
2 |
3 | Utils
4 | #####
5 |
6 | .. automodule:: cosim.utils
7 |
--------------------------------------------------------------------------------
/examples/buffer/corun.py:
--------------------------------------------------------------------------------
1 | """
2 | Buffer: dynamically loading a simulation from Python
3 | ----------------------------------------------------
4 |
5 | Given a PIE executable and a set of CLI args in a JSON file, execute a simulation twice:
6 |
7 | * First, load it dynamically and execute the simulation without shared data buffers.
8 | * Then, load it again, allocate data buffers from Python, execute the simulation,
9 | and check the modified buffers from Python.
10 | """
11 |
12 | import argparse
13 | import sys
14 | from pathlib import Path
15 | from json import load
16 | from cosim.utils import (
17 | enc_args,
18 | dlopen,
19 | dlclose,
20 | byte_buf,
21 | int_buf,
22 | read_byte_buf,
23 | read_int_buf,
24 | )
25 |
26 | PARSER = argparse.ArgumentParser()
27 | PARSER.add_argument(
28 | "testbench", type=str, nargs="+", help="name of a pre-built testbench"
29 | )
30 | PARSER.add_argument(
31 | "--output-path",
32 | type=str,
33 | dest="output_path",
34 | default=str(Path(__file__).parent / "vunit_out"),
35 | help="output path (default: 'vunit_out')",
36 | )
37 |
38 | PARGS = PARSER.parse_args()
39 |
40 | with (
41 | Path(PARGS.output_path) / "cosim" / ("%s.json" % PARGS.testbench[0])
42 | ).open() as json_file:
43 | ARGS = load(json_file)
44 | if "integer" not in PARGS.testbench[0]:
45 | NEW_BUF = byte_buf
46 | READ_BUF = read_byte_buf
47 | else:
48 | NEW_BUF = int_buf
49 | READ_BUF = read_int_buf
50 |
51 | XARGS = enc_args([ARGS["bin"]] + ARGS["sim"])
52 |
53 | print("\nREGULAR EXECUTION")
54 | GHDL = dlopen(ARGS["bin"])
55 | try:
56 | GHDL.main(len(XARGS) - 1, XARGS)
57 | # TOFIX With VHDL 93, the execution is Aborted and Python exits here
58 | except SystemExit as exc:
59 | if exc.code != 0:
60 | sys.exit(exc.code)
61 | dlclose(GHDL)
62 |
63 | print("\nPYTHON ALLOCATION")
64 | GHDL = dlopen(ARGS["bin"])
65 |
66 | DATA = [111, 122, 133, 144, 155]
67 |
68 | # Two pointers/buffers are to be allocated
69 | BUF = [[] for c in range(2)]
70 |
71 | # Allocate and initialize shared data buffer
72 | BUF[1] = NEW_BUF(DATA + [0 for x in range(2 * len(DATA))])
73 |
74 | # Fill 'params' vector
75 | BUF[0] = int_buf(
76 | [-(2 ** 31) + 10, -(2 ** 31), 3, 0, len(DATA)] # clk_step # update # block_length
77 | )
78 |
79 | for x, v in enumerate(BUF):
80 | GHDL.set_string_ptr(x, v)
81 |
82 | for i, v in enumerate(READ_BUF(BUF[1])):
83 | print("py " + str(i) + ": " + str(v))
84 |
85 | GHDL.ghdl_main(len(XARGS) - 1, XARGS)
86 |
87 | for i, v in enumerate(READ_BUF(BUF[1])):
88 | print("py " + str(i) + ": " + str(v))
89 |
90 | dlclose(GHDL)
91 |
--------------------------------------------------------------------------------
/examples/buffer/run.py:
--------------------------------------------------------------------------------
1 | """
2 | Buffer
3 | ------
4 |
5 | An array of type ``uint8_t`` is allocated in a C application and some values
6 | are written to the first ``1/3`` positions. Then, the VHDL simulation is
7 | executed, where the (external) array/buffer is used.
8 |
9 | In the VHDL testbenches, two vector pointers are created, each of them using
10 | a different access mechanism (``extfunc`` or ``extacc``). One of them is used to copy
11 | the first ``1/3`` elements to positions ``[1/3, 2/3)``, while incrementing each value
12 | by one. The second one is used to copy elements from ``[1/3, 2/3)`` to ``[2/3, 3/3)``,
13 | while incrementing each value by two.
14 |
15 | When the simulation is finished, the C application checks whether data was successfully
16 | copied/modified. The content of the buffer is printed both before and after the
17 | simulation.
18 | """
19 |
20 | from sys import argv
21 | from pathlib import Path
22 | from subprocess import check_call
23 | from vunit import VUnit
24 | from cosim.vhpidirect import VHPIDIRECT
25 |
26 | COSIM = VHPIDIRECT()
27 |
28 | SRC = Path(__file__).parent / "src"
29 |
30 | BUILD_ONLY = False
31 | if "--build" in argv:
32 | argv.remove("--build")
33 | BUILD_ONLY = True
34 |
35 | # Compile C applications to objects
36 | C_IOBJ = SRC / "imain.o"
37 | C_BOBJ = SRC / "bmain.o"
38 |
39 | for val in [["int32_t", C_IOBJ], ["uint8_t", C_BOBJ]]:
40 | check_call(
41 | ["gcc", "-fPIC", "-DTYPE=%s" % val[0]]
42 | + ["-I%s" % inc for inc in COSIM.include]
43 | + ["-c", str(SRC / "main.c"), "-o", val[1]]
44 | )
45 |
46 | # Enable the external feature for strings/byte_vectors and integer_vectors
47 | VU = VUnit.from_argv(vhdl_standard="2008", compile_builtins=False)
48 | VU.add_builtins(COSIM.bridge({"string": True, "integer_vector": True}))
49 |
50 | LIB = VU.add_library("lib")
51 | LIB.add_source_files(SRC / "tb_ext_*.vhd")
52 |
53 | # Add the C object to the elaboration of GHDL
54 | for tb in LIB.get_test_benches(pattern="*tb_ext*", allow_empty=False):
55 | tb.set_sim_option("ghdl.elab_flags", ["-Wl," + str(C_BOBJ)])
56 | for tb in LIB.get_test_benches(pattern="*tb_ext*_integer*", allow_empty=False):
57 | tb.set_sim_option("ghdl.elab_flags", ["-Wl," + str(C_IOBJ)], overwrite=True)
58 |
59 | if BUILD_ONLY:
60 | VU.set_sim_option("ghdl.elab_flags", [COSIM.verscript()], overwrite=False)
61 | VU.set_sim_option("ghdl.elab_e", True)
62 | VU._args.elaborate = True # pylint: disable=protected-access
63 | VU.main(post_run=COSIM.post_func)
64 | else:
65 | VU.main()
66 |
--------------------------------------------------------------------------------
/examples/buffer/src/main.c:
--------------------------------------------------------------------------------
1 | /*
2 | Buffer
3 |
4 | An array of type uint8_t is allocated and some values are written to the first 1/3
5 | positions. Then, the VHDL simulation is executed, where the (external) array/buffer
6 | is used. When the simulation is finished, the results are checked. The content of
7 | the buffer is printed both before and after the simulation.
8 |
9 | This source file is used for both string/byte_vector and integer_vector.
10 | Accordingly, TYPE must be defined as uint8_t or int32_t during compilation.
11 | Keep in mind that the buffer (D) is of type uint8_t*, independently of TYPE, i.e.
12 | accesses are casted.
13 |
14 | NOTE: This file is expected to be used along with tb_ext_string.vhd, tb_ext_byte_vector.vhd
15 | or tb_ext_integer_vector.vhd
16 | */
17 |
18 | #include
19 | #include
20 | #include
21 | #include "vhpidirect_user.h"
22 |
23 | const uint32_t length = 5;
24 |
25 | /*
26 | Check procedure, to be executed when GHDL exits.
27 | The simulation is expected to copy the first 1/3 elements to positions [1/3, 2/3),
28 | while incrementing each value by one, and then copy elements from [1/3, 2/3) to
29 | [2/3, 3/3), while incrementing each value by two.
30 | */
31 | static void exit_handler(void) {
32 | unsigned i, j, z, k;
33 | TYPE expected, got;
34 | k = 0;
35 | for (j=0; j<3; j++) {
36 | k += j;
37 | for(i=0; i
13 | #include
14 | #include
15 |
16 | extern int ghdl_main (int argc, char **argv);
17 |
18 | uint8_t *D[1];
19 | const uint32_t length = 10;
20 |
21 | // Check procedure, to be executed when GHDL exits.
22 | // The simulation is expected to copy the first 1/3 elements to positions [1/3, 2/3),
23 | // while incrementing each value by one, and then copy elements from [1/3, 2/3) to
24 | // [2/3, 3/3), while incrementing each value by two.
25 | static void exit_handler(void) {
26 | int i, j, z, k;
27 | uint8_t expected, got;
28 | k = 0;
29 |
30 | for(i=0; i4.4.0"],
57 | description=(
58 | "Resources for interfacing VHDL and foreign languages with VUnit,"
59 | "an open source unit testing framework"
60 | ),
61 | )
62 |
--------------------------------------------------------------------------------
/tests/acceptance/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/VUnit/cosim/9ad3dfa20e34a269b2a4df8e2b18b934ab84dd72/tests/acceptance/__init__.py
--------------------------------------------------------------------------------
/tests/acceptance/test_examples.py:
--------------------------------------------------------------------------------
1 | """
2 | Verify that all example run scripts work correctly
3 | """
4 |
5 | import sys
6 | from os import environ
7 | from pathlib import Path
8 | from subprocess import call
9 | import unittest
10 | import pytest
11 | from vunit.sim_if.common import has_simulator, simulator_check
12 |
13 | ROOT = Path(__file__).parent.parent.parent
14 |
15 |
16 | @unittest.skipIf(
17 | not has_simulator()
18 | or simulator_check(lambda simclass: not simclass.supports_vhpi()),
19 | "A simulator/backend that supports interfacing with external C code is required",
20 | )
21 | class TestExamples(unittest.TestCase):
22 | """
23 | Verify that example projects run correctly
24 | """
25 |
26 | def setUp(self):
27 | self.output_path = str(Path(__file__).parent / "examples_run_out")
28 | self.report_file = str(Path(self.output_path) / "xunit.xml")
29 |
30 | def check(self, run_file, args=None, vhdl_standard="2008", exit_code=0):
31 | """
32 | Run external run file and verify exit code
33 | """
34 | args = args if args is not None else []
35 | new_env = environ.copy()
36 | new_env["VUNIT_VHDL_STANDARD"] = vhdl_standard
37 | retcode = call(
38 | [sys.executable, run_file, "--output-path=%s" % self.output_path]
39 | + (args if args else []),
40 | env=new_env,
41 | )
42 | self.assertEqual(retcode, exit_code)
43 |
44 | def test_copy(self):
45 | self.check(
46 | str(ROOT / "examples" / "copy" / "run.py"),
47 | args=["--clean", "--xunit-xml=%s" % self.report_file],
48 | )
49 |
50 | def test_buffer(self):
51 | self.check(
52 | str(ROOT / "examples" / "buffer" / "run.py"),
53 | args=["--clean", "--xunit-xml=%s" % self.report_file],
54 | )
55 |
56 | @pytest.mark.xfail
57 | def test_buffer_dyn(self):
58 | self.check(
59 | str(ROOT / "examples" / "buffer" / "run.py"),
60 | args=["--build", "--xunit-xml=%s" % self.report_file],
61 | )
62 | self.check(
63 | str(ROOT / "examples" / "buffer" / "corun.py"), args=["tb_external_string"],
64 | )
65 | self.check(
66 | str(ROOT / "examples" / "buffer" / "corun.py"),
67 | args=["tb_external_byte_vector"],
68 | )
69 | self.check(
70 | str(ROOT / "examples" / "buffer" / "corun.py"),
71 | args=["tb_external_integer_vector"],
72 | )
73 |
--------------------------------------------------------------------------------
/tests/lint/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/VUnit/cosim/9ad3dfa20e34a269b2a4df8e2b18b934ab84dd72/tests/lint/__init__.py
--------------------------------------------------------------------------------
/tests/lint/pylintrc:
--------------------------------------------------------------------------------
1 | [MASTER]
2 |
3 | # Specify a configuration file.
4 | #rcfile=
5 |
6 | # Python code to execute, usually for sys.path manipulation such as
7 | # pygtk.require().
8 | #init-hook=
9 |
10 | # Add files or directories to the blacklist. They should be base names, not
11 | # paths.
12 | ignore=CVS
13 |
14 | # Pickle collected data for later comparisons.
15 | persistent=no
16 |
17 | # List of plugins (as comma separated values of python modules names) to load,
18 | # usually to register additional checkers.
19 | load-plugins=
20 |
21 |
22 | [MESSAGES CONTROL]
23 |
24 | # Enable the message, report, category or checker with the given id(s). You can
25 | # either give multiple identifier separated by comma (,) or put this option
26 | # multiple time. See also the "--disable" option for examples.
27 | #enable=
28 |
29 | # Disable the message, report, category or checker with the given id(s). You
30 | # can either give multiple identifiers separated by comma (,) or put this
31 | # option multiple times (only on the command line, not in the configuration
32 | # file where it should appear only once).You can also use "--disable=all" to
33 | # disable everything first and then reenable specific checks. For example, if
34 | # you want to run only the similarities checker, you can use "--disable=all
35 | # --enable=similarities". If you want to run only the classes checker, but have
36 | # no Warning level messages displayed, use"--disable=all --enable=classes
37 | # --disable=W"
38 | #disable=
39 | disable=too-few-public-methods, locally-disabled, redefined-variable-type, duplicate-code, file-ignored,
40 | useless-object-inheritance, bad-continuation,
41 | # Failed to suppress this locally in ostools.py
42 | subprocess-popen-preexec-fn
43 |
44 | [REPORTS]
45 |
46 | # Set the output format. Available formats are text, parseable, colorized, msvs
47 | # (visual studio) and html. You can also give a reporter class, eg
48 | # mypackage.mymodule.MyReporterClass.
49 | output-format=text
50 |
51 | # Put messages in a separate file for each module / package specified on the
52 | # command line instead of printing them on stdout. Reports (if any) will be
53 | # written in a file name "pylint_global.[txt|html]".
54 | files-output=no
55 |
56 | # Tells whether to display a full report or only the messages
57 | reports=no
58 |
59 | # Python expression which should return a note less than 10 (10 is the highest
60 | # note). You have access to the variables errors warning, statement which
61 | # respectively contain the number of errors / warnings messages and the total
62 | # number of statements analyzed. This is used by the global evaluation report
63 | # (RP0004).
64 | evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10)
65 |
66 | # Template used to display messages. This is a python new-style format string
67 | # used to format the message information. See doc for all details
68 | #msg-template=
69 |
70 |
71 | [VARIABLES]
72 |
73 | # Tells whether we should check for unused import in __init__ files.
74 | init-import=no
75 |
76 | # A regular expression matching the beginning of the name of dummy variables
77 | # (i.e. not used).
78 | dummy-variables-rgx=_$|dummy|args|kwargs
79 |
80 | # List of additional names supposed to be defined in builtins. Remember that
81 | # you should avoid to define new builtins when possible.
82 | additional-builtins=
83 |
84 |
85 | [BASIC]
86 |
87 | # List of builtins function names that should not be used, separated by a comma
88 | bad-functions=map,filter,apply,input
89 |
90 | # Regular expression which should only match correct module names
91 | module-rgx=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$
92 |
93 | # Regular expression which should only match correct module level names
94 | const-rgx=(([A-Z_][A-Z0-9_]*)|(__.*__))$
95 |
96 | # Regular expression which should only match correct class names
97 | class-rgx=[A-Z_][a-zA-Z0-9]+$|T
98 |
99 | # Regular expression which should only match correct function names
100 | function-rgx=[a-z_][a-z0-9_]{2,}$
101 |
102 | # Regular expression which should only match correct method names
103 | method-rgx=[a-z_][a-z0-9_]{2,}$
104 |
105 | # Regular expression which should only match correct instance attribute names
106 | attr-rgx=[a-z_][a-z0-9_]{2,30}$
107 |
108 | # Regular expression which should only match correct argument names
109 | argument-rgx=[a-z_][a-z0-9_]{2,30}$
110 |
111 | # Regular expression which should only match correct variable names
112 | variable-rgx=[a-z_][a-z0-9_]{2,}$
113 |
114 | # Regular expression which should only match correct attribute names in class
115 | # bodies
116 | class-attribute-rgx=([A-Za-z_][A-Za-z0-9_]{2,}|(__.*__))$
117 |
118 | # Regular expression which should only match correct list comprehension /
119 | # generator expression variable names
120 | inlinevar-rgx=[A-Za-z_][A-Za-z0-9_]*$
121 |
122 | # Good variable names which should always be accepted, separated by a comma
123 | good-names=i,j,k,ex,Run,_,ui,vu,fg,bg
124 |
125 | # Bad variable names which should always be refused, separated by a comma
126 | bad-names=foo,bar,baz,toto,tutu,tata
127 |
128 | # Regular expression which should only match function or class names that do
129 | # not require a docstring.
130 | # TODO: Do not require docstring on test_ methods
131 | no-docstring-rgx=__.*__|test_.*
132 |
133 | # Minimum line length for functions/classes that require docstrings, shorter
134 | # ones are exempt.
135 | docstring-min-length=4
136 |
137 |
138 | [TYPECHECK]
139 |
140 | # Tells whether missing members accessed in mixin class should be ignored. A
141 | # mixin class is detected if its name ends with "mixin" (case insensitive).
142 | ignore-mixin-members=yes
143 |
144 | # List of classes names for which member attributes should not be checked
145 | # (useful for classes with attributes dynamically set).
146 | ignored-classes=SQLObject
147 |
148 | # List of members which are set dynamically and missed by pylint inference
149 | # system, and so shouldn't trigger E0201 when accessed. Python regular
150 | # expressions are accepted.
151 | generated-members=REQUEST,acl_users,aq_parent
152 |
153 |
154 | [FORMAT]
155 |
156 | # Maximum number of characters on a single line.
157 | max-line-length=120
158 |
159 | # Regexp for a line that is allowed to be longer than the limit.
160 | ignore-long-lines=^\s*(# )??$
161 |
162 | # Allow the body of an if to be on the same line as the test if there is no
163 | # else.
164 | single-line-if-stmt=no
165 |
166 | # List of optional constructs for which whitespace checking is disabled
167 | no-space-check=trailing-comma,dict-separator
168 |
169 | # Maximum number of lines in a module
170 | max-module-lines=1000
171 |
172 | # String used as indentation unit. This is usually " " (4 spaces) or "\t" (1
173 | # tab).
174 | indent-string=' '
175 |
176 |
177 | [MISCELLANEOUS]
178 |
179 | # List of note tags to take in consideration, separated by a comma.
180 | notes=FIXME,XXX,TODO
181 |
182 |
183 | [SIMILARITIES]
184 |
185 | # Minimum lines number of a similarity.
186 | min-similarity-lines=4
187 |
188 | # Ignore comments when computing similarities.
189 | ignore-comments=yes
190 |
191 | # Ignore docstrings when computing similarities.
192 | ignore-docstrings=yes
193 |
194 | # Ignore imports when computing similarities.
195 | ignore-imports=yes
196 |
197 |
198 | [IMPORTS]
199 |
200 | # Deprecated modules which should not be used, separated by a comma
201 | deprecated-modules=regsub,TERMIOS,Bastion,rexec
202 |
203 | # Create a graph of every (i.e. internal and external) dependencies in the
204 | # given file (report RP0402 must not be disabled)
205 | import-graph=
206 |
207 | # Create a graph of external dependencies in the given file (report RP0402 must
208 | # not be disabled)
209 | ext-import-graph=
210 |
211 | # Create a graph of internal dependencies in the given file (report RP0402 must
212 | # not be disabled)
213 | int-import-graph=
214 |
215 |
216 | [CLASSES]
217 | # List of method names used to declare (i.e. assign) instance attributes.
218 | defining-attr-methods=__init__,__new__,setUp
219 |
220 | # List of valid names for the first argument in a class method.
221 | valid-classmethod-first-arg=cls
222 |
223 | # List of valid names for the first argument in a metaclass class method.
224 | valid-metaclass-classmethod-first-arg=mcs
225 |
226 |
227 | [DESIGN]
228 |
229 | # Maximum number of arguments for function / method
230 | max-args=6
231 |
232 | # Argument names that match this expression will be ignored. Default to name
233 | # with leading underscore
234 | ignored-argument-names=_.*|kwargs|args
235 |
236 | # Maximum number of locals for function / method body
237 | max-locals=15
238 |
239 | # Maximum number of return / yield for function / method body
240 | max-returns=6
241 |
242 | # Maximum number of branch for function / method body
243 | max-branches=12
244 |
245 | # Maximum number of statements in function / method body
246 | max-statements=50
247 |
248 | # Maximum number of parents for a class (see R0901).
249 | max-parents=7
250 |
251 | # Maximum number of attributes for a class (see R0902).
252 | max-attributes=7
253 |
254 | # Minimum number of public methods for a class (see R0903).
255 | min-public-methods=2
256 |
257 | # Maximum number of public methods for a class (see R0904).
258 | max-public-methods=20
259 |
260 |
261 | [EXCEPTIONS]
262 |
263 | # Exceptions that will emit a warning when being caught. Defaults to
264 | # "Exception"
265 | overgeneral-exceptions=Exception
266 |
--------------------------------------------------------------------------------
/tests/lint/test_lint.py:
--------------------------------------------------------------------------------
1 | # This Source Code Form is subject to the terms of the Mozilla Public
2 | # License, v. 2.0. If a copy of the MPL was not distributed with this file,
3 | # You can obtain one at http://mozilla.org/MPL/2.0/.
4 | #
5 | # Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com
6 |
7 | """
8 | PEP8 check
9 | """
10 |
11 | import unittest
12 | from subprocess import check_call
13 | from sys import executable
14 | from glob import glob
15 | from pathlib import Path
16 |
17 |
18 | def get_files_and_folders():
19 | """
20 | Return all files and folders which shall be arguments to pycodestyle and pylint
21 | """
22 | root = Path(__file__).parent.parent.parent
23 | ret = list(glob(str(root / "**" / "*.py"), recursive=True))
24 | ret.remove(str(root / "docs" / "conf.py"))
25 | return ret
26 |
27 |
28 | class TestMyPy(unittest.TestCase):
29 | """
30 | Run MyPy static type analysis
31 | """
32 |
33 | @staticmethod
34 | def test_mypy():
35 | check_call([executable, "-m", "mypy", "cosim"])
36 |
37 |
38 | class TestPycodestyle(unittest.TestCase):
39 | """
40 | Test that all python code follows PEP8 Python coding standard
41 | """
42 |
43 | @staticmethod
44 | def test_pycodestyle():
45 | check_call(
46 | [
47 | executable,
48 | "-m",
49 | "pycodestyle",
50 | "--show-source",
51 | "--show-pep8",
52 | "--max-line-length=120",
53 | # W503 mutually exclusive with W504
54 | # E722 bare except checked by pylint
55 | "--ignore=E402,W503,E722,E501,E203",
56 | ]
57 | + get_files_and_folders()
58 | )
59 |
60 |
61 | class TestPylint(unittest.TestCase):
62 | """
63 | Check that there are no pylint errors or warnings
64 | """
65 |
66 | @staticmethod
67 | def test_pylint():
68 | check_call(
69 | [
70 | executable,
71 | "-m",
72 | "pylint",
73 | "--rcfile=" + str(Path(__file__).parent / "pylintrc"),
74 | ]
75 | + get_files_and_folders()
76 | )
77 |
--------------------------------------------------------------------------------
/tox.ini:
--------------------------------------------------------------------------------
1 | [tox]
2 | envlist = py{36,37,38}-{fmt,lint,docs,-acceptance-ghdl}
3 | skip_missing_interpreters = True
4 |
5 | [testenv]
6 | recreate=True
7 |
8 | deps=
9 | fmt: black
10 | pytest
11 | {toxinidir}/../vunit
12 | lint: pycodestyle
13 | lint: pylint
14 | lint: mypy
15 | lint: -Ur{toxinidir}/requirements.txt
16 | docs: -Ur{toxinidir}/requirements.txt
17 | docs: docutils
18 | docs: sphinx
19 |
20 | setenv=
21 | acceptance-ghdl: VUNIT_SIMULATOR=ghdl
22 |
23 | commands=
24 | fmt: {envpython} -m black ./ {posargs}
25 | lint: {envpython} -m pytest -v -ra tests/lint {posargs}
26 | acceptance: {envpython} -m pytest -v -ra tests/acceptance {posargs}
27 | docs: {envpython} -m sphinx -TEWanb html docs {envtmpdir}/docsbuild {posargs}
28 |
--------------------------------------------------------------------------------