From 2bb4d331b629896d3941733454d533075831222a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=98yvind=20Hagen?= Date: Fri, 7 Feb 2020 09:56:15 +0100 Subject: [PATCH 01/79] Fixed RUNTIME_0232 and RUNTIME_022 messages from being printed in Riviera-PRO GUI despite being disabled in startup.tcl. --- vunit/sim_if/rivierapro.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/vunit/sim_if/rivierapro.py b/vunit/sim_if/rivierapro.py index 0cb9b1766..e2e136d29 100644 --- a/vunit/sim_if/rivierapro.py +++ b/vunit/sim_if/rivierapro.py @@ -290,6 +290,10 @@ def _create_load_function( # Make the variable 'LICENSE_QUEUE' visible (if set); otherwise vsim # will not wait for simulation licenses. global LICENSE_QUEUE + # Make the variable 'sv_container_non_existent_entry_verbose' visible + # (if set); otherwise RUNTIME_0232 and RUNTIME_0222 messages will be + # printed even if they have been disabled in startup.tcl. + global sv_container_non_existent_entry_verbose set vsim_failed [catch {{ eval vsim {{{vsim_flags}}} From 9c94659893c442ad1cb5830bf10fb65301add705 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=98yvind=20Hagen?= Date: Sun, 9 Feb 2020 18:43:46 +0100 Subject: [PATCH 02/79] Run the Riviera-PRO 'vsim' command in 'vunit_load' in the global variable context to make sure all the global variables set by the tool is available. --- vunit/sim_if/rivierapro.py | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/vunit/sim_if/rivierapro.py b/vunit/sim_if/rivierapro.py index e2e136d29..3de864546 100644 --- a/vunit/sim_if/rivierapro.py +++ b/vunit/sim_if/rivierapro.py @@ -284,19 +284,13 @@ def _create_load_function( tcl = """ proc vunit_load {{}} {{ - # Make the variable 'aldec' visible; otherwise, the Matlab interface - # is broken because vsim does not find the library aldec_matlab_cosim. - global aldec - # Make the variable 'LICENSE_QUEUE' visible (if set); otherwise vsim - # will not wait for simulation licenses. - global LICENSE_QUEUE - # Make the variable 'sv_container_non_existent_entry_verbose' visible - # (if set); otherwise RUNTIME_0232 and RUNTIME_0222 messages will be - # printed even if they have been disabled in startup.tcl. - global sv_container_non_existent_entry_verbose - + # Run the 'vsim' command in the global variable context. This will make + # variables such as 'aldec' visible, otherwise the Matlab interface + # is broken because vsim does not find the library aldec_matlab_cosim, + # and 'LICENSE_QUEUE' visible (if set); otherwise vsim will not wait + # for simulation licenses. set vsim_failed [catch {{ - eval vsim {{{vsim_flags}}} + uplevel #0 vsim {{{vsim_flags}}} }}] if {{${{vsim_failed}}}} {{ From 455a92bf421c45620c4da77dccbc3b895503e437 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=98yvind=20Hagen?= Date: Mon, 10 Feb 2020 09:09:04 +0100 Subject: [PATCH 03/79] Fixed comment for uplevel vsim in vunit_load for Riviera-PRO simulator. --- vunit/sim_if/rivierapro.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/vunit/sim_if/rivierapro.py b/vunit/sim_if/rivierapro.py index 3de864546..0f5cd4c8e 100644 --- a/vunit/sim_if/rivierapro.py +++ b/vunit/sim_if/rivierapro.py @@ -285,10 +285,10 @@ def _create_load_function( tcl = """ proc vunit_load {{}} {{ # Run the 'vsim' command in the global variable context. This will make - # variables such as 'aldec' visible, otherwise the Matlab interface - # is broken because vsim does not find the library aldec_matlab_cosim, - # and 'LICENSE_QUEUE' visible (if set); otherwise vsim will not wait - # for simulation licenses. + # variables such as 'aldec' and 'LICENSE_QUEUE' visible, if set. Otherwise, + # respectively, the Matlab interface is broken because vsim does not find + # the library aldec_matlab_cosim and vsim will not wait for simulation + # licenses. set vsim_failed [catch {{ uplevel #0 vsim {{{vsim_flags}}} }}] From 4fedefb80ec639c206952c00b905bbdf48ed6fb7 Mon Sep 17 00:00:00 2001 From: eine Date: Mon, 10 Feb 2020 15:59:03 +0100 Subject: [PATCH 04/79] docs: update description of ghdl docker images --- docs/ci.rst | 74 ++++++++++++++++++++++++++++++++++++++++++++++++ docs/cli.rst | 79 +--------------------------------------------------- 2 files changed, 75 insertions(+), 78 deletions(-) create mode 100644 docs/ci.rst diff --git a/docs/ci.rst b/docs/ci.rst new file mode 100644 index 000000000..6f06008d3 --- /dev/null +++ b/docs/ci.rst @@ -0,0 +1,74 @@ +Continuous Integration (CI) Environment +--------------------------------------- + +Because VUnit features the functionality needed to realize continuous and automated testing of HDL code, it is a very valuable resource in continuous integration environments. Once a project ``run.py`` has been setup, tests can be run in a headless environment with standardized `Xunit `_ style output to a file; which allows dynamic interpretation of results avoiding custom (and error-prone) parsing of the logs. + +.. code-block:: console + :caption: Execute VUnit tests on CI server with XML output + + python run.py --xunit-xml test_output.xml + +After tests have finished running, the ``test_output.xml`` file can be parsed +using standard xUnit test parsers such as `Jenkins xUnit Plugin `_. + +Furthermore, VUnit can be easily executed in many different platforms (either operating systems or architectures), because it is written in Python, which is an interpreted language. However, besides the sources and VUnit, a `HDL compiler/simulator `_ is required in order to run the tests. Due to performance, all the HDL simulators are written in compiled languages, which makes the releases platform specific. I.e., each simulator needs to be specifically compiled for a given architecture and operating system. This might represent a burden for the adoption of continuous integration in hardware development teams, as it falls into the category of dev ops. + +Nevertheless, thanks to the striking research about portable development environment solutions in the last decade, there are a bunch of alternatives to ease the path. The 'classic' approach is to use virtual machines with tools +such as `VirtualBox `_, `QEMU `_ or `VMware `_. This is still an option, but for most use cases sharing complete system images is overkill. Here, `containerization or operating-system-level virtualization `_ comes into the game. Without going into technical details, containers are a kind of lightweight virtual machines, and the most known product that uses such a technology is `Docker `_. Indeed, products such as `Vagrant `_ are meant to simplify the usage of virtual machines and/or containers by providing a common (black) box approach. In the end, there are enough open/non-open and free/non-free solutions for each user/company to choose the one that best fits their needs. From the hardware designer point-of-view, we 'just' need a box (no matter the exact underlying technology) that includes VUnit and a simulator. + +Fortunately, contributors of project `GHDL `_ provide ready-to-use docker images at `hub.docker.com/u/ghdl/dashboard `_. Some of these include not only GHDL but also VUnit. Precisely, ``ghdl/vunit:{mcode|llvm|gcc}`` are images based on Debian Buster image with GHDL built from the latest commit of the master branch, and the latest release of VUnit installed through ``pip``. ``ghdl/vunit:{mcode|llvm|gcc}-master`` images include the latest commit of VUnit from the master branch. + +As a result, the burden for the adoption of continuous integration for VUnit users is reduced to using docker; which is available in GNU/Linux, FreeBSD, Windows and macOS, and is supported in most cloud services (`Travis CI `_, `AWS `_, `Codefresh `_, etc.) or CI frameworks (`Jenkins `_, `Drone `_, `GitLab Runner `_, etc.). + +For example, script :vunit_file:`examples/vhdl/docker_runall.sh ` shows how to run all the VHDL examples in any x86 platform: + +.. code-block:: bash + + docker run --rm -t \ + -v /$(pwd)://work \ + -w //work \ + ghdl/vunit:llvm-master sh -c ' \ + VUNIT_SIMULATOR=ghdl; \ + for f in $(find ./ -name 'run.py'); do python3 $f; done \ + ' + +where: + +* ``run``: create and start a container. +* ``--rm``: automatically remove the container when it exits. +* ``-t``: allocate a pseudo-TTY, to get the stdout of the container forwarded. +* ``-v``: bind mount a volume, to share a folder between the host and the container. In this example the current path in the host is used (``$(pwd)``), and it is bind to `/work` inside the container. Note that both paths must be absolute. +* ``-w``: sets the working directory inside the container, i.e. where the commands we provide as arguments are executed. +* ``ghdl/vunit:llvm-master``: the image we want to create a container from. +* ``sh -c``: the command that is executed as soon as the container is created. + +Note that: + +* The arguments to ``sh -c`` are the same commands that you would execute locally, shall all the dependencies be installed in the host: + + .. code-block:: bash + + VUNIT_SIMULATOR=ghdl + for f in $(find ./ -name 'run.py'); do python3 $f; done + +* The leading slashes in ``/$(pwd)`` and ``//work`` are only required for the paths to be properly handled in MINGW shells, and are ignored in other shells. See `docker/for-win#1509 `_. + +Final comments: + +* All the (automated) flow to generate ``ghdl`` docker images is open source and public, in order to let any user learn and extend it. You can easily replicate it to build you own images with other development dependencies you use. + * There are ready-to-use images available with additional tools on top of GHDL and VUnit. For example, ``ghdl/ext`` includes `GTKWave `_. +* Although the licenses of most commercial simulators do not allow to share ready-to-use docker images, it is straightforward to mimic the process. + * If the installation of a tool needs to be executed with a GUI, a slightly different approach is required. See `Propietary applications inside a docker container `_ +* Both GHDL and VUnit are free software. Docker is almost fully open source, but this depends on the host platform. See `Is Docker still free and open source? `_. + +Further info: + +* `What is a container `_ +* `What is docker `_ +* `docs.docker.com/engine/reference `_ + * `run `_ + * `commandline/run `_ +* Docker offers two variants Community Edition (CE) and Enterprise Edition (EE). Any of them can be used. Moreover, part of Docker is being split to `Moby project `_. + * `Announcing Docker Enterprise Edition `_ + * `Introducing Moby Project: a new open-source project to advance the software containerization movement `_ +* If you don't want or cannot install docker, you can still use it online. `Play with Docker `_ (PWD) *"is a Docker playground which allows users to run Docker commands in a matter of seconds. It gives the experience of having a free Alpine Linux Virtual Machine in browser, where you can build and run Docker containers and even create clusters"*. \ No newline at end of file diff --git a/docs/cli.rst b/docs/cli.rst index 81799dda8..944caa3cc 100644 --- a/docs/cli.rst +++ b/docs/cli.rst @@ -196,84 +196,7 @@ Test Output Path Length Environment Variables .. _continuous_integration: -Continuous Integration (CI) Environment ---------------------------------------- - -Because VUnit features the functionality needed to realize continuous and automated testing of HDL code, it is a very valuable resource in continuous integration environments. Once a project ``run.py`` has been setup, tests can be run in a headless environment with standardized `Xunit `_ style output to a file; which allows dynamic interpretation of results avoiding custom (and error-prone) parsing of the logs. - -.. code-block:: console - :caption: Execute VUnit tests on CI server with XML output - - python run.py --xunit-xml test_output.xml - -After tests have finished running, the ``test_output.xml`` file can be parsed -using standard xUnit test parsers such as `Jenkins xUnit Plugin `_. - -Furthermore, VUnit can be easily executed in many different platforms (either operating systems or architectures), because it is written in Python, which is an interpreted language. However, besides the sources and VUnit, a `HDL compiler/simulator `_ is required in order to run the tests. Due to performance, all the HDL simulators are written in compiled languages, which makes the releases platform specific. I.e., each simulator needs to be specifically compiled for a given architecture and operating system. This might represent a burden for the adoption of continuous integration in hardware development teams, as it falls into the category of dev ops. - -Nevertheless, thanks to the striking research about portable development environment solutions in the last decade, there are a bunch of alternatives to ease the path. The 'classic' approach is to use virtual machines with tools -such as `VirtualBox `_, `QEMU `_ or `VMware `_. This is still an option, but for most use cases sharing complete system images is overkill. Here, `containerization or operating-system-level virtualization `_ comes into the game. Without going into technical details, containers are a kind of lightweight virtual machines, and the most known product that uses such a technology is `Docker `_. Indeed, products such as `Vagrant `_ are meant to simplify the usage of virtual machines and/or containers by providing a common (black) box approach. In the end, there are enough open/non-open and free/non-free solutions for each user/company to choose the one that best fits their needs. From the hardware designer point-of-view, we 'just' need a box (no matter the exact underlying technology) that includes VUnit and a simulator. - -Fortunately, contributors of project `GHDL `_ provide ready-to-use docker images at `hub.docker.com/u/ghdl/dashboard `_. Some of these include not only GHDL but also VUnit: - -* ``ghdl/ext:vunit``: Debian Stretch image with GHDL built from the latest commit of the master branch, and the latest release of VUnit installed through ``pip``. -* ``ghdl/ext:vunit-master``: Debian Stretch with GHDL built from the latest commit of the master branch, and the latest commit of VUnit from the master branch. - -As a result, the burden for the adoption of continuous integration for VUnit users is reduced to using docker; which is available in GNU/Linux, FreeBSD, Windows and macOS, and is supported in most cloud services (`Travis CI `_, `AWS `_, `Codefresh `_, etc.) or CI frameworks (`Jenkins `_, `Drone `_, `GitLab Runner `_, etc.). - -For example, script :vunit_file:`examples/vhdl/docker_runall.sh ` shows how to run all the VHDL examples in any x86 platform: - -.. code-block:: bash - - docker run --rm -t \ - -v /$(pwd)://work \ - -w //work \ - ghdl/ext:vunit-master sh -c ' \ - VUNIT_SIMULATOR=ghdl; \ - for f in $(find ./ -name 'run.py'); do python3 $f; done \ - ' - -where: - -* ``run``: create and start a container. -* ``--rm``: automatically remove the container when it exits. -* ``-t``: allocate a pseudo-TTY, to get the stdout of the container forwarded. -* ``-v``: bind mount a volume, to share a folder between the host and the container. In this example the current path in the host is used (``$(pwd)``), and it is bind to `/work` inside the container. Note that both paths must be absolute. -* ``-w``: sets the working directory inside the container, i.e. where the commands we provide as arguments are executed. -* ``ghdl/ext:vunit-master``: the image we want to create a container from. -* ``sh -c``: the command that is executed as soon as the container is created. - -Note that: - -* The arguments to ``sh -c`` are the same commands that you would execute locally, shall all the dependencies be installed in the host: - - .. code-block:: bash - - VUNIT_SIMULATOR=ghdl - for f in $(find ./ -name 'run.py'); do python3 $f; done - -* The leading slashes in ``/$(pwd)`` and ``//work`` are only required for the paths to be properly handled in MINGW shells, and are ignored in other shells. See `docker/for-win#1509 `_. - -Final comments: - -* All the (automated) flow to generate ``ghdl`` docker images is open source and public, in order to let any user learn and extend it. You can easily replicate it to build you own images with other development dependencies you use. - * There are ready-to-use images available with additional tools on top of GHDL and VUnit. For example, ``ghdl/ext:vunit-gtkwave`` includes `GTKWave `_. -* Although the licenses of most commercial simulators do not allow to share ready-to-use docker images, it is straightforward to mimic the process. - * If the installation of a tool needs to be executed with a GUI, a slightly different approach is required. See `Propietary applications inside a docker container `_ -* Both GHDL and VUnit are free software. Docker is almost fully open source, but this depends on the host platform. See `Is Docker still free and open source? `_. - -Further info: - -* `What is a container `_ -* `What is docker `_ -* `docs.docker.com/engine/reference `_ - * `run `_ - * `commandline/run `_ -* Docker offers two variants Community Edition (CE) and Enterprise Edition (EE). Any of them can be used. Moreover, part of Docker is being split to `Moby project `_. - * `Announcing Docker Enterprise Edition `_ - * `Introducing Moby Project: a new open-source project to advance the software containerization movement `_ -* If you don't want or cannot install docker, you can still use it online. `Play with Docker `_ (PWD) *"is a Docker playground which allows users to run Docker commands in a matter of seconds. It gives the experience of having a free Alpine Linux Virtual Machine in browser, where you can build and run Docker containers and even create clusters"*. - +.. include:: ci.rst .. _json_export: From 9ae97725fc1fffd92287d669d3d0ef06b3cfe5d5 Mon Sep 17 00:00:00 2001 From: eine Date: Mon, 10 Feb 2020 15:59:11 +0100 Subject: [PATCH 05/79] docs: use a single vci.rst source file --- docs/verification_components/user_guide.rst | 4 +- .../vci/bus_master.rst | 9 ----- docs/verification_components/vci/stream.rst | 17 --------- docs/verification_components/vci/sync.rst | 9 ----- docs/verification_components/vci/vci.rst | 37 +++++++++++++++++++ 5 files changed, 38 insertions(+), 38 deletions(-) delete mode 100644 docs/verification_components/vci/bus_master.rst delete mode 100644 docs/verification_components/vci/stream.rst delete mode 100644 docs/verification_components/vci/sync.rst create mode 100644 docs/verification_components/vci/vci.rst diff --git a/docs/verification_components/user_guide.rst b/docs/verification_components/user_guide.rst index 8dc67ee0c..81a56c036 100644 --- a/docs/verification_components/user_guide.rst +++ b/docs/verification_components/user_guide.rst @@ -107,6 +107,4 @@ and the VC-developers. :maxdepth: 1 :hidden: - vci/bus_master - vci/stream - vci/sync + vci/vci diff --git a/docs/verification_components/vci/bus_master.rst b/docs/verification_components/vci/bus_master.rst deleted file mode 100644 index cbc359631..000000000 --- a/docs/verification_components/vci/bus_master.rst +++ /dev/null @@ -1,9 +0,0 @@ -.. _bus_master_vci: - -Bus Master VCI -============== - -.. literalinclude:: ../../../vunit/vhdl/verification_components/src/bus_master_pkg.vhd - :caption: Bus master verification component interface - :language: vhdl - :lines: 7- diff --git a/docs/verification_components/vci/stream.rst b/docs/verification_components/vci/stream.rst deleted file mode 100644 index f8a0a273b..000000000 --- a/docs/verification_components/vci/stream.rst +++ /dev/null @@ -1,17 +0,0 @@ -.. _stream_vci: - -Stream Master VCI -================= - -.. literalinclude:: ../../../vunit/vhdl/verification_components/src/stream_master_pkg.vhd - :caption: Stream master verification component interface - :language: vhdl - :lines: 7- - -Stream Slave VCI -================ - -.. literalinclude:: ../../../vunit/vhdl/verification_components/src/stream_slave_pkg.vhd - :caption: Stream slave verification component interface - :language: vhdl - :lines: 7- diff --git a/docs/verification_components/vci/sync.rst b/docs/verification_components/vci/sync.rst deleted file mode 100644 index 6cc0a0e81..000000000 --- a/docs/verification_components/vci/sync.rst +++ /dev/null @@ -1,9 +0,0 @@ -.. _sync_vci: - -Synchronization VCI -=================== - -.. literalinclude:: ../../../vunit/vhdl/verification_components/src/sync_pkg.vhd - :caption: Synchronization verification component interface - :language: vhdl - :lines: 7- diff --git a/docs/verification_components/vci/vci.rst b/docs/verification_components/vci/vci.rst new file mode 100644 index 000000000..0b14bd9ad --- /dev/null +++ b/docs/verification_components/vci/vci.rst @@ -0,0 +1,37 @@ +.. _bus_master_vci: + +Bus Master VCI +============== + +.. literalinclude:: ../../../vunit/vhdl/verification_components/src/bus_master_pkg.vhd + :caption: Bus master verification component interface + :language: vhdl + :lines: 7- + +.. _stream_vci: + +Stream Master VCI +================= + +.. literalinclude:: ../../../vunit/vhdl/verification_components/src/stream_master_pkg.vhd + :caption: Stream master verification component interface + :language: vhdl + :lines: 7- + +Stream Slave VCI +================ + +.. literalinclude:: ../../../vunit/vhdl/verification_components/src/stream_slave_pkg.vhd + :caption: Stream slave verification component interface + :language: vhdl + :lines: 7- + +.. _sync_vci: + +Synchronization VCI +=================== + +.. literalinclude:: ../../../vunit/vhdl/verification_components/src/sync_pkg.vhd + :caption: Synchronization verification component interface + :language: vhdl + :lines: 7- From 9a3b3e34457ddf9525e7d5bd626de1f9b8961379 Mon Sep 17 00:00:00 2001 From: eine Date: Mon, 10 Feb 2020 15:59:16 +0100 Subject: [PATCH 06/79] docs: rework 'posts' as 'blog' --- ...015_09_24_short_introduction_to_vunit.rst} | 9 ++- .../2015_10_08_who_is_using_UVM.rst} | 9 ++- ...urce_verification_with_vunit_and_ghdl.rst} | 9 ++- ...2016_01_26_welcome_to_our_new_website.rst} | 9 ++- ..._chat_with_vunit_users_and_developers.rst} | 7 +-- .../2016_02_01_website_updates.rst} | 9 ++- ...testbench_design_with_message_passing.rst} | 19 +++--- .../2016_08_08_making_osvvm_a_submodule.rst} | 7 +-- ...e_best_value_for_initial_effort_part1.rst} | 10 ++-- ...e_best_value_for_initial_effort_part2.rst} | 14 ++--- ...e_best_value_for_initial_effort_part3.rst} | 24 ++++---- ...017_01_12_vunit_getting_started_1_2_3.rst} | 7 +-- ..._to_handle_complex_top_level_generics.rst} | 7 +-- ...s_support_for_vunit_testing_framework.rst} | 14 ++--- .../2017_10_31_vunit_3_0_color_logging.rst} | 12 ++-- ...vunit_3_0_while_waiting_for_vhdl_2017.rst} | 36 ++++++----- .../2017_11_23_vunit_matlab_integration.rst} | 44 +++++++------- ...2_14_vunit_bfms_as_simple_as_emailing.rst} | 56 +++++++++--------- .../post.rst => blog/2018_02_12_vunit3.rst} | 15 +++-- ..._03_22_vunit_community_developed_bfms.rst} | 10 ++-- ...itment_to_the_vunit_testing_framework.rst} | 34 +++++------ ..._09_22_sigasi_adds_full_vunit_support.rst} | 12 ++-- .../img}/4x.png | Bin .../img}/CPU_load.png | Bin .../img}/VUnit3.0.png | Bin .../img}/activity.png | Bin .../image.jpg => blog/img/bestvalue1.jpg} | Bin .../image.jpg => blog/img/bestvalue2.jpg} | Bin .../image.jpg => blog/img/bestvalue3.jpg} | Bin .../img}/blogging_chat_room_documentation.png | Bin .../img}/caesar_encoder.png | Bin .../image.jpg => blog/img/color_logging.jpg} | Bin .../img}/contributors.png | Bin .../img}/log1.jpg | Bin .../img}/log2.jpg | Bin .../img}/log3.jpg | Bin .../img}/log4.jpg | Bin .../img}/log5.jpg | Bin .../img}/log_example.jpg | Bin .../img}/log_output.png | Bin .../img}/logging_hierarchy.png | Bin .../img}/low_throughput.png | Bin .../figure.jpg => blog/img/matlab_figure.jpg} | Bin .../img}/message_passing_model.png | Bin .../img}/orange_man.jpeg | Bin .../img}/parallel_recipe.jpg | Bin .../img}/sequential_recipe.jpg | Bin .../image.png => blog/img/sigasi_deep.png} | Bin .../image.png => blog/img/sigasi_full.png} | Bin .../img}/the_brain.jpg | Bin .../img}/third_interface.png | Bin .../img}/uvm.png | Bin .../image.jpg => blog/img/vunit_emailing.jpg} | Bin .../img}/vunit_github_io.png | Bin .../image.jpg => blog/img/vunit_matlab.jpg} | Bin .../img}/vunit_popularity.png | Bin .../img/vunit_sigasistudio.jpg} | Bin .../img}/vunit_view.png | Bin .../image.jpg => blog/img/vunit_waiting.jpg} | Bin .../image.png => blog/img/vunit_wishbone.png} | Bin .../img}/world.png | Bin docs/blog/index.rst | 12 ++++ docs/index.rst | 1 + 63 files changed, 183 insertions(+), 203 deletions(-) rename docs/{posts/2015_09_24_short_introduction_to_vunit/post.rst => blog/2015_09_24_short_introduction_to_vunit.rst} (98%) rename docs/{posts/2015_10_08_who_is_using_UVM/post.rst => blog/2015_10_08_who_is_using_UVM.rst} (93%) rename docs/{posts/2015_12_15_free_and_open_source_verification_with_vunit_and_ghdl/post.rst => blog/2015_12_15_free_and_open_source_verification_with_vunit_and_ghdl.rst} (94%) rename docs/{posts/2016_01_26_welcome_to_our_new_website/post.rst => blog/2016_01_26_welcome_to_our_new_website.rst} (91%) rename docs/{posts/2016_01_29_chat_with_vunit_users_and_developers/post.rst => blog/2016_01_29_chat_with_vunit_users_and_developers.rst} (72%) rename docs/{posts/2016_02_01_website_updates/post.rst => blog/2016_02_01_website_updates.rst} (85%) rename docs/{posts/2016_02_21_improving_vhdl_testbench_design_with_message_passing/post.rst => blog/2016_02_21_improving_vhdl_testbench_design_with_message_passing.rst} (99%) rename docs/{posts/2016_08_08_making_osvvm_a_submodule/post.rst => blog/2016_08_08_making_osvvm_a_submodule.rst} (93%) rename docs/{posts/2016_11_15_vunit_the_best_value_for_initial_effort_part 1/post.rst => blog/2016_11_15_vunit_the_best_value_for_initial_effort_part1.rst} (97%) rename docs/{posts/2016_11_16_vunit_the_best_value_for_initial_effort_part 2/post.rst => blog/2016_11_16_vunit_the_best_value_for_initial_effort_part2.rst} (91%) rename docs/{posts/2016_11_22_vunit_the_best_value_for_initial_effort_part 3/post.rst => blog/2016_11_22_vunit_the_best_value_for_initial_effort_part3.rst} (86%) rename docs/{posts/2017_01_12_vunit_getting_started_1_2_3/post.rst => blog/2017_01_12_vunit_getting_started_1_2_3.rst} (90%) rename docs/{posts/2017_06_03_enable_your_simulator_to_handle_complex_top_level_generics/post.rst => blog/2017_06_03_enable_your_simulator_to_handle_complex_top_level_generics.rst} (99%) rename docs/{posts/2017_09_28_sigasi_adds_support_for_vunit_testing_framework/post.rst => blog/2017_09_28_sigasi_adds_support_for_vunit_testing_framework.rst} (91%) rename docs/{posts/2017_10_31_vunit_3_0_color_logging/post.rst => blog/2017_10_31_vunit_3_0_color_logging.rst} (92%) rename docs/{posts/2017_11_07_vunit_3_0_while_waiting_for_vhdl_2017/post.rst => blog/2017_11_07_vunit_3_0_while_waiting_for_vhdl_2017.rst} (96%) rename docs/{posts/2017_11_23_vunit_matlab_integration/post.rst => blog/2017_11_23_vunit_matlab_integration.rst} (97%) rename docs/{posts/2017_12_14_vunit_bfms _as_simple_as_emailing/post.rst => blog/2017_12_14_vunit_bfms_as_simple_as_emailing.rst} (96%) rename docs/{posts/2018_02_12_vunit3/post.rst => blog/2018_02_12_vunit3.rst} (97%) rename docs/{posts/2018_03_22_vunit_community_developed_bfms/post.rst => blog/2018_03_22_vunit_community_developed_bfms.rst} (90%) rename docs/{posts/2018_07_22_sigasi_deepens_its_commitment_to_the_vunit_testing_framework/post.rst => blog/2018_07_22_sigasi_deepens_its_commitment_to_the_vunit_testing_framework.rst} (94%) rename docs/{posts/2018_09_22_sigasi_adds_full_vunit_support/post.rst => blog/2018_09_22_sigasi_adds_full_vunit_support.rst} (89%) rename docs/{posts/2015_09_24_short_introduction_to_vunit => blog/img}/4x.png (100%) rename docs/{posts/2015_12_15_free_and_open_source_verification_with_vunit_and_ghdl => blog/img}/CPU_load.png (100%) rename docs/{posts/2018_02_12_vunit3 => blog/img}/VUnit3.0.png (100%) rename docs/{posts/2018_07_22_sigasi_deepens_its_commitment_to_the_vunit_testing_framework => blog/img}/activity.png (100%) rename docs/{posts/2016_11_15_vunit_the_best_value_for_initial_effort_part 1/image.jpg => blog/img/bestvalue1.jpg} (100%) rename docs/{posts/2016_11_16_vunit_the_best_value_for_initial_effort_part 2/image.jpg => blog/img/bestvalue2.jpg} (100%) rename docs/{posts/2016_11_22_vunit_the_best_value_for_initial_effort_part 3/image.jpg => blog/img/bestvalue3.jpg} (100%) rename docs/{posts/2016_02_01_website_updates => blog/img}/blogging_chat_room_documentation.png (100%) rename docs/{posts/2016_02_21_improving_vhdl_testbench_design_with_message_passing => blog/img}/caesar_encoder.png (100%) rename docs/{posts/2017_10_31_vunit_3_0_color_logging/image.jpg => blog/img/color_logging.jpg} (100%) rename docs/{posts/2018_07_22_sigasi_deepens_its_commitment_to_the_vunit_testing_framework => blog/img}/contributors.png (100%) rename docs/{posts/2017_11_07_vunit_3_0_while_waiting_for_vhdl_2017 => blog/img}/log1.jpg (100%) rename docs/{posts/2017_11_07_vunit_3_0_while_waiting_for_vhdl_2017 => blog/img}/log2.jpg (100%) rename docs/{posts/2017_11_07_vunit_3_0_while_waiting_for_vhdl_2017 => blog/img}/log3.jpg (100%) rename docs/{posts/2017_11_07_vunit_3_0_while_waiting_for_vhdl_2017 => blog/img}/log4.jpg (100%) rename docs/{posts/2017_11_07_vunit_3_0_while_waiting_for_vhdl_2017 => blog/img}/log5.jpg (100%) rename docs/{posts/2017_10_31_vunit_3_0_color_logging => blog/img}/log_example.jpg (100%) rename docs/{posts/2018_02_12_vunit3 => blog/img}/log_output.png (100%) rename docs/{posts/2018_02_12_vunit3 => blog/img}/logging_hierarchy.png (100%) rename docs/{posts/2016_02_21_improving_vhdl_testbench_design_with_message_passing => blog/img}/low_throughput.png (100%) rename docs/{posts/2017_11_23_vunit_matlab_integration/figure.jpg => blog/img/matlab_figure.jpg} (100%) rename docs/{posts/2016_02_21_improving_vhdl_testbench_design_with_message_passing => blog/img}/message_passing_model.png (100%) rename docs/{posts/2016_02_21_improving_vhdl_testbench_design_with_message_passing => blog/img}/orange_man.jpeg (100%) rename docs/{posts/2017_12_14_vunit_bfms _as_simple_as_emailing => blog/img}/parallel_recipe.jpg (100%) rename docs/{posts/2017_12_14_vunit_bfms _as_simple_as_emailing => blog/img}/sequential_recipe.jpg (100%) rename docs/{posts/2018_07_22_sigasi_deepens_its_commitment_to_the_vunit_testing_framework/image.png => blog/img/sigasi_deep.png} (100%) rename docs/{posts/2018_09_22_sigasi_adds_full_vunit_support/image.png => blog/img/sigasi_full.png} (100%) rename docs/{posts/2017_12_14_vunit_bfms _as_simple_as_emailing => blog/img}/the_brain.jpg (100%) rename docs/{posts/2016_02_21_improving_vhdl_testbench_design_with_message_passing => blog/img}/third_interface.png (100%) rename docs/{posts/2015_10_08_who_is_using_UVM => blog/img}/uvm.png (100%) rename docs/{posts/2017_12_14_vunit_bfms _as_simple_as_emailing/image.jpg => blog/img/vunit_emailing.jpg} (100%) rename docs/{posts/2016_01_26_welcome_to_our_new_website => blog/img}/vunit_github_io.png (100%) rename docs/{posts/2017_11_23_vunit_matlab_integration/image.jpg => blog/img/vunit_matlab.jpg} (100%) rename docs/{posts/2018_07_22_sigasi_deepens_its_commitment_to_the_vunit_testing_framework => blog/img}/vunit_popularity.png (100%) rename docs/{posts/2017_09_28_sigasi_adds_support_for_vunit_testing_framework/image.jpg => blog/img/vunit_sigasistudio.jpg} (100%) rename docs/{posts/2018_07_22_sigasi_deepens_its_commitment_to_the_vunit_testing_framework => blog/img}/vunit_view.png (100%) rename docs/{posts/2017_11_07_vunit_3_0_while_waiting_for_vhdl_2017/image.jpg => blog/img/vunit_waiting.jpg} (100%) rename docs/{posts/2018_03_22_vunit_community_developed_bfms/image.png => blog/img/vunit_wishbone.png} (100%) rename docs/{posts/2018_07_22_sigasi_deepens_its_commitment_to_the_vunit_testing_framework => blog/img}/world.png (100%) create mode 100644 docs/blog/index.rst diff --git a/docs/posts/2015_09_24_short_introduction_to_vunit/post.rst b/docs/blog/2015_09_24_short_introduction_to_vunit.rst similarity index 98% rename from docs/posts/2015_09_24_short_introduction_to_vunit/post.rst rename to docs/blog/2015_09_24_short_introduction_to_vunit.rst index d59607d8d..bb6ca9ae1 100644 --- a/docs/posts/2015_09_24_short_introduction_to_vunit/post.rst +++ b/docs/blog/2015_09_24_short_introduction_to_vunit.rst @@ -1,7 +1,6 @@ -.. post:: Sep 24, 2015 - :tags: VUnit - :author: lasplund - :excerpt: 2 +:tags: VUnit +:author: lasplund +:excerpt: 2 .. _short_introduction_to_vunit_post: @@ -10,7 +9,7 @@ Short Introduction to VUnit **Originally posted and commented on** `LinkedIn `_. -.. image:: 4x.png +.. image:: img/4x.png `VUnit `__ is a free and open source unit testing framework for VHDL that was released in November last year. diff --git a/docs/posts/2015_10_08_who_is_using_UVM/post.rst b/docs/blog/2015_10_08_who_is_using_UVM.rst similarity index 93% rename from docs/posts/2015_10_08_who_is_using_UVM/post.rst rename to docs/blog/2015_10_08_who_is_using_UVM.rst index ff5439852..95082d309 100644 --- a/docs/posts/2015_10_08_who_is_using_UVM/post.rst +++ b/docs/blog/2015_10_08_who_is_using_UVM.rst @@ -1,7 +1,6 @@ -.. post:: Oct 10, 2015 - :tags: VUnit - :author: lasplund - :excerpt: 2 +:tags: VUnit +:author: lasplund +:excerpt: 2 Who's Using UVM (or Not) for FPGA Development, and Why? ======================================================= @@ -9,7 +8,7 @@ Who's Using UVM (or Not) for FPGA Development, and Why? **Originally posted and commented on** `LinkedIn `__. -.. image:: uvm.png +.. image:: img/uvm.png Over the last few years a number of open source test solutions have emerged. I'm talking about tools like our diff --git a/docs/posts/2015_12_15_free_and_open_source_verification_with_vunit_and_ghdl/post.rst b/docs/blog/2015_12_15_free_and_open_source_verification_with_vunit_and_ghdl.rst similarity index 94% rename from docs/posts/2015_12_15_free_and_open_source_verification_with_vunit_and_ghdl/post.rst rename to docs/blog/2015_12_15_free_and_open_source_verification_with_vunit_and_ghdl.rst index 7c7887676..89cdb0f32 100644 --- a/docs/posts/2015_12_15_free_and_open_source_verification_with_vunit_and_ghdl/post.rst +++ b/docs/blog/2015_12_15_free_and_open_source_verification_with_vunit_and_ghdl.rst @@ -1,7 +1,6 @@ -.. post:: Dec 15, 2015 - :tags: VUnit - :author: lasplund - :excerpt: 2 +:tags: VUnit +:author: lasplund +:excerpt: 2 Free and Open Source Verification with VUnit and GHDL @@ -9,7 +8,7 @@ Free and Open Source Verification with VUnit and GHDL **Originally posted and commented on** `LinkedIn `__. -.. image:: CPU_load.png +.. image:: img/CPU_load.png `VUnit `__ is a free and open source (FOSS) unit testing framework for VHDL that supports ModelSim, diff --git a/docs/posts/2016_01_26_welcome_to_our_new_website/post.rst b/docs/blog/2016_01_26_welcome_to_our_new_website.rst similarity index 91% rename from docs/posts/2016_01_26_welcome_to_our_new_website/post.rst rename to docs/blog/2016_01_26_welcome_to_our_new_website.rst index 5402b83b1..ce60c9fe7 100644 --- a/docs/posts/2016_01_26_welcome_to_our_new_website/post.rst +++ b/docs/blog/2016_01_26_welcome_to_our_new_website.rst @@ -1,11 +1,10 @@ -.. post:: Jan 26, 2016 - :tags: VUnit - :author: lasplund - :excerpt: 1 +:tags: VUnit +:author: lasplund +:excerpt: 1 Welcome to Our New Website ========================== -.. image:: vunit_github_io.png +.. image:: img/vunit_github_io.png Today I moved VUnit from my personal GitHub account to a dedicated `VUnit account `__. Links to the diff --git a/docs/posts/2016_01_29_chat_with_vunit_users_and_developers/post.rst b/docs/blog/2016_01_29_chat_with_vunit_users_and_developers.rst similarity index 72% rename from docs/posts/2016_01_29_chat_with_vunit_users_and_developers/post.rst rename to docs/blog/2016_01_29_chat_with_vunit_users_and_developers.rst index 97dc1df51..03a976530 100644 --- a/docs/posts/2016_01_29_chat_with_vunit_users_and_developers/post.rst +++ b/docs/blog/2016_01_29_chat_with_vunit_users_and_developers.rst @@ -1,7 +1,6 @@ -.. post:: Jan 29, 2016 - :tags: VUnit - :author: lasplund - :excerpt: 1 +:tags: VUnit +:author: lasplund +:excerpt: 1 Chat with VUnit Users and Developers ==================================== diff --git a/docs/posts/2016_02_01_website_updates/post.rst b/docs/blog/2016_02_01_website_updates.rst similarity index 85% rename from docs/posts/2016_02_01_website_updates/post.rst rename to docs/blog/2016_02_01_website_updates.rst index 4ea4af2e8..23d5b7c53 100644 --- a/docs/posts/2016_02_01_website_updates/post.rst +++ b/docs/blog/2016_02_01_website_updates.rst @@ -1,7 +1,6 @@ -.. post:: Feb 1, 2016 - :tags: VUnit - :author: lasplund - :excerpt: 2 +:tags: VUnit +:author: lasplund +:excerpt: 2 .. _vunit_website_updates: @@ -9,7 +8,7 @@ Website Updates =============== -.. figure:: blogging_chat_room_documentation.png +.. figure:: img/blogging_chat_room_documentation.png Our website has been updated with a `Gitter `__-based diff --git a/docs/posts/2016_02_21_improving_vhdl_testbench_design_with_message_passing/post.rst b/docs/blog/2016_02_21_improving_vhdl_testbench_design_with_message_passing.rst similarity index 99% rename from docs/posts/2016_02_21_improving_vhdl_testbench_design_with_message_passing/post.rst rename to docs/blog/2016_02_21_improving_vhdl_testbench_design_with_message_passing.rst index 74a9ef2eb..7aa563e6c 100644 --- a/docs/posts/2016_02_21_improving_vhdl_testbench_design_with_message_passing/post.rst +++ b/docs/blog/2016_02_21_improving_vhdl_testbench_design_with_message_passing.rst @@ -1,13 +1,12 @@ -.. post:: Feb 21, 2016 - :tags: VUnit - :author: lasplund - :excerpt: 1 - :image: 1 +:tags: VUnit +:author: lasplund +:excerpt: 1 +:image: 1 Improving VHDL Testbench Design with Message Passing ==================================================== -.. figure:: orange_man.jpeg +.. figure:: img/orange_man.jpeg :alt: message passing :align: center @@ -149,7 +148,7 @@ alphabet. This shift value equals 1 in my example so that *a* is encrypted to *b*, *b* is encrypted to *c* and so on. The latency is three clock cycles. -.. figure:: caesar_encoder.png +.. figure:: img/caesar_encoder.png :alt: Caesar Encoder :align: center @@ -227,7 +226,7 @@ below, and we never verify the situation in the hello world example shown previously where the sentences are separated with a single clock cycle. -.. figure:: low_throughput.png +.. figure:: img/low_throughput.png :alt: Low Throughput :align: center @@ -254,7 +253,7 @@ model `__) which communicates with each other using *messages* sent over an abstract communication medium called the *net*. -.. figure:: message_passing_model.png +.. figure:: img/message_passing_model.png :alt: Message Passing Model :align: center @@ -332,7 +331,7 @@ remain. To refine this design pattern I will also have a dedicated still the entry point for understanding the testbench as it will continue to coordinate the actions of the others. -.. figure:: third_interface.png +.. figure:: img/third_interface.png :alt: Third Interface :align: center diff --git a/docs/posts/2016_08_08_making_osvvm_a_submodule/post.rst b/docs/blog/2016_08_08_making_osvvm_a_submodule.rst similarity index 93% rename from docs/posts/2016_08_08_making_osvvm_a_submodule/post.rst rename to docs/blog/2016_08_08_making_osvvm_a_submodule.rst index 1d6da5de8..394ef7e46 100644 --- a/docs/posts/2016_08_08_making_osvvm_a_submodule/post.rst +++ b/docs/blog/2016_08_08_making_osvvm_a_submodule.rst @@ -1,7 +1,6 @@ -.. post:: Aug 8, 2016 - :tags: VUnit, OSVVM - :author: lasplund - :excerpt: 1 +:tags: VUnit, OSVVM +:author: lasplund +:excerpt: 1 Making OSVVM a Git Submodule ==================================================== diff --git a/docs/posts/2016_11_15_vunit_the_best_value_for_initial_effort_part 1/post.rst b/docs/blog/2016_11_15_vunit_the_best_value_for_initial_effort_part1.rst similarity index 97% rename from docs/posts/2016_11_15_vunit_the_best_value_for_initial_effort_part 1/post.rst rename to docs/blog/2016_11_15_vunit_the_best_value_for_initial_effort_part1.rst index 859bb4b5f..7edb826be 100644 --- a/docs/posts/2016_11_15_vunit_the_best_value_for_initial_effort_part 1/post.rst +++ b/docs/blog/2016_11_15_vunit_the_best_value_for_initial_effort_part1.rst @@ -1,13 +1,11 @@ -.. post:: Nov 15, 2016 - :tags: VUnit - :author: lasplund - :image: 1 - :excerpt: 1 +:tags: VUnit +:author: lasplund +:excerpt: 1 VUnit - The Best Value for Initial Effort - Part 1 ================================================== -.. figure:: image.jpg +.. figure:: img/bestvalue1.jpg :alt: Best Value Part 1 :align: center diff --git a/docs/posts/2016_11_16_vunit_the_best_value_for_initial_effort_part 2/post.rst b/docs/blog/2016_11_16_vunit_the_best_value_for_initial_effort_part2.rst similarity index 91% rename from docs/posts/2016_11_16_vunit_the_best_value_for_initial_effort_part 2/post.rst rename to docs/blog/2016_11_16_vunit_the_best_value_for_initial_effort_part2.rst index 32f3469db..1f3dc7113 100644 --- a/docs/posts/2016_11_16_vunit_the_best_value_for_initial_effort_part 2/post.rst +++ b/docs/blog/2016_11_16_vunit_the_best_value_for_initial_effort_part2.rst @@ -1,13 +1,11 @@ -.. post:: Nov 16, 2016 - :tags: VUnit - :author: lasplund - :image: 1 - :excerpt: 1 +:tags: VUnit +:author: lasplund +:excerpt: 1 VUnit - The Best Value for Initial Effort - Part 2 ================================================== -.. figure:: image.jpg +.. figure:: img/bestvalue2.jpg :alt: Best Value Part 2 :align: center @@ -28,7 +26,7 @@ another minute. In my second `video clip `__ I will show how you -can create a compile +can create a compile script for your project within a minute. This script provides incremental compilation, meaning that it will find your source files, figure out their dependencies to create a compile order, and then @@ -38,7 +36,7 @@ files can normally be added and removed without modifications. Another minute of work but this time some real added value. The only requirement is that you are using one of the supported `simulators -`__, +`__, currently ModelSim/Questa, Riviera-Pro, Active-HDL, GHDL, and Cadence Incisive. Support for other simulators is `planned `__. diff --git a/docs/posts/2016_11_22_vunit_the_best_value_for_initial_effort_part 3/post.rst b/docs/blog/2016_11_22_vunit_the_best_value_for_initial_effort_part3.rst similarity index 86% rename from docs/posts/2016_11_22_vunit_the_best_value_for_initial_effort_part 3/post.rst rename to docs/blog/2016_11_22_vunit_the_best_value_for_initial_effort_part3.rst index 0e7edd8b6..cb017221b 100644 --- a/docs/posts/2016_11_22_vunit_the_best_value_for_initial_effort_part 3/post.rst +++ b/docs/blog/2016_11_22_vunit_the_best_value_for_initial_effort_part3.rst @@ -1,34 +1,32 @@ -.. post:: Nov 22, 2016 - :tags: VUnit - :author: lasplund - :image: 1 - :excerpt: 1 +:tags: VUnit +:author: lasplund +:excerpt: 1 VUnit - The Best Value for Initial Effort - Part 3 ================================================== -.. figure:: image.jpg +.. figure:: img/bestvalue3.jpg :alt: Best Value Part 3 :align: center This article was originally posted on `LinkedIn `__ -where you may find some comments on its contents. - +where you may find some comments on its contents. + After spending one minute on `installing VUnit `__ and one minute on `creating a run script `__ for incremental compilation it is time to go full automation. Five lines of code or roughly 30 seconds of work for each testbench is what -it takes to get the following added values: +it takes to get the following added values: - A single command to verify your project designs or, which I will - show in the next blog, part of the designs. + show in the next blog, part of the designs. - Support for distributing the simulations over many CPU cores. If you have a quad core CPU you can have a 4x speed-up. This requires more simulator licenses but if you use a free version of a commercial - simulator or the open source GHDL that is no problem. + simulator or the open source GHDL that is no problem. - Continuous integration with the help of tools like `Jenkins `__ and `Travis `__. Did you notice the build status @@ -40,7 +38,7 @@ it takes to get the following added values: This `video `__ clip will show the details for this step and the resulting code is in my `repository -`__. +`__. All in all I've spent less than ten minutes to convert this project but I haven't really made any changes to how the testbenches are @@ -51,7 +49,7 @@ VUnit must be able to handle legacy asserts in order to be the low-effort solution we're striving for. A better approach is to continuously improve on the code you have, for example when developing tests for new functionality, making manual testbenches self-checking, -or when debugging designs. +or when debugging designs. This was all for now. In the next blog I will talk about test cases in VUnit and how they bring clarity and extra speed to your diff --git a/docs/posts/2017_01_12_vunit_getting_started_1_2_3/post.rst b/docs/blog/2017_01_12_vunit_getting_started_1_2_3.rst similarity index 90% rename from docs/posts/2017_01_12_vunit_getting_started_1_2_3/post.rst rename to docs/blog/2017_01_12_vunit_getting_started_1_2_3.rst index 647be0782..a98f601aa 100644 --- a/docs/posts/2017_01_12_vunit_getting_started_1_2_3/post.rst +++ b/docs/blog/2017_01_12_vunit_getting_started_1_2_3.rst @@ -1,7 +1,6 @@ -.. post:: Jan 12, 2017 - :tags: VUnit - :author: lasplund - :excerpt: 1 +:tags: VUnit +:author: lasplund +:excerpt: 1 VUnit - Getting Started 1-2-3 ============================= diff --git a/docs/posts/2017_06_03_enable_your_simulator_to_handle_complex_top_level_generics/post.rst b/docs/blog/2017_06_03_enable_your_simulator_to_handle_complex_top_level_generics.rst similarity index 99% rename from docs/posts/2017_06_03_enable_your_simulator_to_handle_complex_top_level_generics/post.rst rename to docs/blog/2017_06_03_enable_your_simulator_to_handle_complex_top_level_generics.rst index 846ec7167..630b7dbd7 100644 --- a/docs/posts/2017_06_03_enable_your_simulator_to_handle_complex_top_level_generics/post.rst +++ b/docs/blog/2017_06_03_enable_your_simulator_to_handle_complex_top_level_generics.rst @@ -1,7 +1,6 @@ -.. post:: Jun 03, 2017 - :tags: VUnit - :author: lasplund - :excerpt: 2 +:tags: VUnit +:author: lasplund +:excerpt: 2 Enable Your Simulator to Handle Complex Top-Level Generics ========================================================== diff --git a/docs/posts/2017_09_28_sigasi_adds_support_for_vunit_testing_framework/post.rst b/docs/blog/2017_09_28_sigasi_adds_support_for_vunit_testing_framework.rst similarity index 91% rename from docs/posts/2017_09_28_sigasi_adds_support_for_vunit_testing_framework/post.rst rename to docs/blog/2017_09_28_sigasi_adds_support_for_vunit_testing_framework.rst index aaa5fb790..8dbf49d6b 100644 --- a/docs/posts/2017_09_28_sigasi_adds_support_for_vunit_testing_framework/post.rst +++ b/docs/blog/2017_09_28_sigasi_adds_support_for_vunit_testing_framework.rst @@ -1,13 +1,11 @@ -.. post:: Sep 22, 2017 - :tags: VUnit - :author: lasplund - :image: 1 - :excerpt: 1 +:tags: VUnit +:author: lasplund +:excerpt: 1 Sigasi Adds Support for VUnit Testing Framework =============================================== -.. figure:: image.jpg +.. figure:: img/vunit_sigasistudio.jpg :alt: Sigasi Support :align: center @@ -41,7 +39,7 @@ collect the results. All error feedback in one place. This is actually very common when using Eclipse with software languages and with the new Sigasi Studio 3.6 release the company is taking the `first steps `__ towards such -an integration by recognizing VUnit testbenches and +an integration by recognizing VUnit testbenches and supporting their creation. I'm very much looking forward to the coming releases of Sigasi Studio! - + diff --git a/docs/posts/2017_10_31_vunit_3_0_color_logging/post.rst b/docs/blog/2017_10_31_vunit_3_0_color_logging.rst similarity index 92% rename from docs/posts/2017_10_31_vunit_3_0_color_logging/post.rst rename to docs/blog/2017_10_31_vunit_3_0_color_logging.rst index 95a3eeb50..a07e8f432 100644 --- a/docs/posts/2017_10_31_vunit_3_0_color_logging/post.rst +++ b/docs/blog/2017_10_31_vunit_3_0_color_logging.rst @@ -1,13 +1,11 @@ -.. post:: Oct 31, 2017 - :tags: VUnit - :author: lasplund - :image: 1 - :excerpt: 1 +:tags: VUnit +:author: lasplund +:excerpt: 1 VUnit 3.0 Color Logging ======================= -.. figure:: image.jpg +.. figure:: img/color_logging.jpg :alt: VUnit 3.0 Color Logging :align: center @@ -23,7 +21,7 @@ framework. Color logging is exactly what it sounds like, the addition of colors to simplify browsing of logs. For example -.. figure:: log_example.jpg +.. figure:: img/log_example.jpg :alt: Log Example :align: center diff --git a/docs/posts/2017_11_07_vunit_3_0_while_waiting_for_vhdl_2017/post.rst b/docs/blog/2017_11_07_vunit_3_0_while_waiting_for_vhdl_2017.rst similarity index 96% rename from docs/posts/2017_11_07_vunit_3_0_while_waiting_for_vhdl_2017/post.rst rename to docs/blog/2017_11_07_vunit_3_0_while_waiting_for_vhdl_2017.rst index 66e822cf4..843c63432 100644 --- a/docs/posts/2017_11_07_vunit_3_0_while_waiting_for_vhdl_2017/post.rst +++ b/docs/blog/2017_11_07_vunit_3_0_while_waiting_for_vhdl_2017.rst @@ -1,13 +1,11 @@ -.. post:: Nov 7, 2017 - :tags: VUnit - :author: lasplund - :image: 1 - :excerpt: 1 +:tags: VUnit +:author: lasplund +:excerpt: 1 VUnit 3.0 - While Waiting for VHDL-2017 ======================================= -.. figure:: image.jpg +.. figure:: img/vunit_waiting.jpg :alt: VUnit 3.0 - While Waiting for VHDL-2017 :align: center @@ -140,11 +138,11 @@ messages to a custom logger named after the process path name. test_runner_cleanup(runner); end process; - end architecture; + end architecture; The resulting output is -.. figure:: log1.jpg +.. figure:: img/log1.jpg :align: center It may look like we created a logger named `tb:main` but the colon in @@ -153,7 +151,7 @@ loggers with parent/child relationships. So the single call to `get_logger` will create two loggers if they don't already exist. One logger is named `main` and is the child of the other logger named `tb`. Note that the debug message isn't visible. By default the `log -level` is set not to include such details. +level` is set not to include such details. Now let's create a dummy verification component. It will just take a logger as a generic (not possible prior to VUnit 3.0) and then do some @@ -163,14 +161,14 @@ logging on that logger. Here is the entity declaration. entity verification_component is generic (logger : logger_t := verification_component_logger); - end entity; + end entity; If this component is instantiated without assigning the `logger` generic it will use `verification_component_logger` instead. This is a logger defined by the verification component itself and placed in an -associated package. +associated package. -.. figure:: log2.jpg +.. figure:: img/log2.jpg :align: center To make the log more readable and the example more interesting I'm @@ -189,7 +187,7 @@ provide them with their own loggers. beta : entity work.verification_component generic map (logger => beta_logger); - end block; + end block; What I've done here is to collect all my verification components in a separate block labelled `vc`. `vc` has its own `vc_logger` based on the path @@ -198,9 +196,9 @@ name just like I did for `main_logger`. The loggers for the `alpha` and than providing a complete hierarchical name to `get_logger` I just provide a simple name and the parent logger. -My log output will now look like this +My log output will now look like this -.. figure:: log3.jpg +.. figure:: img/log3.jpg :align: center Now that we have our hierarchy of loggers we can start controlling @@ -215,7 +213,7 @@ being logged on file is handled separately by the file handler. constant main_logger : logger_t := get_logger(main'path_name); begin test_runner_setup(runner, runner_cfg); - + show(main_logger, display_handler, debug); info(main_logger, "Starting testbench"); @@ -226,13 +224,13 @@ being logged on file is handled separately by the file handler. The result is -.. figure:: log4.jpg +.. figure:: img/log4.jpg :align: center I can also control the loggers for `alpha` and `beta` individually but it's also possible to address them collectively by controlling a shared ancestor in the hierarchy. Let's add a configuration process -to `vc`. +to `vc`. .. code-block:: vhdl @@ -257,7 +255,7 @@ to `vc`. The visibility setting applied to `vc_logger` will also be inherited and applied to all its descendants, in this case `alpha` and `beta`. -.. figure:: log5.jpg +.. figure:: img/log5.jpg :align: center That's all for now. Hopefully you've learned something new about diff --git a/docs/posts/2017_11_23_vunit_matlab_integration/post.rst b/docs/blog/2017_11_23_vunit_matlab_integration.rst similarity index 97% rename from docs/posts/2017_11_23_vunit_matlab_integration/post.rst rename to docs/blog/2017_11_23_vunit_matlab_integration.rst index 189681ccc..872031e1a 100644 --- a/docs/posts/2017_11_23_vunit_matlab_integration/post.rst +++ b/docs/blog/2017_11_23_vunit_matlab_integration.rst @@ -1,26 +1,24 @@ -.. post:: Nov 23, 2017 - :tags: VUnit - :author: lasplund - :image: 1 - :excerpt: 1 +:tags: VUnit +:author: lasplund +:excerpt: 1 VUnit Matlab Integration ======================== -.. figure:: image.jpg +.. figure:: img/vunit_matlab.jpg :alt: VUnit Matlab Integration :align: center This article was originally posted on `LinkedIn `__ -where you may find some comments on its contents. - +where you may find some comments on its contents. + Recently I got a question from an ASIC team if it is possible to integrate their VUnit simulations with Matlab. I've been getting this question several times lately so this post will show you how it can be done. It will be based on their use case but hopefully it will serve as inspiration if you have other Matlab use cases or want to integrate -VUnit with some other program. +VUnit with some other program. The Testbench ------------- @@ -35,14 +33,14 @@ samples are generated by repeatedly calling a function loop. The samples generated form a simple ramp function as shown in the figure below. -.. figure:: figure.jpg +.. figure:: img/matlab_figure.jpg :alt: Testbench Progress :align: center Here is the main loop of my VUnit testbench. .. code-block:: vhdl - + test_runner: process is variable data_set : integer_array_t; begin @@ -79,20 +77,20 @@ were discussed in my previous `post In this case I have a one-dimensional array (a vector) created by .. code-block:: vhdl - + data_set := new_1d; The created vecor is empty by default and grows dynamically with every new sample I append. .. code-block:: vhdl - + append(data_set, get_output_sample); Once I have a complete data set I save that to a CSV file and then deallocate the data set such that I can repeat the procedure. .. code-block:: vhdl - + save_csv(data_set, file_name => join(output_path(runner_cfg), "data_set_" & to_string(set) & ".csv")); deallocate(data_set); @@ -104,7 +102,7 @@ The Run Script The run script is similar to most run scripts with a few additions .. code-block:: python - + prj = VUnit.from_argv() prj.add_array_util() @@ -125,20 +123,20 @@ The VUnit array support is an add-on not compiled into `vunit_lib` by default. To include it you have to add .. code-block:: vhdl - + prj.add_array_util() Next I want to create a configuration for my testbench. A VUnit configuration allows me to run my testbench with several different settings. In this example my testbench entity is called `tb_octave` and I get the testbench, compiled into `lib`, with the line .. code-block:: python - + tb_octave = lib.entity("tb_octave") Using the `add_config` method I can now add a configuration to the testbench which I named `Passing test`. .. code-block:: python - + tb_octave.add_config(name="Passing test", generics=dict(size_of_data_set=10, num_of_data_sets=10, @@ -154,7 +152,7 @@ called a `pre_config` function. This is a function that VUnit calls before starting the simulation. .. code-block:: python - + def pre_config(output_path): p = run(["octave", join(root, "octave", "visualize.m"), output_path, plot_title, str(num_of_data_sets)]) @@ -165,7 +163,7 @@ same directory we saw in the testbench before. Note that the `output_path` name doesn't mean that it can't be used for simulation input. A `pre_config` function can for example be used to generate and store an input data file in `output_path` and let the testbench read -that data. +that data. In this example I use `pre_config` to call Matlab (or rather Octave which is a free Matlab clone) using the Python `run` function. Octave @@ -178,7 +176,7 @@ Octave should expect. But where are `plot_title` and `num_of_data_sets` defined? `pre_config` is called by VUnit and it can only provide arguments it knows about. VUnit knows about the `output_path` it created but doesn't know anything about the purpose of the `pre_config` function and what it needs to fulfill that purpose. I can hardcode these values but what if I want to reuse `pre_config` with different values? The trick is to generate the `pre_config` function. .. code-block:: python - + def make_pre_config(plot_title, num_of_data_sets): def pre_config(output_path): p = run(["octave", join(root, "octave", "visualize.m"), output_path, plot_title, str(num_of_data_sets)]) @@ -309,11 +307,11 @@ still a number of flaws with this solution. For example split the data into sets and store them in separate files. I would prefer writing and reading a single open file. - Copying and modifying code is not good reuse. I need to raise the - abstraction and remove details. + abstraction and remove details. - Responsibility for the plot is all over the place. The testbench is in charge of the data, the title is set by the Python script, axis labels are controlled by the M script, and some properties are - hardcoded. + hardcoded. It seems that I will have to revisit this post. Until then... diff --git a/docs/posts/2017_12_14_vunit_bfms _as_simple_as_emailing/post.rst b/docs/blog/2017_12_14_vunit_bfms_as_simple_as_emailing.rst similarity index 96% rename from docs/posts/2017_12_14_vunit_bfms _as_simple_as_emailing/post.rst rename to docs/blog/2017_12_14_vunit_bfms_as_simple_as_emailing.rst index e240f1f3f..ba82ff411 100644 --- a/docs/posts/2017_12_14_vunit_bfms _as_simple_as_emailing/post.rst +++ b/docs/blog/2017_12_14_vunit_bfms_as_simple_as_emailing.rst @@ -1,18 +1,16 @@ -.. post:: Dec 14, 2017 - :tags: VUnit - :author: lasplund - :image: 1 - :excerpt: 1 +:tags: VUnit +:author: lasplund +:excerpt: 1 VUnit BFMs - as Simple as Emailing ================================== -.. figure:: image.jpg +.. figure:: img/vunit_emailing.jpg :alt: VUnit BFMs - as Simple as Emailing :align: center This article was originally posted on `LinkedIn `__ where you may find some comments on its contents. - + VUnit 3.0, our next major release, is around the corner and with it comes a number of updates and additions. One area which we have improved is our support for creating advanced bus functional models @@ -42,7 +40,7 @@ the blue interface, then some data is put on the green port, then there is a write access on the red bus interface. Crisp and clear as a recipe. -.. figure:: sequential_recipe.jpg +.. figure:: img/sequential_recipe.jpg :alt: Sequential Recipe :align: center @@ -52,7 +50,7 @@ However, looking at a real recipe we can see an important difference 2. Split vanilla bean in half and scrape out the pulp. 3. While the oven is preheating, place the cream, vanilla bean and its pulp into a saucepan set over medium-high heat and bring to a - boil. + boil. 4. ... The recipe is sequential in nature but actually describes concurrent @@ -83,7 +81,7 @@ time. This allows the concurrent execution of the `put` and `write` transactions. Procedures like `init`, which await completion, will be blocking and consume simulation time. -.. figure:: parallel_recipe.jpg +.. figure:: img/parallel_recipe.jpg :alt: Parallel Recipe :align: center @@ -93,7 +91,7 @@ selecting what basic BFM procedure to run based on the type of command it receives, read or write for example. The challenging part, and the key to make the advanced BFM approach tractable, is the communication system connecting the test sequencer procedure calls with the BFM -components. +components. No Need to Reinvent the Wheel ----------------------------- @@ -118,7 +116,7 @@ in an email thread. In this example my test sequencer is communicating with a BFM connected to a bus of a pure memory device. For reasons that will appear later I'm calling this device *The Brain*. -.. figure:: the_brain.jpg +.. figure:: img/the_brain.jpg :alt: The Brain :align: center @@ -133,14 +131,14 @@ really communicating with the BFM but that's just a technical detail which I've excluded from the naming. .. code-block:: vhdl - + constant brain : actor_t := new_actor("The Brain"); The test sequencer may have direct access to the brain "email address" but it can also figure it out. .. code-block:: vhdl - + brain := find("The Brain"); Just like searching the contacts list in your email client. @@ -150,12 +148,12 @@ Sending an Email Now that the basics are covered we can start communicating. This is how you send a message to the brain BFM instructing it to start a -write transaction. +write transaction. .. code-block:: vhdl - + write_msg := new_msg(brain_write_msg); - push_integer(write_msg, address); + push_integer(write_msg, address); push_std_ulogic_vector(write_msg, data); send(net, brain, write_msg); @@ -176,7 +174,7 @@ allowed to have the same message type. To handle this a BFM can register its message types and get unique identifiers in return. .. code-block:: vhdl - + constant brain_write_msg : msg_type_t := new_msg_type("write"); When writing an email you add text but you can also attach pictures, @@ -206,14 +204,14 @@ Receiving an Email ------------------ The BFM receiving the test sequencer messages would have a process -with a body starting like this. +with a body starting like this. .. code-block:: vhdl - + begin receive(net, brain, command_msg); msg_type := message_type(command_msg); - + if msg_type = brain_write_msg then address := pop_integer(command_msg); data := pop_std_ulogic_vector(command_msg); @@ -237,7 +235,7 @@ continuation of the if statement above showing how the BFM replies to a read command. .. code-block:: vhdl - + elsif msg_type = brain_read_msg then address := pop_integer(command_msg); read(a, number, of, bus, interface, signals, address, data); @@ -258,7 +256,7 @@ reply of *that* message and ignore everything else. Messages ignored are not deleted but remain in the inbox until you are ready to read them. .. code-block:: vhdl - + -- Initiate a read transaction read_msg := new_msg(brain_read_msg); @@ -269,7 +267,7 @@ not deleted but remain in the inbox until you are ready to read them. receive_reply(net, read_msg, reply_msg); data := pop_std_ulogic_vector(reply_msg); - + Again, these are details that the test sequencer doesn't have to see. The first three lines can be encapsulated in a non-blocking read procedure `brain_read` that lets you do other things while waiting for @@ -281,7 +279,7 @@ promise of future data and `get` retrieves that data and may block if the data is yet to be received. .. code-block:: vhdl - + brain_read(net, address, future); get(net, future, data); @@ -289,7 +287,7 @@ Sometimes you just need a blocking read so a procedure bundling these two should also be provided. .. code-block:: vhdl - + brain_read(net, address, data); The difference between the two `brain_read` procedures is the type of @@ -312,7 +310,7 @@ part of normal emailing. You can make it more like emailing by signing your messages with a `test_sequencer` actor. .. code-block:: vhdl - + msg := new_msg(brain_write_msg, test_sequencer); If you do, all reply messages sent by `brain` will end up in the @@ -325,7 +323,7 @@ it will look for the message in the `brain` outbox. No privacy but we don't really need that. .. code-block:: vhdl - + receive_reply(net, read_msg, reply_msg); Sending Real Emails from within a Testbench @@ -350,7 +348,7 @@ receiver. Instead the message type was pushed/popped to/from the message just like any other message content. This is still possible but not the recommended way of doing it. The newer approach provides better debugging support as described in the `user guide -`__. +`__. .. raw:: html diff --git a/docs/posts/2018_02_12_vunit3/post.rst b/docs/blog/2018_02_12_vunit3.rst similarity index 97% rename from docs/posts/2018_02_12_vunit3/post.rst rename to docs/blog/2018_02_12_vunit3.rst index b6479d802..99aa7f2b5 100644 --- a/docs/posts/2018_02_12_vunit3/post.rst +++ b/docs/blog/2018_02_12_vunit3.rst @@ -1,13 +1,12 @@ -.. post:: Feb 12, 2018 - :tags: VUnit - :author: kraigher, lasplund - :image: 1 - :excerpt: 1 +:tags: VUnit +:author: kraigher, lasplund +:image: 1 +:excerpt: 1 VUnit 3.0 ========= -.. figure:: VUnit3.0.png +.. figure:: img/VUnit3.0.png :alt: VUnit 3.0 :align: center @@ -102,7 +101,7 @@ VUnit 3.0 contains a number of logging framework enhancements that goes hand in hand with the verification components. We have improved the log source hierarchy support which allows loggers to be arranged in a tree structure. -.. figure:: logging_hierarchy.png +.. figure:: img/logging_hierarchy.png :alt: logging hierarchy :align: center @@ -143,7 +142,7 @@ tree depicted above and changes the visibilty for debug messages in the vc logge The result is that debug messages from the `main_logger` is hidden while debug messages from the alpha and beta components are visible. -.. figure:: log_output.png +.. figure:: img/log_output.png :alt: log output :align: center diff --git a/docs/posts/2018_03_22_vunit_community_developed_bfms/post.rst b/docs/blog/2018_03_22_vunit_community_developed_bfms.rst similarity index 90% rename from docs/posts/2018_03_22_vunit_community_developed_bfms/post.rst rename to docs/blog/2018_03_22_vunit_community_developed_bfms.rst index 8c9c506cb..ea05789d8 100644 --- a/docs/posts/2018_03_22_vunit_community_developed_bfms/post.rst +++ b/docs/blog/2018_03_22_vunit_community_developed_bfms.rst @@ -1,13 +1,11 @@ -.. post:: Mar 22, 2018 - :tags: VUnit - :author: lasplund - :image: 1 - :excerpt: 1 +:tags: VUnit +:author: lasplund +:excerpt: 1 VUnit Community Developed BFMs ============================== -.. figure:: image.png +.. figure:: img/vunit_wishbone.png :alt: VUnit Community Developed BFMs :align: center diff --git a/docs/posts/2018_07_22_sigasi_deepens_its_commitment_to_the_vunit_testing_framework/post.rst b/docs/blog/2018_07_22_sigasi_deepens_its_commitment_to_the_vunit_testing_framework.rst similarity index 94% rename from docs/posts/2018_07_22_sigasi_deepens_its_commitment_to_the_vunit_testing_framework/post.rst rename to docs/blog/2018_07_22_sigasi_deepens_its_commitment_to_the_vunit_testing_framework.rst index 351d51cfb..115639cd2 100644 --- a/docs/posts/2018_07_22_sigasi_deepens_its_commitment_to_the_vunit_testing_framework/post.rst +++ b/docs/blog/2018_07_22_sigasi_deepens_its_commitment_to_the_vunit_testing_framework.rst @@ -1,20 +1,18 @@ -.. post:: Jul 22, 2018 - :tags: VUnit - :author: lasplund - :image: 1 - :excerpt: 1 +:tags: VUnit +:author: lasplund +:excerpt: 1 Sigasi Deepens Its Commitment to the VUnit Testing Framework ============================================================ -.. figure:: image.png +.. figure:: img/sigasi_deep.png :alt: Sigasi Deepens Its Commitment to the VUnit Testing Framework :align: center This article was originally posted on `LinkedIn `__ where you may find some comments on its contents. - + Sigasi started to support the VUnit testing framework when their Sigasi Studio IDE became VUnit-aware in the 3.6 release. That release introduced a feature to conveniently configure the VUnit library for @@ -26,7 +24,7 @@ integrated such that tests can be executed with a mouse click and verification progress and results can be monitored in a dedicated VUnit view. -.. figure:: vunit_view.png +.. figure:: img/vunit_view.png :alt: VUnit View :align: center @@ -42,7 +40,7 @@ have started to use VUnit to fully automate their `test environments Sigasi Studio is not the first tool to go beyond convenience support and start building on top of VUnit (see `Eksen `__ for an -example) but it +example) but it is the first commercial tool. This follows a line of logical steps I've seen over the years where the first wave of VUnit adopters are individuals spread around the world (see below), the second wave are @@ -50,7 +48,7 @@ the organizations for which these individuals are working, and the third wave are the tool providers acting to support these organizations. -.. figure:: world.png +.. figure:: img/world.png :alt: Global Activity :align: center @@ -67,7 +65,7 @@ announced VUnit 3.0 which was a release focusing on `BFMs and verification components in general `__. -.. figure:: vunit_popularity.png +.. figure:: img/vunit_popularity.png :alt: VUnit Popularity :align: center @@ -82,7 +80,7 @@ when looking for new employees. Universities include VUnit in their `courses `__ and their `textbooks `__. Tool -providers +providers starts adding convenience support like Sigasi did with the VUnit awareness in Sigasi Studio 3.6 or they bundle VUnit with their tools. Bundling is not something we've pursued due to our continuous @@ -104,7 +102,7 @@ like VUnit. It's the open many-to-many discussions that make sure that the tool stays relevant. Close to 150 users have been active on our website in this way: -.. figure:: contributors.png +.. figure:: img/contributors.png :alt: Contributors :align: center @@ -114,13 +112,13 @@ phase is when the users start investing time and money to contribute to the VUnit ecosystem. It can be anything from `fixing documentation typos `__ to `contributing a BFM `__ or something -independent like building +independent like building tools on top of VUnit in the way Sigasi is doing now. A lot of activity in the `pull request area `__ is a sign -that the project has entered this phase. +that the project has entered this phase. -.. figure:: activity.png +.. figure:: img/activity.png :alt: Active Community :align: center @@ -128,12 +126,12 @@ Having many users actively and publicly involved in maintaining and expanding the code base is something we highly encourage as it improves the pace and continuity of the project. So far 27 users have `contributed `__ -to the code. +to the code. That's all for now. If you're interested in a test drive of Sigasi Studio 4.1 I recommend that you have a look at the early preview which can be downloaded from the `Sigasi preview channel -`__. Make sure that you +`__. Make sure that you have the `latest VUnit version installed `__. Currently only VHDL is supported but SystemVerilog support is also planned. This is a release diff --git a/docs/posts/2018_09_22_sigasi_adds_full_vunit_support/post.rst b/docs/blog/2018_09_22_sigasi_adds_full_vunit_support.rst similarity index 89% rename from docs/posts/2018_09_22_sigasi_adds_full_vunit_support/post.rst rename to docs/blog/2018_09_22_sigasi_adds_full_vunit_support.rst index 3079625ac..dfb46d855 100644 --- a/docs/posts/2018_09_22_sigasi_adds_full_vunit_support/post.rst +++ b/docs/blog/2018_09_22_sigasi_adds_full_vunit_support.rst @@ -1,20 +1,18 @@ -.. post:: Sep 12, 2018 - :tags: VUnit - :author: lasplund - :image: 1 - :excerpt: 1 +:tags: VUnit +:author: lasplund +:excerpt: 1 Sigasi Adds Full VUnit Support ============================== -.. figure:: image.png +.. figure:: img/sigasi_full.png :alt: Sigasi Adds Full VUnit Support :align: center This article was originally posted on `LinkedIn `__ where you may find some comments on its contents. - + Some time ago I gave a `preview `__ of this update of Sigasi Studio and diff --git a/docs/posts/2015_09_24_short_introduction_to_vunit/4x.png b/docs/blog/img/4x.png similarity index 100% rename from docs/posts/2015_09_24_short_introduction_to_vunit/4x.png rename to docs/blog/img/4x.png diff --git a/docs/posts/2015_12_15_free_and_open_source_verification_with_vunit_and_ghdl/CPU_load.png b/docs/blog/img/CPU_load.png similarity index 100% rename from docs/posts/2015_12_15_free_and_open_source_verification_with_vunit_and_ghdl/CPU_load.png rename to docs/blog/img/CPU_load.png diff --git a/docs/posts/2018_02_12_vunit3/VUnit3.0.png b/docs/blog/img/VUnit3.0.png similarity index 100% rename from docs/posts/2018_02_12_vunit3/VUnit3.0.png rename to docs/blog/img/VUnit3.0.png diff --git a/docs/posts/2018_07_22_sigasi_deepens_its_commitment_to_the_vunit_testing_framework/activity.png b/docs/blog/img/activity.png similarity index 100% rename from docs/posts/2018_07_22_sigasi_deepens_its_commitment_to_the_vunit_testing_framework/activity.png rename to docs/blog/img/activity.png diff --git a/docs/posts/2016_11_15_vunit_the_best_value_for_initial_effort_part 1/image.jpg b/docs/blog/img/bestvalue1.jpg similarity index 100% rename from docs/posts/2016_11_15_vunit_the_best_value_for_initial_effort_part 1/image.jpg rename to docs/blog/img/bestvalue1.jpg diff --git a/docs/posts/2016_11_16_vunit_the_best_value_for_initial_effort_part 2/image.jpg b/docs/blog/img/bestvalue2.jpg similarity index 100% rename from docs/posts/2016_11_16_vunit_the_best_value_for_initial_effort_part 2/image.jpg rename to docs/blog/img/bestvalue2.jpg diff --git a/docs/posts/2016_11_22_vunit_the_best_value_for_initial_effort_part 3/image.jpg b/docs/blog/img/bestvalue3.jpg similarity index 100% rename from docs/posts/2016_11_22_vunit_the_best_value_for_initial_effort_part 3/image.jpg rename to docs/blog/img/bestvalue3.jpg diff --git a/docs/posts/2016_02_01_website_updates/blogging_chat_room_documentation.png b/docs/blog/img/blogging_chat_room_documentation.png similarity index 100% rename from docs/posts/2016_02_01_website_updates/blogging_chat_room_documentation.png rename to docs/blog/img/blogging_chat_room_documentation.png diff --git a/docs/posts/2016_02_21_improving_vhdl_testbench_design_with_message_passing/caesar_encoder.png b/docs/blog/img/caesar_encoder.png similarity index 100% rename from docs/posts/2016_02_21_improving_vhdl_testbench_design_with_message_passing/caesar_encoder.png rename to docs/blog/img/caesar_encoder.png diff --git a/docs/posts/2017_10_31_vunit_3_0_color_logging/image.jpg b/docs/blog/img/color_logging.jpg similarity index 100% rename from docs/posts/2017_10_31_vunit_3_0_color_logging/image.jpg rename to docs/blog/img/color_logging.jpg diff --git a/docs/posts/2018_07_22_sigasi_deepens_its_commitment_to_the_vunit_testing_framework/contributors.png b/docs/blog/img/contributors.png similarity index 100% rename from docs/posts/2018_07_22_sigasi_deepens_its_commitment_to_the_vunit_testing_framework/contributors.png rename to docs/blog/img/contributors.png diff --git a/docs/posts/2017_11_07_vunit_3_0_while_waiting_for_vhdl_2017/log1.jpg b/docs/blog/img/log1.jpg similarity index 100% rename from docs/posts/2017_11_07_vunit_3_0_while_waiting_for_vhdl_2017/log1.jpg rename to docs/blog/img/log1.jpg diff --git a/docs/posts/2017_11_07_vunit_3_0_while_waiting_for_vhdl_2017/log2.jpg b/docs/blog/img/log2.jpg similarity index 100% rename from docs/posts/2017_11_07_vunit_3_0_while_waiting_for_vhdl_2017/log2.jpg rename to docs/blog/img/log2.jpg diff --git a/docs/posts/2017_11_07_vunit_3_0_while_waiting_for_vhdl_2017/log3.jpg b/docs/blog/img/log3.jpg similarity index 100% rename from docs/posts/2017_11_07_vunit_3_0_while_waiting_for_vhdl_2017/log3.jpg rename to docs/blog/img/log3.jpg diff --git a/docs/posts/2017_11_07_vunit_3_0_while_waiting_for_vhdl_2017/log4.jpg b/docs/blog/img/log4.jpg similarity index 100% rename from docs/posts/2017_11_07_vunit_3_0_while_waiting_for_vhdl_2017/log4.jpg rename to docs/blog/img/log4.jpg diff --git a/docs/posts/2017_11_07_vunit_3_0_while_waiting_for_vhdl_2017/log5.jpg b/docs/blog/img/log5.jpg similarity index 100% rename from docs/posts/2017_11_07_vunit_3_0_while_waiting_for_vhdl_2017/log5.jpg rename to docs/blog/img/log5.jpg diff --git a/docs/posts/2017_10_31_vunit_3_0_color_logging/log_example.jpg b/docs/blog/img/log_example.jpg similarity index 100% rename from docs/posts/2017_10_31_vunit_3_0_color_logging/log_example.jpg rename to docs/blog/img/log_example.jpg diff --git a/docs/posts/2018_02_12_vunit3/log_output.png b/docs/blog/img/log_output.png similarity index 100% rename from docs/posts/2018_02_12_vunit3/log_output.png rename to docs/blog/img/log_output.png diff --git a/docs/posts/2018_02_12_vunit3/logging_hierarchy.png b/docs/blog/img/logging_hierarchy.png similarity index 100% rename from docs/posts/2018_02_12_vunit3/logging_hierarchy.png rename to docs/blog/img/logging_hierarchy.png diff --git a/docs/posts/2016_02_21_improving_vhdl_testbench_design_with_message_passing/low_throughput.png b/docs/blog/img/low_throughput.png similarity index 100% rename from docs/posts/2016_02_21_improving_vhdl_testbench_design_with_message_passing/low_throughput.png rename to docs/blog/img/low_throughput.png diff --git a/docs/posts/2017_11_23_vunit_matlab_integration/figure.jpg b/docs/blog/img/matlab_figure.jpg similarity index 100% rename from docs/posts/2017_11_23_vunit_matlab_integration/figure.jpg rename to docs/blog/img/matlab_figure.jpg diff --git a/docs/posts/2016_02_21_improving_vhdl_testbench_design_with_message_passing/message_passing_model.png b/docs/blog/img/message_passing_model.png similarity index 100% rename from docs/posts/2016_02_21_improving_vhdl_testbench_design_with_message_passing/message_passing_model.png rename to docs/blog/img/message_passing_model.png diff --git a/docs/posts/2016_02_21_improving_vhdl_testbench_design_with_message_passing/orange_man.jpeg b/docs/blog/img/orange_man.jpeg similarity index 100% rename from docs/posts/2016_02_21_improving_vhdl_testbench_design_with_message_passing/orange_man.jpeg rename to docs/blog/img/orange_man.jpeg diff --git a/docs/posts/2017_12_14_vunit_bfms _as_simple_as_emailing/parallel_recipe.jpg b/docs/blog/img/parallel_recipe.jpg similarity index 100% rename from docs/posts/2017_12_14_vunit_bfms _as_simple_as_emailing/parallel_recipe.jpg rename to docs/blog/img/parallel_recipe.jpg diff --git a/docs/posts/2017_12_14_vunit_bfms _as_simple_as_emailing/sequential_recipe.jpg b/docs/blog/img/sequential_recipe.jpg similarity index 100% rename from docs/posts/2017_12_14_vunit_bfms _as_simple_as_emailing/sequential_recipe.jpg rename to docs/blog/img/sequential_recipe.jpg diff --git a/docs/posts/2018_07_22_sigasi_deepens_its_commitment_to_the_vunit_testing_framework/image.png b/docs/blog/img/sigasi_deep.png similarity index 100% rename from docs/posts/2018_07_22_sigasi_deepens_its_commitment_to_the_vunit_testing_framework/image.png rename to docs/blog/img/sigasi_deep.png diff --git a/docs/posts/2018_09_22_sigasi_adds_full_vunit_support/image.png b/docs/blog/img/sigasi_full.png similarity index 100% rename from docs/posts/2018_09_22_sigasi_adds_full_vunit_support/image.png rename to docs/blog/img/sigasi_full.png diff --git a/docs/posts/2017_12_14_vunit_bfms _as_simple_as_emailing/the_brain.jpg b/docs/blog/img/the_brain.jpg similarity index 100% rename from docs/posts/2017_12_14_vunit_bfms _as_simple_as_emailing/the_brain.jpg rename to docs/blog/img/the_brain.jpg diff --git a/docs/posts/2016_02_21_improving_vhdl_testbench_design_with_message_passing/third_interface.png b/docs/blog/img/third_interface.png similarity index 100% rename from docs/posts/2016_02_21_improving_vhdl_testbench_design_with_message_passing/third_interface.png rename to docs/blog/img/third_interface.png diff --git a/docs/posts/2015_10_08_who_is_using_UVM/uvm.png b/docs/blog/img/uvm.png similarity index 100% rename from docs/posts/2015_10_08_who_is_using_UVM/uvm.png rename to docs/blog/img/uvm.png diff --git a/docs/posts/2017_12_14_vunit_bfms _as_simple_as_emailing/image.jpg b/docs/blog/img/vunit_emailing.jpg similarity index 100% rename from docs/posts/2017_12_14_vunit_bfms _as_simple_as_emailing/image.jpg rename to docs/blog/img/vunit_emailing.jpg diff --git a/docs/posts/2016_01_26_welcome_to_our_new_website/vunit_github_io.png b/docs/blog/img/vunit_github_io.png similarity index 100% rename from docs/posts/2016_01_26_welcome_to_our_new_website/vunit_github_io.png rename to docs/blog/img/vunit_github_io.png diff --git a/docs/posts/2017_11_23_vunit_matlab_integration/image.jpg b/docs/blog/img/vunit_matlab.jpg similarity index 100% rename from docs/posts/2017_11_23_vunit_matlab_integration/image.jpg rename to docs/blog/img/vunit_matlab.jpg diff --git a/docs/posts/2018_07_22_sigasi_deepens_its_commitment_to_the_vunit_testing_framework/vunit_popularity.png b/docs/blog/img/vunit_popularity.png similarity index 100% rename from docs/posts/2018_07_22_sigasi_deepens_its_commitment_to_the_vunit_testing_framework/vunit_popularity.png rename to docs/blog/img/vunit_popularity.png diff --git a/docs/posts/2017_09_28_sigasi_adds_support_for_vunit_testing_framework/image.jpg b/docs/blog/img/vunit_sigasistudio.jpg similarity index 100% rename from docs/posts/2017_09_28_sigasi_adds_support_for_vunit_testing_framework/image.jpg rename to docs/blog/img/vunit_sigasistudio.jpg diff --git a/docs/posts/2018_07_22_sigasi_deepens_its_commitment_to_the_vunit_testing_framework/vunit_view.png b/docs/blog/img/vunit_view.png similarity index 100% rename from docs/posts/2018_07_22_sigasi_deepens_its_commitment_to_the_vunit_testing_framework/vunit_view.png rename to docs/blog/img/vunit_view.png diff --git a/docs/posts/2017_11_07_vunit_3_0_while_waiting_for_vhdl_2017/image.jpg b/docs/blog/img/vunit_waiting.jpg similarity index 100% rename from docs/posts/2017_11_07_vunit_3_0_while_waiting_for_vhdl_2017/image.jpg rename to docs/blog/img/vunit_waiting.jpg diff --git a/docs/posts/2018_03_22_vunit_community_developed_bfms/image.png b/docs/blog/img/vunit_wishbone.png similarity index 100% rename from docs/posts/2018_03_22_vunit_community_developed_bfms/image.png rename to docs/blog/img/vunit_wishbone.png diff --git a/docs/posts/2018_07_22_sigasi_deepens_its_commitment_to_the_vunit_testing_framework/world.png b/docs/blog/img/world.png similarity index 100% rename from docs/posts/2018_07_22_sigasi_deepens_its_commitment_to_the_vunit_testing_framework/world.png rename to docs/blog/img/world.png diff --git a/docs/blog/index.rst b/docs/blog/index.rst new file mode 100644 index 000000000..4dbe37f04 --- /dev/null +++ b/docs/blog/index.rst @@ -0,0 +1,12 @@ +.. _blog: + +Blog +================ +The Blog + +.. toctree:: + :titlesonly: + :glob: + :reversed: + + * \ No newline at end of file diff --git a/docs/index.rst b/docs/index.rst index 624727327..c53500a93 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -34,6 +34,7 @@ Latest Posts .. toctree:: :hidden: + blog/index about installing documentation From 7bfdf1eb4e05e12a50572f5cae52377eeaa8fa61 Mon Sep 17 00:00:00 2001 From: eine Date: Mon, 10 Feb 2020 15:59:24 +0100 Subject: [PATCH 07/79] docs: switch from ablog to alabaster (default) --- docs/_static/VUnit_logo_420x420.png | Bin 11731 -> 19769 bytes docs/_static/style.css | 24 --- docs/_templates/layout.html | 15 -- docs/_templates/quicklinks.html | 5 - docs/conf.py | 317 ++-------------------------- docs/index.rst | 34 ++- tox.ini | 2 +- 7 files changed, 35 insertions(+), 362 deletions(-) delete mode 100644 docs/_static/style.css delete mode 100644 docs/_templates/layout.html delete mode 100644 docs/_templates/quicklinks.html diff --git a/docs/_static/VUnit_logo_420x420.png b/docs/_static/VUnit_logo_420x420.png index 6736f55df8b10a2ff29607d1d0edca66a571c991..0fe53f47770706afc03a19adbed0122933d7c344 100644 GIT binary patch literal 19769 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 a*1U6*{UW-{hn` literal 11731 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 - - -{% endblock %} diff --git a/docs/_templates/quicklinks.html b/docs/_templates/quicklinks.html deleted file mode 100644 index 95c9484ee..000000000 --- a/docs/_templates/quicklinks.html +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/docs/conf.py b/docs/conf.py index 64c6eb1cb..fb0c98315 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -1,147 +1,12 @@ # -*- coding: utf-8 -*- -# vunit build configuration file, created by -# `ablog start` on Fri Jan 29 21:15:19 2016. -# -# Note that not all possible configuration values are present in this file. -# All configuration values have a default; values that are commented out -# serve to show the default. - import os import sys -import ablog -import alabaster from os.path import join -# -- General ABlog Options ---------------------------------------------------- - -# A path relative to the configuration directory for blog archive pages. -# blog_path = 'blog' - -# The “title” for the blog, used in acthive pages. Default is ``'Blog'``. -blog_title = u"VUnit Blog" - -# Base URL for the website, required for generating feeds. -# e.g. blog_baseurl = "http://example.com/" -blog_baseurl = "http://vunit.github.io" - -# Choose to archive only post titles. Archiving only titles can speed -# up project building. -# blog_archive_titles = False - -# -- Blog Authors, Languages, and Locations ----------------------------------- - -# A dictionary of author names mapping to author full display names and -# links. Dictionary keys are what should be used in ``post`` directive -# to refer to the author. Default is ``{}``. -blog_authors = {"Olof Kraigher": ("kraigher", None), "Lars Asplund": ("lasplund", None)} - - -# A dictionary of language code names mapping to full display names and -# links of these languages. Similar to :confval:`blog_authors`, dictionary -# keys should be used in ``post`` directive to refer to the locations. -# Default is ``{}``. -# blog_languages = { -# 'en': ('English', None), -# } - - -# A dictionary of location names mapping to full display names and -# links of these locations. Similar to :confval:`blog_authors`, dictionary -# keys should be used in ``post`` directive to refer to the locations. -# Default is ``{}``. -# blog_locations = { -# 'Earth': ('The Blue Planet', 'http://en.wikipedia.org/wiki/Earth), -# } - - -# -- Blog Post Related -------------------------------------------------------- - -# post_date_format = '%b %d, %Y' - - -# Number of paragraphs (default is ``1``) that will be displayed as an excerpt -# from the post. Setting this ``0`` will result in displaying no post excerpt -# in archive pages. This option can be set on a per post basis using -# post_auto_excerpt = 1 - -# Index of the image that will be displayed in the excerpt of the post. -# Default is ``0``, meaning no image. Setting this to ``1`` will include -# the first image, when available, to the excerpt. This option can be set -# on a per post basis using :rst:dir:`post` directive option ``image``. -# post_auto_image = 0 - -# Number of seconds (default is ``5``) that a redirect page waits before -# refreshing the page to redirect to the post. -# post_redirect_refresh = 5 - -# When ``True``, post title and excerpt is always taken from the section that -# contains the :rst:dir:`post` directive, instead of the document. This is the -# behavior when :rst:dir:`post` is used multiple times in a document. Default -# is ``False``. -# post_always_section = False - -# -- ABlog Sidebars ------------------------------------------------------- - -# There are seven sidebars you can include in your HTML output. -# postcard.html provides information regarding the current post. -# recentposts.html lists most recent five posts. Others provide -# a link to a archive pages generated for each tag, category, and year. -# In addition, there are authors.html, languages.html, and locations.html -# sidebars that link to author and location archive pages. -html_sidebars = { - "**": [ - "about.html", - "quicklinks.html", - "postcard.html", - "navigation.html", - "recentposts.html", - "tagcloud.html", - "categories.html", - "archives.html", - "searchbox.html", - ] -} - -# -- Blog Feed Options -------------------------------------------------------- - -# Turn feeds by setting :confval:`blog_baseurl` configuration variable. -# Choose to create feeds per author, location, tag, category, and year, -# default is ``False``. -# blog_feed_archives = False - -# Choose to display full text in blog feeds, default is ``False``. -# blog_feed_fulltext = False - -# Blog feed subtitle, default is ``None``. -# blog_feed_subtitle = None - -# Choose to feed only post titles, default is ``False``. -# blog_feed_titles = False - -# Specify number of recent posts to include in feeds, default is ``None`` -# for all posts. -# blog_feed_length = None - -# -- Font-Awesome Options ----------------------------------------------------- - -# ABlog templates will use of Font Awesome icons if one of the following -# is ``True`` - -# Link to `Font Awesome`_ at `Bootstrap CDN`_ and use icons in sidebars -# and post footers. Default: ``False`` -fontawesome_link_cdn = ( - "https://maxcdn.bootstrapcdn.com/font-awesome/4.5.0/css/font-awesome.min.css" -) - -# Sphinx_ theme already links to `Font Awesome`_. Default: ``False`` -# fontawesome_included = False - -# Alternatively, you can provide the path to `Font Awesome`_ :file:`.css` -# with the configuration option: fontawesome_css_file -# Path to `Font Awesome`_ :file:`.css` (default is ``None``) that will -# be linked to in HTML output by ABlog. -# fontawesome_css_file = None +# blog_title = u"VUnit Blog" +# blog_baseurl = "http://vunit.github.io" +# blog_authors = {"Olof Kraigher": ("kraigher", None), "Lars Asplund": ("lasplund", None)} # -- Disqus Integration ------------------------------------------------------- @@ -149,213 +14,71 @@ # Disqus_ short name for the blog. disqus_shortname = "vunitframework" -# Choose to disqus pages that are not posts, default is ``False``. -# disqus_pages = False - -# Choose to disqus posts that are drafts (without a published date), -# default is ``False``. -# disqus_drafts = False - # -- Sphinx Options ----------------------------------------------------------- # If your project needs a minimal Sphinx version, state it here. needs_sphinx = "1.2" -# Add any Sphinx extension module names here, as strings. They can be -# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom -# ones. extensions = [ "sphinx.ext.autodoc", "sphinx.ext.extlinks", "sphinx.ext.intersphinx", "sphinx.ext.todo", "sphinxarg.ext", # Automatic argparse command line argument documentation - "alabaster", - "ablog", ] autodoc_default_flags = ["members"] -# Add any paths that contain templates here, relative to this directory. -templates_path = ["_templates", ablog.get_html_templates_path()] - # The suffix(es) of source filenames. source_suffix = ".rst" -# The encoding of source files. -# source_encoding = 'utf-8-sig' - -# The master toctree document. master_doc = "index" -# General information about the project. project = u"VUnit" copyright = u"2014-2018, Lars Asplund" author = u"lasplund" -# The version info for the project you're documenting, acts as replacement for -# |version| and |release|, also used in various other places throughout the -# built documents. -# -# The short X.Y version. version = "" -# The full version, including alpha/beta/rc tags. release = "" -# The language for content autogenerated by Sphinx. Refer to documentation -# for a list of supported languages. -# -# This is also used if you do content translation via gettext catalogs. -# Usually you set "language" from the command line for these cases. language = None -# There are two options for replacing |today|: either, you set today to some -# non-false value, then it is used: -# today = '' -# Else, today_fmt is used as the format for a strftime call. -# today_fmt = '%B %d, %Y' - -# List of patterns, relative to source directory, that match files and -# directories to ignore when looking for source files. exclude_patterns = ["release_notes/*.*"] -# The reST default role (used for this markup: `text`) to use for all -# documents. -# default_role = None - -# If true, '()' will be appended to :func: etc. cross-reference text. -# add_function_parentheses = True - -# If true, the current module name will be prepended to all description -# unit titles (such as .. function::). -# add_module_names = True - -# If true, sectionauthor and moduleauthor directives will be shown in the -# output. They are ignored by default. -# show_authors = False - -# The name of the Pygments (syntax highlighting) style to use. pygments_style = "sphinx" -# A list of ignored prefixes for module index sorting. -# modindex_common_prefix = [] - -# If true, keep warnings as "system message" paragraphs in the built documents. -# keep_warnings = False - -# If true, `todo` and `todoList` produce output, else they produce nothing. todo_include_todos = False - # -- Options for HTML output ---------------------------------------------- -# The theme to use for HTML and HTML Help pages. See the documentation for -# a list of builtin themes. -html_theme = "alabaster" +# html_theme_path = [] + +# html_theme = "sphinx_rtd_theme" -# Theme options are theme-specific and customize the look and feel of a theme -# further. For a list of options available for each theme, see the -# documentation. html_theme_options = { - "github_button": True, - "github_type": "star", - "github_user": "VUnit", - "github_repo": "vunit", - "description": "A test framework for HDL", - "logo": "VUnit_logo_420x420.png", - "logo_name": True, - "travis_button": True, - "page_width": "75%", + "analytics_id": "UA-112393863-1", + # "github_button": True, + # "github_type": "star", + # "github_user": "VUnit", + # "github_repo": "vunit", + # "description": "A test framework for HDL", + # "logo": "VUnit_logo_420x420.png", + # "logo_name": True, + # "travis_button": True, + # "page_width": "75%", } -# Add any paths that contain custom themes here, relative to this directory. -html_theme_path = [alabaster.get_path()] - -html_context = {"css_files": ["_static/style.css"]} -# The name for this set of Sphinx documents. If None, it defaults to -# " v documentation". -# html_title = None - -# A shorter title for the navigation bar. Default is the same as html_title. -# html_short_title = None - -# The name of an image file (relative to this directory) to place at the top -# of the sidebar. -# html_logo = None - -# Add any paths that contain custom static files (such as style sheets) here, -# relative to this directory. They are copied after the builtin static files, -# so a file named "default.css" will overwrite the builtin "default.css". html_static_path = ["_static"] -# The name of an image file (within the static path) to use as favicon of the -# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 -# pixels large. -html_favicon = join(html_static_path[0], "vunit.ico") - -# Add any extra paths that contain custom files (such as robots.txt or -# .htaccess) here, relative to this directory. These files are copied -# directly to the root of the documentation. -# html_extra_path = [] - -# If not '', a 'Last updated on:' timestamp is inserted at every page bottom, -# using the given strftime format. -# html_last_updated_fmt = '%b %d, %Y' - -# If true, SmartyPants will be used to convert quotes and dashes to -# typographically correct entities. -# html_use_smartypants = True - -# Custom sidebar templates, maps document names to template names. -# html_sidebars = {} - -# Additional templates that should be rendered to pages, maps page names to -# template names. -# html_additional_pages = {} - -# If false, no module index is generated. -# html_domain_indices = True - -# If false, no index is generated. -# html_use_index = True - -# If true, the index is split into individual pages for each letter. -# html_split_index = False - -# If true, links to the reST sources are added to the pages. -# html_show_sourcelink = True +html_logo = join(html_static_path[0], "VUnit_logo_420x420.png") -# If true, "Created using Sphinx" is shown in the HTML footer. Default is True. -# html_show_sphinx = True - -# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. -# html_show_copyright = True - -# If true, an OpenSearch description file will be output, and all pages will -# contain a tag referring to it. The value of this option must be the -# base URL from which the finished HTML is served. -# html_use_opensearch = '' - -# This is the file name suffix for HTML files (e.g. ".xhtml"). -# html_file_suffix = None - -# Language to be used for generating the HTML full-text search index. -# Sphinx supports the following languages: -# 'da', 'de', 'en', 'es', 'fi', 'fr', 'hu', 'it', 'ja' -# 'nl', 'no', 'pt', 'ro', 'ru', 'sv', 'tr' -# html_search_language = 'en' - -# A dictionary with options for the search language support, empty by default. -# Now only 'ja' uses this config value -# html_search_options = {'type': 'default'} - -# The name of a javascript file (relative to the configuration directory) that -# implements a search results scorer. If empty, the default will be used. -# html_search_scorer = 'scorer.js' +html_favicon = join(html_static_path[0], "vunit.ico") # Output file base name for HTML help builder. htmlhelp_basename = "vunitdoc" +# -- ExtLinks ------------------------------------------------------------- + extlinks = { "vunit_example": ("https://github.com/VUnit/vunit/tree/master/examples/%s/", ""), "vunit_file": ("https://github.com/VUnit/vunit/tree/master/%s/", ""), diff --git a/docs/index.rst b/docs/index.rst index c53500a93..f04e4347c 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -1,10 +1,21 @@ -.. vunit index file, created by `ablog start` on Fri Jan 29 21:15:19 2016. - You can adapt this file completely to your liking, but it should at least - contain the root `toctree` directive. +.. centered:: |shieldRepo|_ |shieldPyPI|_ |shieldGitter|_ |shieldTwitter|_ + +.. |shieldRepo| image:: https://img.shields.io/badge/VUnit/vunit-0c479d.svg?longCache=true&style=flat-square&logo=github +.. _shieldRepo: https://github.com/VUnit/vunit + +.. |shieldPyPI| image:: https://img.shields.io/pypi/v/vunit_hdl?longCache=true&style=flat-square&label=PyPI&logo=PyPI&logoColor=FFF +.. _shieldPyPI: https://pypi.org/project/vunit-hdl/ + +.. |shieldGitter| image:: https://img.shields.io/gitter/room/VUnit/vunit.svg?longCache=true&style=flat-square&logo=gitter&logoColor=4db797&color=4db797 +.. _shieldGitter: https://gitter.im/VUnit/vunit + +.. |shieldTwitter| image:: https://img.shields.io/twitter/follow/VUnitFramework.svg?longCache=true&style=flat-square&color=1DA1F2&label=%40VUnitFramework&logo=twitter&logoColor=fff +.. _shieldTwitter: https://www.twitter.com/VUnitFramework VUnit ===== + VUnit is an open source unit testing framework for VHDL/SystemVerilog released under the terms of Mozilla Public License, v. 2.0. It features the functionality needed to realize continuous and automated @@ -14,23 +25,6 @@ often" approach through automation. :ref:`Read more ` .. image:: vunit_demo.gif -Latest Posts ------------- - -.. postlist:: 5 - :date: %B %d, %Y - :format: {title} by {author} on {date} - :list-style: disk - :excerpts: - - -.. `toctree` directive, below, contains list of non-post `.rst` files. - This is how they appear in Navigation sidebar. Note that directive - also contains `:hidden:` option so that it is not included inside the page. - - Posts are excluded from this directive so that they aren't double listed - in the sidebar both under Navigation and Recent Posts. - .. toctree:: :hidden: diff --git a/tox.ini b/tox.ini index 778d76690..56834f444 100644 --- a/tox.ini +++ b/tox.ini @@ -15,7 +15,7 @@ deps= docs: docutils docs: sphinx docs: sphinx-argparse - docs: ablog + docs: sphinx_rtd_theme setenv= acceptance-activehdl: VUNIT_SIMULATOR=activehdl From 4d703119401ff3b120831874e7a673301faa0548 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=98yvind=20Hagen?= Date: Mon, 10 Feb 2020 16:23:40 +0100 Subject: [PATCH 08/79] Made comment for uplevel vsim in vunit_load for Riviera-PRO simulator more legible. --- vunit/sim_if/rivierapro.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/vunit/sim_if/rivierapro.py b/vunit/sim_if/rivierapro.py index 0f5cd4c8e..a0a73385c 100644 --- a/vunit/sim_if/rivierapro.py +++ b/vunit/sim_if/rivierapro.py @@ -285,10 +285,11 @@ def _create_load_function( tcl = """ proc vunit_load {{}} {{ # Run the 'vsim' command in the global variable context. This will make - # variables such as 'aldec' and 'LICENSE_QUEUE' visible, if set. Otherwise, - # respectively, the Matlab interface is broken because vsim does not find - # the library aldec_matlab_cosim and vsim will not wait for simulation - # licenses. + # variables such as 'aldec' and 'LICENSE_QUEUE' visible, if set. + # Otherwise: + # - The Matlab interface is broken because vsim does not find the + # library aldec_matlab_cosim + # - vsim will not wait for simulation licenses set vsim_failed [catch {{ uplevel #0 vsim {{{vsim_flags}}} }}] From 1bc9c37179e637d0a8f01c8e7f824a2a513d3b71 Mon Sep 17 00:00:00 2001 From: eine Date: Mon, 10 Feb 2020 16:30:13 +0100 Subject: [PATCH 09/79] update some dates --- docs/conf.py | 2 +- docs/contributing.rst | 2 +- vunit/verilog/include/vunit_defines.svh | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index fb0c98315..a771d03e1 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -35,7 +35,7 @@ master_doc = "index" project = u"VUnit" -copyright = u"2014-2018, Lars Asplund" +copyright = u"2014-2020, Lars Asplund" author = u"lasplund" version = "" diff --git a/docs/contributing.rst b/docs/contributing.rst index 6d100fb07..f1cb231d1 100644 --- a/docs/contributing.rst +++ b/docs/contributing.rst @@ -24,7 +24,7 @@ Copyright is given by adding the copyright notice to the beginning of each file. # License, v. 2.0. If a copy of the MPL was not distributed with this file, # You can obtain one at http://mozilla.org/MPL/2.0/. # - # Copyright (c) 2014-2018, Lars Asplund lars.anders.asplund@gmail.com + # Copyright (c) 2014-2020, Lars Asplund lars.anders.asplund@gmail.com Python related diff --git a/vunit/verilog/include/vunit_defines.svh b/vunit/verilog/include/vunit_defines.svh index 3e807c0b9..1ea158eff 100644 --- a/vunit/verilog/include/vunit_defines.svh +++ b/vunit/verilog/include/vunit_defines.svh @@ -2,7 +2,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this file, // You can obtain one at http://mozilla.org/MPL/2.0/. // -// Copyright (c) 2015-2016, Lars Asplund lars.anders.asplund@gmail.com +// Copyright (c) 2015-2020, Lars Asplund lars.anders.asplund@gmail.com `define WATCHDOG(runtime) \ initial begin \ @@ -34,7 +34,7 @@ arg_str = arg_str.substr(i, arg_str.len()-1); \ break; \ end \ - end + end `define CREATE_MSG(full_msg,func_name,got,expected,prefix,msg=__none__) \ string __none__; \ string got_str; \ @@ -45,7 +45,7 @@ expected_str ="";\ `CREATE_ARG_STRING(got, got_str); \ `CREATE_ARG_STRING(expected, expected_str); \ - full_msg = {func_name, " failed! Got ",`"got`", "=", got_str, " expected ", prefix, expected_str, ". ", msg}; + full_msg = {func_name, " failed! Got ",`"got`", "=", got_str, " expected ", prefix, expected_str, ". ", msg}; `define CHECK_EQUAL(got,expected,msg=__none__) \ assert ((got) === (expected)) else \ begin \ From a7f2bbe7b769679e7d6631c967feca7b96403d3c Mon Sep 17 00:00:00 2001 From: David Stadelmann Date: Mon, 10 Feb 2020 17:16:53 +0100 Subject: [PATCH 10/79] add possibility to configure random stalls for axi_stream (#557) * Added possibility to configure random stalls At creation of master/slave one can provide a configuration which tells how often a stall should occure, and if it occures what the minimal/maximal stall length of such a stall shall be. * Added lines for linter * Removed whitespaces and added check for zero stall activity where not expected * replaced configuration functions by constants configured by generic * uncommented disabled checks * fixed ordering of the signals in if statement and used VHDL 2008 * Fixed order of generics to be backwards compatible based on pull request comment of 1138-4EB * Change of stall generics to provide [0-100]% stall * replaces stat signals with records * only include RandomPType with use instead of all * made random variable privat to process * moved procedures that are private to a private package * give meaningful name to generics to avoid comment * fixed return value for p_actor in new_axi_stream_slave Changed new_actor to p_actor * added procedure drive_invalid_output * replaced for loop with direct call to test case * introduced new_stall_config function * unified random stall test cases for axi_stream * removed explicit numbers in test case this makes it more general and percentage numbers can be varied through the generics without having to manual change the vhdl code. * replaced if statement for min/max evaluation by min/max functions * reformated using black * fixed typo * fixed formating issues * Fixed year of license text in axi_stream_private_pkg.vhd * uppercased test bench variable --- vunit/vhdl/verification_components/run.py | 7 + .../src/axi_stream_master.vhd | 141 ++++++++++-------- .../src/axi_stream_pkg.vhd | 41 ++++- .../src/axi_stream_private_pkg.vhd | 68 +++++++++ .../src/axi_stream_slave.vhd | 125 +++++++++------- .../test/tb_axi_stream.vhd | 113 +++++++++++++- 6 files changed, 373 insertions(+), 122 deletions(-) create mode 100644 vunit/vhdl/verification_components/src/axi_stream_private_pkg.vhd diff --git a/vunit/vhdl/verification_components/run.py b/vunit/vhdl/verification_components/run.py index 1b5ab9261..db7e52409 100644 --- a/vunit/vhdl/verification_components/run.py +++ b/vunit/vhdl/verification_components/run.py @@ -132,5 +132,12 @@ def gen_avalon_master_tests(obj, *args): name="max_waits=%d" % max_waits, generics=dict(max_waits=max_waits) ) +TB_AXI_STREAM.test("test random stall on master").add_config( + name="stall_master", generics=dict(g_stall_percentage_master=30) +) + +TB_AXI_STREAM.test("test random stall on slave").add_config( + name="stall_slave", generics=dict(g_stall_percentage_slave=30) +) ui.main() diff --git a/vunit/vhdl/verification_components/src/axi_stream_master.vhd b/vunit/vhdl/verification_components/src/axi_stream_master.vhd index 2673edf4d..5288b24d6 100644 --- a/vunit/vhdl/verification_components/src/axi_stream_master.vhd +++ b/vunit/vhdl/verification_components/src/axi_stream_master.vhd @@ -11,9 +11,13 @@ context work.vunit_context; context work.com_context; use work.stream_master_pkg.all; use work.axi_stream_pkg.all; +use work.axi_stream_private_pkg.all; use work.queue_pkg.all; use work.sync_pkg.all; +library osvvm; +use osvvm.RandomPkg.RandomPType; + entity axi_stream_master is generic ( master : axi_stream_master_t; @@ -42,6 +46,22 @@ architecture a of axi_stream_master is constant message_queue : queue_t := new_queue; signal notify_bus_process_done : std_logic := '0'; + procedure drive_invalid_output(signal l_tdata : out std_logic_vector(data_length(master)-1 downto 0); + signal l_tkeep : out std_logic_vector(data_length(master)/8-1 downto 0); + signal l_tstrb : out std_logic_vector(data_length(master)/8-1 downto 0); + signal l_tid : out std_logic_vector(id_length(master)-1 downto 0); + signal l_tdest : out std_logic_vector(dest_length(master)-1 downto 0); + signal l_tuser : out std_logic_vector(user_length(master)-1 downto 0)) + is + begin + l_tdata <= (others => drive_invalid_val); + l_tkeep <= (others => drive_invalid_val); + l_tstrb <= (others => drive_invalid_val); + l_tid <= (others => drive_invalid_val); + l_tdest <= (others => drive_invalid_val); + l_tuser <= (others => drive_invalid_val_user); + end procedure; + begin main : process @@ -69,68 +89,71 @@ begin bus_process : process variable msg : msg_t; variable msg_type : msg_type_t; + variable rnd : RandomPType; begin - if drive_invalid then - tdata <= (others => drive_invalid_val); - tkeep <= (others => drive_invalid_val); - tstrb <= (others => drive_invalid_val); - tid <= (others => drive_invalid_val); - tdest <= (others => drive_invalid_val); - tuser <= (others => drive_invalid_val_user); - end if; - - -- Wait for messages to arrive on the queue, posted by the process above - wait until rising_edge(aclk) and (not is_empty(message_queue) or areset_n = '0'); - - if (areset_n = '0') then - tvalid <= '0'; - else - while not is_empty(message_queue) loop - msg := pop(message_queue); - msg_type := message_type(msg); - - if msg_type = wait_for_time_msg then - handle_sync_message(net, msg_type, msg); - -- Re-align with the clock when a wait for time message was handled, because this breaks edge alignment. - wait until rising_edge(aclk); - elsif msg_type = notify_request_msg then - -- Ignore this message, but expect it - elsif msg_type = stream_push_msg or msg_type = push_axi_stream_msg then - tvalid <= '1'; - tdata <= pop_std_ulogic_vector(msg); - if msg_type = push_axi_stream_msg then - tlast <= pop_std_ulogic(msg); - tkeep <= pop_std_ulogic_vector(msg); - tstrb <= pop_std_ulogic_vector(msg); - tid <= pop_std_ulogic_vector(msg); - tdest <= pop_std_ulogic_vector(msg); - tuser <= pop_std_ulogic_vector(msg); - else - if pop_boolean(msg) then - tlast <= '1'; + rnd.InitSeed(rnd'instance_name); + loop + if drive_invalid then + drive_invalid_output(tdata, tkeep, tstrb, tid, tdest, tuser); + end if; + + -- Wait for messages to arrive on the queue, posted by the process above + wait until rising_edge(aclk) and (not is_empty(message_queue) or areset_n = '0'); + + if (areset_n = '0') then + tvalid <= '0'; + else + while not is_empty(message_queue) loop + msg := pop(message_queue); + msg_type := message_type(msg); + + if msg_type = wait_for_time_msg then + handle_sync_message(net, msg_type, msg); + -- Re-align with the clock when a wait for time message was handled, because this breaks edge alignment. + wait until rising_edge(aclk); + elsif msg_type = notify_request_msg then + -- Ignore this message, but expect it + elsif msg_type = stream_push_msg or msg_type = push_axi_stream_msg then + drive_invalid_output(tdata, tkeep, tstrb, tid, tdest, tuser); + -- stall according to probability configuration + probability_stall_axi_stream(aclk, master, rnd); + + tvalid <= '1'; + tdata <= pop_std_ulogic_vector(msg); + if msg_type = push_axi_stream_msg then + tlast <= pop_std_ulogic(msg); + tkeep <= pop_std_ulogic_vector(msg); + tstrb <= pop_std_ulogic_vector(msg); + tid <= pop_std_ulogic_vector(msg); + tdest <= pop_std_ulogic_vector(msg); + tuser <= pop_std_ulogic_vector(msg); else - tlast <= '0'; + if pop_boolean(msg) then + tlast <= '1'; + else + tlast <= '0'; + end if; + tkeep <= (others => '1'); + tstrb <= (others => '1'); + tid <= (others => '0'); + tdest <= (others => '0'); + tuser <= (others => '0'); end if; - tkeep <= (others => '1'); - tstrb <= (others => '1'); - tid <= (others => '0'); - tdest <= (others => '0'); - tuser <= (others => '0'); + wait until ((tvalid and tready) = '1' or areset_n = '0') and rising_edge(aclk); + tvalid <= '0'; + tlast <= '0'; + else + unexpected_msg_type(msg_type); end if; - wait until ((tvalid and tready) = '1' or areset_n = '0') and rising_edge(aclk); - tvalid <= '0'; - tlast <= '0'; - else - unexpected_msg_type(msg_type); - end if; - - delete(msg); - end loop; - - notify_bus_process_done <= '1'; - wait until notify_bus_process_done = '1'; - notify_bus_process_done <= '0'; - end if; + + delete(msg); + end loop; + + notify_bus_process_done <= '1'; + wait until notify_bus_process_done = '1'; + notify_bus_process_done <= '0'; + end if; + end loop; end process; axi_stream_monitor_generate : if master.p_monitor /= null_axi_stream_monitor generate @@ -171,4 +194,4 @@ begin ); end generate axi_stream_protocol_checker_generate; -end architecture; \ No newline at end of file +end architecture; diff --git a/vunit/vhdl/verification_components/src/axi_stream_pkg.vhd b/vunit/vhdl/verification_components/src/axi_stream_pkg.vhd index 49a6fdab3..e56d298f9 100644 --- a/vunit/vhdl/verification_components/src/axi_stream_pkg.vhd +++ b/vunit/vhdl/verification_components/src/axi_stream_pkg.vhd @@ -19,6 +19,18 @@ context work.data_types_context; package axi_stream_pkg is + type stall_config_t is record + stall_probability : real range 0.0 to 1.0; + min_stall_cycles : natural; + max_stall_cycles : natural; + end record; + + constant null_stall_config : stall_config_t := ( + stall_probability => 0.0, + min_stall_cycles => 0, + max_stall_cycles => 0 + ); + type axi_stream_component_type_t is (null_component, default_component, custom_component); type axi_stream_protocol_checker_t is record @@ -99,6 +111,7 @@ package axi_stream_pkg is p_id_length : natural; p_dest_length : natural; p_user_length : natural; + p_stall_config : stall_config_t; p_logger : logger_t; p_monitor : axi_stream_monitor_t; p_protocol_checker : axi_stream_protocol_checker_t; @@ -110,6 +123,7 @@ package axi_stream_pkg is p_id_length : natural; p_dest_length : natural; p_user_length : natural; + p_stall_config : stall_config_t; p_logger : logger_t; p_monitor : axi_stream_monitor_t; p_protocol_checker : axi_stream_protocol_checker_t; @@ -123,6 +137,7 @@ package axi_stream_pkg is id_length : natural := 0; dest_length : natural := 0; user_length : natural := 0; + stall_config : stall_config_t := null_stall_config; logger : logger_t := axi_stream_logger; actor : actor_t := null_actor; monitor : axi_stream_monitor_t := null_axi_stream_monitor; @@ -134,6 +149,7 @@ package axi_stream_pkg is id_length : natural := 0; dest_length : natural := 0; user_length : natural := 0; + stall_config : stall_config_t := null_stall_config; logger : logger_t := axi_stream_logger; actor : actor_t := null_actor; monitor : axi_stream_monitor_t := null_axi_stream_monitor; @@ -285,6 +301,12 @@ package axi_stream_pkg is variable msg : inout msg_t; variable axi_transaction : out axi_stream_transaction_t); + function new_stall_config( + stall_probability : real range 0.0 to 1.0; + min_stall_cycles : natural; + max_stall_cycles : natural + ) return stall_config_t; + end package; package body axi_stream_pkg is @@ -343,6 +365,7 @@ package body axi_stream_pkg is id_length : natural := 0; dest_length : natural := 0; user_length : natural := 0; + stall_config : stall_config_t := null_stall_config; logger : logger_t := axi_stream_logger; actor : actor_t := null_actor; monitor : axi_stream_monitor_t := null_axi_stream_monitor; @@ -361,6 +384,7 @@ package body axi_stream_pkg is p_id_length => id_length, p_dest_length => dest_length, p_user_length => user_length, + p_stall_config => stall_config, p_logger => logger, p_monitor => p_monitor, p_protocol_checker => p_protocol_checker); @@ -371,6 +395,7 @@ package body axi_stream_pkg is id_length : natural := 0; dest_length : natural := 0; user_length : natural := 0; + stall_config : stall_config_t := null_stall_config; logger : logger_t := axi_stream_logger; actor : actor_t := null_actor; monitor : axi_stream_monitor_t := null_axi_stream_monitor; @@ -384,11 +409,12 @@ package body axi_stream_pkg is p_actor := actor when actor /= null_actor else new_actor; p_protocol_checker := get_valid_protocol_checker(data_length, id_length, dest_length, user_length, logger, actor, protocol_checker, "slave"); - return (p_actor => new_actor, + return (p_actor => p_actor, p_data_length => data_length, p_id_length => id_length, p_dest_length => dest_length, p_user_length => user_length, + p_stall_config => stall_config, p_logger => logger, p_monitor => p_monitor, p_protocol_checker => p_protocol_checker); @@ -780,4 +806,17 @@ package body axi_stream_pkg is end if; end; + function new_stall_config( + stall_probability : real range 0.0 to 1.0; + min_stall_cycles : natural; + max_stall_cycles : natural) return stall_config_t is + variable stall_config : stall_config_t; + begin + stall_config := ( + stall_probability => stall_probability, + min_stall_cycles => min_stall_cycles, + max_stall_cycles => max_stall_cycles); + return stall_config; + end; + end package body; diff --git a/vunit/vhdl/verification_components/src/axi_stream_private_pkg.vhd b/vunit/vhdl/verification_components/src/axi_stream_private_pkg.vhd new file mode 100644 index 000000000..6abfb700a --- /dev/null +++ b/vunit/vhdl/verification_components/src/axi_stream_private_pkg.vhd @@ -0,0 +1,68 @@ +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2020, Lars Asplund lars.anders.asplund@gmail.com + +library ieee; +use ieee.std_logic_1164.all; + +library osvvm; +use osvvm.RandomPkg.RandomPType; + +use work.axi_stream_pkg.all; + +package axi_stream_private_pkg is + procedure probability_stall_axi_stream( + signal aclk : in std_logic; + axi_stream : in axi_stream_slave_t; + rnd : inout RandomPType + ); + + procedure probability_stall_axi_stream( + signal aclk : in std_logic; + axi_stream : in axi_stream_master_t; + rnd : inout RandomPType + ); + + procedure probability_stall_axi_stream( + signal aclk : in std_logic; + stall_config : in stall_config_t; + rnd : inout RandomPType + ); + +end package; + +package body axi_stream_private_pkg is + + procedure probability_stall_axi_stream( + signal aclk : in std_logic; + axi_stream : in axi_stream_master_t; + rnd : inout RandomPType) is + begin + probability_stall_axi_stream(aclk, axi_stream.p_stall_config, rnd); + end procedure; + + procedure probability_stall_axi_stream( + signal aclk : in std_logic; + axi_stream : in axi_stream_slave_t; + rnd : inout RandomPType) is + begin + probability_stall_axi_stream(aclk, axi_stream.p_stall_config, rnd); + end procedure; + + procedure probability_stall_axi_stream( + signal aclk : in std_logic; + stall_config : in stall_config_t; + rnd : inout RandomPType) is + variable num_stall_cycles : natural := 0; + begin + if rnd.Uniform(0.0, 1.0) < stall_config.stall_probability then + num_stall_cycles := rnd.FavorSmall(stall_config.min_stall_cycles, stall_config.max_stall_cycles); + end if; + for stall in 0 to num_stall_cycles-1 loop + wait until rising_edge(aclk); + end loop; + end procedure; + +end package body; diff --git a/vunit/vhdl/verification_components/src/axi_stream_slave.vhd b/vunit/vhdl/verification_components/src/axi_stream_slave.vhd index 08306b900..0889c172b 100644 --- a/vunit/vhdl/verification_components/src/axi_stream_slave.vhd +++ b/vunit/vhdl/verification_components/src/axi_stream_slave.vhd @@ -11,9 +11,13 @@ context work.vunit_context; context work.com_context; use work.stream_slave_pkg.all; use work.axi_stream_pkg.all; +use work.axi_stream_private_pkg.all; use work.sync_pkg.all; use work.string_ptr_pkg.all; +library osvvm; +use osvvm.RandomPkg.RandomPType; + entity axi_stream_slave is generic ( slave : axi_stream_slave_t); @@ -76,66 +80,73 @@ begin tdest(tdest'range), tuser(tuser'range) ); + variable rnd : RandomPType; begin - -- Wait for messages to arrive on the queue, posted by the process above - wait until rising_edge(aclk) and (not is_empty(message_queue)); - - while not is_empty(message_queue) loop - msg := pop(message_queue); - msg_type := message_type(msg); - - if msg_type = wait_for_time_msg then - handle_sync_message(net, msg_type, msg); - wait until rising_edge(aclk); - elsif msg_type = notify_request_msg then - -- Ignore this message, but expect it - elsif msg_type = stream_pop_msg or msg_type = pop_axi_stream_msg then - tready <= '1'; - wait until (tvalid and tready) = '1' and rising_edge(aclk); - tready <= '0'; - - axi_stream_transaction := ( - tdata => tdata, - tlast => tlast = '1', - tkeep => tkeep, - tstrb => tstrb, - tid => tid, - tdest => tdest, - tuser => tuser - ); - - reply_msg := new_axi_stream_transaction_msg(axi_stream_transaction); - reply(net, msg, reply_msg); - elsif msg_type = check_axi_stream_msg then - tready <= '1'; - wait until (tvalid and tready) = '1' and rising_edge(aclk); - tready <= '0'; - - report_msg := new_string_ptr(pop_string(msg)); - if tdata'length > 0 then - check_equal(tdata, pop_std_ulogic_vector(msg), "TDATA mismatch, " & to_string(report_msg)); - check_equal(tkeep, pop_std_ulogic_vector(msg), "TKEEP mismatch, " & to_string(report_msg)); - check_equal(tstrb, pop_std_ulogic_vector(msg), "TSTRB mismatch, " & to_string(report_msg)); - end if; - check_equal(tlast, pop_std_ulogic(msg), "TLAST mismatch, " & to_string(report_msg)); - if tid'length > 0 then - check_equal(tid, pop_std_ulogic_vector(msg), "TID mismatch, " & to_string(report_msg)); - end if; - if tdest'length > 0 then - check_equal(tdest, pop_std_ulogic_vector(msg), "TDEST mismatch, " & to_string(report_msg)); + rnd.InitSeed(rnd'instance_name); + loop + -- Wait for messages to arrive on the queue, posted by the process above + wait until rising_edge(aclk) and (not is_empty(message_queue)); + + while not is_empty(message_queue) loop + msg := pop(message_queue); + msg_type := message_type(msg); + + if msg_type = wait_for_time_msg then + handle_sync_message(net, msg_type, msg); + wait until rising_edge(aclk); + elsif msg_type = notify_request_msg then + -- Ignore this message, but expect it + elsif msg_type = stream_pop_msg or msg_type = pop_axi_stream_msg then + + -- stall according to probability configuration + probability_stall_axi_stream(aclk, slave, rnd); + + tready <= '1'; + wait until (tvalid and tready) = '1' and rising_edge(aclk); + tready <= '0'; + + axi_stream_transaction := ( + tdata => tdata, + tlast => tlast = '1', + tkeep => tkeep, + tstrb => tstrb, + tid => tid, + tdest => tdest, + tuser => tuser + ); + + reply_msg := new_axi_stream_transaction_msg(axi_stream_transaction); + reply(net, msg, reply_msg); + elsif msg_type = check_axi_stream_msg then + tready <= '1'; + wait until (tvalid and tready) = '1' and rising_edge(aclk); + tready <= '0'; + + report_msg := new_string_ptr(pop_string(msg)); + if tdata'length > 0 then + check_equal(tdata, pop_std_ulogic_vector(msg), "TDATA mismatch, " & to_string(report_msg)); + check_equal(tkeep, pop_std_ulogic_vector(msg), "TKEEP mismatch, " & to_string(report_msg)); + check_equal(tstrb, pop_std_ulogic_vector(msg), "TSTRB mismatch, " & to_string(report_msg)); + end if; + check_equal(tlast, pop_std_ulogic(msg), "TLAST mismatch, " & to_string(report_msg)); + if tid'length > 0 then + check_equal(tid, pop_std_ulogic_vector(msg), "TID mismatch, " & to_string(report_msg)); + end if; + if tdest'length > 0 then + check_equal(tdest, pop_std_ulogic_vector(msg), "TDEST mismatch, " & to_string(report_msg)); + end if; + if tuser'length > 0 then + check_equal(tuser, pop_std_ulogic_vector(msg), "TUSER mismatch, " & to_string(report_msg)); + end if; + else + unexpected_msg_type(msg_type); end if; - if tuser'length > 0 then - check_equal(tuser, pop_std_ulogic_vector(msg), "TUSER mismatch, " & to_string(report_msg)); - end if; - else - unexpected_msg_type(msg_type); - end if; - end loop; - - notify_bus_process_done <= '1'; - wait until notify_bus_process_done = '1'; - notify_bus_process_done <= '0'; + end loop; + notify_bus_process_done <= '1'; + wait until notify_bus_process_done = '1'; + notify_bus_process_done <= '0'; + end loop; end process; axi_stream_monitor_generate : if slave.p_monitor /= null_axi_stream_monitor generate diff --git a/vunit/vhdl/verification_components/test/tb_axi_stream.vhd b/vunit/vhdl/verification_components/test/tb_axi_stream.vhd index 3ab98fed2..eb506afea 100644 --- a/vunit/vhdl/verification_components/test/tb_axi_stream.vhd +++ b/vunit/vhdl/verification_components/test/tb_axi_stream.vhd @@ -21,23 +21,31 @@ entity tb_axi_stream is runner_cfg : string; g_id_length : natural := 8; g_dest_length : natural := 8; - g_user_length : natural := 8 + g_user_length : natural := 8; + g_stall_percentage_master : natural range 0 to 100 := 0; + g_stall_percentage_slave : natural range 0 to 100 := 0 ); end entity; architecture a of tb_axi_stream is + + constant min_stall_cycles : natural := 5; + constant max_stall_cycles : natural := 15; + constant master_stall_config : stall_config_t := new_stall_config(stall_probability => real(g_stall_percentage_master)/100.0, min_stall_cycles => min_stall_cycles, max_stall_cycles => max_stall_cycles); + constant slave_stall_config : stall_config_t := new_stall_config(stall_probability => real(g_stall_percentage_slave)/100.0 , min_stall_cycles => min_stall_cycles, max_stall_cycles => max_stall_cycles); + constant master_axi_stream : axi_stream_master_t := new_axi_stream_master( data_length => 8, id_length => g_id_length, dest_length => g_dest_length, user_length => g_user_length, - logger => get_logger("master"), actor => new_actor("master"), - monitor => default_axi_stream_monitor, protocol_checker => default_axi_stream_protocol_checker + stall_config => master_stall_config, logger => get_logger("master"), actor => new_actor("master"), + monitor => default_axi_stream_monitor, protocol_checker => default_axi_stream_protocol_checker ); constant master_stream : stream_master_t := as_stream(master_axi_stream); constant master_sync : sync_handle_t := as_sync(master_axi_stream); constant slave_axi_stream : axi_stream_slave_t := new_axi_stream_slave( data_length => 8, id_length => g_id_length, dest_length => g_dest_length, user_length => g_user_length, - logger => get_logger("slave"), actor => new_actor("slave"), - monitor => default_axi_stream_monitor, protocol_checker => default_axi_stream_protocol_checker + stall_config => slave_stall_config, logger => get_logger("slave"), actor => new_actor("slave"), + monitor => default_axi_stream_monitor, protocol_checker => default_axi_stream_protocol_checker ); constant slave_stream : stream_slave_t := as_stream(slave_axi_stream); constant slave_sync : sync_handle_t := as_sync(slave_axi_stream); @@ -75,6 +83,24 @@ architecture a of tb_axi_stream is signal not_valid_id : std_logic; signal not_valid_dest : std_logic; signal not_valid_user : std_logic; + + ----------------------------------------------------------------------------- + -- signals used for the statistics for stall evaluation + type axis_stall_stats_fields_t is record + length, min, max, events : natural; + prev, start : std_logic; + end record; + + type axis_stall_stats_t is record + valid : axis_stall_stats_fields_t; + ready : axis_stall_stats_fields_t; + end record; + + signal axis_stall_stats : axis_stall_stats_t := ( + valid => (0, 1000, 0, 0, '0', '0'), + ready => (0, 1000, 0, 0, '0', '0') + ); + begin main : process @@ -464,6 +490,37 @@ begin check_equal(now, timestamp + 20 ns, " transaction time incorrect"); + elsif run("test random stall on master") or run("test random stall on slave") then + wait until rising_edge(aclk); + for i in 0 to 100 loop + pop_stream(net, slave_stream, reference); + push(reference_queue, reference); + end loop; + for i in 0 to 100 loop + push_stream(net, master_stream, std_logic_vector(to_unsigned(i + 1, data'length)), true); + end loop; + + wait_until_idle(net, master_sync); -- wait until all transfers are done before checking them + wait_until_idle(net, slave_sync); + + for i in 0 to 100 loop + reference := pop(reference_queue); + await_pop_stream_reply(net, reference, data); + check_equal(data, to_unsigned(i + 1, data'length), result("for await pop stream data")); + end loop; + info("There have been " & to_string(axis_stall_stats.valid.events) & " tvalid stall events"); + info("Min stall length was " & to_string(axis_stall_stats.valid.min)); + info("Max stall length was " & to_string(axis_stall_stats.valid.max)); + if running_test_case = "test random stall on master" then + check((axis_stall_stats.valid.events < (g_stall_percentage_master+10)) and (axis_stall_stats.valid.events > (g_stall_percentage_master-10)), "Checking that the tvalid stall probability lies within reasonable boundaries"); + check((axis_stall_stats.valid.min >= min_stall_cycles) and (axis_stall_stats.valid.max <= max_stall_cycles), "Checking that the minimal and maximal stall lenghts are in expected boundaries"); + check_equal(axis_stall_stats.ready.events, 0, "Checking that there are zero tready stall events"); + else + check((axis_stall_stats.ready.events < (g_stall_percentage_slave+10)) and (axis_stall_stats.ready.events > (g_stall_percentage_slave-10)), "Checking that the tready stall probability lies within reasonable boundaries"); + check((axis_stall_stats.ready.min >= min_stall_cycles) and (axis_stall_stats.ready.max <= max_stall_cycles), "Checking that the minimal and maximal stall lenghts are in expected boundaries"); + check_equal(axis_stall_stats.valid.events, 0, "Checking that there are zero tvalid stall events"); + end if; + end if; test_runner_cleanup(runner); end process; @@ -556,5 +613,51 @@ begin tuser => tuser ); + statistics : process(aclk) + begin + if rising_edge(aclk) then + axis_stall_stats.valid.prev <= tvalid; + axis_stall_stats.ready.prev <= tready; + ------------------------------------------------------------------------- + -- TVALID and TREADY stall events counters + if tvalid and (not tready) and axis_stall_stats.ready.prev then + axis_stall_stats.ready.events <= axis_stall_stats.ready.events + 1; + end if; + if (not tvalid) and tready and axis_stall_stats.valid.prev then + axis_stall_stats.valid.events <= axis_stall_stats.valid.events + 1; + end if; + + ------------------------------------------------------------------------- + -- TVALID Minmal and Maximal Stall lengths + if tvalid then + axis_stall_stats.valid.start <= '1'; + end if; + + if (not tvalid) and axis_stall_stats.valid.start then + axis_stall_stats.valid.length <= axis_stall_stats.valid.length + 1; + end if; + if tvalid and axis_stall_stats.valid.start and (not axis_stall_stats.valid.prev) then + axis_stall_stats.valid.length <= 0; + axis_stall_stats.valid.min <= minimum(axis_stall_stats.valid.length, axis_stall_stats.valid.min); + axis_stall_stats.valid.max <= maximum(axis_stall_stats.valid.length, axis_stall_stats.valid.max); + end if; + ------------------------------------------------------------------------- + -- TREADY Minmal and Maximal Stall lengths + if tready then + axis_stall_stats.ready.start <= '1'; + end if; + + if (not tready) and axis_stall_stats.ready.start then + axis_stall_stats.ready.length <= axis_stall_stats.ready.length + 1; + end if; + if tready and axis_stall_stats.ready.start and (not axis_stall_stats.ready.prev) then + axis_stall_stats.ready.length <= 0; + axis_stall_stats.ready.min <= minimum(axis_stall_stats.ready.length, axis_stall_stats.ready.min); + axis_stall_stats.ready.max <= maximum(axis_stall_stats.ready.length, axis_stall_stats.ready.max); + end if; + + end if; + end process; + aclk <= not aclk after 5 ns; end architecture; From 25bc6a50a2b795a83be4f3ad5eeaec053670c104 Mon Sep 17 00:00:00 2001 From: eine Date: Tue, 11 Feb 2020 03:36:55 +0100 Subject: [PATCH 11/79] docs: update references to CI services --- docs/ci.rst | 2 +- docs/contributing.rst | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/docs/ci.rst b/docs/ci.rst index 6f06008d3..fb8a36f34 100644 --- a/docs/ci.rst +++ b/docs/ci.rst @@ -18,7 +18,7 @@ such as `VirtualBox `_, `QEMU `_ provide ready-to-use docker images at `hub.docker.com/u/ghdl/dashboard `_. Some of these include not only GHDL but also VUnit. Precisely, ``ghdl/vunit:{mcode|llvm|gcc}`` are images based on Debian Buster image with GHDL built from the latest commit of the master branch, and the latest release of VUnit installed through ``pip``. ``ghdl/vunit:{mcode|llvm|gcc}-master`` images include the latest commit of VUnit from the master branch. -As a result, the burden for the adoption of continuous integration for VUnit users is reduced to using docker; which is available in GNU/Linux, FreeBSD, Windows and macOS, and is supported in most cloud services (`Travis CI `_, `AWS `_, `Codefresh `_, etc.) or CI frameworks (`Jenkins `_, `Drone `_, `GitLab Runner `_, etc.). +As a result, the burden for the adoption of continuous integration for VUnit users is reduced to using docker; which is available in GNU/Linux, FreeBSD, Windows and macOS, and is supported in most cloud services (`GitHub Actions `_, `Travis CI `_, `AWS `_, `Codefresh `_, etc.) or CI frameworks (`Jenkins `_, `Drone `_, `GitLab Runner `_, etc.). For example, script :vunit_file:`examples/vhdl/docker_runall.sh ` shows how to run all the VHDL examples in any x86 platform: diff --git a/docs/contributing.rst b/docs/contributing.rst index f1cb231d1..587dc650c 100644 --- a/docs/contributing.rst +++ b/docs/contributing.rst @@ -132,9 +132,10 @@ style. For example :vunit_file:`examples/vhdl/uart/src/uart_tx.vhd` Continous Integration --------------------- -VUnit runs all test and lint checks on both Windows using AppVeyor and -Linux using Travis CI with several versions of Python. GHDL is used to -run the VHDL tests of all our libraries and examples. +VUnit runs all test and lint checks on both GNU/Linux and Windows +with several versions of Python (typically, the oldest and newest +supported by both VUnit and the CI environment). `GHDL `_ +is used to run the VHDL tests of all our libraries and examples. All tests will be automatically run on any pull request and they are expected to pass for us to approve the merge. From a57dee0679c102b502f64f972d40e4798e1923dc Mon Sep 17 00:00:00 2001 From: eine <6628437+eine@users.noreply.github.com> Date: Fri, 14 Feb 2020 22:59:30 +0100 Subject: [PATCH 12/79] rename compile option 'ghdl.flags' to 'ghdl.a_flags' (#624) --- docs/py/opts.rst | 5 ++++- vunit/sim_if/ghdl.py | 21 +++++++++++++++++++-- vunit/ui/__init__.py | 4 ++-- vunit/ui/library.py | 4 ++-- vunit/ui/source.py | 4 ++-- vunit/ui/test.py | 2 +- vunit/ui/testbench.py | 2 +- 7 files changed, 31 insertions(+), 11 deletions(-) diff --git a/docs/py/opts.rst b/docs/py/opts.rst index e52e9a8eb..3cce2ea15 100644 --- a/docs/py/opts.rst +++ b/docs/py/opts.rst @@ -7,10 +7,13 @@ Compilation options allow customization of compilation behavior. Since simulator differing options available, generic options may be specified through this interface. The following compilation options are known. -``ghdl.flags`` +``ghdl.a_flags`` Extra arguments passed to ``ghdl -a`` command during compilation. Must be a list of strings. +``ghdl.flags`` + Deprecated alias of ``ghdl.a_flags``. It will be removed in future releases. + ``incisive.irun_vhdl_flags`` Extra arguments passed to the Incisive ``irun`` command when compiling VHDL files. Must be a list of strings. diff --git a/vunit/sim_if/ghdl.py b/vunit/sim_if/ghdl.py index 92c091304..0abf787a6 100644 --- a/vunit/sim_if/ghdl.py +++ b/vunit/sim_if/ghdl.py @@ -14,6 +14,7 @@ import subprocess import shlex from sys import stdout # To avoid output catched in non-verbose mode +from warnings import warn from ..exceptions import CompileError from ..ostools import Process from . import SimulatorInterface, ListOfStringOption, StringOption, BooleanOption @@ -32,7 +33,10 @@ class GHDLInterface(SimulatorInterface): supports_gui_flag = True supports_colors_in_gui = True - compile_options = [ListOfStringOption("ghdl.flags")] + compile_options = [ + ListOfStringOption("ghdl.a_flags"), + ListOfStringOption("ghdl.flags"), + ] sim_options = [ ListOfStringOption("ghdl.sim_flags"), @@ -213,7 +217,20 @@ def compile_vhdl_file_command(self, source_file): ] for library in self._project.get_libraries(): cmd += ["-P%s" % library.directory] - cmd += source_file.compile_options.get("ghdl.flags", []) + + a_flags = source_file.compile_options.get("ghdl.a_flags", []) + flags = source_file.compile_options.get("ghdl.flags", []) + if flags != []: + warn( + ( + "'ghdl.flags' is deprecated and it will be removed in future releases; " + "use 'ghdl.a_flags' instead" + ), + Warning, + ) + a_flags += flags + + cmd += a_flags cmd += [source_file.name] return cmd diff --git a/vunit/ui/__init__.py b/vunit/ui/__init__.py index 6e958ebcb..a82305f6d 100644 --- a/vunit/ui/__init__.py +++ b/vunit/ui/__init__.py @@ -395,7 +395,7 @@ def set_sim_option(self, name, value, allow_empty=False, overwrite=True): .. code-block:: python - prj.set_sim_option("ghdl.flags", ["--no-vital-checks"]) + prj.set_sim_option("ghdl.a_flags", ["--no-vital-checks"]) .. note:: Only affects test benches added *before* the option is set. @@ -418,7 +418,7 @@ def set_compile_option(self, name, value, allow_empty=False): .. code-block:: python - prj.set_compile_option("ghdl.flags", ["--no-vital-checks"]) + prj.set_compile_option("ghdl.a_flags", ["--no-vital-checks"]) .. note:: diff --git a/vunit/ui/library.py b/vunit/ui/library.py index 360bc1875..a30ba0029 100644 --- a/vunit/ui/library.py +++ b/vunit/ui/library.py @@ -94,7 +94,7 @@ def set_sim_option(self, name, value, allow_empty=False, overwrite=True): .. code-block:: python - lib.set_sim_option("ghdl.flags", ["--no-vital-checks"]) + lib.set_sim_option("ghdl.a_flags", ["--no-vital-checks"]) .. note:: Only affects test benches added *before* the option is set. @@ -114,7 +114,7 @@ def set_compile_option(self, name, value, allow_empty=False): .. code-block:: python - lib.set_compile_option("ghdl.flags", ["--no-vital-checks"]) + lib.set_compile_option("ghdl.a_flags", ["--no-vital-checks"]) .. note:: diff --git a/vunit/ui/source.py b/vunit/ui/source.py index 874542fe7..d3c2c5353 100644 --- a/vunit/ui/source.py +++ b/vunit/ui/source.py @@ -30,7 +30,7 @@ def set_compile_option(self, name, value): .. code-block:: python - files.set_compile_option("ghdl.flags", ["--no-vital-checks"]) + files.set_compile_option("ghdl.a_flags", ["--no-vital-checks"]) """ for source_file in self: source_file.set_compile_option(name, value) @@ -108,7 +108,7 @@ def set_compile_option(self, name, value): .. code-block:: python - my_file.set_compile_option("ghdl.flags", ["--no-vital-checks"]) + my_file.set_compile_option("ghdl.a_flags", ["--no-vital-checks"]) """ self._source_file.set_compile_option(name, value) diff --git a/vunit/ui/test.py b/vunit/ui/test.py index 510bb916c..805b2030b 100644 --- a/vunit/ui/test.py +++ b/vunit/ui/test.py @@ -148,7 +148,7 @@ def set_sim_option(self, name, value, overwrite=True): .. code-block:: python - test.set_sim_option("ghdl.flags", ["--no-vital-checks"]) + test.set_sim_option("ghdl.a_flags", ["--no-vital-checks"]) """ self._test_case.set_sim_option(name, value, overwrite) diff --git a/vunit/ui/testbench.py b/vunit/ui/testbench.py index e804cdeb7..3eb069e11 100644 --- a/vunit/ui/testbench.py +++ b/vunit/ui/testbench.py @@ -98,7 +98,7 @@ def set_sim_option(self, name, value, overwrite=True): .. code-block:: python - test_bench.set_sim_option("ghdl.flags", ["--no-vital-checks"]) + test_bench.set_sim_option("ghdl.a_flags", ["--no-vital-checks"]) """ self._test_bench.set_sim_option(name, value, overwrite) From aed30f0ab239ff6585b7e15ccb1d37ed420a6893 Mon Sep 17 00:00:00 2001 From: eine <6628437+eine@users.noreply.github.com> Date: Sat, 15 Feb 2020 00:48:43 +0100 Subject: [PATCH 13/79] json4vhdl: use base16 encodings (#595) * json4vhdl: support b16 encodings * tb_set_generic: test generic string length --- examples/vhdl/json4vhdl/run.py | 18 ++- .../vhdl/json4vhdl/src/test/tb_json_gens.vhd | 104 +++++++++--------- tests/acceptance/artificial/vhdl/run.py | 5 + .../artificial/vhdl/tb_set_generic.vhd | 39 ++++--- vunit/__init__.py | 1 - vunit/builtins.py | 2 +- vunit/json4vhdl.py | 15 ++- vunit/vhdl/JSON-for-VHDL | 2 +- 8 files changed, 113 insertions(+), 73 deletions(-) diff --git a/examples/vhdl/json4vhdl/run.py b/examples/vhdl/json4vhdl/run.py index a9ac6d543..85932aafb 100644 --- a/examples/vhdl/json4vhdl/run.py +++ b/examples/vhdl/json4vhdl/run.py @@ -14,17 +14,29 @@ """ from pathlib import Path -from vunit import VUnit, read_json, encode_json +from vunit import VUnit +from vunit.json4vhdl import read_json, encode_json, b16encode TEST_PATH = Path(__file__).parent / "src" / "test" VU = VUnit.from_argv() VU.add_json4vhdl() -VU.add_library("test").add_source_files(TEST_PATH / "*.vhd") +LIB = VU.add_library("test") +LIB.add_source_files(TEST_PATH / "*.vhd") TB_CFG = read_json(TEST_PATH / "data" / "data.json") TB_CFG["dump_debug_data"] = False -VU.set_generic("tb_cfg", encode_json(TB_CFG)) +JSON_STR = encode_json(TB_CFG) +JSON_FILE = Path("data") / "data.json" + +TB = LIB.get_test_benches()[0] + +TB.get_tests("stringified*")[0].set_generic("tb_cfg", JSON_STR) +TB.get_tests("b16encoded stringified*")[0].set_generic("tb_cfg", b16encode(JSON_STR)) +TB.get_tests("JSON file*")[0].set_generic("tb_cfg", JSON_FILE) +TB.get_tests("b16encoded JSON file*")[0].set_generic( + "tb_cfg", b16encode(str(TEST_PATH / JSON_FILE)) +) VU.main() diff --git a/examples/vhdl/json4vhdl/src/test/tb_json_gens.vhd b/examples/vhdl/json4vhdl/src/test/tb_json_gens.vhd index 849fc3c9f..97aeb81d1 100644 --- a/examples/vhdl/json4vhdl/src/test/tb_json_gens.vhd +++ b/examples/vhdl/json4vhdl/src/test/tb_json_gens.vhd @@ -10,71 +10,73 @@ context JSON.json_ctx; entity tb_json_gens is generic ( - runner_cfg : string; - tb_path : string; - tb_cfg : string; - tb_cfg_file : string := "data/data.json" + runner_cfg : string; + tb_path : string; + tb_cfg : string ); end entity; architecture tb of tb_json_gens is - -- tb_cfg contains stringified content - constant JSONContent : T_JSON := jsonLoad(tb_cfg); +begin + main: process - -- tb_cfg is the path of a JSON file - constant JSONFileContent : T_JSON := jsonLoad(tb_path & tb_cfg_file); + procedure run_test(JSONContent : T_JSON) is + -- get array of integers from JSON content + constant img_arr : integer_vector := jsonGetIntegerArray(JSONContent, "Image"); + begin + -- Content extracted from the JSON + info("JSONContent: " & lf & JSONContent.Content); - -- record to be filled by function decode - type img_t is record - image_width : positive; - image_height : positive; - dump_debug_data : boolean; - end record img_t; + -- Integer array, extracted by function jsonGetIntegerArray with data from the JSON + for i in 0 to img_arr'length-1 loop + info("Image array [" & integer'image(i) & "]: " & integer'image(img_arr(i))); + end loop; - -- function to fill img_t with content extracted from a JSON input - impure function decode(Content : T_JSON) return img_t is - begin - return (image_width => positive'value( jsonGetString(Content, "Image/0") ), - image_height => positive'value( jsonGetString(Content, "Image/1") ), - dump_debug_data => jsonGetBoolean(Content, "dump_debug_data") ); - end function decode; + -- Image dimensions as strings, get from the content from the JSON file + info("Image: " & jsonGetString(JSONContent, "Image/0") & ',' & jsonGetString(JSONContent, "Image/1")); - constant img : img_t := decode(JSONContent); + -- Some other content, deep in the JSON + info("Platform/ML505/FPGA: " & jsonGetString(JSONContent, "Platform/ML505/FPGA")); + info("Platform/KC705/IIC/0/Devices/0/Name: " & jsonGetString(JSONContent, "Platform/KC705/IIC/0/Devices/0/Name")); + end procedure; - -- get array of integers from JSON content - constant img_arr : integer_vector := jsonGetIntegerArray(JSONContent, "Image"); + procedure run_record_test(JSONContent : T_JSON) is + type img_t is record + image_width : positive; + image_height : positive; + dump_debug_data : boolean; + end record img_t; + + -- fill img_t with content extracted from a JSON input + constant img : img_t := ( + image_width => positive'value( jsonGetString(JSONContent, "Image/0") ), + image_height => positive'value( jsonGetString(JSONContent, "Image/1") ), + dump_debug_data => jsonGetBoolean(JSONContent, "dump_debug_data") + ); + begin + -- Image dimensions in a record, filled with data from the stringified generic + info("Image: " & integer'image(img.image_width) & ',' & integer'image(img.image_height)); + end procedure; + + variable JSONContent : T_JSON; -begin - main: process begin test_runner_setup(runner, runner_cfg); while test_suite loop - if run("test") then - -- Content extracted from the stringified generic - info("JSONContent: " & lf & JSONContent.Content); - - -- Full path of the JSON file, and extracted content - info("tb_path & tb_cfg_file: " & tb_path & tb_cfg_file); - info("JSONFileContent: " & lf & JSONFileContent.Content); - - -- Image dimensions in a record, filled by function decode with data from the stringified generic - info("Image: " & integer'image(img.image_width) & ',' & integer'image(img.image_height)); - - -- Integer array, extracted by function decode_array with data from the stringified generic - for i in 0 to img_arr'length-1 loop - info("Image array [" & integer'image(i) & "]: " & integer'image(img_arr(i))); - end loop; - - -- Image dimensions as strings, get from the content from the JSON file - info("Image: " & jsonGetString(JSONFileContent, "Image/0") & ',' & jsonGetString(JSONFileContent, "Image/1")); - - -- Some other content, deep in the JSON sources - info("Platform/ML505/FPGA: " & jsonGetString(JSONContent, "Platform/ML505/FPGA")); - info("Platform/ML505/FPGA: " & jsonGetString(JSONFileContent, "Platform/ML505/FPGA")); - - info("Platform/KC705/IIC/0/Devices/0/Name: " & jsonGetString(JSONContent, "Platform/KC705/IIC/0/Devices/0/Name")); - info("Platform/KC705/IIC/0/Devices/0/Name: " & jsonGetString(JSONFileContent, "Platform/KC705/IIC/0/Devices/0/Name")); + info("RAW generic: " & tb_cfg); + if run("stringified JSON generic") then + JSONContent := jsonLoad(tb_cfg); + run_test(JSONContent); + run_record_test(JSONContent); + elsif run("b16encoded stringified JSON generic") then + JSONContent := jsonLoad(tb_cfg); + run_test(JSONContent); + run_record_test(JSONContent); + elsif run("JSON file path generic") then + run_test(jsonLoad(tb_path & tb_cfg)); + elsif run("b16encoded JSON file path generic") then + run_test(jsonLoad(tb_cfg)); end if; end loop; test_runner_cleanup(runner); diff --git a/tests/acceptance/artificial/vhdl/run.py b/tests/acceptance/artificial/vhdl/run.py index 42cff8e43..4c2ecb47a 100644 --- a/tests/acceptance/artificial/vhdl/run.py +++ b/tests/acceptance/artificial/vhdl/run.py @@ -67,6 +67,11 @@ def configure_tb_set_generic(ui): tb.set_generic("str_val", "4ns") tb.set_generic("str_space_val", "1 2 3") tb.set_generic("str_quote_val", 'a"b') + str_long_num = 512 + tb.set_generic("str_long_num", str_long_num) + tb.set_generic( + "str_long_val", "".join(["0123456789abcdef" for x in range(str_long_num)]) + ) def configure_tb_assert_stop_level(ui): diff --git a/tests/acceptance/artificial/vhdl/tb_set_generic.vhd b/tests/acceptance/artificial/vhdl/tb_set_generic.vhd index 4173973f8..902407451 100644 --- a/tests/acceptance/artificial/vhdl/tb_set_generic.vhd +++ b/tests/acceptance/artificial/vhdl/tb_set_generic.vhd @@ -9,37 +9,48 @@ context vunit_lib.vunit_context; entity tb_set_generic is generic ( - runner_cfg : string; - is_ghdl : boolean; - true_boolean : boolean; - false_boolean : boolean; + runner_cfg : string; + is_ghdl : boolean; + true_boolean : boolean; + false_boolean : boolean; negative_integer : integer; positive_integer : integer; - negative_real : real := 0.0; - positive_real : real := 0.0; - time_val : time := 0 ns; - str_val : string; - str_space_val : string; - str_quote_val : string); + negative_real : real := 0.0; + positive_real : real := 0.0; + time_val : time := 0 ns; + str_val : string; + str_space_val : string; + str_quote_val : string; + str_long_num : integer := 64; + str_long_val : string); end entity; architecture tb of tb_set_generic is + impure function str_long(num: natural) return string is + variable str: string(1 to 16*num); + begin + for x in 1 to num loop + str((x-1)*16+1 to x*16) := "0123456789abcdef"; + end loop; + return str; + end; begin main : process begin test_runner_setup(runner, runner_cfg); - assert true_boolean = true; - assert false_boolean = false; + assert true_boolean = true; + assert false_boolean = false; assert negative_integer = -10000; assert positive_integer = 99999; if not is_ghdl then assert negative_real = -9999.9; assert positive_real = 2222.2; - assert time_val = 4 ns; + assert time_val = 4 ns; end if; - assert str_val = "4ns"; + assert str_val = "4ns"; assert str_space_val = "1 2 3"; assert str_quote_val = "a""b"; + assert str_long_val = str_long(str_long_num); test_runner_cleanup(runner); end process; end architecture; diff --git a/vunit/__init__.py b/vunit/__init__.py index af2cedcf1..218c7688c 100644 --- a/vunit/__init__.py +++ b/vunit/__init__.py @@ -13,7 +13,6 @@ from vunit.ui import VUnit from vunit.vunit_cli import VUnitCLI from vunit.about import version, doc -from vunit.json4vhdl import read_json, encode_json # Repository root ROOT = abspath(join(dirname(__file__), "..")) diff --git a/vunit/builtins.py b/vunit/builtins.py index eb1eadc42..ee31f6502 100644 --- a/vunit/builtins.py +++ b/vunit/builtins.py @@ -237,7 +237,7 @@ def _add_json4vhdl(self): except KeyError: library = self._vunit_obj.add_library(library_name) - library.add_source_files(VHDL_PATH / "JSON-for-VHDL" / "vhdl" / "*.vhdl") + library.add_source_files(VHDL_PATH / "JSON-for-VHDL" / "src" / "*.vhdl") def add_verilog_builtins(self): """ diff --git a/vunit/json4vhdl.py b/vunit/json4vhdl.py index 61b4e5f31..a29152bea 100644 --- a/vunit/json4vhdl.py +++ b/vunit/json4vhdl.py @@ -8,10 +8,12 @@ json4vhdl helper functions """ +from typing import Union import json +from base64 import b16encode as b16enc -def encode_json(obj): +def encode_json(obj: object): """ Convert object to stringified JSON @@ -26,7 +28,7 @@ def encode_json(obj): return json.dumps(obj, separators=(",", ":")) -def read_json(filename): +def read_json(filename: str): """ Read a JSON file and return an object @@ -39,3 +41,12 @@ def read_json(filename): generics = read_json(join(root, "src/test/data/data.json")) """ return json.loads(open(filename, "r").read()) + + +def b16encode(data: Union[str, bytes]): + """ + Encode a str|bytes using Base16 and return a str|bytes + """ + if isinstance(data, str): + return b16enc(bytes(data, "utf-8")).decode("utf-8") + return b16encode(data) diff --git a/vunit/vhdl/JSON-for-VHDL b/vunit/vhdl/JSON-for-VHDL index 80100fd6d..c8a6f517a 160000 --- a/vunit/vhdl/JSON-for-VHDL +++ b/vunit/vhdl/JSON-for-VHDL @@ -1 +1 @@ -Subproject commit 80100fd6dd8c0cf27c7356391f6cbfb5efcddd42 +Subproject commit c8a6f517aabf66ce3089669cc8e98852921e268b From a45f4d4791c35a03f722b07a7a588ce2e82fd685 Mon Sep 17 00:00:00 2001 From: eine <6628437+eine@users.noreply.github.com> Date: Sat, 15 Feb 2020 01:17:20 +0100 Subject: [PATCH 14/79] ci: add GHA workflow 'coverage' (#599) --- .github/run.sh | 8 ++++++++ .github/workflows/coverage.yml | 37 ++++++++++++++++++++++++++++++++++ tox.ini | 7 ++++++- 3 files changed, 51 insertions(+), 1 deletion(-) create mode 100755 .github/run.sh create mode 100644 .github/workflows/coverage.yml diff --git a/.github/run.sh b/.github/run.sh new file mode 100755 index 000000000..da3e2e6c1 --- /dev/null +++ b/.github/run.sh @@ -0,0 +1,8 @@ +#!/usr/bin/env sh + +docker run --rm -t \ + -v $(pwd):/src \ + -w /src \ + -e PYTHONPATH=/src \ + "$IMAGE" \ + "$@" diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml new file mode 100644 index 000000000..4fc0d2801 --- /dev/null +++ b/.github/workflows/coverage.yml @@ -0,0 +1,37 @@ +name: 'coverage' + +on: + push: + schedule: + - cron: '0 0 * * 5' + +jobs: + + coverage: + runs-on: ubuntu-latest + env: + DOCKER_REGISTRY: docker.pkg.github.com + IMAGE: docker.pkg.github.com/vunit/vunit/dev:llvm + steps: + - uses: actions/checkout@v1 + - run: git submodule update --init --recursive + - uses: actions/setup-python@v1 + with: + python-version: 3.7 + - name: Docker login + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + echo "$GITHUB_TOKEN" | docker login -u vunit-gha --password-stdin "$DOCKER_REGISTRY" + docker pull $IMAGE + docker logout "$DOCKER_REGISTRY" + - name: Run coverage + run: | + ./.github/run.sh tox -e coverage + ./.github/run.sh coverage html --directory=htmlcov + - name: Report coverage + run: ./.github/run.sh coverage report -m --skip-covered + - uses: actions/upload-artifact@master + with: + name: VUnit_coverage + path: htmlcov diff --git a/tox.ini b/tox.ini index 56834f444..68a9fe694 100644 --- a/tox.ini +++ b/tox.ini @@ -1,5 +1,5 @@ [tox] -envlist = py{36,37,38}-fmt, py{36,37,38}-{unit}, py{36,37,38}-{lint,docs}, py{36,37,38}-{acceptance,vcomponents}-{activehdl,ghdl,modelsim,rivierapro} +envlist = py{36,37,38}-fmt, py{36,37,38}-{unit}, py{36,37,38}-{lint,docs}, py{36,37,38}-{acceptance,vcomponents}-{activehdl,ghdl,modelsim,rivierapro}, py{36,37,38}-coverage skip_missing_interpreters = True [testenv] @@ -12,6 +12,10 @@ deps= lint: pycodestyle lint: pylint lint: mypy + coverage: coverage + coverage: pycodestyle + coverage: pylint + coverage: mypy docs: docutils docs: sphinx docs: sphinx-argparse @@ -30,3 +34,4 @@ commands= docs: {envpython} tools/build_docs.py {envtmpdir}/docsbuild {posargs} acceptance: {envpython} -m pytest -v -ra tests/acceptance {posargs} vcomponents: {envpython} vunit/vhdl/verification_components/run.py --clean + coverage: {envpython} -m coverage run --branch --source vunit/ -m unittest discover tests/ From abd179d9ac74cad5a00b40b70ef586b86b1f020b Mon Sep 17 00:00:00 2001 From: umarcor Date: Sat, 7 Dec 2019 13:56:49 +0100 Subject: [PATCH 15/79] ci: upload 'docs' artifacts --- .github/workflows/docs.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index e5cedc201..45d8e7153 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -23,6 +23,10 @@ jobs: pip install -U virtualenv tox --progress-bar off - name: build docs run: tox -e py38-docs -- --color + - uses: actions/upload-artifact@master + with: + name: VUnit-site + path: .tox/py38-docs/tmp/docsbuild/ - name: 'publish site to gh-pages' if: github.event_name != 'pull_request' && github.repository == 'VUnit/vunit' env: From 795462007cc3f5156eec141eda69fa33461d6324 Mon Sep 17 00:00:00 2001 From: umarcor Date: Sat, 7 Dec 2019 15:58:51 +0100 Subject: [PATCH 16/79] clean tox.ini --- tox.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tox.ini b/tox.ini index 68a9fe694..7495f4a7d 100644 --- a/tox.ini +++ b/tox.ini @@ -1,5 +1,5 @@ [tox] -envlist = py{36,37,38}-fmt, py{36,37,38}-{unit}, py{36,37,38}-{lint,docs}, py{36,37,38}-{acceptance,vcomponents}-{activehdl,ghdl,modelsim,rivierapro}, py{36,37,38}-coverage +envlist = py{36,37,38}-{fmt,unit,lint,docs}, py{36,37,38}-{acceptance,vcomponents}-{activehdl,ghdl,modelsim,rivierapro}, py{36,37,38}-coverage skip_missing_interpreters = True [testenv] From 14e88b502011ae3fc232ecd3b75552bb4b321af1 Mon Sep 17 00:00:00 2001 From: umarcor Date: Wed, 19 Feb 2020 06:01:11 +0100 Subject: [PATCH 17/79] builtins: ensure that files added through '_add_files' do actually exist --- vunit/builtins.py | 5 +++-- vunit/ui/common.py | 30 ++++++++++++++++++++++++++++-- vunit/ui/library.py | 25 +++---------------------- 3 files changed, 34 insertions(+), 26 deletions(-) diff --git a/vunit/builtins.py b/vunit/builtins.py index ee31f6502..22522445a 100644 --- a/vunit/builtins.py +++ b/vunit/builtins.py @@ -13,6 +13,7 @@ from warnings import warn from vunit.vhdl_standard import VHDL, VHDLStandard from vunit.sim_if.common import simulator_check +from vunit.ui.common import get_checked_file_names_from_globs VHDL_PATH = (Path(__file__).parent / "vhdl").resolve() VERILOG_PATH = (Path(__file__).parent / "verilog").resolve() @@ -43,7 +44,7 @@ def add(name, deps=tuple()): def add(self, name, args=None): self._builtins_adder.add(name, args) - def _add_files(self, pattern): + def _add_files(self, pattern=None, allow_empty=True): """ Add files with naming convention to indicate which standard is supported """ @@ -52,7 +53,7 @@ def _add_files(self, pattern): and self._vhdl_standard.supports_context ) - for file_name in glob(str(pattern)): + for file_name in get_checked_file_names_from_globs(str(pattern), allow_empty): base_file_name = Path(file_name).name standards = set() diff --git a/vunit/ui/common.py b/vunit/ui/common.py index becad3489..24238e2a4 100644 --- a/vunit/ui/common.py +++ b/vunit/ui/common.py @@ -8,12 +8,14 @@ UI common functions """ +from pathlib import Path +from glob import glob from os import environ from logging import getLogger -from typing import Optional +from typing import Optional, List +from ..sim_if import is_string_not_iterable from ..vhdl_standard import VHDL, VHDLStandard - LOGGER = getLogger(__name__) TEST_OUTPUT_PATH = "test_output" @@ -49,3 +51,27 @@ def check_not_empty(lst, allow_empty, error_msg): if (not allow_empty) and (not lst): raise ValueError(error_msg + ". Use allow_empty=True to avoid exception.") return lst + + +def get_checked_file_names_from_globs(pattern, allow_empty): + """ + Get file names from globs and check that exist + """ + if is_string_not_iterable(pattern): + patterns = [pattern] + elif isinstance(pattern, Path): + patterns = [str(pattern)] + else: + patterns = pattern + + file_names: List[str] = [] + for pattern_instance in patterns: + new_file_names = glob(str(pattern_instance)) + check_not_empty( + new_file_names, + allow_empty, + "Pattern %r did not match any file" % pattern_instance, + ) + file_names += new_file_names + + return file_names diff --git a/vunit/ui/library.py b/vunit/ui/library.py index a30ba0029..e90ac9274 100644 --- a/vunit/ui/library.py +++ b/vunit/ui/library.py @@ -9,15 +9,13 @@ """ from pathlib import Path -from glob import glob from fnmatch import fnmatch -from typing import Optional, List +from typing import Optional from ..vhdl_standard import VHDL, VHDLStandard from ..project import Project -from ..sim_if import is_string_not_iterable from ..source_file import file_type_of, FILE_TYPES, VERILOG_FILE_TYPES from ..builtins import add_verilog_include_dir -from .common import check_not_empty +from .common import check_not_empty, get_checked_file_names_from_globs from .source import SourceFile, SourceFileList from .testbench import TestBench from .packagefacade import PackageFacade @@ -188,23 +186,6 @@ def add_source_files( # pylint: disable=too-many-arguments library.add_source_files("*.vhd") """ - if is_string_not_iterable(pattern): - patterns = [pattern] - elif isinstance(pattern, Path): - patterns = [str(pattern)] - else: - patterns = pattern - - file_names: List[str] = [] - for pattern_instance in patterns: - new_file_names = glob(str(pattern_instance)) - check_not_empty( - new_file_names, - allow_empty, - "Pattern %r did not match any file" % pattern_instance, - ) - file_names += new_file_names - return SourceFileList( source_files=[ self.add_source_file( @@ -216,7 +197,7 @@ def add_source_files( # pylint: disable=too-many-arguments no_parse=no_parse, file_type=file_type, ) - for file_name in file_names + for file_name in get_checked_file_names_from_globs(pattern, allow_empty) ] ) From 142fb5fa765e4adc53ba62d3ec70300f447f1052 Mon Sep 17 00:00:00 2001 From: umarcor Date: Fri, 6 Dec 2019 14:31:18 +0100 Subject: [PATCH 18/79] external: move VHPIDIRECT implementation and example to separate repo --- examples/vhdl/external_buffer/cp.py | 28 ------ examples/vhdl/external_buffer/run.py | 76 --------------- examples/vhdl/external_buffer/src/cp.c | 94 ------------------- examples/vhdl/external_buffer/src/main.c | 80 ---------------- .../src/tb_ext_byte_vector.vhd | 55 ----------- .../src/tb_ext_integer_vector.vhd | 55 ----------- .../external_buffer/src/tb_ext_string.vhd | 55 ----------- .../src/tb_extcp_byte_vector.vhd | 48 ---------- .../external_buffer/src/tb_extcp_string.vhd | 48 ---------- tests/acceptance/test_external_run_scripts.py | 8 -- vunit/builtins.py | 79 +++++----------- vunit/ui/__init__.py | 8 +- .../external_integer_vector_pkg.vhd} | 27 +++++- .../external_string_pkg.vhd} | 27 +++++- .../external_integer_vector-novhpi.vhd | 24 ----- .../external/external_integer_vector-vhpi.vhd | 28 ------ .../src/external/external_string-novhpi.vhd | 24 ----- .../src/external/external_string-vhpi.vhd | 28 ------ .../vhdl/data_types/src/external/ghdl/grt.ver | 16 ---- .../vhdl/data_types/src/external/ghdl/stubs.c | 53 ----------- .../src/external/ghdl/vhpidirect_user.h | 49 ---------- 21 files changed, 77 insertions(+), 833 deletions(-) delete mode 100644 examples/vhdl/external_buffer/cp.py delete mode 100644 examples/vhdl/external_buffer/run.py delete mode 100644 examples/vhdl/external_buffer/src/cp.c delete mode 100644 examples/vhdl/external_buffer/src/main.c delete mode 100644 examples/vhdl/external_buffer/src/tb_ext_byte_vector.vhd delete mode 100644 examples/vhdl/external_buffer/src/tb_ext_integer_vector.vhd delete mode 100644 examples/vhdl/external_buffer/src/tb_ext_string.vhd delete mode 100644 examples/vhdl/external_buffer/src/tb_extcp_byte_vector.vhd delete mode 100644 examples/vhdl/external_buffer/src/tb_extcp_string.vhd rename vunit/vhdl/data_types/src/{external/external_integer_vector-body.vhd => api/external_integer_vector_pkg.vhd} (53%) rename vunit/vhdl/data_types/src/{external/external_string-body.vhd => api/external_string_pkg.vhd} (53%) delete mode 100644 vunit/vhdl/data_types/src/external/external_integer_vector-novhpi.vhd delete mode 100644 vunit/vhdl/data_types/src/external/external_integer_vector-vhpi.vhd delete mode 100644 vunit/vhdl/data_types/src/external/external_string-novhpi.vhd delete mode 100644 vunit/vhdl/data_types/src/external/external_string-vhpi.vhd delete mode 100644 vunit/vhdl/data_types/src/external/ghdl/grt.ver delete mode 100644 vunit/vhdl/data_types/src/external/ghdl/stubs.c delete mode 100644 vunit/vhdl/data_types/src/external/ghdl/vhpidirect_user.h diff --git a/examples/vhdl/external_buffer/cp.py b/examples/vhdl/external_buffer/cp.py deleted file mode 100644 index c8afd3ec7..000000000 --- a/examples/vhdl/external_buffer/cp.py +++ /dev/null @@ -1,28 +0,0 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this file, -# You can obtain one at http://mozilla.org/MPL/2.0/. -# -# Copyright (c) 2014-2020, Lars Asplund lars.anders.asplund@gmail.com - -from subprocess import check_call -from shutil import which -from pathlib import Path -from vunit import VUnit - -SRC_PATH = Path(__file__).parent / "src" - -C_OBJ = SRC_PATH / "cp.o" -# Compile C application to an object -check_call([which("gcc"), "-fPIC", "-c", str(SRC_PATH / "cp.c"), "-o", str(C_OBJ)]) - -# Enable the external feature for strings -VU = VUnit.from_argv(vhdl_standard="2008", compile_builtins=False) -VU.add_builtins({"string": True}) - -LIB = VU.add_library("lib") -LIB.add_source_files(SRC_PATH / "tb_extcp_*.vhd") - -# Add the C object to the elaboration of GHDL -VU.set_sim_option("ghdl.elab_flags", ["-Wl," + str(C_OBJ)]) - -VU.main() diff --git a/examples/vhdl/external_buffer/run.py b/examples/vhdl/external_buffer/run.py deleted file mode 100644 index 70f90120d..000000000 --- a/examples/vhdl/external_buffer/run.py +++ /dev/null @@ -1,76 +0,0 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this file, -# You can obtain one at http://mozilla.org/MPL/2.0/. -# -# Copyright (c) 2014-2020, Lars Asplund lars.anders.asplund@gmail.com - -""" -External Buffer ---------------- - -`Interfacing with foreign languages (C) through VHPIDIRECT `_ - -An array of type ``uint8_t`` is allocated in a C application and some values -are written to the first ``1/3`` positions. Then, the VHDL simulation is -executed, where the (external) array/buffer is used. - -In the VHDL testbenches, two vector pointers are created, each of them using -a different access mechanism (``extfunc`` or ``extacc``). One of them is used to copy -the first ``1/3`` elements to positions ``[1/3, 2/3)``, while incrementing each value -by one. The second one is used to copy elements from ``[1/3, 2/3)`` to ``[2/3, 3/3)``, -while incrementing each value by two. - -When the simulation is finished, the C application checks whether data was successfully -copied/modified. The content of the buffer is printed both before and after the -simulation. -""" - -from subprocess import check_call -from shutil import which -from pathlib import Path -from vunit import VUnit, ROOT - -SRC_PATH = Path(__file__).parent / "src" -EXT_SRCS = Path(ROOT) / "vunit" / "vhdl" / "data_types" / "src" / "external" / "ghdl" - -# Compile C applications to an objects -C_IOBJ = SRC_PATH / "imain.o" -C_BOBJ = SRC_PATH / "bmain.o" - -for val in [["int32_t", C_IOBJ], ["uint8_t", C_BOBJ]]: - check_call( - [ - which("gcc"), - "-fPIC", - "-DTYPE=" + val[0], - "-I", - EXT_SRCS, - "-c", - SRC_PATH / "main.c", - "-o", - val[1], - ] - ) - -# Enable the external feature for strings/byte_vectors and integer_vectors -VU = VUnit.from_argv(vhdl_standard="2008", compile_builtins=False) -VU.add_builtins({"string": True, "integer": True}) - -LIB = VU.add_library("lib") -LIB.add_source_files(SRC_PATH / "tb_ext_*.vhd") - -# Add the C object to the elaboration of GHDL -for tb in LIB.get_test_benches(pattern="*tb_ext*", allow_empty=False): - tb.set_sim_option( - "ghdl.elab_flags", - ["-Wl," + str(C_BOBJ), "-Wl,-Wl,--version-script=" + str(EXT_SRCS / "grt.ver")], - overwrite=True, - ) -for tb in LIB.get_test_benches(pattern="*tb_ext*_integer*", allow_empty=False): - tb.set_sim_option( - "ghdl.elab_flags", - ["-Wl," + str(C_IOBJ), "-Wl,-Wl,--version-script=" + str(EXT_SRCS / "grt.ver")], - overwrite=True, - ) - -VU.main() diff --git a/examples/vhdl/external_buffer/src/cp.c b/examples/vhdl/external_buffer/src/cp.c deleted file mode 100644 index 2db97ec66..000000000 --- a/examples/vhdl/external_buffer/src/cp.c +++ /dev/null @@ -1,94 +0,0 @@ -/* -External Buffer - -Interfacing with foreign languages (C) through VHPIDIRECT: -https://ghdl.readthedocs.io/en/latest/using/Foreign.html - -Two arrays of type uint8_t are allocated and some values are written to the first. -Then, the VHDL simulation is executed, where the (external) array/buffer -is used. When the simulation is finished, the results are checked. The content of -the buffer is printed both before and after the simulation. - -NOTE: This file is expected to be used along with tb_extcp_byte_vector.vhd or tb_extcp_string.vhd -*/ - -#include -#include -#include - -extern int ghdl_main (int argc, char **argv); - -uint8_t *D[2]; -const uint32_t length = 10; - -// Check procedure, to be executed when GHDL exits. -// The simulation is expected to copy the first 1/3 elements to positions [1/3, 2/3), -// while incrementing each value by one, and then copy elements from [1/3, 2/3) to -// [2/3, 3/3), while incrementing each value by two. -static void exit_handler(void) { - uint i; - for ( i=0; i -#include -#include -#include "vhpidirect_user.h" - -const uint32_t length = 5; - -/* - Check procedure, to be executed when GHDL exits. - The simulation is expected to copy the first 1/3 elements to positions [1/3, 2/3), - while incrementing each value by one, and then copy elements from [1/3, 2/3) to - [2/3, 3/3), while incrementing each value by two. -*/ -static void exit_handler(void) { - unsigned i, j, z, k; - TYPE expected, got; - k = 0; - for (j=0; j<3; j++) { - k += j; - for(i=0; i, 'integer': }. Allowed values are: None, False/True or - ['path/to/custom/file']. + :param external: struct to provide bridges for the external VHDL API. + { + 'string': ['path/to/custom/file'], + 'integer': ['path/to/custom/file'] + }. """ self._add_files(VHDL_PATH / "data_types" / "src" / "*.vhd") - use_ext = {"string": False, "integer": False} - files = {"string": None, "integer": None} - - if external: - for ind, val in external.items(): - if isinstance(val, bool): - use_ext[ind] = val - else: - use_ext[ind] = True - files[ind] = val - - for _, val in use_ext.items(): - if val and simulator_check(lambda simclass: not simclass.supports_vhpi()): - raise RuntimeError( - "the selected simulator does not support VHPI; must use non-VHPI packages..." + for key in ["string", "integer_vector"]: + self._add_files( + pattern=str( + VHDL_PATH + / "data_types" + / "src" + / "api" + / ("external_%s_pkg.vhd" % key) ) - - ext_path = VHDL_PATH / "data_types" / "src" / "external" - - def default_files(cond, type_str): - """ - Return name of VHDL file with default VHPIDIRECT foreign declarations. - """ - return [ - str( - ext_path - / ( - "external_" - + type_str - + "-" - + ("" if cond else "no") - + "vhpi.vhd" - ) - ), - str(ext_path / ("external_" + type_str + "-body.vhd")), - ] - - if not files["string"]: - files["string"] = default_files(use_ext["string"], "string") - - if not files["integer"]: - files["integer"] = default_files(use_ext["integer"], "integer_vector") - - for _, val in files.items(): - for name in val: - self._add_files(name) + if external is None + or key not in external + or not external[key] + or external[key] is True + else external[key], + allow_empty=False, + ) def _add_array_util(self): """ @@ -250,9 +221,11 @@ def add_vhdl_builtins(self, external=None): """ Add vunit VHDL builtin libraries - :param external: struct to select whether to enable external models for 'string' and/or 'integer' vectors. - {'string': , 'integer': }. Allowed values are: None, False/True or - ['path/to/custom/file']. + :param external: struct to provide bridges for the external VHDL API. + { + 'string': ['path/to/custom/file'], + 'integer': ['path/to/custom/file'] + }. """ self._add_data_types(external=external) self._add_files(VHDL_PATH / "*.vhd") diff --git a/vunit/ui/__init__.py b/vunit/ui/__init__.py index a82305f6d..d6dca10ed 100644 --- a/vunit/ui/__init__.py +++ b/vunit/ui/__init__.py @@ -953,9 +953,11 @@ def add_builtins(self, external=None): """ Add vunit VHDL builtin libraries - :param external: struct to select whether to enable external models for 'string' and/or 'integer' vectors. - {'string': , 'integer': }. Allowed values are: None, False/True or - ['path/to/custom/file']. + :param external: struct to provide bridges for the external VHDL API. + { + 'string': ['path/to/custom/file'], + 'integer': ['path/to/custom/file'] + }. """ self._builtins.add_vhdl_builtins(external=external) diff --git a/vunit/vhdl/data_types/src/external/external_integer_vector-body.vhd b/vunit/vhdl/data_types/src/api/external_integer_vector_pkg.vhd similarity index 53% rename from vunit/vhdl/data_types/src/external/external_integer_vector-body.vhd rename to vunit/vhdl/data_types/src/api/external_integer_vector_pkg.vhd index 08f716c48..2675bb92b 100644 --- a/vunit/vhdl/data_types/src/external/external_integer_vector-body.vhd +++ b/vunit/vhdl/data_types/src/api/external_integer_vector_pkg.vhd @@ -4,25 +4,44 @@ -- -- Copyright (c) 2014-2020, Lars Asplund lars.anders.asplund@gmail.com +use work.types_pkg.all; + +package external_integer_vector_pkg is + procedure write_integer ( + id : integer; + i : integer; + v : integer + ); + + impure function read_integer ( + id : integer; + i : integer + ) return integer; + + impure function get_ptr ( + id : integer + ) return extintvec_access_t; +end package; + package body external_integer_vector_pkg is procedure write_integer ( id : integer; i : integer; v : integer - )is begin - assert false report "VHPI write_integer" severity failure; + ) is begin + assert false report "EXTERNAL write_integer" severity failure; end; impure function read_integer ( id : integer; i : integer ) return integer is begin - assert false report "VHPI read_integer" severity failure; + assert false report "EXTERNAL read_integer" severity failure; end; impure function get_ptr ( id : integer ) return extintvec_access_t is begin - assert false report "VHPI get_intvec_ptr" severity failure; + assert false report "EXTERNAL get_intvec_ptr" severity failure; end; end package body; diff --git a/vunit/vhdl/data_types/src/external/external_string-body.vhd b/vunit/vhdl/data_types/src/api/external_string_pkg.vhd similarity index 53% rename from vunit/vhdl/data_types/src/external/external_string-body.vhd rename to vunit/vhdl/data_types/src/api/external_string_pkg.vhd index dff05329b..e0b93a6b1 100644 --- a/vunit/vhdl/data_types/src/external/external_string-body.vhd +++ b/vunit/vhdl/data_types/src/api/external_string_pkg.vhd @@ -4,25 +4,44 @@ -- -- Copyright (c) 2014-2020, Lars Asplund lars.anders.asplund@gmail.com +use work.types_pkg.all; + +package external_string_pkg is + procedure write_char ( + id : integer; + i : integer; + v : character + ); + + impure function read_char ( + id : integer; + i : integer + ) return character; + + impure function get_ptr ( + id : integer + ) return extstring_access_t; +end package; + package body external_string_pkg is procedure write_char ( id : integer; i : integer; v : character - )is begin - assert false report "VHPI write_char" severity failure; + ) is begin + assert false report "EXTERNAL write_char" severity failure; end; impure function read_char ( id : integer; i : integer ) return character is begin - assert false report "VHPI read_char" severity failure; + assert false report "EXTERNAL read_char" severity failure; end; impure function get_ptr ( id : integer ) return extstring_access_t is begin - assert false report "VHPI get_string_ptr" severity failure; + assert false report "EXTERNAL get_string_ptr" severity failure; end; end package body; diff --git a/vunit/vhdl/data_types/src/external/external_integer_vector-novhpi.vhd b/vunit/vhdl/data_types/src/external/external_integer_vector-novhpi.vhd deleted file mode 100644 index 6ebf849d9..000000000 --- a/vunit/vhdl/data_types/src/external/external_integer_vector-novhpi.vhd +++ /dev/null @@ -1,24 +0,0 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2020, Lars Asplund lars.anders.asplund@gmail.com - -use work.types_pkg.all; - -package external_integer_vector_pkg is - procedure write_integer ( - id : integer; - i : integer; - v : integer - ); - - impure function read_integer ( - id : integer; - i : integer - ) return integer; - - impure function get_ptr ( - id : integer - ) return extintvec_access_t; -end package; diff --git a/vunit/vhdl/data_types/src/external/external_integer_vector-vhpi.vhd b/vunit/vhdl/data_types/src/external/external_integer_vector-vhpi.vhd deleted file mode 100644 index 34e4ba518..000000000 --- a/vunit/vhdl/data_types/src/external/external_integer_vector-vhpi.vhd +++ /dev/null @@ -1,28 +0,0 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2020, Lars Asplund lars.anders.asplund@gmail.com - -use work.types_pkg.all; - -package external_integer_vector_pkg is - procedure write_integer ( - id : integer; - i : integer; - v : integer - ); - - impure function read_integer ( - id : integer; - i : integer - ) return integer; - - impure function get_ptr ( - id : integer - ) return extintvec_access_t; - - attribute foreign of write_integer : procedure is "VHPIDIRECT write_integer"; - attribute foreign of read_integer : function is "VHPIDIRECT read_integer"; - attribute foreign of get_ptr : function is "VHPIDIRECT get_intvec_ptr"; -end package; diff --git a/vunit/vhdl/data_types/src/external/external_string-novhpi.vhd b/vunit/vhdl/data_types/src/external/external_string-novhpi.vhd deleted file mode 100644 index 21c813552..000000000 --- a/vunit/vhdl/data_types/src/external/external_string-novhpi.vhd +++ /dev/null @@ -1,24 +0,0 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2020, Lars Asplund lars.anders.asplund@gmail.com - -use work.types_pkg.all; - -package external_string_pkg is - procedure write_char ( - id : integer; - i : integer; - v : character - ); - - impure function read_char ( - id : integer; - i : integer - ) return character; - - impure function get_ptr ( - id : integer - ) return extstring_access_t; -end package; diff --git a/vunit/vhdl/data_types/src/external/external_string-vhpi.vhd b/vunit/vhdl/data_types/src/external/external_string-vhpi.vhd deleted file mode 100644 index f54bd6217..000000000 --- a/vunit/vhdl/data_types/src/external/external_string-vhpi.vhd +++ /dev/null @@ -1,28 +0,0 @@ --- This Source Code Form is subject to the terms of the Mozilla Public --- License, v. 2.0. If a copy of the MPL was not distributed with this file, --- You can obtain one at http://mozilla.org/MPL/2.0/. --- --- Copyright (c) 2014-2020, Lars Asplund lars.anders.asplund@gmail.com - -use work.types_pkg.all; - -package external_string_pkg is - procedure write_char ( - id : integer; - i : integer; - v : character - ); - - impure function read_char ( - id : integer; - i : integer - ) return character; - - impure function get_ptr ( - id : integer - ) return extstring_access_t; - - attribute foreign of write_char : procedure is "VHPIDIRECT write_char"; - attribute foreign of read_char : function is "VHPIDIRECT read_char"; - attribute foreign of get_ptr : function is "VHPIDIRECT get_string_ptr"; -end package; diff --git a/vunit/vhdl/data_types/src/external/ghdl/grt.ver b/vunit/vhdl/data_types/src/external/ghdl/grt.ver deleted file mode 100644 index cd09ff8c6..000000000 --- a/vunit/vhdl/data_types/src/external/ghdl/grt.ver +++ /dev/null @@ -1,16 +0,0 @@ -VHPIDIRECT { - global: -main; -oct_main; -ghdl_main; -read_char; -write_char; -read_integer; -write_integer; -set_string_ptr; -get_string_ptr; -set_intvec_ptr; -get_intvec_ptr; - local: - *; -}; diff --git a/vunit/vhdl/data_types/src/external/ghdl/stubs.c b/vunit/vhdl/data_types/src/external/ghdl/stubs.c deleted file mode 100644 index 9ffb4ef26..000000000 --- a/vunit/vhdl/data_types/src/external/ghdl/stubs.c +++ /dev/null @@ -1,53 +0,0 @@ -#include -#include -#include - -void set_string_ptr(uint8_t id, uint8_t *p) { - printf("ERROR set_string_ptr: THIS IS A STUB\n"); - exit(1); - return; -} - -uintptr_t get_string_ptr(uint8_t id) { - printf("ERROR get_string_ptr: THIS IS A STUB\n"); - exit(1); - return NULL; -} - -void write_char( uint8_t id, uint32_t i, uint8_t v ) { - printf("ERROR write_char: THIS IS A STUB\n"); - exit(1); - return; -} - -uint8_t read_char( uint8_t id, uint32_t i ) { - printf("ERROR read_char: THIS IS A STUB\n"); - exit(1); - return 0; -} - -//--- - -void set_intvec_ptr(uint8_t id, uint8_t *p) { - printf("ERROR set_intvec_ptr: THIS IS A STUB\n"); - exit(1); - return; -} - -uintptr_t get_intvec_ptr(uint8_t id) { - printf("ERROR get_intvec_ptr: THIS IS A STUB\n"); - exit(1); - return NULL; -} - -void write_integer(uint8_t id, uint32_t i, int32_t v) { - printf("ERROR write_integer: THIS IS A STUB\n"); - exit(1); - return; -} - -int32_t read_integer(uint8_t id, uint32_t i) { - printf("ERROR read_integer: THIS IS A STUB\n"); - exit(1); - return 0; -} diff --git a/vunit/vhdl/data_types/src/external/ghdl/vhpidirect_user.h b/vunit/vhdl/data_types/src/external/ghdl/vhpidirect_user.h deleted file mode 100644 index 2541c2b39..000000000 --- a/vunit/vhdl/data_types/src/external/ghdl/vhpidirect_user.h +++ /dev/null @@ -1,49 +0,0 @@ -#include - -extern int ghdl_main (int argc, char **argv); - -uint8_t *D[256]; - -//--- - -// External string/byte_vector through access (mode = extacc) - -void set_string_ptr(uint8_t id, uintptr_t p) { - D[id] = (uint8_t*)p; -} - -uintptr_t get_string_ptr(uint8_t id) { - return (uintptr_t)D[id]; -} - -// External string/byte_vector through functions (mode = extfnc) - -void write_char(uint8_t id, uint32_t i, uint8_t v) { - ((uint8_t*)D[id])[i] = v; -} - -uint8_t read_char(uint8_t id, uint32_t i) { - return ((uint8_t*)D[id])[i]; -} - -//--- - -// External integer_vector through access (mode = extacc) - -void set_intvec_ptr(uint8_t id, uintptr_t p) { - D[id] = (uint8_t*)p; -} - -uintptr_t get_intvec_ptr(uint8_t id) { - return (uintptr_t)D[id]; -} - -// External integer_vector through functions (mode = extfnc) - -void write_integer(uint8_t id, uint32_t i, int32_t v) { - ((int32_t*)D[id])[i] = v; -} - -int32_t read_integer(uint8_t id, uint32_t i) { - return ((int32_t*)D[id])[i]; -} From cf06849599ed6b3560b90aea99eaad2ed15cbbc0 Mon Sep 17 00:00:00 2001 From: umarcor Date: Fri, 6 Dec 2019 20:07:15 +0100 Subject: [PATCH 19/79] docs: add section 'external VHDL API' --- docs/data_types/cosim.png | Bin 0 -> 9852 bytes docs/data_types/ext_integer_vector.rst | 7 ++++ docs/data_types/ext_string.rst | 7 ++++ docs/data_types/external_api.rst | 26 +++++++++++++++ docs/data_types/integer_array.rst | 8 +++++ docs/data_types/queue.rst | 8 +++++ docs/data_types/user_guide.rst | 43 +++++++++++-------------- docs/genindex.rst | 6 ++++ docs/index.rst | 5 +-- 9 files changed, 84 insertions(+), 26 deletions(-) create mode 100644 docs/data_types/cosim.png create mode 100644 docs/data_types/ext_integer_vector.rst create mode 100644 docs/data_types/ext_string.rst create mode 100644 docs/data_types/external_api.rst create mode 100644 docs/data_types/integer_array.rst create mode 100644 docs/data_types/queue.rst create mode 100644 docs/genindex.rst diff --git a/docs/data_types/cosim.png b/docs/data_types/cosim.png new file mode 100644 index 0000000000000000000000000000000000000000..87eadc889b2bfc51582217d9240041f7e2069184 GIT binary patch literal 9852 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 literal 0 HcmV?d00001 diff --git a/docs/data_types/ext_integer_vector.rst b/docs/data_types/ext_integer_vector.rst new file mode 100644 index 000000000..3f8a5967b --- /dev/null +++ b/docs/data_types/ext_integer_vector.rst @@ -0,0 +1,7 @@ +.. _ext_integer_vector_pkg: + +*external integer vector* package +--------------------------------- +.. literalinclude:: ../../vunit/vhdl/data_types/src/api/external_integer_vector_pkg.vhd + :language: vhdl + :lines: 7- diff --git a/docs/data_types/ext_string.rst b/docs/data_types/ext_string.rst new file mode 100644 index 000000000..0f5216d2c --- /dev/null +++ b/docs/data_types/ext_string.rst @@ -0,0 +1,7 @@ +.. _ext_string_pkg: + +*external string* package +------------------------- +.. literalinclude:: ../../vunit/vhdl/data_types/src/api/external_string_pkg.vhd + :language: vhdl + :lines: 7- \ No newline at end of file diff --git a/docs/data_types/external_api.rst b/docs/data_types/external_api.rst new file mode 100644 index 000000000..f3f467e3d --- /dev/null +++ b/docs/data_types/external_api.rst @@ -0,0 +1,26 @@ +.. figure:: cosim.png + :alt: Interfacing VHDL and foreign languages with VUnit + :align: center + +In version 4.3.0 the prototype of an external VHDL API was introduced. This experimental feature exposes a subset of resources from some of VUnit's internal data types. Creation functions for these data types accept two new optional parameters: + +* **mode**: selects between *internal* (default), external access (*extacc*) or external function (*extfnc*). +* **eid**: when the selected mode is external, this parameter is to be used between VHDL and the foreign language as a unique identifier for the vector. + +In mode *extacc*, VHDL retrieves a pointer to an already allocated buffer, and then data is directly read/written from VHDL. Hence, it is required for the pointer, that must be allocated externally, to be accesible to both the VHDL and the foreign language. The expected use case for this mode is to wrap the VHDL simulation in C/C++ or Python, so that the simulation is a child of the main process. + +Conversely, mode *extfnc* is to be used when data is not available in the same memory space where the VHDL simulation is executed. Each time a value needs to be read/written, a function callback is executed (``read_*``/``write_*``), providing ``eid`` as an argument. + +List of types that are currently in the external VHDL API: + +* **string_ptr**, and **byte_vector_ptr** as an alias (:ref:`External string API `) +* **integer_vector_ptr** (:ref:`External integer vector API `) + +.. important:: By default, bodies of the external API functions/procedures include forced failure assertions. Hence, using ``mode/=internal`` without providing a *bridge* to a foreign language will make tests fail. Bridges must be provided through `vu.add_builtins(external=)`, where `` defaults to ``{"string": False, "integer_vector": False}``. Each field should contain a list of VHDL files to replace the *dummy* default. See `VUnit/cosim `_ for reference implementations of bridges and examples. + +.. toctree:: + :hidden: + + ext_string + ext_integer_vector + diff --git a/docs/data_types/integer_array.rst b/docs/data_types/integer_array.rst new file mode 100644 index 000000000..24da1eb42 --- /dev/null +++ b/docs/data_types/integer_array.rst @@ -0,0 +1,8 @@ +.. _integer_array_pkg: + +*integer_array* package +----------------------- + +.. literalinclude:: ../../vunit/vhdl/data_types/src/integer_array_pkg.vhd + :language: vhdl + :lines: 7- \ No newline at end of file diff --git a/docs/data_types/queue.rst b/docs/data_types/queue.rst new file mode 100644 index 000000000..cbec0bc67 --- /dev/null +++ b/docs/data_types/queue.rst @@ -0,0 +1,8 @@ +.. _queue_pkg: + +*queue* package +--------------- + +.. literalinclude:: ../../vunit/vhdl/data_types/src/queue_pkg.vhd + :language: vhdl + :lines: 7- \ No newline at end of file diff --git a/docs/data_types/user_guide.rst b/docs/data_types/user_guide.rst index f952e9a22..017812f00 100644 --- a/docs/data_types/user_guide.rst +++ b/docs/data_types/user_guide.rst @@ -1,35 +1,30 @@ .. _data_types_library: Data Types -========== +########## -Introduction ------------- VUnit comes with a number of convenient data types included: -:queue_t: A queue (fifo) which to which any VHDL primitive data type - can be pushed and popped by serializing the data to bytes - internally. This queue can be used to for example push - expected data from a driver process to a checker process in - a test bench. :ref:`Queue API ` +* **queue_t** (:ref:`Queue API `) + Queue (FIFO) to which any VHDL primitive data type can be pushed and + popped (by serializing data to bytes internally). This queue can be + used to, for example, push expected data from a driver process to a + checker process in a test bench. -:integer_array_t: An dynamic array of integers in up to 3 dimensions. - Supports dynamic append and reshape operations. - Supports reading and writing data to/from *.csv* or *.raw* byte files. - :ref:`Integer array API ` +* **integer_array_t** (:ref:`Integer array API `) + Dynamic array of integers in up to 3 dimensions. Supports dynamic + append and reshape operations, and reading/writing data to/from + *.csv* or *.raw* byte files. -.. _queue_pkg: +.. toctree:: + :hidden: -queue package -------------- -.. literalinclude:: ../../vunit/vhdl/data_types/src/queue_pkg.vhd - :language: vhdl - :lines: 7- + queue + integer_array -.. _integer_array_pkg: +.. _data_types_library:external: -integer_array package ----------------------- -.. literalinclude:: ../../vunit/vhdl/data_types/src/integer_array_pkg.vhd - :language: vhdl - :lines: 7- +External VHDL API +================= + +.. include:: external_api.rst diff --git a/docs/genindex.rst b/docs/genindex.rst new file mode 100644 index 000000000..398e88470 --- /dev/null +++ b/docs/genindex.rst @@ -0,0 +1,6 @@ +.. # This file is a placeholder and will be replaced + +.. _genindex: + +Index +##### diff --git a/docs/index.rst b/docs/index.rst index f04e4347c..7aee1ae99 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -20,8 +20,8 @@ VUnit is an open source unit testing framework for VHDL/SystemVerilog released under the terms of Mozilla Public License, v. 2.0. It features the functionality needed to realize continuous and automated testing of your HDL code. VUnit doesn't replace but rather complements -traditional testing methodologies by supporting a "test early and -often" approach through automation. :ref:`Read more ` +traditional testing methodologies by supporting a *"test early and +often"* approach through automation. :ref:`Read more ` .. image:: vunit_demo.gif @@ -35,3 +35,4 @@ often" approach through automation. :ref:`Read more ` testimonials/testimonials contributing release_notes + genindex From 58ce7605ac1121ac57ff575f299be30c39eac25c Mon Sep 17 00:00:00 2001 From: umarcor Date: Sun, 21 Apr 2019 11:55:03 +0200 Subject: [PATCH 20/79] ghdl_interface: refactor --- tests/unit/test_ghdl_interface.py | 2 +- vunit/sim_if/ghdl.py | 54 +++++++++++++++---------------- 2 files changed, 28 insertions(+), 28 deletions(-) diff --git a/tests/unit/test_ghdl_interface.py b/tests/unit/test_ghdl_interface.py index af8f41c55..9414ccdf1 100644 --- a/tests/unit/test_ghdl_interface.py +++ b/tests/unit/test_ghdl_interface.py @@ -228,7 +228,7 @@ def test_elaborate_e_project(self): self.assertEqual( simif._get_command( # pylint: disable=protected-access - config, join("output_path", "ghdl"), True + config, join("output_path", "ghdl"), True, True, None ), [ join("prefix", "ghdl"), diff --git a/vunit/sim_if/ghdl.py b/vunit/sim_if/ghdl.py index 0abf787a6..489558b19 100644 --- a/vunit/sim_if/ghdl.py +++ b/vunit/sim_if/ghdl.py @@ -234,7 +234,7 @@ def compile_vhdl_file_command(self, source_file): cmd += [source_file.name] return cmd - def _get_command(self, config, output_path, ghdl_e): + def _get_command(self, config, output_path, elaborate_only, ghdl_e, wave_file): """ Return GHDL simulation command """ @@ -251,24 +251,32 @@ def _get_command(self, config, output_path, ghdl_e): "--workdir=%s" % self._project.get_library(config.library_name).directory ] cmd += ["-P%s" % lib.directory for lib in self._project.get_libraries()] + + bin_path = join( + output_path, "%s-%s" % (config.entity_name, config.architecture_name) + ) if self._has_output_flag(): - cmd += [ - "-o", - join( - output_path, - "%s-%s" % (config.entity_name, config.architecture_name), - ), - ] + cmd += ["-o", bin_path] cmd += config.sim_options.get("ghdl.elab_flags", []) cmd += [config.entity_name, config.architecture_name] + sim = config.sim_options.get("ghdl.sim_flags", []) + for name, value in config.generics.items(): + sim += ["-g%s=%s" % (name, value)] + sim += ["--assert-level=%s" % config.vhdl_assert_stop_level] + if config.sim_options.get("disable_ieee_warnings", False): + sim += ["--ieee-asserts=disable"] + + if wave_file: + if self._gtkwave_fmt == "ghw": + sim += ["--wave=%s" % wave_file] + elif self._gtkwave_fmt == "vcd": + sim += ["--vcd=%s" % wave_file] + if not ghdl_e: - cmd += config.sim_options.get("ghdl.sim_flags", []) - for name, value in config.generics.items(): - cmd += ["-g%s=%s" % (name, value)] - cmd += ["--assert-level=%s" % config.vhdl_assert_stop_level] - if config.sim_options.get("disable_ieee_warnings", False): - cmd += ["--ieee-asserts=disable"] + cmd += sim + if elaborate_only: + cmd += ["--no-run"] return cmd @@ -286,25 +294,17 @@ def simulate( # pylint: disable=too-many-locals ghdl_e = elaborate_only and config.sim_options.get("ghdl.elab_e", False) - cmd = self._get_command(config, script_path, ghdl_e) - - if elaborate_only and not ghdl_e: - cmd += ["--no-run"] - - if self._gtkwave_fmt is not None and not ghdl_e: + if self._gtkwave_fmt is not None: data_file_name = join(script_path, "wave.%s" % self._gtkwave_fmt) - if exists(data_file_name): os.remove(data_file_name) - - if self._gtkwave_fmt == "ghw": - cmd += ["--wave=%s" % data_file_name] - elif self._gtkwave_fmt == "vcd": - cmd += ["--vcd=%s" % data_file_name] - else: data_file_name = None + cmd = self._get_command( + config, script_path, elaborate_only, ghdl_e, data_file_name + ) + status = True try: proc = Process(cmd) From 2654e2782181ad130c4a5f8af06ffde235436053 Mon Sep 17 00:00:00 2001 From: umarcor Date: Sun, 21 Apr 2019 11:55:03 +0200 Subject: [PATCH 21/79] ghdl_interface: with ghdl_e, save runtime args to JSON file --- vunit/sim_if/ghdl.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/vunit/sim_if/ghdl.py b/vunit/sim_if/ghdl.py index 489558b19..c733f7033 100644 --- a/vunit/sim_if/ghdl.py +++ b/vunit/sim_if/ghdl.py @@ -8,11 +8,13 @@ Interface for GHDL simulator """ +from pathlib import Path from os.path import exists, join, abspath import os import logging import subprocess import shlex +from json import dump from sys import stdout # To avoid output catched in non-verbose mode from warnings import warn from ..exceptions import CompileError @@ -277,6 +279,23 @@ def _get_command(self, config, output_path, elaborate_only, ghdl_e, wave_file): cmd += sim if elaborate_only: cmd += ["--no-run"] + else: + try: + os.makedirs(output_path, mode=0o777) + except OSError: + pass + with open(join(output_path, "args.json"), "w") as fname: + dump( + { + "bin": str( + Path(output_path) + / ("%s-%s" % (config.entity_name, config.architecture_name)) + ), + "build": cmd[1:], + "sim": sim, + }, + fname, + ) return cmd From 0fab3c88a8cd66bbba1a98c43cef5cd0fe4b0752 Mon Sep 17 00:00:00 2001 From: eine Date: Mon, 2 Mar 2020 09:14:00 +0100 Subject: [PATCH 22/79] ci: update GHDL to v0.37 --- .github/workflows/push.yml | 6 +++--- vunit/sim_if/ghdl.py | 30 ++++++++++++++++++++++++++---- 2 files changed, 29 insertions(+), 7 deletions(-) diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml index 61bd6c2e6..424c36664 100644 --- a/.github/workflows/push.yml +++ b/.github/workflows/push.yml @@ -103,14 +103,14 @@ jobs: if: endsWith( matrix.task, '-ghdl' ) shell: bash run: | - curl -fsSL -o ghdl.zip https://github.com/ghdl/ghdl/releases/download/v0.36/ghdl-0.36-mingw32-mcode.zip + curl -fsSL -o ghdl.zip https://github.com/ghdl/ghdl/releases/download/v0.37/ghdl-0.37-mingw32-mcode.zip 7z x ghdl.zip "-o../ghdl" -y - mv ../ghdl/GHDL/0.36-mingw32-mcode/ ../ghdl-v0.36 + mv ../ghdl/GHDL/0.37-mingw32-mcode/ ../ghdl-v0.37 rm -rf ../ghdl ghdl.zip - name: run job shell: bash run: | - export PATH=$PATH:$(pwd)/../ghdl-v0.36/bin + export PATH=$PATH:$(pwd)/../ghdl-v0.37/bin tox -e py${{ matrix.task }} -- --color=yes deploy: diff --git a/vunit/sim_if/ghdl.py b/vunit/sim_if/ghdl.py index c733f7033..0e70e9f88 100644 --- a/vunit/sim_if/ghdl.py +++ b/vunit/sim_if/ghdl.py @@ -14,6 +14,7 @@ import logging import subprocess import shlex +import re from json import dump from sys import stdout # To avoid output catched in non-verbose mode from warnings import warn @@ -115,6 +116,15 @@ def has_valid_exit_code(self): """ return self._vhdl_standard >= VHDL.STD_2008 + @classmethod + def _get_version_output(cls, prefix): + """ + Get the output of 'ghdl --version' + """ + return subprocess.check_output( + [join(prefix, cls.executable), "--version"] + ).decode() + @classmethod def determine_backend(cls, prefix): """ @@ -125,9 +135,7 @@ def determine_backend(cls, prefix): "llvm code generator": "llvm", "GCC back-end code generator": "gcc", } - output = subprocess.check_output( - [join(prefix, cls.executable), "--version"] - ).decode() + output = cls._get_version_output(prefix) for name, backend in mapping.items(): if name in output: LOGGER.debug("Detected GHDL %s", name) @@ -142,12 +150,26 @@ def determine_backend(cls, prefix): "No known GHDL back-end could be detected from running 'ghdl --version'" ) + @classmethod + def determine_version(cls, prefix): + """ + Determine the GHDL version + """ + return float( + re.match( + r"GHDL ([0-9]*\.[0-9]*).*\(.*\) \[Dunoon edition\]", + cls._get_version_output(prefix), + ).group(1) + ) + @classmethod def supports_vhpi(cls): """ Return if the simulator supports VHPI """ - return cls.determine_backend(cls.find_prefix_from_path()) != "mcode" + return (cls.determine_backend(cls.find_prefix_from_path()) != "mcode") or ( + cls.determine_version(cls.find_prefix_from_path()) > 0.36 + ) def _has_output_flag(self): """ From 1af512851ca588a677481b78295c650d3af4a1bd Mon Sep 17 00:00:00 2001 From: eine Date: Wed, 12 Feb 2020 11:56:41 +0100 Subject: [PATCH 23/79] use pathlib instead of join, dirname, etc. --- docs/conf.py | 6 +- examples/vhdl/vivado/vivado_util.py | 34 +- tests/acceptance/artificial/verilog/run.py | 12 +- tests/acceptance/artificial/vhdl/run.py | 12 +- tests/acceptance/test_artificial.py | 18 +- tests/acceptance/test_dependencies.py | 16 +- tests/acceptance/test_external_run_scripts.py | 94 ++--- tests/lint/test_license.py | 38 +- tests/lint/test_pycodestyle.py | 12 +- tests/lint/test_pylint.py | 4 +- tests/lint/test_readme.py | 4 +- tests/unit/test_activehdl_interface.py | 70 ++-- tests/unit/test_configuration.py | 12 +- tests/unit/test_csv_logs.py | 6 +- tests/unit/test_database.py | 4 +- tests/unit/test_ghdl_interface.py | 26 +- tests/unit/test_incisive_interface.py | 335 ++++++++++-------- tests/unit/test_modelsim_interface.py | 90 ++--- tests/unit/test_ostools.py | 10 +- tests/unit/test_project.py | 14 +- tests/unit/test_rivierapro_interface.py | 78 ++-- tests/unit/test_simulator_interface.py | 14 +- tests/unit/test_test_bench.py | 96 +++-- tests/unit/test_test_bench_list.py | 22 +- tests/unit/test_test_report.py | 12 +- tests/unit/test_test_runner.py | 25 +- tests/unit/test_test_suites.py | 10 +- tests/unit/test_ui.py | 79 +++-- tests/unit/test_verilog_parser.py | 18 +- tests/unit/test_verilog_preprocessor.py | 22 +- tools/build_docs.py | 4 +- tools/create_release_notes.py | 15 +- tools/docs_utils.py | 40 ++- tools/release.py | 30 +- vunit/__init__.py | 4 +- vunit/configuration.py | 4 +- vunit/csv_logs.py | 4 +- vunit/database.py | 8 +- vunit/ostools.py | 18 +- vunit/parsing/verilog/parser.py | 8 +- vunit/parsing/verilog/preprocess.py | 6 +- vunit/project.py | 12 +- vunit/sim_if/__init__.py | 19 +- vunit/sim_if/activehdl.py | 71 ++-- vunit/sim_if/ghdl.py | 36 +- vunit/sim_if/incisive.py | 75 ++-- vunit/sim_if/modelsim.py | 30 +- vunit/sim_if/rivierapro.py | 56 +-- vunit/sim_if/vsim_simulator_mixin.py | 47 +-- vunit/source_file.py | 3 +- vunit/test/bench.py | 4 +- vunit/test/report.py | 4 +- vunit/test/runner.py | 23 +- vunit/test/suites.py | 4 +- vunit/ui/__init__.py | 53 +-- vunit/ui/packagefacade.py | 8 +- vunit/ui/results.py | 27 +- .../vhdl/check/tools/generate_check_equal.py | 10 +- .../vhdl/check/tools/generate_check_match.py | 10 +- vunit/vhdl/com/run.py | 18 +- vunit/vhdl/dictionary/run.py | 12 +- vunit/vhdl/logging/run.py | 13 +- vunit/vhdl/path/run.py | 13 +- vunit/vhdl/random/run.py | 14 +- vunit/vhdl/run/run.py | 12 +- vunit/vhdl/string_ops/run.py | 12 +- vunit/vhdl/verification_components/run.py | 28 +- vunit/vhdl_parser.py | 4 +- vunit/vivado/vivado.py | 21 +- vunit/vunit_cli.py | 4 +- 70 files changed, 1062 insertions(+), 915 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index a771d03e1..4787dada8 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -2,7 +2,7 @@ import os import sys -from os.path import join +from pathlib import Path # blog_title = u"VUnit Blog" # blog_baseurl = "http://vunit.github.io" @@ -70,9 +70,9 @@ html_static_path = ["_static"] -html_logo = join(html_static_path[0], "VUnit_logo_420x420.png") +html_logo = str(Path(html_static_path[0]) / "VUnit_logo_420x420.png") -html_favicon = join(html_static_path[0], "vunit.ico") +html_favicon = str(Path(html_static_path[0]) / "vunit.ico") # Output file base name for HTML help builder. htmlhelp_basename = "vunitdoc" diff --git a/examples/vhdl/vivado/vivado_util.py b/examples/vhdl/vivado/vivado_util.py index 6ef8c5219..ac11b6e27 100644 --- a/examples/vhdl/vivado/vivado_util.py +++ b/examples/vhdl/vivado/vivado_util.py @@ -5,7 +5,7 @@ # Copyright (c) 2014-2020, Lars Asplund lars.anders.asplund@gmail.com import sys -from os.path import join, exists, abspath, dirname +from pathlib import Path from vunit.sim_if.factory import SIMULATOR_FACTORY from vunit.vivado import ( run_vivado, @@ -19,14 +19,16 @@ def add_vivado_ip(vunit_obj, output_path, project_file): Add vivado (and compile if necessary) vivado ip to vunit project. """ - if not exists(project_file): + if not Path(project_file).exists(): print("Could not find vivado project %s" % project_file) sys.exit(1) - standard_library_path = join(output_path, "standard") + opath = Path(output_path) + + standard_library_path = str(opath / "standard") compile_standard_libraries(vunit_obj, standard_library_path) - project_ip_path = join(output_path, "project_ip") + project_ip_path = str(opath / "project_ip") add_project_ip(vunit_obj, project_file, project_ip_path) @@ -34,12 +36,15 @@ def compile_standard_libraries(vunit_obj, output_path): """ Compile Xilinx standard libraries using Vivado TCL command """ - done_token = join(output_path, "all_done.txt") + done_token = str(Path(output_path) / "all_done.txt") simulator_class = SIMULATOR_FACTORY.select_simulator() - if not exists(done_token): - print("Compiling standard libraries into %s ..." % abspath(output_path)) + if not Path(done_token).exists(): + print( + "Compiling standard libraries into %s ..." + % str(Path(output_path).resolve()) + ) simname = simulator_class.name # Vivado calls rivierapro for riviera @@ -47,7 +52,7 @@ def compile_standard_libraries(vunit_obj, output_path): simname = "riviera" run_vivado( - join(dirname(__file__), "tcl", "compile_standard_libs.tcl"), + str(Path(__file__).parent / "tcl" / "compile_standard_libs.tcl"), tcl_args=[ simname, simulator_class.find_prefix().replace("\\", "/"), @@ -57,12 +62,13 @@ def compile_standard_libraries(vunit_obj, output_path): else: print( - "Standard libraries already exists in %s, skipping" % abspath(output_path) + "Standard libraries already exists in %s, skipping" + % str(Path(output_path).resolve()) ) for library_name in ["unisim", "unimacro", "unifast", "secureip", "xpm"]: - path = join(output_path, library_name) - if exists(path): + path = str(Path(output_path) / library_name) + if Path(path).exists(): vunit_obj.add_external_library(library_name, path) with open(done_token, "w") as fptr: @@ -79,16 +85,16 @@ def add_project_ip(vunit_obj, project_file, output_path, vivado_path=None, clean returns the list of SourceFile objects added """ - compile_order_file = join(output_path, "compile_order.txt") + compile_order_file = str(Path(output_path) / "compile_order.txt") - if clean or not exists(compile_order_file): + if clean or not Path(compile_order_file).exists(): create_compile_order_file( project_file, compile_order_file, vivado_path=vivado_path ) else: print( "Vivado project Compile order already exists, re-using: %s" - % abspath(compile_order_file) + % str(Path(compile_order_file).resolve()) ) return add_from_compile_order_file(vunit_obj, compile_order_file) diff --git a/tests/acceptance/artificial/verilog/run.py b/tests/acceptance/artificial/verilog/run.py index 7d36e8202..2852d418e 100644 --- a/tests/acceptance/artificial/verilog/run.py +++ b/tests/acceptance/artificial/verilog/run.py @@ -4,14 +4,14 @@ # # Copyright (c) 2014-2020, Lars Asplund lars.anders.asplund@gmail.com -from os.path import join, dirname +from pathlib import Path from vunit.verilog import VUnit -root = dirname(__file__) +ROOT = Path(__file__).parent VU = VUnit.from_argv() LIB = VU.add_library("lib") -LIB.add_source_files(join(root, "*.sv"), defines={"DEFINE_FROM_RUN_PY": ""}) +LIB.add_source_files(ROOT / "*.sv", defines={"DEFINE_FROM_RUN_PY": ""}) def configure_tb_with_parameter_config(): @@ -35,7 +35,7 @@ def configure_tb_with_parameter_config(): ) def post_check(output_path): - with open(join(output_path, "post_check.txt"), "r") as fptr: + with (Path(output_path) / "post_check.txt").open("r") as fptr: return fptr.read() == "Test 4 was here" tests[4].add_config( @@ -49,7 +49,7 @@ def post_check(output_path): def configure_tb_same_sim_all_pass(ui): def post_check(output_path): - with open(join(output_path, "post_check.txt"), "r") as fptr: + with (Path(output_path) / "post_check.txt").open("r") as fptr: return fptr.read() == "Test 3 was here" module = ui.library("lib").module("tb_same_sim_all_pass") @@ -59,6 +59,6 @@ def post_check(output_path): configure_tb_with_parameter_config() configure_tb_same_sim_all_pass(VU) LIB.module("tb_other_file_tests").scan_tests_from_file( - join(root, "other_file_tests.sv") + str(ROOT / "other_file_tests.sv") ) VU.main() diff --git a/tests/acceptance/artificial/vhdl/run.py b/tests/acceptance/artificial/vhdl/run.py index 4c2ecb47a..274d10efb 100644 --- a/tests/acceptance/artificial/vhdl/run.py +++ b/tests/acceptance/artificial/vhdl/run.py @@ -4,14 +4,14 @@ # # Copyright (c) 2014-2020, Lars Asplund lars.anders.asplund@gmail.com -from os.path import join, dirname +from pathlib import Path from vunit import VUnit -root = dirname(__file__) +ROOT = Path(__file__).parent VU = VUnit.from_argv() LIB = VU.add_library("lib") -LIB.add_source_files(join(root, "*.vhd")) +LIB.add_source_files(ROOT / "*.vhd") def configure_tb_with_generic_config(): @@ -33,7 +33,7 @@ def configure_tb_with_generic_config(): ) def post_check(output_path): - with open(join(output_path, "post_check.txt"), "r") as fptr: + with (Path(output_path) / "post_check.txt").open("r") as fptr: return "Test 4 was here" in fptr.read() tests[4].add_config( @@ -45,7 +45,7 @@ def post_check(output_path): def configure_tb_same_sim_all_pass(ui): def post_check(output_path): - with open(join(output_path, "post_check.txt"), "r") as fptr: + with (Path(output_path) / "post_check.txt").open("r") as fptr: return "Test 3 was here" in fptr.read() ent = ui.library("lib").entity("tb_same_sim_all_pass") @@ -93,6 +93,6 @@ def configure_tb_assert_stop_level(ui): LIB.entity("tb_no_generic_override").set_generic("g_val", False) LIB.entity("tb_ieee_warning").test("pass").set_sim_option("disable_ieee_warnings", True) LIB.entity("tb_other_file_tests").scan_tests_from_file( - join(root, "other_file_tests.vhd") + str(ROOT / "other_file_tests.vhd") ) VU.main() diff --git a/tests/acceptance/test_artificial.py b/tests/acceptance/test_artificial.py index bc30cc0cf..b6917f1f8 100644 --- a/tests/acceptance/test_artificial.py +++ b/tests/acceptance/test_artificial.py @@ -9,13 +9,15 @@ """ import unittest -from os.path import join, dirname +from pathlib import Path from os import environ from subprocess import call import sys from tests.common import check_report from vunit.sim_if.common import has_simulator, simulator_is +ROOT = Path(__file__).parent + @unittest.skipUnless(has_simulator(), "Requires simulator") class TestVunitArtificial(unittest.TestCase): @@ -26,18 +28,14 @@ class TestVunitArtificial(unittest.TestCase): def setUp(self): if simulator_is("activehdl"): - self.output_path = join(dirname(__file__), "artificial_out") + self.output_path = str(ROOT / "artificial_out") else: # Spaces in path intentional to verify that it is supported - self.output_path = join(dirname(__file__), "artificial _out") + self.output_path = str(ROOT / "artificial _out") - self.report_file = join(self.output_path, "xunit.xml") - self.artificial_run_vhdl = join( - dirname(__file__), "artificial", "vhdl", "run.py" - ) - self.artificial_run_verilog = join( - dirname(__file__), "artificial", "verilog", "run.py" - ) + self.report_file = str(Path(self.output_path) / "xunit.xml") + self.artificial_run_vhdl = str(ROOT / "artificial" / "vhdl" / "run.py") + self.artificial_run_verilog = str(ROOT / "artificial" / "verilog" / "run.py") @unittest.skipUnless( simulator_is("modelsim", "rivierapro"), diff --git a/tests/acceptance/test_dependencies.py b/tests/acceptance/test_dependencies.py index 7099a5d9c..4a89ab8b6 100644 --- a/tests/acceptance/test_dependencies.py +++ b/tests/acceptance/test_dependencies.py @@ -11,9 +11,11 @@ import unittest -from os.path import join, dirname +from pathlib import Path from vunit import VUnit +ROOT = Path(__file__).parent + class TestDependencies(unittest.TestCase): """ @@ -21,8 +23,8 @@ class TestDependencies(unittest.TestCase): """ def setUp(self): - self.data_path = join(dirname(__file__), "dependencies") - self.output_path = join(dirname(__file__), "dependencies_vunit_out") + self.data_path = str(ROOT / "dependencies") + self.output_path = str(ROOT / "dependencies_vunit_out") def test_package_body_dependencies(self): """ @@ -36,9 +38,11 @@ def run(value): Utility function to first run with pkg_body1 then pkg_body2 """ - tb_pkg_file_name = join(self.data_path, "tb_pkg.vhd") - pkg_file_name = join(self.data_path, "pkg.vhd") - pkg_body_file_name = join(self.data_path, "pkg_body%i.vhd" % value) + dpath = Path(self.data_path) + + tb_pkg_file_name = str(dpath / "tb_pkg.vhd") + pkg_file_name = str(dpath / "pkg.vhd") + pkg_body_file_name = str(dpath / ("pkg_body%i.vhd" % value)) argv = ["--output-path=%s" % self.output_path, "-v"] if value == 1: diff --git a/tests/acceptance/test_external_run_scripts.py b/tests/acceptance/test_external_run_scripts.py index 90e979bf0..3fdba5a69 100644 --- a/tests/acceptance/test_external_run_scripts.py +++ b/tests/acceptance/test_external_run_scripts.py @@ -9,15 +9,17 @@ """ import unittest +from pathlib import Path from os import environ -from os.path import join, dirname from subprocess import call import sys from tests.common import check_report -from vunit import ROOT +from vunit import ROOT as RSTR from vunit.builtins import VHDL_PATH from vunit.sim_if.common import has_simulator, simulator_is, simulator_check +ROOT = Path(RSTR) + def simulator_supports_verilog(): """ @@ -34,15 +36,15 @@ class TestExternalRunScripts(unittest.TestCase): """ def test_vhdl_uart_example_project(self): - self.check(join(ROOT, "examples", "vhdl", "uart", "run.py")) + self.check(ROOT / "examples" / "vhdl" / "uart" / "run.py") @unittest.skipUnless(simulator_supports_verilog(), "Verilog") def test_verilog_uart_example_project(self): - self.check(join(ROOT, "examples", "verilog", "uart", "run.py")) + self.check(ROOT / "examples" / "verilog" / "uart" / "run.py") @unittest.skipUnless(simulator_supports_verilog(), "Verilog") def test_verilog_ams_example(self): - self.check(join(ROOT, "examples", "verilog", "verilog_ams", "run.py")) + self.check(ROOT / "examples" / "verilog" / "verilog_ams" / "run.py") check_report( self.report_file, [ @@ -52,10 +54,10 @@ def test_verilog_ams_example(self): ) def test_vhdl_logging_example_project(self): - self.check(join(ROOT, "examples", "vhdl", "logging", "run.py")) + self.check(ROOT / "examples" / "vhdl" / "logging" / "run.py") def test_vhdl_run_example_project(self): - self.check(join(ROOT, "examples", "vhdl", "run", "run.py"), exit_code=1) + self.check(ROOT / "examples" / "vhdl" / "run" / "run.py", exit_code=1) check_report( self.report_file, [ @@ -98,7 +100,7 @@ def test_vhdl_run_example_project(self): def test_vhdl_third_party_integration_example_project(self): self.check( - join(ROOT, "examples", "vhdl", "third_party_integration", "run.py"), + ROOT / "examples" / "vhdl" / "third_party_integration" / "run.py", exit_code=1, ) check_report( @@ -117,10 +119,10 @@ def test_vhdl_third_party_integration_example_project(self): ) def test_vhdl_check_example_project(self): - self.check(join(ROOT, "examples", "vhdl", "check", "run.py")) + self.check(ROOT / "examples" / "vhdl" / "check" / "run.py") def test_vhdl_generate_tests_example_project(self): - self.check(join(ROOT, "examples", "vhdl", "generate_tests", "run.py")) + self.check(ROOT / "examples" / "vhdl" / "generate_tests" / "run.py") check_report( self.report_file, [ @@ -137,7 +139,7 @@ def test_vhdl_generate_tests_example_project(self): ) def test_vhdl_composite_generics_example_project(self): - self.check(join(ROOT, "examples", "vhdl", "composite_generics", "run.py")) + self.check(ROOT / "examples" / "vhdl" / "composite_generics" / "run.py") check_report( self.report_file, [ @@ -150,19 +152,19 @@ def test_vhdl_composite_generics_example_project(self): simulator_is("ghdl"), "Support complex JSON strings as generic" ) def test_vhdl_json4vhdl_example_project(self): - self.check(join(ROOT, "examples", "vhdl", "json4vhdl", "run.py")) + self.check(ROOT / "examples" / "vhdl" / "json4vhdl" / "run.py") def test_vhdl_array_example_project(self): - self.check(join(ROOT, "examples", "vhdl", "array", "run.py")) + self.check(ROOT / "examples" / "vhdl" / "array" / "run.py") def test_vhdl_array_axis_vcs_example_project(self): - self.check(join(ROOT, "examples", "vhdl", "array_axis_vcs", "run.py")) + self.check(ROOT / "examples" / "vhdl" / "array_axis_vcs" / "run.py") def test_vhdl_axi_dma_example_project(self): - self.check(join(ROOT, "examples", "vhdl", "axi_dma", "run.py")) + self.check(ROOT / "examples" / "vhdl" / "axi_dma" / "run.py") def test_vhdl_user_guide_example_project(self): - self.check(join(ROOT, "examples", "vhdl", "user_guide", "run.py"), exit_code=1) + self.check(ROOT / "examples" / "vhdl" / "user_guide" / "run.py", exit_code=1) check_report( self.report_file, [ @@ -174,9 +176,7 @@ def test_vhdl_user_guide_example_project(self): @unittest.skipUnless(simulator_supports_verilog(), "Verilog") def test_verilog_user_guide_example_project(self): - self.check( - join(ROOT, "examples", "verilog", "user_guide", "run.py"), exit_code=1 - ) + self.check(ROOT / "examples" / "verilog" / "user_guide" / "run.py", exit_code=1) check_report( self.report_file, [ @@ -194,85 +194,85 @@ def test_verilog_user_guide_example_project(self): ) def test_vhdl_com_example_project(self): - self.check(join(ROOT, "examples", "vhdl", "com", "run.py")) + self.check(ROOT / "examples" / "vhdl" / "com" / "run.py") def test_array_vhdl_2008(self): - self.check(join(VHDL_PATH, "array", "run.py")) + self.check(VHDL_PATH / "array" / "run.py") def test_data_types_vhdl_2008(self): - self.check(join(VHDL_PATH, "data_types", "run.py")) + self.check(VHDL_PATH / "data_types" / "run.py") def test_data_types_vhdl_2002(self): - self.check(join(VHDL_PATH, "data_types", "run.py"), vhdl_standard="2002") + self.check(VHDL_PATH / "data_types" / "run.py", vhdl_standard="2002") def test_data_types_vhdl_93(self): - self.check(join(VHDL_PATH, "data_types", "run.py"), vhdl_standard="93") + self.check(VHDL_PATH / "data_types" / "run.py", vhdl_standard="93") def test_random_vhdl_2008(self): - self.check(join(VHDL_PATH, "random", "run.py")) + self.check(VHDL_PATH / "random" / "run.py") def test_check_vhdl_2008(self): - self.check(join(VHDL_PATH, "check", "run.py")) + self.check(VHDL_PATH / "check" / "run.py") def test_check_vhdl_2002(self): - self.check(join(VHDL_PATH, "check", "run.py"), vhdl_standard="2002") + self.check(VHDL_PATH / "check" / "run.py", vhdl_standard="2002") def test_check_vhdl_93(self): - self.check(join(VHDL_PATH, "check", "run.py"), vhdl_standard="93") + self.check(VHDL_PATH / "check" / "run.py", vhdl_standard="93") def test_logging_vhdl_2008(self): - self.check(join(VHDL_PATH, "logging", "run.py")) + self.check(VHDL_PATH / "logging" / "run.py") def test_logging_vhdl_2002(self): - self.check(join(VHDL_PATH, "logging", "run.py"), vhdl_standard="2002") + self.check(VHDL_PATH / "logging" / "run.py", vhdl_standard="2002") def test_logging_vhdl_93(self): - self.check(join(VHDL_PATH, "logging", "run.py"), vhdl_standard="93") + self.check(VHDL_PATH / "logging" / "run.py", vhdl_standard="93") def test_run_vhdl_2008(self): - self.check(join(VHDL_PATH, "run", "run.py")) + self.check(VHDL_PATH / "run" / "run.py") def test_run_vhdl_2002(self): - self.check(join(VHDL_PATH, "run", "run.py"), vhdl_standard="2002") + self.check(VHDL_PATH / "run" / "run.py", vhdl_standard="2002") def test_run_vhdl_93(self): - self.check(join(VHDL_PATH, "run", "run.py"), vhdl_standard="93") + self.check(VHDL_PATH / "run" / "run.py", vhdl_standard="93") def test_string_ops_vhdl_2008(self): - self.check(join(VHDL_PATH, "string_ops", "run.py")) + self.check(VHDL_PATH / "string_ops" / "run.py") def test_string_ops_vhdl_2002(self): - self.check(join(VHDL_PATH, "string_ops", "run.py"), vhdl_standard="2002") + self.check(VHDL_PATH / "string_ops" / "run.py", vhdl_standard="2002") def test_string_ops_vhdl_93(self): - self.check(join(VHDL_PATH, "string_ops", "run.py"), vhdl_standard="93") + self.check(VHDL_PATH / "string_ops" / "run.py", vhdl_standard="93") def test_dictionary_vhdl_2008(self): - self.check(join(VHDL_PATH, "dictionary", "run.py")) + self.check(VHDL_PATH / "dictionary" / "run.py") def test_dictionary_vhdl_2002(self): - self.check(join(VHDL_PATH, "dictionary", "run.py"), vhdl_standard="2002") + self.check(VHDL_PATH / "dictionary" / "run.py", vhdl_standard="2002") def test_dictionary_vhdl_93(self): - self.check(join(VHDL_PATH, "dictionary", "run.py"), vhdl_standard="93") + self.check(VHDL_PATH / "dictionary" / "run.py", vhdl_standard="93") def test_path_vhdl_2008(self): - self.check(join(VHDL_PATH, "path", "run.py")) + self.check(VHDL_PATH / "path" / "run.py") def test_path_vhdl_2002(self): - self.check(join(VHDL_PATH, "path", "run.py"), vhdl_standard="2002") + self.check(VHDL_PATH / "path" / "run.py", vhdl_standard="2002") def test_path_vhdl_93(self): - self.check(join(VHDL_PATH, "path", "run.py"), vhdl_standard="93") + self.check(VHDL_PATH / "path" / "run.py", vhdl_standard="93") def test_com_vhdl_2008(self): - self.check(join(VHDL_PATH, "com", "run.py")) + self.check(VHDL_PATH / "com" / "run.py") def setUp(self): - self.output_path = join(dirname(__file__), "external_run_out") - self.report_file = join(self.output_path, "xunit.xml") + self.output_path = str(Path(__file__).parent / "external_run_out") + self.report_file = str(Path(self.output_path) / "xunit.xml") - def check(self, run_file, args=None, vhdl_standard="2008", exit_code=0): + def check(self, run_file: Path, args=None, vhdl_standard="2008", exit_code=0): """ Run external run file and verify exit code """ diff --git a/tests/lint/test_license.py b/tests/lint/test_license.py index 33afcc374..8e90c687b 100644 --- a/tests/lint/test_license.py +++ b/tests/lint/test_license.py @@ -10,14 +10,17 @@ import unittest from warnings import simplefilter, catch_warnings -from os.path import join, splitext, abspath, commonprefix +from pathlib import Path +from os.path import commonprefix from os import walk import re -from vunit import ROOT +from vunit import ROOT as RSTR from vunit.builtins import VHDL_PATH from vunit import ostools from vunit.about import license_text +ROOT = Path(RSTR) + RE_LICENSE_NOTICE = re.compile( r"(?P#|--|//) This Source Code Form is subject to the terms of the Mozilla Public" + "\n" @@ -48,13 +51,13 @@ def test_that_a_valid_license_exists_in_source_files_and_that_global_licensing_i for file_name in find_licensed_files(): code = ostools.read_file(file_name) self._check_license(code, file_name) - if splitext(file_name)[1] in (".vhd", ".vhdl", ".v", ".sv"): + if Path(file_name).suffix in (".vhd", ".vhdl", ".v", ".sv"): self._check_no_trailing_whitespace(code, file_name) def test_that_license_file_matches_vunit_license_text(self): with catch_warnings(): simplefilter("ignore", category=DeprecationWarning) - with open(join(ROOT, "LICENSE.txt"), "rU") as lic: + with (ROOT / "LICENSE.txt").open("rU") as lic: self.assertEqual(lic.read(), license_text()) def _check_license(self, code, file_name): @@ -128,34 +131,37 @@ def find_licensed_files(): Return all licensed files """ licensed_files = [] - osvvm_directory = abspath(join(VHDL_PATH, "osvvm")) - json4vhdl_directory = abspath(join(VHDL_PATH, "JSON-for-VHDL")) - for root, _, files in walk(ROOT): + for root, _, files in walk(RSTR): for file_name in files: if "preprocessed" in root: continue if "codecs" in root: continue - if root == join(ROOT, "docs"): + if root == str(ROOT / "docs"): continue - if join(ROOT, "venv") in root: + if str(ROOT / "venv") in root: continue - if join(ROOT, ".tox") in root: + if str(ROOT / ".tox") in root: continue - if is_prefix_of(osvvm_directory, abspath(join(root, file_name))): + if is_prefix_of( + (VHDL_PATH / "osvvm").resolve(), (Path(root) / file_name).resolve(), + ): continue - if is_prefix_of(json4vhdl_directory, abspath(join(root, file_name))): + if is_prefix_of( + (VHDL_PATH / "JSON-for-VHDL").resolve(), + (Path(root) / file_name).resolve(), + ): continue - if splitext(file_name)[1] in (".vhd", ".vhdl", ".py", ".v", ".sv"): - licensed_files.append(join(root, file_name)) + if Path(file_name).suffix in (".vhd", ".vhdl", ".py", ".v", ".sv"): + licensed_files.append(str(Path(root) / file_name)) return licensed_files -def is_prefix_of(prefix, of_path): +def is_prefix_of(prefix: Path, of_path: Path): """ Return True if 'prefix' is a prefix of 'of_path' """ - return commonprefix([prefix, of_path]) == prefix + return commonprefix([str(prefix), str(of_path)]) == str(prefix) def main(): diff --git a/tests/lint/test_pycodestyle.py b/tests/lint/test_pycodestyle.py index b730c41ed..9e48feeaf 100644 --- a/tests/lint/test_pycodestyle.py +++ b/tests/lint/test_pycodestyle.py @@ -12,8 +12,10 @@ from subprocess import check_call import sys from glob import glob -from os.path import join -from vunit import ROOT +from pathlib import Path +from vunit import ROOT as RSTR + +ROOT = Path(RSTR) class TestPycodestyle(unittest.TestCase): @@ -43,7 +45,7 @@ def get_files_and_folders(): """ Return all files and folders which shall be arguments to pycodestyle and pylint """ - ret = [join(ROOT, "vunit")] - ret += list(glob(join(ROOT, "*.py"))) - ret += list(glob(join(ROOT, "tools", "*.py"))) + ret = [str(ROOT / "vunit")] + ret += list(glob(str(ROOT / "*.py"))) + ret += list(glob(str(ROOT / "tools" / "*.py"))) return ret diff --git a/tests/lint/test_pylint.py b/tests/lint/test_pylint.py index b9ccdc02d..d71ce3054 100644 --- a/tests/lint/test_pylint.py +++ b/tests/lint/test_pylint.py @@ -11,7 +11,7 @@ import unittest from subprocess import check_call -from os.path import join, dirname +from pathlib import Path import sys from tests.lint.test_pycodestyle import get_files_and_folders @@ -28,7 +28,7 @@ def test_pylint(): sys.executable, "-m", "pylint", - "--rcfile=" + join(dirname(__file__), "pylintrc"), + "--rcfile=" + str(Path(__file__).parent / "pylintrc"), ] + get_files_and_folders() ) diff --git a/tests/lint/test_readme.py b/tests/lint/test_readme.py index a7b5e2f52..16e962d34 100644 --- a/tests/lint/test_readme.py +++ b/tests/lint/test_readme.py @@ -10,7 +10,7 @@ import unittest from warnings import simplefilter, catch_warnings -from os.path import join +from pathlib import Path from vunit import ROOT from vunit.about import doc @@ -23,5 +23,5 @@ class TestReadMe(unittest.TestCase): def test_that_readme_file_matches_vunit_docstring(self): with catch_warnings(): simplefilter("ignore", category=DeprecationWarning) - with open(join(ROOT, "README.rst"), "rU") as readme: + with Path(ROOT, "README.rst").open("rU") as readme: self.assertEqual(readme.read(), doc()) diff --git a/tests/unit/test_activehdl_interface.py b/tests/unit/test_activehdl_interface.py index 0848df920..d01952fc2 100644 --- a/tests/unit/test_activehdl_interface.py +++ b/tests/unit/test_activehdl_interface.py @@ -10,7 +10,7 @@ import unittest -from os.path import join, dirname, exists +from pathlib import Path import os from shutil import rmtree from unittest import mock @@ -58,18 +58,18 @@ def test_compile_project_vhdl_2008(self, process, check_output): ) simif.compile_project(project) process.assert_any_call( - [join("prefix", "vlib"), "lib", "lib_path"], + [str(Path("prefix") / "vlib"), "lib", "lib_path"], cwd=self.output_path, env=simif.get_env(), ) process.assert_called_with( - [join("prefix", "vmap"), "lib", "lib_path"], + [str(Path("prefix") / "vmap"), "lib", "lib_path"], cwd=self.output_path, env=simif.get_env(), ) check_output.assert_called_once_with( [ - join("prefix", "vcom"), + str(Path("prefix") / "vcom"), "-quiet", "-j", self.output_path, @@ -93,18 +93,18 @@ def test_compile_project_vhdl_2002(self, process, check_output): ) simif.compile_project(project) process.assert_any_call( - [join("prefix", "vlib"), "lib", "lib_path"], + [str(Path("prefix") / "vlib"), "lib", "lib_path"], cwd=self.output_path, env=simif.get_env(), ) process.assert_called_with( - [join("prefix", "vmap"), "lib", "lib_path"], + [str(Path("prefix") / "vmap"), "lib", "lib_path"], cwd=self.output_path, env=simif.get_env(), ) check_output.assert_called_once_with( [ - join("prefix", "vcom"), + str(Path("prefix") / "vcom"), "-quiet", "-j", self.output_path, @@ -128,18 +128,18 @@ def test_compile_project_vhdl_93(self, process, check_output): ) simif.compile_project(project) process.assert_any_call( - [join("prefix", "vlib"), "lib", "lib_path"], + [str(Path("prefix") / "vlib"), "lib", "lib_path"], cwd=self.output_path, env=simif.get_env(), ) process.assert_called_with( - [join("prefix", "vmap"), "lib", "lib_path"], + [str(Path("prefix") / "vmap"), "lib", "lib_path"], cwd=self.output_path, env=simif.get_env(), ) check_output.assert_called_once_with( [ - join("prefix", "vcom"), + str(Path("prefix") / "vcom"), "-quiet", "-j", self.output_path, @@ -162,18 +162,18 @@ def test_compile_project_vhdl_extra_flags(self, process, check_output): source_file.set_compile_option("activehdl.vcom_flags", ["custom", "flags"]) simif.compile_project(project) process.assert_any_call( - [join("prefix", "vlib"), "lib", "lib_path"], + [str(Path("prefix") / "vlib"), "lib", "lib_path"], cwd=self.output_path, env=simif.get_env(), ) process.assert_called_with( - [join("prefix", "vmap"), "lib", "lib_path"], + [str(Path("prefix") / "vmap"), "lib", "lib_path"], cwd=self.output_path, env=simif.get_env(), ) check_output.assert_called_once_with( [ - join("prefix", "vcom"), + str(Path("prefix") / "vcom"), "-quiet", "-j", self.output_path, @@ -190,7 +190,7 @@ def test_compile_project_vhdl_extra_flags(self, process, check_output): @mock.patch("vunit.sim_if.check_output", autospec=True, return_value="") @mock.patch("vunit.sim_if.activehdl.Process", autospec=True) def test_compile_project_verilog(self, process, check_output): - library_cfg = join(self.output_path, "library.cfg") + library_cfg = str(Path(self.output_path) / "library.cfg") simif = ActiveHDLInterface(prefix="prefix", output_path=self.output_path) project = Project() project.add_library("lib", "lib_path") @@ -198,18 +198,18 @@ def test_compile_project_verilog(self, process, check_output): project.add_source_file("file.v", "lib", file_type="verilog") simif.compile_project(project) process.assert_any_call( - [join("prefix", "vlib"), "lib", "lib_path"], + [str(Path("prefix") / "vlib"), "lib", "lib_path"], cwd=self.output_path, env=simif.get_env(), ) process.assert_called_with( - [join("prefix", "vmap"), "lib", "lib_path"], + [str(Path("prefix") / "vmap"), "lib", "lib_path"], cwd=self.output_path, env=simif.get_env(), ) check_output.assert_called_once_with( [ - join("prefix", "vlog"), + str(Path("prefix") / "vlog"), "-quiet", "-lc", library_cfg, @@ -225,7 +225,7 @@ def test_compile_project_verilog(self, process, check_output): @mock.patch("vunit.sim_if.check_output", autospec=True, return_value="") @mock.patch("vunit.sim_if.activehdl.Process", autospec=True) def test_compile_project_system_verilog(self, process, check_output): - library_cfg = join(self.output_path, "library.cfg") + library_cfg = str(Path(self.output_path) / "library.cfg") simif = ActiveHDLInterface(prefix="prefix", output_path=self.output_path) project = Project() project.add_library("lib", "lib_path") @@ -233,18 +233,18 @@ def test_compile_project_system_verilog(self, process, check_output): project.add_source_file("file.sv", "lib", file_type="systemverilog") simif.compile_project(project) process.assert_any_call( - [join("prefix", "vlib"), "lib", "lib_path"], + [str(Path("prefix") / "vlib"), "lib", "lib_path"], cwd=self.output_path, env=simif.get_env(), ) process.assert_called_with( - [join("prefix", "vmap"), "lib", "lib_path"], + [str(Path("prefix") / "vmap"), "lib", "lib_path"], cwd=self.output_path, env=simif.get_env(), ) check_output.assert_called_once_with( [ - join("prefix", "vlog"), + str(Path("prefix") / "vlog"), "-quiet", "-lc", library_cfg, @@ -260,7 +260,7 @@ def test_compile_project_system_verilog(self, process, check_output): @mock.patch("vunit.sim_if.check_output", autospec=True, return_value="") @mock.patch("vunit.sim_if.activehdl.Process", autospec=True) def test_compile_project_verilog_extra_flags(self, process, check_output): - library_cfg = join(self.output_path, "library.cfg") + library_cfg = str(Path(self.output_path) / "library.cfg") simif = ActiveHDLInterface(prefix="prefix", output_path=self.output_path) project = Project() project.add_library("lib", "lib_path") @@ -269,18 +269,18 @@ def test_compile_project_verilog_extra_flags(self, process, check_output): source_file.set_compile_option("activehdl.vlog_flags", ["custom", "flags"]) simif.compile_project(project) process.assert_any_call( - [join("prefix", "vlib"), "lib", "lib_path"], + [str(Path("prefix") / "vlib"), "lib", "lib_path"], cwd=self.output_path, env=simif.get_env(), ) process.assert_called_with( - [join("prefix", "vmap"), "lib", "lib_path"], + [str(Path("prefix") / "vmap"), "lib", "lib_path"], cwd=self.output_path, env=simif.get_env(), ) check_output.assert_called_once_with( [ - join("prefix", "vlog"), + str(Path("prefix") / "vlog"), "-quiet", "-lc", library_cfg, @@ -298,7 +298,7 @@ def test_compile_project_verilog_extra_flags(self, process, check_output): @mock.patch("vunit.sim_if.check_output", autospec=True, return_value="") @mock.patch("vunit.sim_if.activehdl.Process", autospec=True) def test_compile_project_verilog_include(self, process, check_output): - library_cfg = join(self.output_path, "library.cfg") + library_cfg = str(Path(self.output_path) / "library.cfg") simif = ActiveHDLInterface(prefix="prefix", output_path=self.output_path) project = Project() project.add_library("lib", "lib_path") @@ -308,18 +308,18 @@ def test_compile_project_verilog_include(self, process, check_output): ) simif.compile_project(project) process.assert_any_call( - [join("prefix", "vlib"), "lib", "lib_path"], + [str(Path("prefix") / "vlib"), "lib", "lib_path"], cwd=self.output_path, env=simif.get_env(), ) process.assert_called_with( - [join("prefix", "vmap"), "lib", "lib_path"], + [str(Path("prefix") / "vmap"), "lib", "lib_path"], cwd=self.output_path, env=simif.get_env(), ) check_output.assert_called_once_with( [ - join("prefix", "vlog"), + str(Path("prefix") / "vlog"), "-quiet", "-lc", library_cfg, @@ -336,7 +336,7 @@ def test_compile_project_verilog_include(self, process, check_output): @mock.patch("vunit.sim_if.check_output", autospec=True, return_value="") @mock.patch("vunit.sim_if.activehdl.Process", autospec=True) def test_compile_project_verilog_define(self, process, check_output): - library_cfg = join(self.output_path, "library.cfg") + library_cfg = str(Path(self.output_path) / "library.cfg") simif = ActiveHDLInterface(prefix="prefix", output_path=self.output_path) project = Project() project.add_library("lib", "lib_path") @@ -346,18 +346,18 @@ def test_compile_project_verilog_define(self, process, check_output): ) simif.compile_project(project) process.assert_any_call( - [join("prefix", "vlib"), "lib", "lib_path"], + [str(Path("prefix") / "vlib"), "lib", "lib_path"], cwd=self.output_path, env=simif.get_env(), ) process.assert_called_with( - [join("prefix", "vmap"), "lib", "lib_path"], + [str(Path("prefix") / "vmap"), "lib", "lib_path"], cwd=self.output_path, env=simif.get_env(), ) check_output.assert_called_once_with( [ - join("prefix", "vlog"), + str(Path("prefix") / "vlog"), "-quiet", "-lc", library_cfg, @@ -390,7 +390,7 @@ def test_supports_vhdl_package_generics_false(self, find_prefix): self.assertFalse(simif.supports_vhdl_package_generics()) def setUp(self): - self.output_path = join(dirname(__file__), "test_activehdl_out") + self.output_path = str(Path(__file__).parent / "test_activehdl_out") renew_path(self.output_path) self.project = Project() self.cwd = os.getcwd() @@ -398,7 +398,7 @@ def setUp(self): def tearDown(self): os.chdir(self.cwd) - if exists(self.output_path): + if Path(self.output_path).exists(): rmtree(self.output_path) diff --git a/tests/unit/test_configuration.py b/tests/unit/test_configuration.py index da56de9cc..aa6b598ea 100644 --- a/tests/unit/test_configuration.py +++ b/tests/unit/test_configuration.py @@ -13,7 +13,7 @@ import unittest import contextlib -from os.path import join +from pathlib import Path from unittest import mock from tests.common import with_tempdir, create_tempdir from tests.unit.test_test_bench import Entity @@ -61,10 +61,12 @@ def test_does_not_add_tb_path_generic(self): @with_tempdir def test_adds_tb_path_generic(self, tempdir): design_unit_tb_path = Entity( - "tb_entity_without_tb_path", file_name=join(tempdir, "file.vhd") + "tb_entity_without_tb_path", file_name=str(Path(tempdir) / "file.vhd") + ) + tb_path = str(Path(tempdir) / "other_path") + design_unit_tb_path.original_file_name = str( + Path(tb_path) / "original_file.vhd" ) - tb_path = join(tempdir, "other_path") - design_unit_tb_path.original_file_name = join(tb_path, "original_file.vhd") design_unit_tb_path.generic_names = ["runner_cfg", "tb_path"] config_tb_path = Configuration("name", design_unit_tb_path) self.assertEqual( @@ -298,7 +300,7 @@ def _create_config(**kwargs): Helper function to create a config """ with create_tempdir() as tempdir: - design_unit = Entity("tb_entity", file_name=join(tempdir, "file.vhd")) + design_unit = Entity("tb_entity", file_name=str(Path(tempdir) / "file.vhd")) design_unit.generic_names = ["runner_cfg"] yield Configuration("name", design_unit, **kwargs) diff --git a/tests/unit/test_csv_logs.py b/tests/unit/test_csv_logs.py index 34fd1c9fb..cca5bd5a7 100644 --- a/tests/unit/test_csv_logs.py +++ b/tests/unit/test_csv_logs.py @@ -11,7 +11,7 @@ import unittest from shutil import rmtree from os import remove -from os.path import join +from pathlib import Path from tempfile import NamedTemporaryFile, mkdtemp from vunit.csv_logs import CsvLogs @@ -25,8 +25,8 @@ def setUp(self): self._log_files = [] self._all_fields_dir = mkdtemp() self._few_fields_dir = mkdtemp() - self._all_fields_files = join(self._all_fields_dir, "*.csv") - self._few_fields_files = join(self._few_fields_dir, "*.csv") + self._all_fields_files = str(Path(self._all_fields_dir) / "*.csv") + self._few_fields_files = str(Path(self._few_fields_dir) / "*.csv") def make_log(directory, contents): """ diff --git a/tests/unit/test_database.py b/tests/unit/test_database.py index f01bd47ae..f07104bd6 100644 --- a/tests/unit/test_database.py +++ b/tests/unit/test_database.py @@ -9,7 +9,7 @@ """ import unittest -from os.path import join +from pathlib import Path from tests.common import with_tempdir from vunit.database import DataBase, PickledDataBase @@ -26,7 +26,7 @@ class TestDataBase(unittest.TestCase): @staticmethod def create_database(tempdir, new=False): - return DataBase(join(tempdir, "database"), new=new) + return DataBase(str(Path(tempdir) / "database"), new=new) def _test_add_items(self, tempdir): """ diff --git a/tests/unit/test_ghdl_interface.py b/tests/unit/test_ghdl_interface.py index 9414ccdf1..2698f4d2d 100644 --- a/tests/unit/test_ghdl_interface.py +++ b/tests/unit/test_ghdl_interface.py @@ -9,7 +9,7 @@ """ import unittest -from os.path import join, dirname, exists +from pathlib import Path import os from shutil import rmtree from unittest import mock @@ -120,7 +120,7 @@ def test_compile_project_2008(self, check_output): simif.compile_project(project) check_output.assert_called_once_with( [ - join("prefix", "ghdl"), + str(Path("prefix") / "ghdl"), "-a", "--workdir=lib_path", "--work=lib", @@ -146,7 +146,7 @@ def test_compile_project_2002(self, check_output): simif.compile_project(project) check_output.assert_called_once_with( [ - join("prefix", "ghdl"), + str(Path("prefix") / "ghdl"), "-a", "--workdir=lib_path", "--work=lib", @@ -172,7 +172,7 @@ def test_compile_project_93(self, check_output): simif.compile_project(project) check_output.assert_called_once_with( [ - join("prefix", "ghdl"), + str(Path("prefix") / "ghdl"), "-a", "--workdir=lib_path", "--work=lib", @@ -197,7 +197,7 @@ def test_compile_project_extra_flags(self, check_output): simif.compile_project(project) check_output.assert_called_once_with( [ - join("prefix", "ghdl"), + str(Path("prefix") / "ghdl"), "-a", "--workdir=lib_path", "--work=lib", @@ -211,9 +211,9 @@ def test_compile_project_extra_flags(self, check_output): ) def test_elaborate_e_project(self): - design_unit = Entity("tb_entity", file_name=join("tempdir", "file.vhd")) - design_unit.original_file_name = join( - "tempdir", "other_path", "original_file.vhd" + design_unit = Entity("tb_entity", file_name=str(Path("tempdir") / "file.vhd")) + design_unit.original_file_name = str( + Path("tempdir") / "other_path" / "original_file.vhd" ) design_unit.generic_names = ["runner_cfg", "tb_path"] @@ -228,17 +228,17 @@ def test_elaborate_e_project(self): self.assertEqual( simif._get_command( # pylint: disable=protected-access - config, join("output_path", "ghdl"), True, True, None + config, str(Path("output_path") / "ghdl"), True, True, None ), [ - join("prefix", "ghdl"), + str(Path("prefix") / "ghdl"), "-e", "--std=08", "--work=lib", "--workdir=lib_path", "-Plib_path", "-o", - join("output_path", "ghdl", "tb_entity-arch"), + str(Path("output_path") / "ghdl" / "tb_entity-arch"), "tb_entity", "arch", ], @@ -254,7 +254,7 @@ def test_compile_project_verilog_error(self): self.assertRaises(CompileError, simif.compile_project, project) def setUp(self): - self.output_path = join(dirname(__file__), "test_ghdl_interface_out") + self.output_path = str(Path(__file__).parent / "test_ghdl_interface_out") renew_path(self.output_path) self.project = Project() self.cwd = os.getcwd() @@ -262,5 +262,5 @@ def setUp(self): def tearDown(self): os.chdir(self.cwd) - if exists(self.output_path): + if Path(self.output_path).exists(): rmtree(self.output_path) diff --git a/tests/unit/test_incisive_interface.py b/tests/unit/test_incisive_interface.py index c584f991a..7f6829c10 100644 --- a/tests/unit/test_incisive_interface.py +++ b/tests/unit/test_incisive_interface.py @@ -12,7 +12,7 @@ import unittest -from os.path import join, dirname, exists, basename +from pathlib import Path import os from shutil import rmtree from unittest import mock @@ -44,9 +44,9 @@ def test_compile_project_vhdl_2008( "file.vhd", "lib", file_type="vhdl", vhdl_standard=VHDL.standard("2008") ) simif.compile_project(project) - args_file = join(self.output_path, "irun_compile_vhdl_file_lib.args") + args_file = str(Path(self.output_path) / "irun_compile_vhdl_file_lib.args") check_output.assert_called_once_with( - [join("prefix", "irun"), "-f", args_file], env=simif.get_env() + [str(Path("prefix") / "irun"), "-f", args_file], env=simif.get_env() ) self.assertEqual( read_file(args_file).splitlines(), @@ -58,10 +58,11 @@ def test_compile_project_vhdl_2008( "-nowarn DLCVAR", "-v200x -extv200x", "-work work", - '-cdslib "%s"' % join(self.output_path, "cds.lib"), - '-log "%s"' % join(self.output_path, "irun_compile_vhdl_file_lib.log"), + '-cdslib "%s"' % str(Path(self.output_path) / "cds.lib"), + '-log "%s"' + % str(Path(self.output_path) / "irun_compile_vhdl_file_lib.log"), "-quiet", - '-nclibdirname ""', + '-nclibdirname "."', "-makelib lib_path", '"file.vhd"', "-endlib", @@ -69,7 +70,7 @@ def test_compile_project_vhdl_2008( ) self.assertEqual( - read_file(join(self.output_path, "cds.lib")), + read_file(str(Path(self.output_path) / "cds.lib")), """\ ## cds.lib: Defines the locations of compiled libraries. softinclude cds_root_irun/tools/inca/files/cds.lib @@ -98,9 +99,9 @@ def test_compile_project_vhdl_2002( "file.vhd", "lib", file_type="vhdl", vhdl_standard=VHDL.standard("2002") ) simif.compile_project(project) - args_file = join(self.output_path, "irun_compile_vhdl_file_lib.args") + args_file = str(Path(self.output_path) / "irun_compile_vhdl_file_lib.args") check_output.assert_called_once_with( - [join("prefix", "irun"), "-f", args_file], env=simif.get_env() + [str(Path("prefix") / "irun"), "-f", args_file], env=simif.get_env() ) self.assertEqual( read_file(args_file).splitlines(), @@ -112,10 +113,11 @@ def test_compile_project_vhdl_2002( "-nowarn DLCVAR", "-v200x -extv200x", "-work work", - '-cdslib "%s"' % join(self.output_path, "cds.lib"), - '-log "%s"' % join(self.output_path, "irun_compile_vhdl_file_lib.log"), + '-cdslib "%s"' % str(Path(self.output_path) / "cds.lib"), + '-log "%s"' + % str(Path(self.output_path) / "irun_compile_vhdl_file_lib.log"), "-quiet", - '-nclibdirname ""', + '-nclibdirname "."', "-makelib lib_path", '"file.vhd"', "-endlib", @@ -138,9 +140,9 @@ def test_compile_project_vhdl_93( "file.vhd", "lib", file_type="vhdl", vhdl_standard=VHDL.standard("93") ) simif.compile_project(project) - args_file = join(self.output_path, "irun_compile_vhdl_file_lib.args") + args_file = str(Path(self.output_path) / "irun_compile_vhdl_file_lib.args") check_output.assert_called_once_with( - [join("prefix", "irun"), "-f", args_file], env=simif.get_env() + [str(Path("prefix") / "irun"), "-f", args_file], env=simif.get_env() ) self.assertEqual( read_file(args_file).splitlines(), @@ -152,10 +154,11 @@ def test_compile_project_vhdl_93( "-nowarn DLCVAR", "-v93", "-work work", - '-cdslib "%s"' % join(self.output_path, "cds.lib"), - '-log "%s"' % join(self.output_path, "irun_compile_vhdl_file_lib.log"), + '-cdslib "%s"' % str(Path(self.output_path) / "cds.lib"), + '-log "%s"' + % str(Path(self.output_path) / "irun_compile_vhdl_file_lib.log"), "-quiet", - '-nclibdirname ""', + '-nclibdirname "."', "-makelib lib_path", '"file.vhd"', "-endlib", @@ -177,9 +180,9 @@ def test_compile_project_vhdl_extra_flags( source_file = project.add_source_file("file.vhd", "lib", file_type="vhdl") source_file.set_compile_option("incisive.irun_vhdl_flags", ["custom", "flags"]) simif.compile_project(project) - args_file = join(self.output_path, "irun_compile_vhdl_file_lib.args") + args_file = str(Path(self.output_path) / "irun_compile_vhdl_file_lib.args") check_output.assert_called_once_with( - [join("prefix", "irun"), "-f", args_file], env=simif.get_env() + [str(Path("prefix") / "irun"), "-f", args_file], env=simif.get_env() ) self.assertEqual( read_file(args_file).splitlines(), @@ -191,12 +194,13 @@ def test_compile_project_vhdl_extra_flags( "-nowarn DLCVAR", "-v200x -extv200x", "-work work", - '-cdslib "%s"' % join(self.output_path, "cds.lib"), - '-log "%s"' % join(self.output_path, "irun_compile_vhdl_file_lib.log"), + '-cdslib "%s"' % str(Path(self.output_path) / "cds.lib"), + '-log "%s"' + % str(Path(self.output_path) / "irun_compile_vhdl_file_lib.log"), "-quiet", "custom", "flags", - '-nclibdirname ""', + '-nclibdirname "."', "-makelib lib_path", '"file.vhd"', "-endlib", @@ -219,9 +223,9 @@ def test_compile_project_vhdl_hdlvar( write_file("file.vhd", "") project.add_source_file("file.vhd", "lib", file_type="vhdl") simif.compile_project(project) - args_file = join(self.output_path, "irun_compile_vhdl_file_lib.args") + args_file = str(Path(self.output_path) / "irun_compile_vhdl_file_lib.args") check_output.assert_called_once_with( - [join("prefix", "irun"), "-f", args_file], env=simif.get_env() + [str(Path("prefix") / "irun"), "-f", args_file], env=simif.get_env() ) self.assertEqual( read_file(args_file).splitlines(), @@ -233,11 +237,12 @@ def test_compile_project_vhdl_hdlvar( "-nowarn DLCVAR", "-v200x -extv200x", "-work work", - '-cdslib "%s"' % join(self.output_path, "cds.lib"), + '-cdslib "%s"' % str(Path(self.output_path) / "cds.lib"), '-hdlvar "custom_hdlvar"', - '-log "%s"' % join(self.output_path, "irun_compile_vhdl_file_lib.log"), + '-log "%s"' + % str(Path(self.output_path) / "irun_compile_vhdl_file_lib.log"), "-quiet", - '-nclibdirname ""', + '-nclibdirname "."', "-makelib lib_path", '"file.vhd"', "-endlib", @@ -258,9 +263,9 @@ def test_compile_project_verilog( write_file("file.v", "") project.add_source_file("file.v", "lib", file_type="verilog") simif.compile_project(project) - args_file = join(self.output_path, "irun_compile_verilog_file_lib.args") + args_file = str(Path(self.output_path) / "irun_compile_verilog_file_lib.args") check_output.assert_called_once_with( - [join("prefix", "irun"), "-f", args_file], env=simif.get_env() + [str(Path("prefix") / "irun"), "-f", args_file], env=simif.get_env() ) self.assertEqual( read_file(args_file).splitlines(), @@ -272,12 +277,12 @@ def test_compile_project_verilog( "-nowarn DLCPTH", "-nowarn DLCVAR", "-work work", - '-cdslib "%s"' % join(self.output_path, "cds.lib"), + '-cdslib "%s"' % str(Path(self.output_path) / "cds.lib"), '-log "%s"' - % join(self.output_path, "irun_compile_verilog_file_lib.log"), + % str(Path(self.output_path) / "irun_compile_verilog_file_lib.log"), "-quiet", '-incdir "cds_root_irun/tools/spectre/etc/ahdl/"', - '-nclibdirname ""', + '-nclibdirname "."', "-makelib lib", '"file.v"', "-endlib", @@ -298,9 +303,9 @@ def test_compile_project_system_verilog( write_file("file.sv", "") project.add_source_file("file.sv", "lib", file_type="systemverilog") simif.compile_project(project) - args_file = join(self.output_path, "irun_compile_verilog_file_lib.args") + args_file = str(Path(self.output_path) / "irun_compile_verilog_file_lib.args") check_output.assert_called_once_with( - [join("prefix", "irun"), "-f", args_file], env=simif.get_env() + [str(Path("prefix") / "irun"), "-f", args_file], env=simif.get_env() ) self.assertEqual( read_file(args_file).splitlines(), @@ -312,12 +317,12 @@ def test_compile_project_system_verilog( "-nowarn DLCPTH", "-nowarn DLCVAR", "-work work", - '-cdslib "%s"' % join(self.output_path, "cds.lib"), + '-cdslib "%s"' % str(Path(self.output_path) / "cds.lib"), '-log "%s"' - % join(self.output_path, "irun_compile_verilog_file_lib.log"), + % str(Path(self.output_path) / "irun_compile_verilog_file_lib.log"), "-quiet", '-incdir "cds_root_irun/tools/spectre/etc/ahdl/"', - '-nclibdirname ""', + '-nclibdirname "."', "-makelib lib", '"file.sv"', "-endlib", @@ -325,7 +330,7 @@ def test_compile_project_system_verilog( ) self.assertEqual( - read_file(join(self.output_path, "cds.lib")), + read_file(str(Path(self.output_path) / "cds.lib")), """\ ## cds.lib: Defines the locations of compiled libraries. softinclude cds_root_irun/tools/inca/files/cds.lib @@ -355,9 +360,9 @@ def test_compile_project_verilog_extra_flags( "incisive.irun_verilog_flags", ["custom", "flags"] ) simif.compile_project(project) - args_file = join(self.output_path, "irun_compile_verilog_file_lib.args") + args_file = str(Path(self.output_path) / "irun_compile_verilog_file_lib.args") check_output.assert_called_once_with( - [join("prefix", "irun"), "-f", args_file], env=simif.get_env() + [str(Path("prefix") / "irun"), "-f", args_file], env=simif.get_env() ) self.assertEqual( read_file(args_file).splitlines(), @@ -371,12 +376,12 @@ def test_compile_project_verilog_extra_flags( "-work work", "custom", "flags", - '-cdslib "%s"' % join(self.output_path, "cds.lib"), + '-cdslib "%s"' % str(Path(self.output_path) / "cds.lib"), '-log "%s"' - % join(self.output_path, "irun_compile_verilog_file_lib.log"), + % str(Path(self.output_path) / "irun_compile_verilog_file_lib.log"), "-quiet", '-incdir "cds_root_irun/tools/spectre/etc/ahdl/"', - '-nclibdirname ""', + '-nclibdirname "."', "-makelib lib", '"file.v"', "-endlib", @@ -399,9 +404,9 @@ def test_compile_project_verilog_include( "file.v", "lib", file_type="verilog", include_dirs=["include"] ) simif.compile_project(project) - args_file = join(self.output_path, "irun_compile_verilog_file_lib.args") + args_file = str(Path(self.output_path) / "irun_compile_verilog_file_lib.args") check_output.assert_called_once_with( - [join("prefix", "irun"), "-f", args_file], env=simif.get_env() + [str(Path("prefix") / "irun"), "-f", args_file], env=simif.get_env() ) self.assertEqual( read_file(args_file).splitlines(), @@ -413,13 +418,13 @@ def test_compile_project_verilog_include( "-nowarn DLCPTH", "-nowarn DLCVAR", "-work work", - '-cdslib "%s"' % join(self.output_path, "cds.lib"), + '-cdslib "%s"' % str(Path(self.output_path) / "cds.lib"), '-log "%s"' - % join(self.output_path, "irun_compile_verilog_file_lib.log"), + % str(Path(self.output_path) / "irun_compile_verilog_file_lib.log"), "-quiet", '-incdir "include"', '-incdir "cds_root_irun/tools/spectre/etc/ahdl/"', - '-nclibdirname ""', + '-nclibdirname "."', "-makelib lib", '"file.v"', "-endlib", @@ -442,9 +447,9 @@ def test_compile_project_verilog_define( "file.v", "lib", file_type="verilog", defines=dict(defname="defval") ) simif.compile_project(project) - args_file = join(self.output_path, "irun_compile_verilog_file_lib.args") + args_file = str(Path(self.output_path) / "irun_compile_verilog_file_lib.args") check_output.assert_called_once_with( - [join("prefix", "irun"), "-f", args_file], env=simif.get_env() + [str(Path("prefix") / "irun"), "-f", args_file], env=simif.get_env() ) self.assertEqual( read_file(args_file).splitlines(), @@ -456,13 +461,13 @@ def test_compile_project_verilog_define( "-nowarn DLCPTH", "-nowarn DLCVAR", "-work work", - '-cdslib "%s"' % join(self.output_path, "cds.lib"), + '-cdslib "%s"' % str(Path(self.output_path) / "cds.lib"), '-log "%s"' - % join(self.output_path, "irun_compile_verilog_file_lib.log"), + % str(Path(self.output_path) / "irun_compile_verilog_file_lib.log"), "-quiet", '-incdir "cds_root_irun/tools/spectre/etc/ahdl/"', "-define defname=defval", - '-nclibdirname ""', + '-nclibdirname "."', "-makelib lib", '"file.v"', "-endlib", @@ -487,9 +492,9 @@ def test_compile_project_verilog_hdlvar( "file.v", "lib", file_type="verilog", defines=dict(defname="defval") ) simif.compile_project(project) - args_file = join(self.output_path, "irun_compile_verilog_file_lib.args") + args_file = str(Path(self.output_path) / "irun_compile_verilog_file_lib.args") check_output.assert_called_once_with( - [join("prefix", "irun"), "-f", args_file], env=simif.get_env() + [str(Path("prefix") / "irun"), "-f", args_file], env=simif.get_env() ) self.assertEqual( read_file(args_file).splitlines(), @@ -501,14 +506,14 @@ def test_compile_project_verilog_hdlvar( "-nowarn DLCPTH", "-nowarn DLCVAR", "-work work", - '-cdslib "%s"' % join(self.output_path, "cds.lib"), + '-cdslib "%s"' % str(Path(self.output_path) / "cds.lib"), '-hdlvar "custom_hdlvar"', '-log "%s"' - % join(self.output_path, "irun_compile_verilog_file_lib.log"), + % str(Path(self.output_path) / "irun_compile_verilog_file_lib.log"), "-quiet", '-incdir "cds_root_irun/tools/spectre/etc/ahdl/"', "-define defname=defval", - '-nclibdirname ""', + '-nclibdirname "."', "-makelib lib", '"file.v"', "-endlib", @@ -522,7 +527,7 @@ def test_create_cds_lib(self, find_cds_root_irun, find_cds_root_virtuoso): find_cds_root_virtuoso.return_value = None IncisiveInterface(prefix="prefix", output_path=self.output_path) self.assertEqual( - read_file(join(self.output_path, "cds.lib")), + read_file(str(Path(self.output_path) / "cds.lib")), """\ ## cds.lib: Defines the locations of compiled libraries. softinclude cds_root_irun/tools/inca/files/cds.lib @@ -541,7 +546,7 @@ def test_create_cds_lib_virtuoso(self, find_cds_root_irun, find_cds_root_virtuos find_cds_root_virtuoso.return_value = "cds_root_virtuoso" IncisiveInterface(prefix="prefix", output_path=self.output_path) self.assertEqual( - read_file(join(self.output_path, "cds.lib")), + read_file(str(Path(self.output_path) / "cds.lib")), """\ ## cds.lib: Defines the locations of compiled libraries. softinclude cds_root_irun/tools/inca/files/cds.lib @@ -574,20 +579,26 @@ def test_simulate_vhdl( config = make_config() self.assertTrue(simif.simulate("suite_output_path", "test_suite_name", config)) - elaborate_args_file = join( - "suite_output_path", simif.name, "irun_elaborate.args" + elaborate_args_file = str( + Path("suite_output_path") / simif.name / "irun_elaborate.args" + ) + simulate_args_file = str( + Path("suite_output_path") / simif.name / "irun_simulate.args" ) - simulate_args_file = join("suite_output_path", simif.name, "irun_simulate.args") run_command.assert_has_calls( [ mock.call( - [join("prefix", "irun"), "-f", basename(elaborate_args_file)], - cwd=dirname(elaborate_args_file), + [ + str(Path("prefix") / "irun"), + "-f", + Path(elaborate_args_file).name, + ], + cwd=str(Path(elaborate_args_file).parent), env=simif.get_env(), ), mock.call( - [join("prefix", "irun"), "-f", basename(simulate_args_file)], - cwd=dirname(simulate_args_file), + [str(Path("prefix") / "irun"), "-f", Path(simulate_args_file).name], + cwd=str(Path(simulate_args_file).parent), env=simif.get_env(), ), ] @@ -607,10 +618,10 @@ def test_simulate_vhdl( "-ncerror EVBSTR", "-ncerror EVBNAT", "-work work", - '-nclibdirname "%s"' % join(self.output_path, "libraries"), - '-cdslib "%s"' % join(self.output_path, "cds.lib"), + '-nclibdirname "%s"' % str(Path(self.output_path) / "libraries"), + '-cdslib "%s"' % str(Path(self.output_path) / "cds.lib"), '-log "%s"' - % join("suite_output_path", simif.name, "irun_elaborate.log"), + % str(Path("suite_output_path") / simif.name / "irun_elaborate.log"), "-quiet", '-reflib "lib_path"', "-access +r", @@ -632,10 +643,10 @@ def test_simulate_vhdl( "-ncerror EVBSTR", "-ncerror EVBNAT", "-work work", - '-nclibdirname "%s"' % join(self.output_path, "libraries"), - '-cdslib "%s"' % join(self.output_path, "cds.lib"), + '-nclibdirname "%s"' % str(Path(self.output_path) / "libraries"), + '-cdslib "%s"' % str(Path(self.output_path) / "cds.lib"), '-log "%s"' - % join("suite_output_path", simif.name, "irun_simulate.log"), + % str(Path("suite_output_path") / simif.name / "irun_simulate.log"), "-quiet", '-reflib "lib_path"', "-access +r", @@ -666,20 +677,26 @@ def test_simulate_verilog( config = make_config(verilog=True) self.assertTrue(simif.simulate("suite_output_path", "test_suite_name", config)) - elaborate_args_file = join( - "suite_output_path", simif.name, "irun_elaborate.args" + elaborate_args_file = str( + Path("suite_output_path") / simif.name / "irun_elaborate.args" + ) + simulate_args_file = str( + Path("suite_output_path") / simif.name / "irun_simulate.args" ) - simulate_args_file = join("suite_output_path", simif.name, "irun_simulate.args") run_command.assert_has_calls( [ mock.call( - [join("prefix", "irun"), "-f", basename(elaborate_args_file)], - cwd=dirname(elaborate_args_file), + [ + str(Path("prefix") / "irun"), + "-f", + Path(elaborate_args_file).name, + ], + cwd=str(Path(elaborate_args_file).parent), env=simif.get_env(), ), mock.call( - [join("prefix", "irun"), "-f", basename(simulate_args_file)], - cwd=dirname(simulate_args_file), + [str(Path("prefix") / "irun"), "-f", Path(simulate_args_file).name], + cwd=str(Path(simulate_args_file).parent), env=simif.get_env(), ), ] @@ -699,10 +716,10 @@ def test_simulate_verilog( "-ncerror EVBSTR", "-ncerror EVBNAT", "-work work", - '-nclibdirname "%s"' % join(self.output_path, "libraries"), - '-cdslib "%s"' % join(self.output_path, "cds.lib"), + '-nclibdirname "%s"' % str(Path(self.output_path) / "libraries"), + '-cdslib "%s"' % str(Path(self.output_path) / "cds.lib"), '-log "%s"' - % join("suite_output_path", simif.name, "irun_elaborate.log"), + % str(Path("suite_output_path") / simif.name / "irun_elaborate.log"), "-quiet", '-reflib "lib_path"', "-access +r", @@ -724,10 +741,10 @@ def test_simulate_verilog( "-ncerror EVBSTR", "-ncerror EVBNAT", "-work work", - '-nclibdirname "%s"' % join(self.output_path, "libraries"), - '-cdslib "%s"' % join(self.output_path, "cds.lib"), + '-nclibdirname "%s"' % str(Path(self.output_path) / "libraries"), + '-cdslib "%s"' % str(Path(self.output_path) / "cds.lib"), '-log "%s"' - % join("suite_output_path", simif.name, "irun_simulate.log"), + % str(Path("suite_output_path") / simif.name / "irun_simulate.log"), "-quiet", '-reflib "lib_path"', "-access +r", @@ -749,20 +766,26 @@ def test_simulate_extra_flags( sim_options={"incisive.irun_sim_flags": ["custom", "flags"]} ) self.assertTrue(simif.simulate("suite_output_path", "test_suite_name", config)) - elaborate_args_file = join( - "suite_output_path", simif.name, "irun_elaborate.args" + elaborate_args_file = str( + Path("suite_output_path") / simif.name / "irun_elaborate.args" + ) + simulate_args_file = str( + Path("suite_output_path") / simif.name / "irun_simulate.args" ) - simulate_args_file = join("suite_output_path", simif.name, "irun_simulate.args") run_command.assert_has_calls( [ mock.call( - [join("prefix", "irun"), "-f", basename(elaborate_args_file)], - cwd=dirname(elaborate_args_file), + [ + str(Path("prefix") / "irun"), + "-f", + Path(elaborate_args_file).name, + ], + cwd=str(Path(elaborate_args_file).parent), env=simif.get_env(), ), mock.call( - [join("prefix", "irun"), "-f", basename(simulate_args_file)], - cwd=dirname(simulate_args_file), + [str(Path("prefix") / "irun"), "-f", Path(simulate_args_file).name], + cwd=str(Path(simulate_args_file).parent), env=simif.get_env(), ), ] @@ -789,20 +812,26 @@ def test_simulate_generics_and_parameters( verilog=True, generics={"genstr": "genval", "genint": 1, "genbool": True} ) self.assertTrue(simif.simulate("suite_output_path", "test_suite_name", config)) - elaborate_args_file = join( - "suite_output_path", simif.name, "irun_elaborate.args" + elaborate_args_file = str( + Path("suite_output_path") / simif.name / "irun_elaborate.args" + ) + simulate_args_file = str( + Path("suite_output_path") / simif.name / "irun_simulate.args" ) - simulate_args_file = join("suite_output_path", simif.name, "irun_simulate.args") run_command.assert_has_calls( [ mock.call( - [join("prefix", "irun"), "-f", basename(elaborate_args_file)], - cwd=dirname(elaborate_args_file), + [ + str(Path("prefix") / "irun"), + "-f", + Path(elaborate_args_file).name, + ], + cwd=str(Path(elaborate_args_file).parent), env=simif.get_env(), ), mock.call( - [join("prefix", "irun"), "-f", basename(simulate_args_file)], - cwd=dirname(simulate_args_file), + [str(Path("prefix") / "irun"), "-f", Path(simulate_args_file).name], + cwd=str(Path(simulate_args_file).parent), env=simif.get_env(), ), ] @@ -827,20 +856,26 @@ def test_simulate_hdlvar( ) config = make_config() self.assertTrue(simif.simulate("suite_output_path", "test_suite_name", config)) - elaborate_args_file = join( - "suite_output_path", simif.name, "irun_elaborate.args" + elaborate_args_file = str( + Path("suite_output_path") / simif.name / "irun_elaborate.args" + ) + simulate_args_file = str( + Path("suite_output_path") / simif.name / "irun_simulate.args" ) - simulate_args_file = join("suite_output_path", simif.name, "irun_simulate.args") run_command.assert_has_calls( [ mock.call( - [join("prefix", "irun"), "-f", basename(elaborate_args_file)], - cwd=dirname(elaborate_args_file), + [ + str(Path("prefix") / "irun"), + "-f", + Path(elaborate_args_file).name, + ], + cwd=str(Path(elaborate_args_file).parent), env=simif.get_env(), ), mock.call( - [join("prefix", "irun"), "-f", basename(simulate_args_file)], - cwd=dirname(simulate_args_file), + [str(Path("prefix") / "irun"), "-f", Path(simulate_args_file).name], + cwd=str(Path(simulate_args_file).parent), env=simif.get_env(), ), ] @@ -863,14 +898,18 @@ def test_elaborate(self, run_command, find_cds_root_irun, find_cds_root_virtuoso "suite_output_path", "test_suite_name", config, elaborate_only=True ) ) - elaborate_args_file = join( - "suite_output_path", simif.name, "irun_elaborate.args" + elaborate_args_file = str( + Path("suite_output_path") / simif.name / "irun_elaborate.args" ) run_command.assert_has_calls( [ mock.call( - [join("prefix", "irun"), "-f", basename(elaborate_args_file)], - cwd=dirname(elaborate_args_file), + [ + str(Path("prefix") / "irun"), + "-f", + Path(elaborate_args_file).name, + ], + cwd=str(Path(elaborate_args_file).parent), env=simif.get_env(), ) ] @@ -890,10 +929,10 @@ def test_elaborate(self, run_command, find_cds_root_irun, find_cds_root_virtuoso "-ncerror EVBSTR", "-ncerror EVBNAT", "-work work", - '-nclibdirname "%s"' % join(self.output_path, "libraries"), - '-cdslib "%s"' % join(self.output_path, "cds.lib"), + '-nclibdirname "%s"' % str(Path(self.output_path) / "libraries"), + '-cdslib "%s"' % str(Path(self.output_path) / "cds.lib"), '-log "%s"' - % join("suite_output_path", simif.name, "irun_elaborate.log"), + % str(Path("suite_output_path") / simif.name / "irun_elaborate.log"), "-quiet", "-access +r", '-input "@run"', @@ -912,14 +951,18 @@ def test_elaborate_fail( simif = IncisiveInterface(prefix="prefix", output_path=self.output_path) config = make_config() self.assertFalse(simif.simulate("suite_output_path", "test_suite_name", config)) - elaborate_args_file = join( - "suite_output_path", simif.name, "irun_elaborate.args" + elaborate_args_file = str( + Path("suite_output_path") / simif.name / "irun_elaborate.args" ) run_command.assert_has_calls( [ mock.call( - [join("prefix", "irun"), "-f", basename(elaborate_args_file)], - cwd=dirname(elaborate_args_file), + [ + str(Path("prefix") / "irun"), + "-f", + Path(elaborate_args_file).name, + ], + cwd=str(Path(elaborate_args_file).parent), env=simif.get_env(), ) ] @@ -938,20 +981,26 @@ def test_simulate_fail( simif = IncisiveInterface(prefix="prefix", output_path=self.output_path) config = make_config() self.assertFalse(simif.simulate("suite_output_path", "test_suite_name", config)) - elaborate_args_file = join( - "suite_output_path", simif.name, "irun_elaborate.args" + elaborate_args_file = str( + Path("suite_output_path") / simif.name / "irun_elaborate.args" + ) + simulate_args_file = str( + Path("suite_output_path") / simif.name / "irun_simulate.args" ) - simulate_args_file = join("suite_output_path", simif.name, "irun_simulate.args") run_command.assert_has_calls( [ mock.call( - [join("prefix", "irun"), "-f", basename(elaborate_args_file)], - cwd=dirname(elaborate_args_file), + [ + str(Path("prefix") / "irun"), + "-f", + Path(elaborate_args_file).name, + ], + cwd=str(Path(elaborate_args_file).parent), env=simif.get_env(), ), mock.call( - [join("prefix", "irun"), "-f", basename(simulate_args_file)], - cwd=dirname(simulate_args_file), + [str(Path("prefix") / "irun"), "-f", Path(simulate_args_file).name], + cwd=str(Path(simulate_args_file).parent), env=simif.get_env(), ), ] @@ -980,20 +1029,26 @@ def test_simulate_gui( simif.compile_project(project) config = make_config() self.assertTrue(simif.simulate("suite_output_path", "test_suite_name", config)) - elaborate_args_file = join( - "suite_output_path", simif.name, "irun_elaborate.args" + elaborate_args_file = str( + Path("suite_output_path") / simif.name / "irun_elaborate.args" + ) + simulate_args_file = str( + Path("suite_output_path") / simif.name / "irun_simulate.args" ) - simulate_args_file = join("suite_output_path", simif.name, "irun_simulate.args") run_command.assert_has_calls( [ mock.call( - [join("prefix", "irun"), "-f", basename(elaborate_args_file)], - cwd=dirname(elaborate_args_file), + [ + str(Path("prefix") / "irun"), + "-f", + Path(elaborate_args_file).name, + ], + cwd=str(Path(elaborate_args_file).parent), env=simif.get_env(), ), mock.call( - [join("prefix", "irun"), "-f", basename(simulate_args_file)], - cwd=dirname(simulate_args_file), + [str(Path("prefix") / "irun"), "-f", Path(simulate_args_file).name], + cwd=str(Path(simulate_args_file).parent), env=simif.get_env(), ), ] @@ -1012,10 +1067,10 @@ def test_simulate_gui( "-ncerror EVBSTR", "-ncerror EVBNAT", "-work work", - '-nclibdirname "%s"' % join(self.output_path, "libraries"), - '-cdslib "%s"' % join(self.output_path, "cds.lib"), + '-nclibdirname "%s"' % str(Path(self.output_path) / "libraries"), + '-cdslib "%s"' % str(Path(self.output_path) / "cds.lib"), '-log "%s"' - % join("suite_output_path", simif.name, "irun_elaborate.log"), + % str(Path("suite_output_path") / simif.name / "irun_elaborate.log"), "-quiet", '-reflib "lib_path"', "-access +rwc", @@ -1037,10 +1092,10 @@ def test_simulate_gui( "-ncerror EVBSTR", "-ncerror EVBNAT", "-work work", - '-nclibdirname "%s"' % join(self.output_path, "libraries"), - '-cdslib "%s"' % join(self.output_path, "cds.lib"), + '-nclibdirname "%s"' % str(Path(self.output_path) / "libraries"), + '-cdslib "%s"' % str(Path(self.output_path) / "cds.lib"), '-log "%s"' - % join("suite_output_path", simif.name, "irun_simulate.log"), + % str(Path("suite_output_path") / simif.name / "irun_simulate.log"), "-quiet", '-reflib "lib_path"', "-access +rwc", @@ -1050,7 +1105,7 @@ def test_simulate_gui( ) def setUp(self): - self.output_path = join(dirname(__file__), "test_incisive_out") + self.output_path = str(Path(__file__).parent / "test_incisive_out") renew_path(self.output_path) self.project = Project() self.cwd = os.getcwd() @@ -1058,7 +1113,7 @@ def setUp(self): def tearDown(self): os.chdir(self.cwd) - if exists(self.output_path): + if Path(self.output_path).exists(): rmtree(self.output_path) diff --git a/tests/unit/test_modelsim_interface.py b/tests/unit/test_modelsim_interface.py index f01e3ef7f..b30c005ab 100644 --- a/tests/unit/test_modelsim_interface.py +++ b/tests/unit/test_modelsim_interface.py @@ -10,7 +10,7 @@ import unittest -from os.path import join, dirname, exists +from pathlib import Path import os from shutil import rmtree from unittest import mock @@ -39,13 +39,13 @@ def test_compile_project_vhdl_2008(self, process, check_output): "file.vhd", "lib", file_type="vhdl", vhdl_standard=VHDL.standard("2008") ) simif.compile_project(project) - process_args = [join(self.prefix_path, "vlib"), "-unix", "lib_path"] + process_args = [str(Path(self.prefix_path) / "vlib"), "-unix", "lib_path"] process.assert_called_once_with(process_args, env=simif.get_env()) check_args = [ - join(self.prefix_path, "vcom"), + str(Path(self.prefix_path) / "vcom"), "-quiet", "-modelsimini", - join(self.output_path, "modelsim.ini"), + str(Path(self.output_path) / "modelsim.ini"), "-2008", "-work", "lib", @@ -66,13 +66,13 @@ def test_compile_project_vhdl_2002(self, process, check_output): "file.vhd", "lib", file_type="vhdl", vhdl_standard=VHDL.standard("2002") ) simif.compile_project(project) - process_args = [join(self.prefix_path, "vlib"), "-unix", "lib_path"] + process_args = [str(Path(self.prefix_path) / "vlib"), "-unix", "lib_path"] process.assert_called_once_with(process_args, env=simif.get_env()) check_args = [ - join(self.prefix_path, "vcom"), + str(Path(self.prefix_path) / "vcom"), "-quiet", "-modelsimini", - join(self.output_path, "modelsim.ini"), + str(Path(self.output_path) / "modelsim.ini"), "-2002", "-work", "lib", @@ -93,13 +93,13 @@ def test_compile_project_vhdl_93(self, process, check_output): "file.vhd", "lib", file_type="vhdl", vhdl_standard=VHDL.standard("93") ) simif.compile_project(project) - process_args = [join(self.prefix_path, "vlib"), "-unix", "lib_path"] + process_args = [str(Path(self.prefix_path) / "vlib"), "-unix", "lib_path"] process.assert_called_once_with(process_args, env=simif.get_env()) check_args = [ - join(self.prefix_path, "vcom"), + str(Path(self.prefix_path) / "vcom"), "-quiet", "-modelsimini", - join(self.output_path, "modelsim.ini"), + str(Path(self.output_path) / "modelsim.ini"), "-93", "-work", "lib", @@ -119,13 +119,13 @@ def test_compile_project_vhdl_extra_flags(self, process, check_output): source_file = project.add_source_file("file.vhd", "lib", file_type="vhdl") source_file.set_compile_option("modelsim.vcom_flags", ["custom", "flags"]) simif.compile_project(project) - process_args = [join(self.prefix_path, "vlib"), "-unix", "lib_path"] + process_args = [str(Path(self.prefix_path) / "vlib"), "-unix", "lib_path"] process.assert_called_once_with(process_args, env=simif.get_env()) check_args = [ - join(self.prefix_path, "vcom"), + str(Path(self.prefix_path) / "vcom"), "-quiet", "-modelsimini", - join(self.output_path, "modelsim.ini"), + str(Path(self.output_path) / "modelsim.ini"), "custom", "flags", "-2008", @@ -146,13 +146,13 @@ def test_compile_project_verilog(self, process, check_output): write_file("file.v", "") project.add_source_file("file.v", "lib", file_type="verilog") simif.compile_project(project) - process_args = [join(self.prefix_path, "vlib"), "-unix", "lib_path"] + process_args = [str(Path(self.prefix_path) / "vlib"), "-unix", "lib_path"] process.assert_called_once_with(process_args, env=simif.get_env()) check_args = [ - join(self.prefix_path, "vlog"), + str(Path(self.prefix_path) / "vlog"), "-quiet", "-modelsimini", - join(self.output_path, "modelsim.ini"), + str(Path(self.output_path) / "modelsim.ini"), "-work", "lib", "file.v", @@ -172,13 +172,13 @@ def test_compile_project_system_verilog(self, process, check_output): write_file("file.sv", "") project.add_source_file("file.sv", "lib", file_type="systemverilog") simif.compile_project(project) - process_args = [join(self.prefix_path, "vlib"), "-unix", "lib_path"] + process_args = [str(Path(self.prefix_path) / "vlib"), "-unix", "lib_path"] process.assert_called_once_with(process_args, env=simif.get_env()) check_args = [ - join(self.prefix_path, "vlog"), + str(Path(self.prefix_path) / "vlog"), "-quiet", "-modelsimini", - join(self.output_path, "modelsim.ini"), + str(Path(self.output_path) / "modelsim.ini"), "-sv", "-work", "lib", @@ -200,13 +200,13 @@ def test_compile_project_verilog_extra_flags(self, process, check_output): source_file = project.add_source_file("file.v", "lib", file_type="verilog") source_file.set_compile_option("modelsim.vlog_flags", ["custom", "flags"]) simif.compile_project(project) - process_args = [join(self.prefix_path, "vlib"), "-unix", "lib_path"] + process_args = [str(Path(self.prefix_path) / "vlib"), "-unix", "lib_path"] process.assert_called_once_with(process_args, env=simif.get_env()) check_args = [ - join(self.prefix_path, "vlog"), + str(Path(self.prefix_path) / "vlog"), "-quiet", "-modelsimini", - join(self.output_path, "modelsim.ini"), + str(Path(self.output_path) / "modelsim.ini"), "custom", "flags", "-work", @@ -230,13 +230,13 @@ def test_compile_project_verilog_include(self, process, check_output): "file.v", "lib", file_type="verilog", include_dirs=["include"] ) simif.compile_project(project) - process_args = [join(self.prefix_path, "vlib"), "-unix", "lib_path"] + process_args = [str(Path(self.prefix_path) / "vlib"), "-unix", "lib_path"] process.assert_called_once_with(process_args, env=simif.get_env()) check_args = [ - join(self.prefix_path, "vlog"), + str(Path(self.prefix_path) / "vlog"), "-quiet", "-modelsimini", - join(self.output_path, "modelsim.ini"), + str(Path(self.output_path) / "modelsim.ini"), "-work", "lib", "file.v", @@ -259,13 +259,13 @@ def test_compile_project_verilog_define(self, process, check_output): "file.v", "lib", file_type="verilog", defines={"defname": "defval"} ) simif.compile_project(project) - process_args = [join(self.prefix_path, "vlib"), "-unix", "lib_path"] + process_args = [str(Path(self.prefix_path) / "vlib"), "-unix", "lib_path"] process.assert_called_once_with(process_args, env=simif.get_env()) process_args = [ - join(self.prefix_path, "vlog"), + str(Path(self.prefix_path) / "vlog"), "-quiet", "-modelsimini", - join(self.output_path, "modelsim.ini"), + str(Path(self.output_path) / "modelsim.ini"), "-work", "lib", "file.v", @@ -275,10 +275,15 @@ def test_compile_project_verilog_define(self, process, check_output): ] check_output.assert_called_once_with(process_args, env=simif.get_env()) + def _get_inis(self): + return ( + str(Path(self.output_path) / "modelsim.ini"), + str(Path(self.prefix_path) / ".." / "modelsim.ini"), + str(Path(self.test_path) / "my_modelsim.ini"), + ) + def test_copies_modelsim_ini_file_from_install(self): - modelsim_ini = join(self.output_path, "modelsim.ini") - installed_modelsim_ini = join(self.prefix_path, "..", "modelsim.ini") - user_modelsim_ini = join(self.test_path, "my_modelsim.ini") + (modelsim_ini, installed_modelsim_ini, user_modelsim_ini) = self._get_inis() with open(installed_modelsim_ini, "w") as fptr: fptr.write("installed") @@ -293,9 +298,7 @@ def test_copies_modelsim_ini_file_from_install(self): self.assertEqual(fptr.read(), "installed") def test_copies_modelsim_ini_file_from_user(self): - modelsim_ini = join(self.output_path, "modelsim.ini") - installed_modelsim_ini = join(self.prefix_path, "..", "modelsim.ini") - user_modelsim_ini = join(self.test_path, "my_modelsim.ini") + (modelsim_ini, installed_modelsim_ini, user_modelsim_ini) = self._get_inis() with open(installed_modelsim_ini, "w") as fptr: fptr.write("installed") @@ -312,9 +315,7 @@ def test_copies_modelsim_ini_file_from_user(self): self.assertEqual(fptr.read(), "user") def test_overwrites_modelsim_ini_file_from_install(self): - modelsim_ini = join(self.output_path, "modelsim.ini") - installed_modelsim_ini = join(self.prefix_path, "..", "modelsim.ini") - user_modelsim_ini = join(self.test_path, "my_modelsim.ini") + (modelsim_ini, installed_modelsim_ini, user_modelsim_ini) = self._get_inis() with open(modelsim_ini, "w") as fptr: fptr.write("existing") @@ -332,9 +333,7 @@ def test_overwrites_modelsim_ini_file_from_install(self): self.assertEqual(fptr.read(), "installed") def test_overwrites_modelsim_ini_file_from_user(self): - modelsim_ini = join(self.output_path, "modelsim.ini") - installed_modelsim_ini = join(self.prefix_path, "..", "modelsim.ini") - user_modelsim_ini = join(self.test_path, "my_modelsim.ini") + (modelsim_ini, installed_modelsim_ini, user_modelsim_ini) = self._get_inis() with open(modelsim_ini, "w") as fptr: fptr.write("existing") @@ -354,13 +353,14 @@ def test_overwrites_modelsim_ini_file_from_user(self): self.assertEqual(fptr.read(), "user") def setUp(self): - self.test_path = join(dirname(__file__), "test_modelsim_out") - self.output_path = join(self.test_path, "modelsim") - self.prefix_path = join(self.test_path, "prefix", "bin") + self.test_path = str(Path(__file__).parent / "test_modelsim_out") + + self.output_path = str(Path(self.test_path) / "modelsim") + self.prefix_path = str(Path(self.test_path) / "prefix" / "bin") renew_path(self.test_path) renew_path(self.output_path) renew_path(self.prefix_path) - installed_modelsim_ini = join(self.prefix_path, "..", "modelsim.ini") + installed_modelsim_ini = str(Path(self.prefix_path) / ".." / "modelsim.ini") write_file(installed_modelsim_ini, "[Library]") self.project = Project() self.cwd = os.getcwd() @@ -368,5 +368,5 @@ def setUp(self): def tearDown(self): os.chdir(self.cwd) - if exists(self.test_path): + if Path(self.test_path).exists(): rmtree(self.test_path) diff --git a/tests/unit/test_ostools.py b/tests/unit/test_ostools.py index 6b6fb76da..834a4085a 100644 --- a/tests/unit/test_ostools.py +++ b/tests/unit/test_ostools.py @@ -10,8 +10,8 @@ from unittest import TestCase +from pathlib import Path from shutil import rmtree -from os.path import exists, dirname, join, abspath import sys from vunit.ostools import Process, renew_path @@ -22,11 +22,11 @@ class TestOSTools(TestCase): """ def setUp(self): - self.tmp_dir = join(dirname(__file__), "test_ostools_tmp") + self.tmp_dir = str(Path(__file__).parent / "test_ostools_tmp") renew_path(self.tmp_dir) def tearDown(self): - if exists(self.tmp_dir): + if Path(self.tmp_dir).exists(): rmtree(self.tmp_dir) def make_file(self, file_name, contents): @@ -34,7 +34,7 @@ def make_file(self, file_name, contents): Create a file in the temporary directory with contents Returns the absolute path to the file. """ - full_file_name = abspath(join(self.tmp_dir, file_name)) + full_file_name = str((Path(self.tmp_dir) / file_name).resolve()) with open(full_file_name, "w") as outfile: outfile.write(contents) return full_file_name @@ -101,7 +101,7 @@ def test_output_is_parallel(self): self.assertEqual(message, "message") def test_non_utf8_in_output(self): - python_script = join(dirname(__file__), "non_utf8_printer.py") + python_script = str(Path(__file__).parent / "non_utf8_printer.py") output = [] process = Process([sys.executable, python_script]) process.consume_output(output.append) diff --git a/tests/unit/test_project.py b/tests/unit/test_project.py index 7fcfc30c8..3f4691bb8 100644 --- a/tests/unit/test_project.py +++ b/tests/unit/test_project.py @@ -12,9 +12,9 @@ import unittest -from shutil import rmtree -from os.path import join, exists, dirname +from pathlib import Path import os +from shutil import rmtree from time import sleep import itertools from unittest import mock @@ -30,7 +30,7 @@ class TestProject(unittest.TestCase): # pylint: disable=too-many-public-methods """ def setUp(self): - self.output_path = join(dirname(__file__), "test_project_out") + self.output_path = str(Path(__file__).parent / "test_project_out") renew_path(self.output_path) self.project = Project() self.cwd = os.getcwd() @@ -38,7 +38,7 @@ def setUp(self): def tearDown(self): os.chdir(self.cwd) - if exists(self.output_path): + if Path(self.output_path).exists(): rmtree(self.output_path) def test_parses_entity_architecture(self): @@ -196,7 +196,7 @@ def test_multiple_identical_file_names_with_different_path_in_same_library(self) self.project.add_library("lib", "lib_path") a_foo = self.add_source_file( "lib", - join("a", "foo.vhd"), + str(Path("a") / "foo.vhd"), """ entity a_foo is end entity; @@ -205,7 +205,7 @@ def test_multiple_identical_file_names_with_different_path_in_same_library(self) b_foo = self.add_source_file( "lib", - join("b", "foo.vhd"), + str(Path("b") / "foo.vhd"), """ entity b_foo is end entity; @@ -891,7 +891,7 @@ def test_updating_creates_hash_files(self): for source_file in files: self.update(source_file) - self.assertTrue(exists(self.hash_file_name_of(source_file))) + self.assertTrue(Path(self.hash_file_name_of(source_file)).exists()) def test_should_not_recompile_updated_files(self): file1, file2, file3 = self.create_dummy_three_file_project() diff --git a/tests/unit/test_rivierapro_interface.py b/tests/unit/test_rivierapro_interface.py index 2e8cd846d..d0a42eaae 100644 --- a/tests/unit/test_rivierapro_interface.py +++ b/tests/unit/test_rivierapro_interface.py @@ -10,7 +10,7 @@ import unittest -from os.path import join, dirname, exists +from pathlib import Path import os from shutil import rmtree from unittest import mock @@ -37,18 +37,18 @@ def test_compile_project_vhdl_2019(self, process, check_output): ) simif.compile_project(project) process.assert_any_call( - [join("prefix", "vlib"), "lib", "lib_path"], + [str(Path("prefix") / "vlib"), "lib", "lib_path"], cwd=self.output_path, env=simif.get_env(), ) process.assert_called_with( - [join("prefix", "vmap"), "lib", "lib_path"], + [str(Path("prefix") / "vmap"), "lib", "lib_path"], cwd=self.output_path, env=simif.get_env(), ) check_output.assert_called_once_with( [ - join("prefix", "vcom"), + str(Path("prefix") / "vcom"), "-quiet", "-j", self.output_path, @@ -72,18 +72,18 @@ def test_compile_project_vhdl_2008(self, process, check_output): ) simif.compile_project(project) process.assert_any_call( - [join("prefix", "vlib"), "lib", "lib_path"], + [str(Path("prefix") / "vlib"), "lib", "lib_path"], cwd=self.output_path, env=simif.get_env(), ) process.assert_called_with( - [join("prefix", "vmap"), "lib", "lib_path"], + [str(Path("prefix") / "vmap"), "lib", "lib_path"], cwd=self.output_path, env=simif.get_env(), ) check_output.assert_called_once_with( [ - join("prefix", "vcom"), + str(Path("prefix") / "vcom"), "-quiet", "-j", self.output_path, @@ -107,18 +107,18 @@ def test_compile_project_vhdl_2002(self, process, check_output): ) simif.compile_project(project) process.assert_any_call( - [join("prefix", "vlib"), "lib", "lib_path"], + [str(Path("prefix") / "vlib"), "lib", "lib_path"], cwd=self.output_path, env=simif.get_env(), ) process.assert_called_with( - [join("prefix", "vmap"), "lib", "lib_path"], + [str(Path("prefix") / "vmap"), "lib", "lib_path"], cwd=self.output_path, env=simif.get_env(), ) check_output.assert_called_once_with( [ - join("prefix", "vcom"), + str(Path("prefix") / "vcom"), "-quiet", "-j", self.output_path, @@ -142,18 +142,18 @@ def test_compile_project_vhdl_93(self, process, check_output): ) simif.compile_project(project) process.assert_any_call( - [join("prefix", "vlib"), "lib", "lib_path"], + [str(Path("prefix") / "vlib"), "lib", "lib_path"], cwd=self.output_path, env=simif.get_env(), ) process.assert_called_with( - [join("prefix", "vmap"), "lib", "lib_path"], + [str(Path("prefix") / "vmap"), "lib", "lib_path"], cwd=self.output_path, env=simif.get_env(), ) check_output.assert_called_once_with( [ - join("prefix", "vcom"), + str(Path("prefix") / "vcom"), "-quiet", "-j", self.output_path, @@ -176,18 +176,18 @@ def test_compile_project_vhdl_extra_flags(self, process, check_output): source_file.set_compile_option("rivierapro.vcom_flags", ["custom", "flags"]) simif.compile_project(project) process.assert_any_call( - [join("prefix", "vlib"), "lib", "lib_path"], + [str(Path("prefix") / "vlib"), "lib", "lib_path"], cwd=self.output_path, env=simif.get_env(), ) process.assert_called_with( - [join("prefix", "vmap"), "lib", "lib_path"], + [str(Path("prefix") / "vmap"), "lib", "lib_path"], cwd=self.output_path, env=simif.get_env(), ) check_output.assert_called_once_with( [ - join("prefix", "vcom"), + str(Path("prefix") / "vcom"), "-quiet", "-j", self.output_path, @@ -204,7 +204,7 @@ def test_compile_project_vhdl_extra_flags(self, process, check_output): @mock.patch("vunit.sim_if.check_output", autospec=True, return_value="") @mock.patch("vunit.sim_if.rivierapro.Process", autospec=True) def test_compile_project_verilog(self, process, check_output): - library_cfg = join(self.output_path, "library.cfg") + library_cfg = str(Path(self.output_path) / "library.cfg") simif = RivieraProInterface(prefix="prefix", output_path=self.output_path) project = Project() project.add_library("lib", "lib_path") @@ -212,18 +212,18 @@ def test_compile_project_verilog(self, process, check_output): project.add_source_file("file.v", "lib", file_type="verilog") simif.compile_project(project) process.assert_any_call( - [join("prefix", "vlib"), "lib", "lib_path"], + [str(Path("prefix") / "vlib"), "lib", "lib_path"], cwd=self.output_path, env=simif.get_env(), ) process.assert_called_with( - [join("prefix", "vmap"), "lib", "lib_path"], + [str(Path("prefix") / "vmap"), "lib", "lib_path"], cwd=self.output_path, env=simif.get_env(), ) check_output.assert_called_once_with( [ - join("prefix", "vlog"), + str(Path("prefix") / "vlog"), "-quiet", "-lc", library_cfg, @@ -239,7 +239,7 @@ def test_compile_project_verilog(self, process, check_output): @mock.patch("vunit.sim_if.check_output", autospec=True, return_value="") @mock.patch("vunit.sim_if.rivierapro.Process", autospec=True) def test_compile_project_system_verilog(self, process, check_output): - library_cfg = join(self.output_path, "library.cfg") + library_cfg = str(Path(self.output_path) / "library.cfg") simif = RivieraProInterface(prefix="prefix", output_path=self.output_path) project = Project() project.add_library("lib", "lib_path") @@ -247,18 +247,18 @@ def test_compile_project_system_verilog(self, process, check_output): project.add_source_file("file.sv", "lib", file_type="systemverilog") simif.compile_project(project) process.assert_any_call( - [join("prefix", "vlib"), "lib", "lib_path"], + [str(Path("prefix") / "vlib"), "lib", "lib_path"], cwd=self.output_path, env=simif.get_env(), ) process.assert_called_with( - [join("prefix", "vmap"), "lib", "lib_path"], + [str(Path("prefix") / "vmap"), "lib", "lib_path"], cwd=self.output_path, env=simif.get_env(), ) check_output.assert_called_once_with( [ - join("prefix", "vlog"), + str(Path("prefix") / "vlog"), "-quiet", "-lc", library_cfg, @@ -275,7 +275,7 @@ def test_compile_project_system_verilog(self, process, check_output): @mock.patch("vunit.sim_if.check_output", autospec=True, return_value="") @mock.patch("vunit.sim_if.rivierapro.Process", autospec=True) def test_compile_project_verilog_extra_flags(self, process, check_output): - library_cfg = join(self.output_path, "library.cfg") + library_cfg = str(Path(self.output_path) / "library.cfg") simif = RivieraProInterface(prefix="prefix", output_path=self.output_path) project = Project() project.add_library("lib", "lib_path") @@ -284,18 +284,18 @@ def test_compile_project_verilog_extra_flags(self, process, check_output): source_file.set_compile_option("rivierapro.vlog_flags", ["custom", "flags"]) simif.compile_project(project) process.assert_any_call( - [join("prefix", "vlib"), "lib", "lib_path"], + [str(Path("prefix") / "vlib"), "lib", "lib_path"], cwd=self.output_path, env=simif.get_env(), ) process.assert_called_with( - [join("prefix", "vmap"), "lib", "lib_path"], + [str(Path("prefix") / "vmap"), "lib", "lib_path"], cwd=self.output_path, env=simif.get_env(), ) check_output.assert_called_once_with( [ - join("prefix", "vlog"), + str(Path("prefix") / "vlog"), "-quiet", "-lc", library_cfg, @@ -313,7 +313,7 @@ def test_compile_project_verilog_extra_flags(self, process, check_output): @mock.patch("vunit.sim_if.check_output", autospec=True, return_value="") @mock.patch("vunit.sim_if.rivierapro.Process", autospec=True) def test_compile_project_verilog_include(self, process, check_output): - library_cfg = join(self.output_path, "library.cfg") + library_cfg = str(Path(self.output_path) / "library.cfg") simif = RivieraProInterface(prefix="prefix", output_path=self.output_path) project = Project() project.add_library("lib", "lib_path") @@ -323,16 +323,18 @@ def test_compile_project_verilog_include(self, process, check_output): ) simif.compile_project(project) process.assert_any_call( - [join("prefix", "vlib"), "lib", "lib_path"], cwd=self.output_path, env=None + [str(Path("prefix") / "vlib"), "lib", "lib_path"], + cwd=self.output_path, + env=None, ) process.assert_called_with( - [join("prefix", "vmap"), "lib", "lib_path"], + [str(Path("prefix") / "vmap"), "lib", "lib_path"], cwd=self.output_path, env=simif.get_env(), ) check_output.assert_called_once_with( [ - join("prefix", "vlog"), + str(Path("prefix") / "vlog"), "-quiet", "-lc", library_cfg, @@ -349,7 +351,7 @@ def test_compile_project_verilog_include(self, process, check_output): @mock.patch("vunit.sim_if.check_output", autospec=True, return_value="") @mock.patch("vunit.sim_if.rivierapro.Process", autospec=True) def test_compile_project_verilog_define(self, process, check_output): - library_cfg = join(self.output_path, "library.cfg") + library_cfg = str(Path(self.output_path) / "library.cfg") simif = RivieraProInterface(prefix="prefix", output_path=self.output_path) project = Project() project.add_library("lib", "lib_path") @@ -359,18 +361,18 @@ def test_compile_project_verilog_define(self, process, check_output): ) simif.compile_project(project) process.assert_any_call( - [join("prefix", "vlib"), "lib", "lib_path"], + [str(Path("prefix") / "vlib"), "lib", "lib_path"], cwd=self.output_path, env=simif.get_env(), ) process.assert_called_with( - [join("prefix", "vmap"), "lib", "lib_path"], + [str(Path("prefix") / "vmap"), "lib", "lib_path"], cwd=self.output_path, env=simif.get_env(), ) check_output.assert_called_once_with( [ - join("prefix", "vlog"), + str(Path("prefix") / "vlog"), "-quiet", "-lc", library_cfg, @@ -385,7 +387,7 @@ def test_compile_project_verilog_define(self, process, check_output): ) def setUp(self): - self.output_path = join(dirname(__file__), "test_rivierapro_out") + self.output_path = str(Path(__file__).parent / "test_rivierapro_out") renew_path(self.output_path) self.project = Project() self.cwd = os.getcwd() @@ -393,5 +395,5 @@ def setUp(self): def tearDown(self): os.chdir(self.cwd) - if exists(self.output_path): + if Path(self.output_path).exists(): rmtree(self.output_path) diff --git a/tests/unit/test_simulator_interface.py b/tests/unit/test_simulator_interface.py index 936f7a1b6..7e6e76b66 100644 --- a/tests/unit/test_simulator_interface.py +++ b/tests/unit/test_simulator_interface.py @@ -9,8 +9,8 @@ """ import unittest -from os.path import join, dirname, exists -import os +from pathlib import Path +from os import chdir, getcwd import subprocess from shutil import rmtree from unittest import mock @@ -248,15 +248,15 @@ def find_prefix_from_path(cls): environ.get.assert_called_once_with("VUNIT_SIMNAME_PATH", None) def setUp(self): - self.output_path = join(dirname(__file__), "test_simulator_interface__out") + self.output_path = str(Path(__file__).parent / "test_simulator_interface__out") renew_path(self.output_path) self.project = Project() - self.cwd = os.getcwd() - os.chdir(self.output_path) + self.cwd = getcwd() + chdir(self.output_path) def tearDown(self): - os.chdir(self.cwd) - if exists(self.output_path): + chdir(self.cwd) + if Path(self.output_path).exists(): rmtree(self.output_path) diff --git a/tests/unit/test_test_bench.py b/tests/unit/test_test_bench.py index 8ca79360e..a6cfa6112 100644 --- a/tests/unit/test_test_bench.py +++ b/tests/unit/test_test_bench.py @@ -12,8 +12,7 @@ import unittest -from os.path import join - +from pathlib import Path from unittest import mock from tests.common import with_tempdir, get_vhdl_test_bench from vunit.test.bench import ( @@ -40,7 +39,7 @@ class TestTestBench(unittest.TestCase): @with_tempdir def test_that_single_vhdl_test_is_created(self, tempdir): - design_unit = Entity("tb_entity", file_name=join(tempdir, "file.vhd")) + design_unit = Entity("tb_entity", file_name=str(Path(tempdir) / "file.vhd")) test_bench = TestBench(design_unit) tests = self.create_tests(test_bench) self.assert_has_tests(tests, ["lib.tb_entity.all"]) @@ -49,14 +48,14 @@ def test_that_single_vhdl_test_is_created(self, tempdir): @with_tempdir def test_no_architecture_at_creation(tempdir): design_unit = Entity( - "tb_entity", file_name=join(tempdir, "file.vhd"), no_arch=True + "tb_entity", file_name=str(Path(tempdir) / "file.vhd"), no_arch=True ) TestBench(design_unit) @with_tempdir def test_no_architecture_gives_runtime_error(self, tempdir): design_unit = Entity( - "tb_entity", file_name=join(tempdir, "file.vhd"), no_arch=True + "tb_entity", file_name=str(Path(tempdir) / "file.vhd"), no_arch=True ) test_bench = TestBench(design_unit) try: @@ -68,14 +67,14 @@ def test_no_architecture_gives_runtime_error(self, tempdir): @with_tempdir def test_that_single_verilog_test_is_created(self, tempdir): - design_unit = Module("tb_module", file_name=join(tempdir, "file.v")) + design_unit = Module("tb_module", file_name=str(Path(tempdir) / "file.v")) test_bench = TestBench(design_unit) tests = self.create_tests(test_bench) self.assert_has_tests(tests, ["lib.tb_module.all"]) @with_tempdir def test_create_default_test(self, tempdir): - design_unit = Entity("tb_entity", file_name=join(tempdir, "file.vhd")) + design_unit = Entity("tb_entity", file_name=str(Path(tempdir) / "file.vhd")) design_unit.generic_names = ["runner_cfg"] test_bench = TestBench(design_unit) tests = self.create_tests(test_bench) @@ -83,9 +82,11 @@ def test_create_default_test(self, tempdir): @with_tempdir def test_multiple_architectures_are_not_allowed_for_test_bench(self, tempdir): - design_unit = Entity("tb_entity", file_name=join(tempdir, "file.vhd")) + design_unit = Entity("tb_entity", file_name=str(Path(tempdir) / "file.vhd")) design_unit.generic_names = ["runner_cfg"] - design_unit.add_architecture("arch2", file_name=join(tempdir, "arch2.vhd")) + design_unit.add_architecture( + "arch2", file_name=str(Path(tempdir) / "arch2.vhd") + ) try: TestBench(design_unit) except RuntimeError as exc: @@ -101,7 +102,7 @@ def test_multiple_architectures_are_not_allowed_for_test_bench(self, tempdir): def test_creates_tests_vhdl(self, tempdir): design_unit = Entity( "tb_entity", - file_name=join(tempdir, "file.vhd"), + file_name=str(Path(tempdir) / "file.vhd"), contents="""\ if run("Test 1") --if run("Test 2") @@ -135,7 +136,7 @@ def test_creates_tests_vhdl(self, tempdir): def test_creates_tests_verilog(self, tempdir): design_unit = Module( "tb_module", - file_name=join(tempdir, "file.v"), + file_name=str(Path(tempdir) / "file.v"), contents="""\ `TEST_CASE("Test 1") `TEST_CASE ("Test 2") @@ -168,7 +169,9 @@ def test_creates_tests_verilog(self, tempdir): @with_tempdir def test_keyerror_on_non_existent_test(self, tempdir): design_unit = Entity( - "tb_entity", file_name=join(tempdir, "file.vhd"), contents='if run("Test")' + "tb_entity", + file_name=str(Path(tempdir) / "file.vhd"), + contents='if run("Test")', ) design_unit.generic_names = ["runner_cfg", "name"] test_bench = TestBench(design_unit) @@ -177,14 +180,14 @@ def test_keyerror_on_non_existent_test(self, tempdir): @with_tempdir def test_creates_tests_when_adding_architecture_late(self, tempdir): design_unit = Entity( - "tb_entity", file_name=join(tempdir, "file.vhd"), no_arch=True + "tb_entity", file_name=str(Path(tempdir) / "file.vhd"), no_arch=True ) design_unit.generic_names = ["runner_cfg"] test_bench = TestBench(design_unit) design_unit.add_architecture( "arch", - file_name=join(tempdir, "arch.vhd"), + file_name=str(Path(tempdir) / "file.vhd"), contents="""\ if run("Test_1") --if run("Test_2") @@ -196,11 +199,11 @@ def test_creates_tests_when_adding_architecture_late(self, tempdir): @with_tempdir def test_scan_tests_from_file(self, tempdir): - design_unit = Entity("tb_entity", file_name=join(tempdir, "file.vhd")) + design_unit = Entity("tb_entity", file_name=str(Path(tempdir) / "file.vhd")) design_unit.generic_names = ["runner_cfg"] test_bench = TestBench(design_unit) - file_name = join(tempdir, "file.vhd") + file_name = str(Path(tempdir) / "file.vhd") write_file( file_name, """\ @@ -212,16 +215,15 @@ def test_scan_tests_from_file(self, tempdir): tests = self.create_tests(test_bench) self.assert_has_tests(tests, ["lib.tb_entity.Test_1", "lib.tb_entity.Test_2"]) - @with_tempdir - def test_scan_tests_from_file_location_unix(self, tempdir): - design_unit = Entity("tb_entity", file_name=join(tempdir, "file.vhd")) + def _test_scan_tests_from_file_location(self, tempdir, code): + fstr = str(Path(tempdir) / "file.vhd") + + design_unit = Entity("tb_entity", file_name=fstr) design_unit.generic_names = ["runner_cfg"] test_bench = TestBench(design_unit) - file_name = join(tempdir, "file.vhd") - code = 'foo \n bar \n if run("Test_1")' - write_file(file_name, code) - test_bench.scan_tests_from_file(file_name) + write_file(fstr, code) + test_bench.scan_tests_from_file(fstr) tests = self.create_tests(test_bench) test_info = tests[0].test_information location = test_info["lib.tb_entity.Test_1"].location @@ -229,24 +231,20 @@ def test_scan_tests_from_file_location_unix(self, tempdir): assert location.length == len("Test_1") @with_tempdir - def test_scan_tests_from_file_location_dos(self, tempdir): - design_unit = Entity("tb_entity", file_name=join(tempdir, "file.vhd")) - design_unit.generic_names = ["runner_cfg"] - test_bench = TestBench(design_unit) + def test_scan_tests_from_file_location_unix(self, tempdir): + self._test_scan_tests_from_file_location( + tempdir, 'foo \n bar \n if run("Test_1")' + ) - file_name = join(tempdir, "file.vhd") - code = 'foo \r\n bar \r\n if run("Test_1")' - write_file(file_name, code) - test_bench.scan_tests_from_file(file_name) - tests = self.create_tests(test_bench) - test_info = tests[0].test_information - location = test_info["lib.tb_entity.Test_1"].location - assert location.offset == code.find("Test_1") - assert location.length == len("Test_1") + @with_tempdir + def test_scan_tests_from_file_location_dos(self, tempdir): + self._test_scan_tests_from_file_location( + tempdir, 'foo \r\n bar \r\n if run("Test_1")' + ) @with_tempdir def test_scan_tests_from_missing_file(self, tempdir): - design_unit = Entity("tb_entity", file_name=join(tempdir, "file.vhd")) + design_unit = Entity("tb_entity", file_name=str(Path(tempdir) / "file.vhd")) design_unit.generic_names = ["runner_cfg"] test_bench = TestBench(design_unit) @@ -259,7 +257,7 @@ def test_scan_tests_from_missing_file(self, tempdir): @with_tempdir def test_does_not_add_all_suffix_with_named_configurations(self, tempdir): - design_unit = Entity("tb_entity", file_name=join(tempdir, "file.vhd")) + design_unit = Entity("tb_entity", file_name=str(Path(tempdir) / "file.vhd")) design_unit.generic_names = ["runner_cfg"] test_bench = TestBench(design_unit) @@ -274,7 +272,7 @@ def test_does_not_add_all_suffix_with_named_configurations(self, tempdir): def test_that_run_in_same_simulation_attribute_works(self, tempdir): design_unit = Entity( "tb_entity", - file_name=join(tempdir, "file.vhd"), + file_name=str(Path(tempdir) / "file.vhd"), contents="""\ -- vunit: run_all_in_same_sim if run("Test_1") @@ -291,7 +289,7 @@ def test_that_run_in_same_simulation_attribute_works(self, tempdir): @with_tempdir def test_add_config(self, tempdir): - design_unit = Entity("tb_entity", file_name=join(tempdir, "file.vhd")) + design_unit = Entity("tb_entity", file_name=str(Path(tempdir) / "file.vhd")) design_unit.generic_names = ["runner_cfg", "value", "global_value"] test_bench = TestBench(design_unit) @@ -331,7 +329,7 @@ def test_add_config(self, tempdir): def test_test_case_add_config(self, tempdir): design_unit = Entity( "tb_entity", - file_name=join(tempdir, "file.vhd"), + file_name=str(Path(tempdir) / "file.vhd"), contents=""" if run("test 1") if run("test 2") @@ -389,7 +387,7 @@ def test_runtime_error_on_configuration_of_individual_test_with_same_sim( ): design_unit = Entity( "tb_entity", - file_name=join(tempdir, "file.vhd"), + file_name=str(Path(tempdir) / "file.vhd"), contents="""\ -- vunit: run_all_in_same_sim if run("Test 1") @@ -407,7 +405,7 @@ def test_runtime_error_on_configuration_of_individual_test_with_same_sim( def test_run_all_in_same_sim_can_be_configured(self, tempdir): design_unit = Entity( "tb_entity", - file_name=join(tempdir, "file.vhd"), + file_name=str(Path(tempdir) / "file.vhd"), contents="""\ -- vunit: run_all_in_same_sim if run("Test 1") @@ -436,7 +434,7 @@ def test_run_all_in_same_sim_can_be_configured(self, tempdir): def test_global_user_attributes_not_supported_yet(self, tempdir): design_unit = Entity( "tb_entity", - file_name=join(tempdir, "file.vhd"), + file_name=str(Path(tempdir) / "file.vhd"), contents="""\ -- vunit: .attr0 if run("Test 1") @@ -451,7 +449,7 @@ def test_global_user_attributes_not_supported_yet(self, tempdir): self.assertEqual( str(exc), "File global attributes are not yet supported: .attr0 in %s line 1" - % join(tempdir, "file.vhd"), + % str(Path(tempdir) / "file.vhd"), ) else: assert False, "RuntimeError not raised" @@ -460,7 +458,7 @@ def test_global_user_attributes_not_supported_yet(self, tempdir): def test_error_on_global_attributes_on_tests(self, tempdir): design_unit = Entity( "tb_entity", - file_name=join(tempdir, "file.vhd"), + file_name=str(Path(tempdir) / "file.vhd"), contents="""\ if run("Test 1") -- vunit: run_all_in_same_sim @@ -475,14 +473,14 @@ def test_error_on_global_attributes_on_tests(self, tempdir): self.assertEqual( str(exc), "Attribute run_all_in_same_sim is global and cannot be associated with test Test 1: %s line 2" - % join(tempdir, "file.vhd"), + % str(Path(tempdir) / "file.vhd"), ) else: assert False, "RuntimeError not raised" @with_tempdir def test_test_information(self, tempdir): - file_name = join(tempdir, "file.vhd") + file_name = str(Path(tempdir) / "file.vhd") for same_sim in [True, False]: contents = get_vhdl_test_bench( @@ -521,7 +519,7 @@ def test_test_information(self, tempdir): @with_tempdir def test_fail_on_unknown_sim_option(self, tempdir): - design_unit = Entity("tb_entity", file_name=join(tempdir, "file.vhd")) + design_unit = Entity("tb_entity", file_name=str(Path(tempdir) / "file.vhd")) design_unit.generic_names = ["runner_cfg"] test_bench = TestBench(design_unit) self.assertRaises(ValueError, test_bench.set_sim_option, "unknown", "value") diff --git a/tests/unit/test_test_bench_list.py b/tests/unit/test_test_bench_list.py index d926ab36e..7cb920eae 100644 --- a/tests/unit/test_test_bench_list.py +++ b/tests/unit/test_test_bench_list.py @@ -11,7 +11,7 @@ """ import unittest -from os.path import join +from pathlib import Path from unittest import mock from tests.unit.test_test_bench import Entity, Module from tests.common import with_tempdir @@ -29,19 +29,21 @@ def test_get_test_benches_in_empty_library(self): @with_tempdir def test_tb_filter_requires_runner_cfg(self, tempdir): - design_unit = Entity("tb_entity", file_name=join(tempdir, "file.vhd")) + fname = str(Path(tempdir) / "file.vhd") + + design_unit = Entity("tb_entity", file_name=fname) design_unit.generic_names = ["runner_cfg"] self.assertTrue(tb_filter(design_unit)) - design_unit = Entity("tb_entity", file_name=join(tempdir, "file.vhd")) + design_unit = Entity("tb_entity", file_name=fname) design_unit.generic_names = [] self.assertFalse(tb_filter(design_unit)) - design_unit = Module("tb_module", file_name=join(tempdir, "file.vhd")) + design_unit = Module("tb_module", file_name=fname) design_unit.generic_names = ["runner_cfg"] self.assertTrue(tb_filter(design_unit)) - design_unit = Module("tb_module", file_name=join(tempdir, "file.vhd")) + design_unit = Module("tb_module", file_name=fname) design_unit.generic_names = [] self.assertFalse(tb_filter(design_unit)) @@ -51,7 +53,9 @@ def test_tb_filter_match_prefix_and_suffix_only(self, tempdir): Issue #263 """ with mock.patch("vunit.test.bench_list.LOGGER", autospec=True) as logger: - design_unit = Entity("mul_tbl_scale", file_name=join(tempdir, "file.vhd")) + design_unit = Entity( + "mul_tbl_scale", file_name=str(Path(tempdir) / "file.vhd") + ) self.assertFalse(tb_filter(design_unit)) self.assertFalse(logger.warning.called) @@ -59,7 +63,9 @@ def test_tb_filter_match_prefix_and_suffix_only(self, tempdir): def test_tb_filter_warning_on_missing_runner_cfg_when_matching_tb_pattern( self, tempdir ): - design_unit = Module("tb_module_not_ok", file_name=join(tempdir, "file.vhd")) + design_unit = Module( + "tb_module_not_ok", file_name=str(Path(tempdir) / "file.vhd") + ) design_unit.generic_names = [] with mock.patch("vunit.test.bench_list.LOGGER", autospec=True) as logger: @@ -82,7 +88,7 @@ def test_tb_filter_warning_on_missing_runner_cfg_when_matching_tb_pattern( @with_tempdir def test_tb_filter_warning_on_runner_cfg_but_not_matching_tb_pattern(self, tempdir): design_unit = Entity( - "entity_ok_but_warning", file_name=join(tempdir, "file.vhd") + "entity_ok_but_warning", file_name=str(Path(tempdir) / "file.vhd") ) design_unit.generic_names = ["runner_cfg"] diff --git a/tests/unit/test_test_report.py b/tests/unit/test_test_report.py index 71225e6e6..1e51f2f9b 100644 --- a/tests/unit/test_test_report.py +++ b/tests/unit/test_test_report.py @@ -10,7 +10,7 @@ from unittest import TestCase import os -from os.path import basename, dirname, join +from pathlib import Path from xml.etree import ElementTree from vunit.test.report import TestReport, PASSED, SKIPPED, FAILED from vunit.ui.common import TEST_OUTPUT_PATH @@ -26,7 +26,7 @@ def setUp(self): self.printer = StubPrinter() self.output_file_contents = 'Output file contents\n&13!--"<\\xml>' - self.output_file_name = join(dirname(__file__), "test_report_output.txt") + self.output_file_name = str(Path(__file__).parent / "test_report_output.txt") with open(self.output_file_name, "w") as fwrite: fwrite.write(self.output_file_contents) @@ -277,15 +277,15 @@ def test_junit_report_with_testcase_classname(self): ) def test_dict_report_with_all_passed_tests(self): - opath = dirname(dirname(self.output_file_name)) - test_path = join(opath, TEST_OUTPUT_PATH, "unit") - output_file_name = join(test_path, basename(self.output_file_name)) + opath = Path(self.output_file_name).parent.parent + test_path = opath / TEST_OUTPUT_PATH / "unit" + output_file_name = test_path / Path(self.output_file_name).name results = Results( opath, None, self._report_with_all_passed_tests(output_file_name) ) report = results.get_report() for _, test in report.tests.items(): - self.assertEqual(basename(test.path), test.relpath) + self.assertEqual(test.path.name, test.relpath) test0 = report.tests["passed_test0"] test1 = report.tests["passed_test1"] self.assertEqual( diff --git a/tests/unit/test_test_runner.py b/tests/unit/test_test_runner.py index 3f498d818..0b4c84a28 100644 --- a/tests/unit/test_test_runner.py +++ b/tests/unit/test_test_runner.py @@ -8,7 +8,7 @@ Test the test runner """ -from os.path import join, abspath +from pathlib import Path import unittest from unittest import mock from tests.common import with_tempdir @@ -143,8 +143,9 @@ def test_create_output_path_on_linux(self): test_output = create_output_path(output_path, test_name) self.assertEqual( test_output, - join( - abspath(output_path), test_name + "_" + hash_string(test_name) + str( + Path(output_path).resolve() + / (test_name + "_" + hash_string(test_name)) ), ) @@ -153,8 +154,9 @@ def test_create_output_path_on_linux(self): test_output = create_output_path(output_path, test_name) self.assertEqual( test_output, - join( - abspath(output_path), test_name + "_" + hash_string(test_name) + str( + Path(output_path).resolve() + / (test_name + "_" + hash_string(test_name)) ), ) @@ -164,8 +166,9 @@ def test_create_output_path_on_linux(self): test_output = create_output_path(output_path, test_name) self.assertEqual( test_output, - join( - abspath(output_path), safe_name + "_" + hash_string(test_name) + str( + Path(output_path).resolve() + / (safe_name + "_" + hash_string(test_name)) ), ) @@ -185,8 +188,9 @@ def test_create_output_path_on_windows(self): test_output = create_output_path(output_path, test_name) self.assertEqual( test_output, - join( - abspath(output_path), test_name + "_" + hash_string(test_name) + str( + Path(output_path).resolve() + / (test_name + "_" + hash_string(test_name)) ), ) @@ -195,7 +199,8 @@ def test_create_output_path_on_windows(self): test_name = "_" * 400 test_output = create_output_path(output_path, test_name) self.assertEqual( - test_output, join(abspath(output_path), hash_string(test_name)) + test_output, + str(Path(output_path).resolve() / hash_string(test_name)), ) @staticmethod diff --git a/tests/unit/test_test_suites.py b/tests/unit/test_test_suites.py index 40d4b386c..abee45e8e 100644 --- a/tests/unit/test_test_suites.py +++ b/tests/unit/test_test_suites.py @@ -8,7 +8,7 @@ Test the test suites """ -from os.path import join +from pathlib import Path from unittest import TestCase from tests.common import create_tempdir from vunit.test.suites import TestRun @@ -94,9 +94,9 @@ def _read_test_results(self, expected, contents): Helper method to test the read_test_results function """ with create_tempdir() as path: - file_name = join(path, "vunit_results") + file_name = Path(path) / "vunit_results" if contents is not None: - with open(file_name, "w") as fptr: + with file_name.open("w") as fptr: fptr.write(contents) run = TestRun( @@ -162,9 +162,9 @@ def _test_exit_code( Helper method to test the check_results function """ with create_tempdir() as path: - file_name = join(path, "vunit_results") + file_name = Path(path) / "vunit_results" if contents is not None: - with open(file_name, "w") as fptr: + with file_name.open("w") as fptr: fptr.write(contents) sim_if = SimulatorInterface diff --git a/tests/unit/test_ui.py b/tests/unit/test_ui.py index 1338a5868..56c657e21 100644 --- a/tests/unit/test_ui.py +++ b/tests/unit/test_ui.py @@ -13,8 +13,7 @@ import unittest from string import Template from pathlib import Path -import os -from os.path import join, dirname, basename, exists, abspath +from os import chdir, getcwd import json import re from re import MULTILINE @@ -35,17 +34,17 @@ class TestUi(unittest.TestCase): """ def setUp(self): - self.tmp_path = join(dirname(__file__), "test_ui_tmp") + self.tmp_path = str(Path(__file__).parent / "test_ui_tmp") renew_path(self.tmp_path) - self.cwd = os.getcwd() - os.chdir(self.tmp_path) + self.cwd = getcwd() + chdir(self.tmp_path) - self._output_path = join(self.tmp_path, "output") - self._preprocessed_path = join(self._output_path, "preprocessed") + self._output_path = str(Path(self.tmp_path) / "output") + self._preprocessed_path = str(Path(self._output_path) / "preprocessed") def tearDown(self): - os.chdir(self.cwd) - if exists(self.tmp_path): + chdir(self.cwd) + if Path(self.tmp_path).exists(): rmtree(self.tmp_path) def test_global_custom_preprocessors_should_be_applied_in_the_order_they_are_added( @@ -75,10 +74,10 @@ def test_global_custom_preprocessors_should_be_applied_in_the_order_they_are_add end architecture; """ ) - with open(join(self._preprocessed_path, "lib", basename(file_name))) as fread: + fname = Path(file_name).name + with (Path(self._preprocessed_path) / "lib" / fname).open() as fread: self.assertEqual( - fread.read(), - pp_source.substitute(entity="ent0", file=basename(file_name)), + fread.read(), pp_source.substitute(entity="ent0", file=fname), ) def test_global_check_and_location_preprocessors_should_be_applied_after_global_custom_preprocessors( @@ -90,8 +89,8 @@ def test_global_check_and_location_preprocessors_should_be_applied_after_global_ ui.enable_check_preprocessing() ui.add_preprocessor(TestPreprocessor()) - file_name = self.create_entity_file() - ui.add_source_files(file_name, "lib") + entity_file = Path(self.create_entity_file()) + ui.add_source_files(str(entity_file), "lib") pp_source = Template( """\ @@ -113,10 +112,10 @@ def test_global_check_and_location_preprocessors_should_be_applied_after_global_ end architecture; """ ) - with open(join(self._preprocessed_path, "lib", basename(file_name))) as fread: + with (Path(self._preprocessed_path) / "lib" / entity_file.name).open() as fread: self.assertEqual( fread.read(), - pp_source.substitute(entity="ent0", file=basename(file_name)), + pp_source.substitute(entity="ent0", file=entity_file.name), ) def test_locally_specified_preprocessors_should_be_used_instead_of_any_globally_defined_preprocessors( @@ -151,9 +150,11 @@ def test_locally_specified_preprocessors_should_be_used_instead_of_any_globally_ """ ) self.assertFalse( - exists(join(self._preprocessed_path, "lib", basename(file_name1))) + (Path(self._preprocessed_path) / "lib" / Path(file_name1).name).exists() ) - with open(join(self._preprocessed_path, "lib", basename(file_name2))) as fread: + with ( + Path(self._preprocessed_path) / "lib" / Path(file_name2).name + ).open() as fread: expectd = pp_source.substitute( entity="ent2", report='log("Here I am!"); -- VUnitfier preprocessor: Report turned off, keeping original code.', @@ -183,16 +184,17 @@ def test_recovers_from_preprocessing_error(self, logger): end architecture; """ ) - file_name = join(self.tmp_path, "ent1.vhd") + file_name = Path(self.tmp_path) / "ent1.vhd" contents = source_with_error.substitute(entity="ent1") - self.create_file(file_name, contents) + self.create_file(str(file_name), contents) ui.add_source_file(file_name, "lib") logger.assert_called_once_with( - "Failed to preprocess %s", Path(file_name).resolve() + "Failed to preprocess %s", str(Path(file_name).resolve()) + ) + self.assertFalse( + (Path(self._preprocessed_path) / "lib" / file_name.name).exists() ) - pp_file = join(self._preprocessed_path, "lib", basename(file_name)) - self.assertFalse(exists(pp_file)) def test_supported_source_file_suffixes(self): """Test adding a supported filetype, of any case, is accepted.""" @@ -221,23 +223,26 @@ def test_unsupported_source_file_suffixes(self): def test_exception_on_adding_zero_files(self): ui = self._create_ui() lib = ui.add_library("lib") + dname = Path(__file__).parent self.assertRaisesRegex( ValueError, "Pattern.*missing1.vhd.*", lib.add_source_files, - join(dirname(__file__), "missing1.vhd"), + str(dname / "missing1.vhd"), ) self.assertRaisesRegex( ValueError, "File.*missing2.vhd.*", lib.add_source_file, - join(dirname(__file__), "missing2.vhd"), + str(dname / "missing2.vhd"), ) def test_no_exception_on_adding_zero_files_when_allowed(self): ui = self._create_ui() lib = ui.add_library("lib") - lib.add_source_files(join(dirname(__file__), "missing.vhd"), allow_empty=True) + lib.add_source_files( + str(Path(__file__).parent / "missing.vhd"), allow_empty=True + ) def test_get_test_benchs_and_test(self): ui = self._create_ui() @@ -475,7 +480,7 @@ def test_filtering_tests(self, tempdir): def setup(ui): " Setup the project " lib = ui.add_library("lib") - file_name = join(tempdir, "tb_filter.vhd") + file_name = str(Path(tempdir) / "tb_filter.vhd") create_vhdl_test_bench_file( "tb_filter", file_name, @@ -554,17 +559,18 @@ def check_stdout(ui, expected): @with_tempdir def test_export_json(self, tempdir): - json_file = join(tempdir, "export.json") + tdir = Path(tempdir) + json_file = str(tdir / "export.json") ui = self._create_ui("--export-json", json_file) lib1 = ui.add_library("lib1") lib2 = ui.add_library("lib2") - file_name1 = join(tempdir, "tb_foo.vhd") + file_name1 = str(tdir / "tb_foo.vhd") create_vhdl_test_bench_file("tb_foo", file_name1) lib1.add_source_file(file_name1) - file_name2 = join(tempdir, "tb_bar.vhd") + file_name2 = str(tdir / "tb_bar.vhd") create_vhdl_test_bench_file( "tb_bar", file_name2, @@ -595,7 +601,12 @@ def test_export_json(self, tempdir): # Check the contents of the files section self.assertEqual( set((item["library_name"], item["file_name"]) for item in data["files"]), - set([("lib1", abspath(file_name1)), ("lib2", abspath(file_name2))]), + set( + [ + ("lib1", str(Path(file_name1).resolve())), + ("lib2", str(Path(file_name2).resolve())), + ] + ), ) # Check the contents of the tests section @@ -752,7 +763,7 @@ def check(action): lib = ui.add_library("lib") action(ui, lib) add_source_file.assert_called_once_with( - Path("verilog.v").resolve(), + str(Path("verilog.v").resolve()), "lib", file_type="verilog", include_dirs=all_include_dirs, @@ -788,7 +799,7 @@ def check(action): lib = ui.add_library("lib") action(ui, lib) add_source_file.assert_called_once_with( - Path("verilog.v").resolve(), + str(Path("verilog.v").resolve()), "lib", file_type="verilog", include_dirs=all_include_dirs, @@ -825,7 +836,7 @@ def test_add_source_files_has_no_parse(self): lib.add_source_file(file_name, no_parse=no_parse) add_source_file.assert_called_once_with( - Path("verilog.v").resolve(), + str(Path("verilog.v").resolve()), "lib", file_type="verilog", include_dirs=all_include_dirs, diff --git a/tests/unit/test_verilog_parser.py b/tests/unit/test_verilog_parser.py index c963bb6aa..de368c9da 100644 --- a/tests/unit/test_verilog_parser.py +++ b/tests/unit/test_verilog_parser.py @@ -10,7 +10,7 @@ from unittest import TestCase, mock import os -from os.path import join, dirname, exists +from pathlib import Path import time import shutil from vunit.ostools import renew_path @@ -23,7 +23,7 @@ class TestVerilogParser(TestCase): # pylint: disable=too-many-public-methods """ def setUp(self): - self.output_path = join(dirname(__file__), "test_verilog_parser_out") + self.output_path = str(Path(__file__).parent / "test_verilog_parser_out") renew_path(self.output_path) self.cwd = os.getcwd() os.chdir(self.output_path) @@ -327,10 +327,10 @@ def test_cached_parsing_updated_by_includes(self): def test_cached_parsing_updated_by_higher_priority_file(self): cache = {} - include_paths = [self.output_path, join(self.output_path, "lower_prio")] + include_paths = [self.output_path, str(Path(self.output_path) / "lower_prio")] self.write_file( - join("lower_prio", "include.svh"), + str(Path("lower_prio") / "include.svh"), """ module mod_lower_prio; endmodule; @@ -381,11 +381,11 @@ def write_file(self, file_name, contents): """ Write file with contents into output path """ - full_name = join(self.output_path, file_name) - full_path = dirname(full_name) - if not exists(full_path): - os.makedirs(full_path) - with open(full_name, "w") as fptr: + full_name = Path(self.output_path) / file_name + full_path = full_name.parent + if not full_path.exists(): + os.makedirs(str(full_path)) + with full_name.open("w") as fptr: fptr.write(contents) def parse(self, code, include_paths=None, cache=None, defines=None): diff --git a/tests/unit/test_verilog_preprocessor.py b/tests/unit/test_verilog_preprocessor.py index 81ee6cad4..0ba3caa16 100644 --- a/tests/unit/test_verilog_preprocessor.py +++ b/tests/unit/test_verilog_preprocessor.py @@ -12,7 +12,7 @@ Test of the Verilog preprocessor """ -from os.path import join, dirname, exists +from pathlib import Path import os from unittest import TestCase, mock import shutil @@ -28,7 +28,7 @@ class TestVerilogPreprocessor(TestCase): """ def setUp(self): - self.output_path = join(dirname(__file__), "test_verilog_preprocessor_out") + self.output_path = str(Path(__file__).parent / "test_verilog_preprocessor_out") renew_path(self.output_path) self.cwd = os.getcwd() os.chdir(self.output_path) @@ -150,7 +150,7 @@ def test_preprocess_include_directive(self): '`include "include.svh"', include_paths=[self.output_path] ) result.assert_has_tokens("hello hey") - result.assert_included_files([join(self.output_path, "include.svh")]) + result.assert_included_files([str(Path(self.output_path) / "include.svh")]) def test_detects_circular_includes(self): self.write_file("include1.svh", '`include "include2.svh"') @@ -267,7 +267,7 @@ def test_preprocess_include_directive_from_define(self): include_paths=[self.output_path], ) result.assert_has_tokens("hello hey") - result.assert_included_files([join(self.output_path, "include.svh")]) + result.assert_included_files([str(Path(self.output_path) / "include.svh")]) def test_preprocess_include_directive_from_define_with_args(self): self.write_file("include.svh", "hello hey") @@ -278,7 +278,7 @@ def test_preprocess_include_directive_from_define_with_args(self): include_paths=[self.output_path], ) result.assert_has_tokens("hello hey") - result.assert_included_files([join(self.output_path, "include.svh")]) + result.assert_included_files([str(Path(self.output_path) / "include.svh")]) def test_preprocess_macros_are_recursively_expanded(self): result = self.preprocess( @@ -674,7 +674,7 @@ def test_preprocess_error_in_include_file(self): '\n\n`include "include.svh"', include_paths=[self.output_path] ) result.assert_has_tokens("\n\n") - result.assert_included_files([join(self.output_path, "include.svh")]) + result.assert_included_files([str(Path(self.output_path) / "include.svh")]) result.logger.warning.assert_called_once_with( "Verilog `include bad argument\n%s", "from fn.v line 3:\n" @@ -863,11 +863,11 @@ def write_file(self, file_name, contents): """ Write file with contents into output path """ - full_name = join(self.output_path, file_name) - full_path = dirname(full_name) - if not exists(full_path): - os.makedirs(full_path) - with open(full_name, "w") as fptr: + full_name = Path(self.output_path) / file_name + full_path = full_name.parent + if not full_path.exists(): + os.makedirs(str(full_path)) + with full_name.open("w") as fptr: fptr.write(contents) diff --git a/tools/build_docs.py b/tools/build_docs.py index d0c5c87d2..1211b76ae 100644 --- a/tools/build_docs.py +++ b/tools/build_docs.py @@ -9,7 +9,7 @@ """ from subprocess import check_call -from os.path import join, dirname +from pathlib import Path import sys from sys import argv from create_release_notes import create_release_notes @@ -30,7 +30,7 @@ def main(): ] + ([] if len(argv) < 2 else argv[2:]) + [ "-TEWanb", "html", - join(dirname(__file__), "..", "docs"), + Path(__file__).parent.parent / "docs", argv[1], ] ) diff --git a/tools/create_release_notes.py b/tools/create_release_notes.py index 6404d0899..e4f35d8ab 100644 --- a/tools/create_release_notes.py +++ b/tools/create_release_notes.py @@ -8,21 +8,22 @@ Create monolithic release notes file from several input files """ -from os.path import join, dirname, basename, splitext, relpath +from pathlib import Path +from os.path import relpath from glob import glob from subprocess import check_output, CalledProcessError from shutil import which import datetime -def get_releases(source_path): +def get_releases(source_path: Path): """ Get all releases defined by release note files """ - release_notes = join(source_path, "release_notes") + release_notes = source_path / "release_notes" releases = [] for idx, file_name in enumerate( - sorted(glob(join(release_notes, "*.rst")), reverse=True) + sorted(glob(str(release_notes / "*.rst")), reverse=True) ): releases.append(Release(file_name, is_latest=idx == 0)) return releases @@ -32,7 +33,7 @@ def create_release_notes(): """ Create monolithic release notes file from several input files """ - source_path = join(dirname(__file__), "..", "docs") + source_path = Path(__file__).parent.parent / "docs" releases = get_releases(source_path) latest_release = releases[0] @@ -40,7 +41,7 @@ def create_release_notes(): def banner(fptr): fptr.write("\n" + ("-" * 80) + "\n\n") - with open(join(source_path, "release_notes.rst"), "w") as fptr: + with (source_path / "release_notes.rst").open("w") as fptr: fptr.write( """ .. _release_notes: @@ -96,7 +97,7 @@ class Release(object): def __init__(self, file_name, is_latest): self.file_name = file_name - self.name = splitext(basename(file_name))[0] + self.name = str(Path(file_name).with_suffix("").name) self.tag = "v" + self.name self.is_latest = is_latest diff --git a/tools/docs_utils.py b/tools/docs_utils.py index 621719d42..aa394fa87 100644 --- a/tools/docs_utils.py +++ b/tools/docs_utils.py @@ -10,25 +10,25 @@ import sys import inspect -from os.path import basename, dirname, isdir, isfile, join -from os import listdir, remove +from pathlib import Path +from os import listdir, remove -ROOT = join(dirname(__file__), "..", "docs") +ROOT = Path(__file__).parent.parent / "docs" def examples(): """ Traverses the examples directory and generates examples.rst with the docstrings """ - eg_path = join(ROOT, "..", "examples") - egs_fptr = open(join(ROOT, "examples.rst"), "w+") + eg_path = ROOT.parent / "examples" + egs_fptr = (ROOT / "examples.rst").open("w+") egs_fptr.write("\n".join([".. _examples:\n", "Examples", "========", "\n"])) for language, subdir in {"VHDL": "vhdl", "SystemVerilog": "verilog"}.items(): egs_fptr.write("\n".join([language, "~~~~~~~~~~~~~~~~~~~~~~~", "\n"])) - for item in listdir(join(eg_path, subdir)): - loc = join(eg_path, subdir, item) - if isdir(loc): + for item in listdir(str(eg_path / subdir)): + loc = eg_path / subdir / item + if loc.is_dir(): _data = _get_eg_doc( loc, "https://github.com/VUnit/vunit/tree/master/examples/%s/%s" @@ -38,37 +38,39 @@ def examples(): egs_fptr.write(_data) -def _get_eg_doc(location, ref): +def _get_eg_doc(location: Path, ref): """ Reads the docstring from a run.py file and rewrites the title to make it a ref """ - if not isfile(join(location, "run.py")): + nstr = str(location.name) + + if not (location / "run.py").is_file(): print( "WARNING: Example subdir '" - + basename(location) + + nstr + "' does not contain a 'run.py' file. Skipping..." ) return None - print("Generating '_main.py' from 'run.py' in '" + basename(location) + "'...") - with open(join(location, "run.py"), "r") as ifile: - with open(join(location, "_main.py"), "w") as ofile: + print("Generating '_main.py' from 'run.py' in '" + nstr + "'...") + with (location / "run.py").open("r") as ifile: + with (location / "_main.py").open("w") as ofile: ofile.writelines(["def _main():\n"]) ofile.writelines(["".join([" ", x]) for x in ifile]) - print("Extracting docs from '" + basename(location) + "'...") - sys.path.append(location) + print("Extracting docs from '" + nstr + "'...") + sys.path.append(str(location)) from _main import _main # pylint: disable=import-error,import-outside-toplevel eg_doc = inspect.getdoc(_main) del sys.modules["_main"] - sys.path.remove(location) - remove(join(location, "_main.py")) + sys.path.remove(str(location)) + remove(str(location / "_main.py")) if not eg_doc: print( "WARNING: 'run.py' file in example subdir '" - + basename(location) + + nstr + "' does not contain a docstring. Skipping..." ) return "" diff --git a/tools/release.py b/tools/release.py index cdf4d389a..c3cbc8f48 100644 --- a/tools/release.py +++ b/tools/release.py @@ -15,7 +15,7 @@ import json from urllib.request import urlopen # pylint: disable=no-name-in-module, import-error import sys -from os.path import dirname, join, exists +from pathlib import Path import subprocess from shutil import which @@ -67,8 +67,8 @@ def make_release_commit(version): """ Add release notes and make the release commit """ - run(["git", "add", release_note_file_name(version)]) - run(["git", "add", ABOUT_PY]) + run(["git", "add", str(release_note_file_name(version))]) + run(["git", "add", str(ABOUT_PY)]) run(["git", "commit", "-m", "Release %s" % version]) run(["git", "tag", "v%s" % version, "-a", "-m", "release %s" % version]) @@ -77,7 +77,7 @@ def make_next_pre_release_commit(version): """ Add release notes and make the release commit """ - run(["git", "add", ABOUT_PY]) + run(["git", "add", str(ABOUT_PY)]) run(["git", "commit", "-m", "Start of next release candidate %s" % version]) @@ -87,18 +87,18 @@ def validate_new_release(version, pre_tag): """ release_note = release_note_file_name(version) - if not exists(release_note): + if not release_note.exists(): print( "Not releasing version %s since release note %s does not exist" - % (version, release_note) + % (version, str(release_note)) ) sys.exit(1) - with open(release_note, "r") as fptr: + with release_note.open("r") as fptr: if not fptr.read(): print( "Not releasing version %s since release note %s is empty" - % (version, release_note) + % (version, str(release_note)) ) sys.exit(1) @@ -135,7 +135,7 @@ def set_version(version): Update vunit/about.py with correct version """ - with open(ABOUT_PY, "r") as fptr: + with ABOUT_PY.open("r") as fptr: content = fptr.read() print("Set local version to %s" % version) @@ -143,14 +143,14 @@ def set_version(version): 'VERSION = "%s"' % get_local_version(), 'VERSION = "%s"' % version ) - with open(ABOUT_PY, "w") as fptr: + with ABOUT_PY.open("w") as fptr: fptr.write(content) assert get_local_version() == version -def release_note_file_name(version): - return join(REPO_ROOT, "docs", "release_notes", version + ".rst") +def release_note_file_name(version) -> Path: + return REPO_ROOT / "docs" / "release_notes" / (version + ".rst") def get_local_version(): @@ -160,7 +160,7 @@ def get_local_version(): """ version = ( subprocess.check_output( - [sys.executable, join(REPO_ROOT, "setup.py"), "--version"] + [sys.executable, str(REPO_ROOT / "setup.py"), "--version"] ) .decode() .strip() @@ -180,8 +180,8 @@ def run(cmd): subprocess.check_call(cmd) -REPO_ROOT = join(dirname(__file__), "..") -ABOUT_PY = join(REPO_ROOT, "vunit", "about.py") +REPO_ROOT = Path(__file__).parent.parent +ABOUT_PY = REPO_ROOT / "vunit" / "about.py" if __name__ == "__main__": diff --git a/vunit/__init__.py b/vunit/__init__.py index 218c7688c..77d52478a 100644 --- a/vunit/__init__.py +++ b/vunit/__init__.py @@ -8,14 +8,14 @@ Public VUnit interface """ -from os.path import dirname, join, abspath +from pathlib import Path import vunit.version_check from vunit.ui import VUnit from vunit.vunit_cli import VUnitCLI from vunit.about import version, doc # Repository root -ROOT = abspath(join(dirname(__file__), "..")) +ROOT = str(Path(__file__).parent.parent.resolve()) __version__ = version() __doc__ = doc() # pylint: disable=redefined-builtin diff --git a/vunit/configuration.py b/vunit/configuration.py index d45a3474e..1d38cd240 100644 --- a/vunit/configuration.py +++ b/vunit/configuration.py @@ -10,7 +10,7 @@ import logging import inspect -from os.path import dirname +from pathlib import Path from copy import copy from vunit.sim_if.factory import SIMULATOR_FACTORY @@ -46,7 +46,7 @@ def __init__( # pylint: disable=too-many-arguments self.sim_options = {} if sim_options is None else sim_options self.attributes = {} if attributes is None else attributes - self.tb_path = dirname(design_unit.original_file_name) + self.tb_path = str(Path(design_unit.original_file_name).parent) # Fill in tb_path generic with location of test bench if "tb_path" in design_unit.generic_names: diff --git a/vunit/csv_logs.py b/vunit/csv_logs.py index 7e2ce3550..a22e14574 100644 --- a/vunit/csv_logs.py +++ b/vunit/csv_logs.py @@ -10,7 +10,7 @@ from csv import Sniffer, DictReader, DictWriter from glob import glob -from os.path import abspath +from pathlib import Path class CsvLogs(object): @@ -35,7 +35,7 @@ def __iter__(self): def add(self, pattern): # pylint: disable=missing-docstring - for csv_file in [abspath(p) for p in glob(pattern)]: + for csv_file in [str(Path(p).resolve()) for p in glob(pattern)]: with open(csv_file, "r") as fread: sample = fread.readline() fread.seek(0) diff --git a/vunit/database.py b/vunit/database.py index 2381ea28a..7e35cc7a5 100644 --- a/vunit/database.py +++ b/vunit/database.py @@ -8,7 +8,7 @@ A simple file based database """ -from os.path import join, exists +from pathlib import Path import os import pickle import io @@ -39,7 +39,7 @@ def __init__(self, path, new=False): if new: renew_path(path) - elif not exists(path): + elif not Path(path).exists(): os.makedirs(path) # Map keys to nodes indexes @@ -55,7 +55,7 @@ def _discover_nodes(self): """ keys_to_nodes = {} for file_base_name in os.listdir(self._path): - key = self._read_key(join(self._path, file_base_name)) + key = self._read_key(str(Path(self._path) / file_base_name)) assert key not in keys_to_nodes # Two nodes contains the same key keys_to_nodes[key] = int(file_base_name) return keys_to_nodes @@ -100,7 +100,7 @@ def _to_file_name(self, key): """ Convert key to file name """ - return join(self._path, str(self._keys_to_nodes[key])) + return str(Path(self._path) / str(self._keys_to_nodes[key])) def _allocate_node_for_key(self, key): """ diff --git a/vunit/ostools.py b/vunit/ostools.py index 04f3e70e7..dbc284ddb 100644 --- a/vunit/ostools.py +++ b/vunit/ostools.py @@ -15,8 +15,10 @@ import threading import shutil from queue import Queue, Empty -from os.path import exists, getmtime, dirname, relpath, splitdrive +from pathlib import Path +from os.path import getmtime, relpath, splitdrive import os +from os import getcwd, makedirs import io import logging @@ -296,12 +298,12 @@ def read_file(file_name, encoding="utf-8", newline=None): def write_file(file_name, contents, encoding="utf-8"): """ To stub during testing """ - path = dirname(file_name) + path = str(Path(file_name).parent) if path == "": path = "." if not file_exists(path): - os.makedirs(path) + makedirs(path) with io.open(file_name, "wb") as file_to_write: file_to_write.write(contents.encode(encoding=encoding)) @@ -309,7 +311,7 @@ def write_file(file_name, contents, encoding="utf-8"): def file_exists(file_name): """ To stub during testing """ - return exists(file_name) + return Path(file_name).exists() def get_modification_time(file_name): @@ -334,14 +336,14 @@ def renew_path(path): """ if IS_WINDOWS_SYSTEM: retries = 10 - while retries > 0 and exists(path): + while retries > 0 and Path(path).exists(): shutil.rmtree(path, ignore_errors=retries > 1) time.sleep(0.01) retries -= 1 else: - if exists(path): + if Path(path).exists(): shutil.rmtree(path) - os.makedirs(path) + makedirs(path) def simplify_path(path): @@ -349,7 +351,7 @@ def simplify_path(path): Return relative path towards current working directory unless it is a separate Windows drive """ - cwd = os.getcwd() + cwd = getcwd() drive_cwd = splitdrive(cwd)[0] drive_path = splitdrive(path)[0] if drive_path == drive_cwd: diff --git a/vunit/parsing/verilog/parser.py b/vunit/parsing/verilog/parser.py index 3e152dc05..a2ec6629a 100644 --- a/vunit/parsing/verilog/parser.py +++ b/vunit/parsing/verilog/parser.py @@ -12,7 +12,7 @@ """ import logging -from os.path import dirname, exists, abspath +from pathlib import Path from vunit.ostools import read_file from vunit.parsing.encodings import HDL_FILE_ENCODING from vunit.parsing.tokenizer import TokenStream, EOFException, LocationException @@ -63,7 +63,7 @@ def parse(self, file_name, include_paths=None, defines=None): defines = {} if defines is None else defines include_paths = [] if include_paths is None else include_paths - include_paths = [dirname(file_name)] + include_paths + include_paths = [str(Path(file_name).parent)] + include_paths cached = self._lookup_parse_cache(file_name, include_paths, defines) if cached is not None: @@ -99,7 +99,7 @@ def _key(file_name): """ Returns the database key for parse results of file_name """ - return ("CachedVerilogParser.parse(%s)" % abspath(file_name)).encode() + return ("CachedVerilogParser.parse(%s)" % str(Path(file_name).resolve)).encode() def _store_result(self, file_name, result, included_files, defines): """ @@ -124,7 +124,7 @@ def _content_hash(self, file_name): """ Hash the contents of the file """ - if file_name is None or not exists(file_name): + if file_name is None or not Path(file_name).exists(): return None if file_name not in self._content_cache: self._content_cache[file_name] = file_content_hash( diff --git a/vunit/parsing/verilog/preprocess.py b/vunit/parsing/verilog/preprocess.py index 4a0116e95..c93713c9a 100644 --- a/vunit/parsing/verilog/preprocess.py +++ b/vunit/parsing/verilog/preprocess.py @@ -10,7 +10,7 @@ """ Verilog parsing functionality """ -from os.path import join, exists, abspath +from pathlib import Path import logging from vunit.parsing.tokenizer import ( TokenStream, @@ -350,8 +350,8 @@ def find_included_file(include_paths, file_name): Find the file to include given include_paths """ for include_path in include_paths: - full_name = abspath(join(include_path, file_name)) - if exists(full_name): + full_name = str((Path(include_path) / file_name).resolve()) + if Path(full_name).exists(): return full_name return None diff --git a/vunit/project.py b/vunit/project.py index b66c32744..dc97cceeb 100644 --- a/vunit/project.py +++ b/vunit/project.py @@ -9,10 +9,9 @@ """ Functionality to represent and operate on a HDL code project """ -from os.path import join, basename, dirname, isdir, exists +from typing import Optional from pathlib import Path import logging -from typing import Optional from collections import OrderedDict from vunit.hashing import hash_string from vunit.dependency_graph import DependencyGraph, CircularDependencyException @@ -610,9 +609,12 @@ def _hash_file_name_of(self, source_file): Returns the name of the hash file associated with the source_file """ library = self.get_library(source_file.library.name) - prefix = hash_string(dirname(source_file.name)) - return join( - library.directory, prefix, basename(source_file.name) + ".vunit_hash" + prefix = hash_string(str(Path(source_file.name).parent)) + return str( + Path(library.directory) + / prefix + / Path(source_file.name).name + / ".vunit_hash" ) def update(self, source_file): diff --git a/vunit/sim_if/__init__.py b/vunit/sim_if/__init__.py index 3077962c4..6709b758f 100644 --- a/vunit/sim_if/__init__.py +++ b/vunit/sim_if/__init__.py @@ -10,7 +10,9 @@ import sys import os +from os import environ, listdir, pathsep import subprocess +from pathlib import Path from typing import List from ..ostools import Process, simplify_path from ..exceptions import CompileError @@ -77,12 +79,12 @@ def find_executable(executable): """ Return a list of all executables found in PATH """ - path = os.environ.get("PATH", None) + path = environ.get("PATH", None) if path is None: return [] - paths = path.split(os.pathsep) - _, ext = os.path.splitext(executable) + paths = path.split(pathsep) + ext = Path(executable).suffix if (sys.platform == "win32" or os.name == "os2") and (ext != ".exe"): executable = executable + ".exe" @@ -92,7 +94,7 @@ def find_executable(executable): result.append(executable) for prefix in paths: - file_name = os.path.join(prefix, executable) + file_name = str(Path(prefix) / executable) if isfile(file_name): # the file exists, we have a shot at spawn working result.append(file_name) @@ -133,7 +135,7 @@ def find_toolchain(cls, executables, constraints=None): all_paths = [ [ - os.path.abspath(os.path.dirname(executables)) + str(Path(executables).parent.resolve()) for executables in cls.find_executable(name) ] for name in executables @@ -329,12 +331,13 @@ def get_env(): def isfile(file_name): """ - Case insensitive os.path.isfile + Case insensitive Path.is_file() """ - if not os.path.isfile(file_name): + fpath = Path(file_name) + if not fpath.is_file(): return False - return os.path.basename(file_name) in os.listdir(os.path.dirname(file_name)) + return str(fpath.name) in listdir(str(fpath.parent)) def run_command(command, cwd=None, env=None): diff --git a/vunit/sim_if/activehdl.py b/vunit/sim_if/activehdl.py index e3dc0d2c6..d5af99616 100644 --- a/vunit/sim_if/activehdl.py +++ b/vunit/sim_if/activehdl.py @@ -9,7 +9,7 @@ """ from functools import total_ordering -from os.path import join, dirname, abspath +from pathlib import Path import os import re import logging @@ -58,7 +58,9 @@ def supports_vhdl_package_generics(cls): """ Returns True when this simulator supports VHDL package generics """ - proc = Process([join(cls.find_prefix(), "vcom"), "-version"], env=cls.get_env()) + proc = Process( + [str(Path(cls.find_prefix()) / "vcom"), "-version"], env=cls.get_env() + ) consumer = VersionConsumer() proc.consume_output(consumer) if consumer.version is not None: @@ -68,7 +70,7 @@ def supports_vhdl_package_generics(cls): def __init__(self, prefix, output_path, gui=False): SimulatorInterface.__init__(self, output_path, gui) - self._library_cfg = join(output_path, "library.cfg") + self._library_cfg = str(Path(output_path) / "library.cfg") self._prefix = prefix self._create_library_cfg() self._libraries = [] @@ -112,7 +114,12 @@ def compile_vhdl_file_command(self, source_file): Returns the command to compile a VHDL file """ return ( - [join(self._prefix, "vcom"), "-quiet", "-j", dirname(self._library_cfg)] + [ + str(Path(self._prefix) / "vcom"), + "-quiet", + "-j", + str(Path(self._library_cfg).parent), + ] + source_file.compile_options.get("activehdl.vcom_flags", []) + [ self._std_str(source_file.get_vhdl_standard()), @@ -126,7 +133,7 @@ def compile_verilog_file_command(self, source_file): """ Returns the command to compile a Verilog file """ - args = [join(self._prefix, "vlog"), "-quiet", "-lc", self._library_cfg] + args = [str(Path(self._prefix) / "vlog"), "-quiet", "-lc", self._library_cfg] args += source_file.compile_options.get("activehdl.vlog_flags", []) args += ["-work", source_file.library.name, source_file.name] for library in self._libraries: @@ -143,13 +150,15 @@ def create_library(self, library_name, path, mapped_libraries=None): """ mapped_libraries = mapped_libraries if mapped_libraries is not None else {} - if not file_exists(dirname(abspath(path))): - os.makedirs(dirname(abspath(path))) + apath = str(Path(path).parent.resolve()) + + if not file_exists(apath): + os.makedirs(apath) if not file_exists(path): proc = Process( - [join(self._prefix, "vlib"), library_name, path], - cwd=dirname(self._library_cfg), + [str(Path(self._prefix) / "vlib"), library_name, path], + cwd=str(Path(self._library_cfg).parent), env=self.get_env(), ) proc.consume_output(callback=None) @@ -158,8 +167,8 @@ def create_library(self, library_name, path, mapped_libraries=None): return proc = Process( - [join(self._prefix, "vmap"), library_name, path], - cwd=dirname(self._library_cfg), + [str(Path(self._prefix) / "vmap"), library_name, path], + cwd=str(Path(self._library_cfg).parent), env=self.get_env(), ) proc.consume_output(callback=None) @@ -173,7 +182,8 @@ def _create_library_cfg(self): with open(self._library_cfg, "w") as ofile: ofile.write( - '$INCLUDE = "%s"\n' % join(self._prefix, "..", "vlib", "library.cfg") + '$INCLUDE = "%s"\n' + % str(Path(self._prefix).parent / "vlib" / "library.cfg") ) _library_re = re.compile(r'([a-zA-Z_]+)\s=\s"(.*)"') @@ -192,7 +202,9 @@ def _get_mapped_libraries(self): continue key = match.group(1) value = match.group(2) - libraries[key] = abspath(join(dirname(self._library_cfg), dirname(value))) + libraries[key] = str( + (Path(self._library_cfg).parent / Path(value).parent).resolve() + ) return libraries def _vsim_extra_args(self, config): @@ -243,7 +255,7 @@ def _create_load_function(self, config, output_path): vsim_flags.append(config.architecture_name) if config.sim_options.get("enable_coverage", False): - coverage_file_path = join(output_path, "coverage.acdb") + coverage_file_path = str(Path(output_path) / "coverage.acdb") self._coverage_files.add(coverage_file_path) vsim_flags += ["-acdb_file {%s}" % fix_path(coverage_file_path)] @@ -323,12 +335,12 @@ def merge_coverage(self, file_name, args=None): merge_command += " -o {%s}" % fix_path(file_name) + "\n" - merge_script_name = join(self._output_path, "acdb_merge.tcl") + merge_script_name = str(Path(self._output_path) / "acdb_merge.tcl") with open(merge_script_name, "w") as fptr: fptr.write(merge_command + "\n") vcover_cmd = [ - join(self._prefix, "vsimsa"), + str(Path(self._prefix) / "vsimsa"), "-tcl", "%s" % fix_path(merge_script_name), ] @@ -380,7 +392,7 @@ def _create_gui_script(self, common_file_name, config): init_file = config.sim_options.get(self.name + ".init_file.gui", None) if init_file is not None: - tcl += 'source "%s"\n' % fix_path(abspath(init_file)) + tcl += 'source "%s"\n' % fix_path(str(Path(init_file).resolve())) tcl += ( 'puts "VUnit help: Design already loaded. Use run -all to run the test."\n' @@ -399,10 +411,10 @@ def _run_batch_file(self, batch_file_name, gui, cwd): try: args = [ - join(self._prefix, "vsim"), + str(Path(self._prefix) / "vsim"), "-gui" if gui else "-c", "-l", - join(dirname(batch_file_name), "transcript"), + str(Path(batch_file_name).parent / "transcript"), "-do", todo, ] @@ -417,24 +429,27 @@ def simulate(self, output_path, test_suite_name, config, elaborate_only): """ Run a test bench """ - script_path = join(output_path, self.name) - common_file_name = join(script_path, "common.tcl") - batch_file_name = join(script_path, "batch.tcl") - gui_file_name = join(script_path, "gui.tcl") + script_path = Path(output_path) / self.name + common_file_name = script_path / "common.tcl" + batch_file_name = script_path / "batch.tcl" + gui_file_name = script_path / "gui.tcl" write_file(common_file_name, self._create_common_script(config, output_path)) - write_file(gui_file_name, self._create_gui_script(common_file_name, config)) write_file( - batch_file_name, self._create_batch_script(common_file_name, elaborate_only) + gui_file_name, self._create_gui_script(str(common_file_name), config) + ) + write_file( + str(batch_file_name), + self._create_batch_script(str(common_file_name), elaborate_only), ) if self._gui: - gui_path = join(script_path, "gui") + gui_path = str(script_path / "gui") renew_path(gui_path) - return self._run_batch_file(gui_file_name, gui=True, cwd=gui_path) + return self._run_batch_file(str(gui_file_name), gui=True, cwd=gui_path) return self._run_batch_file( - batch_file_name, gui=False, cwd=dirname(self._library_cfg) + str(batch_file_name), gui=False, cwd=str(Path(self._library_cfg).parent) ) diff --git a/vunit/sim_if/ghdl.py b/vunit/sim_if/ghdl.py index 0e70e9f88..b32c1604d 100644 --- a/vunit/sim_if/ghdl.py +++ b/vunit/sim_if/ghdl.py @@ -9,8 +9,7 @@ """ from pathlib import Path -from os.path import exists, join, abspath -import os +from os import environ, makedirs, remove import logging import subprocess import shlex @@ -32,7 +31,7 @@ class GHDLInterface(SimulatorInterface): """ name = "ghdl" - executable = os.environ.get("GHDL", "ghdl") + executable = environ.get("GHDL", "ghdl") supports_gui_flag = True supports_colors_in_gui = True @@ -122,7 +121,7 @@ def _get_version_output(cls, prefix): Get the output of 'ghdl --version' """ return subprocess.check_output( - [join(prefix, cls.executable), "--version"] + [str(Path(prefix) / cls.executable), "--version"] ).decode() @classmethod @@ -183,8 +182,8 @@ def setup_library_mapping(self, project): """ self._project = project for library in project.get_libraries(): - if not exists(library.directory): - os.makedirs(library.directory) + if not Path(library.directory).exists(): + makedirs(library.directory) vhdl_standards = set( source_file.get_vhdl_standard() @@ -233,7 +232,7 @@ def compile_vhdl_file_command(self, source_file): Returns the command to compile a vhdl file """ cmd = [ - join(self._prefix, self.executable), + str(Path(self._prefix) / self.executable), "-a", "--workdir=%s" % source_file.library.directory, "--work=%s" % source_file.library.name, @@ -262,7 +261,7 @@ def _get_command(self, config, output_path, elaborate_only, ghdl_e, wave_file): """ Return GHDL simulation command """ - cmd = [join(self._prefix, self.executable)] + cmd = [str(Path(self._prefix) / self.executable)] if ghdl_e: cmd += ["-e"] @@ -276,8 +275,9 @@ def _get_command(self, config, output_path, elaborate_only, ghdl_e, wave_file): ] cmd += ["-P%s" % lib.directory for lib in self._project.get_libraries()] - bin_path = join( - output_path, "%s-%s" % (config.entity_name, config.architecture_name) + bin_path = str( + Path(output_path) + / ("%s-%s" % (config.entity_name, config.architecture_name)) ) if self._has_output_flag(): cmd += ["-o", bin_path] @@ -306,7 +306,7 @@ def _get_command(self, config, output_path, elaborate_only, ghdl_e, wave_file): os.makedirs(output_path, mode=0o777) except OSError: pass - with open(join(output_path, "args.json"), "w") as fname: + with (Path(output_path) / "args.json").open("w") as fname: dump( { "bin": str( @@ -328,17 +328,17 @@ def simulate( # pylint: disable=too-many-locals Simulate with entity as top level using generics """ - script_path = join(output_path, self.name) + script_path = str(Path(output_path) / self.name) - if not exists(script_path): - os.makedirs(script_path) + if not Path(script_path).exists(): + makedirs(script_path) ghdl_e = elaborate_only and config.sim_options.get("ghdl.elab_e", False) if self._gtkwave_fmt is not None: - data_file_name = join(script_path, "wave.%s" % self._gtkwave_fmt) - if exists(data_file_name): - os.remove(data_file_name) + data_file_name = str(Path(script_path) / ("wave.%s" % self._gtkwave_fmt)) + if Path(data_file_name).exists(): + remove(data_file_name) else: data_file_name = None @@ -358,7 +358,7 @@ def simulate( # pylint: disable=too-many-locals init_file = config.sim_options.get(self.name + ".gtkwave_script.gui", None) if init_file is not None: - cmd += ["--script", "{}".format(abspath(init_file))] + cmd += ["--script", "{}".format(str(Path(init_file).resolve()))] stdout.write("%s\n" % " ".join(cmd)) subprocess.call(cmd) diff --git a/vunit/sim_if/incisive.py b/vunit/sim_if/incisive.py index c0ec4bad4..5dc683b00 100644 --- a/vunit/sim_if/incisive.py +++ b/vunit/sim_if/incisive.py @@ -8,7 +8,8 @@ Interface for the Cadence Incisive simulator """ -from os.path import join, dirname, abspath, relpath +from pathlib import Path +from os.path import relpath import os import subprocess import logging @@ -94,9 +95,9 @@ def __init__( # pylint: disable=too-many-arguments self._libraries = [] self._log_level = log_level if cdslib is None: - self._cdslib = abspath(join(output_path, "cds.lib")) + self._cdslib = str((Path(output_path) / "cds.lib").resolve()) else: - self._cdslib = abspath(cdslib) + self._cdslib = str(Path(cdslib).resolve()) self._hdlvar = hdlvar self._cds_root_irun = self.find_cds_root_irun() self._create_cdslib() @@ -106,7 +107,7 @@ def find_cds_root_irun(self): Finds irun cds root """ return subprocess.check_output( - [join(self._prefix, "cds_root"), "irun"] + [str(Path(self._prefix) / "cds_root"), "irun"] ).splitlines()[0] def find_cds_root_virtuoso(self): @@ -115,7 +116,7 @@ def find_cds_root_virtuoso(self): """ try: return subprocess.check_output( - [join(self._prefix, "cds_root"), "virtuoso"] + [str(Path(self._prefix) / "cds_root"), "virtuoso"] ).splitlines()[0] except subprocess.CalledProcessError: return None @@ -192,7 +193,7 @@ def compile_vhdl_file_command(self, source_file): """ Returns command to compile a VHDL file """ - cmd = join(self._prefix, "irun") + cmd = str(Path(self._prefix) / "irun") args = [] args += ["-compile"] args += ["-nocopyright"] @@ -205,9 +206,9 @@ def compile_vhdl_file_command(self, source_file): args += self._hdlvar_args() args += [ '-log "%s"' - % join( - self._output_path, - "irun_compile_vhdl_file_%s.log" % source_file.library.name, + % str( + Path(self._output_path) + / ("irun_compile_vhdl_file_%s.log" % source_file.library.name) ) ] if not self._log_level == "debug": @@ -216,13 +217,13 @@ def compile_vhdl_file_command(self, source_file): args += ["-messages"] args += ["-libverbose"] args += source_file.compile_options.get("incisive.irun_vhdl_flags", []) - args += ['-nclibdirname "%s"' % dirname(source_file.library.directory)] + args += ['-nclibdirname "%s"' % str(Path(source_file.library.directory).parent)] args += ["-makelib %s" % source_file.library.directory] args += ['"%s"' % source_file.name] args += ["-endlib"] - argsfile = join( - self._output_path, - "irun_compile_vhdl_file_%s.args" % source_file.library.name, + argsfile = str( + Path(self._output_path) + / ("irun_compile_vhdl_file_%s.args" % source_file.library.name) ) write_file(argsfile, "\n".join(args)) return [cmd, "-f", argsfile] @@ -231,7 +232,7 @@ def compile_verilog_file_command(self, source_file): """ Returns commands to compile a Verilog file """ - cmd = join(self._prefix, "irun") + cmd = str(Path(self._prefix) / "irun") args = [] args += ["-compile"] args += ["-nocopyright"] @@ -248,9 +249,9 @@ def compile_verilog_file_command(self, source_file): args += self._hdlvar_args() args += [ '-log "%s"' - % join( - self._output_path, - "irun_compile_verilog_file_%s.log" % source_file.library.name, + % str( + Path(self._output_path) + / ("irun_compile_verilog_file_%s.log" % source_file.library.name) ) ] if not self._log_level == "debug": @@ -266,13 +267,13 @@ def compile_verilog_file_command(self, source_file): for key, value in source_file.defines.items(): args += ["-define %s=%s" % (key, value.replace('"', '\\"'))] - args += ['-nclibdirname "%s"' % dirname(source_file.library.directory)] + args += ['-nclibdirname "%s"' % str(Path(source_file.library.directory).parent)] args += ["-makelib %s" % source_file.library.name] args += ['"%s"' % source_file.name] args += ["-endlib"] - argsfile = join( - self._output_path, - "irun_compile_verilog_file_%s.args" % source_file.library.name, + argsfile = str( + Path(self._output_path) + / ("irun_compile_verilog_file_%s.args" % source_file.library.name) ) write_file(argsfile, "\n".join(args)) return [cmd, "-f", argsfile] @@ -283,8 +284,10 @@ def create_library(self, library_name, library_path, mapped_libraries=None): """ mapped_libraries = mapped_libraries if mapped_libraries is not None else {} - if not file_exists(dirname(abspath(library_path))): - os.makedirs(dirname(abspath(library_path))) + lpath = str(Path(library_path).resolve().parent) + + if not file_exists(lpath): + os.makedirs(lpath) if ( library_name in mapped_libraries @@ -310,7 +313,7 @@ def simulate( # pylint: disable=too-many-locals Elaborates and Simulates with entity as top level using generics """ - script_path = join(output_path, self.name) + script_path = str(Path(output_path) / self.name) launch_gui = self._gui is not False and not elaborate_only if elaborate_only: @@ -319,7 +322,7 @@ def simulate( # pylint: disable=too-many-locals steps = ["elaborate", "simulate"] for step in steps: - cmd = join(self._prefix, "irun") + cmd = str(Path(self._prefix) / "irun") args = [] if step == "elaborate": args += ["-elaborate"] @@ -345,12 +348,12 @@ def simulate( # pylint: disable=too-many-locals ] # promote to error: "bad natural literal in generic association" args += ["-work work"] args += [ - '-nclibdirname "%s"' % (join(self._output_path, "libraries")) + '-nclibdirname "%s"' % (str(Path(self._output_path) / "libraries")) ] # @TODO: ugly args += config.sim_options.get("incisive.irun_sim_flags", []) args += ['-cdslib "%s"' % self._cdslib] args += self._hdlvar_args() - args += ['-log "%s"' % join(script_path, "irun_%s.log" % step)] + args += ['-log "%s"' % str(Path(script_path) / ("irun_%s.log" % step))] if not self._log_level == "debug": args += ["-quiet"] else: @@ -369,21 +372,15 @@ def simulate( # pylint: disable=too-many-locals if config.architecture_name is None: # we have a SystemVerilog toplevel: - args += [ - "-top %s" - % join("%s.%s:sv" % (config.library_name, config.entity_name)) - ] + args += ["-top %s.%s:sv" % (config.library_name, config.entity_name)] else: # we have a VHDL toplevel: args += [ - "-top %s" - % join( - "%s.%s:%s" - % ( - config.library_name, - config.entity_name, - config.architecture_name, - ) + "-top %s.%s:%s" + % ( + config.library_name, + config.entity_name, + config.architecture_name, ) ] argsfile = "%s/irun_%s.args" % (script_path, step) diff --git a/vunit/sim_if/modelsim.py b/vunit/sim_if/modelsim.py index 1a5af9352..e14e22f37 100644 --- a/vunit/sim_if/modelsim.py +++ b/vunit/sim_if/modelsim.py @@ -8,7 +8,7 @@ Interface towards Mentor Graphics ModelSim """ -from os.path import join, dirname, abspath +from pathlib import Path import os import logging import io @@ -70,7 +70,7 @@ def find_prefix_from_path(cls): """ def has_modelsim_ini(path): - return os.path.isfile(join(path, "..", "modelsim.ini")) + return os.path.isfile(str(Path(path) / "modelsim.ini")) return cls.find_toolchain(["vsim"], constraints=[has_modelsim_ini]) @@ -87,7 +87,7 @@ def __init__(self, prefix, output_path, persistent=False, gui=False): self, prefix, persistent, - sim_cfg_file_name=join(output_path, "modelsim.ini"), + sim_cfg_file_name=str(Path(output_path) / "modelsim.ini"), ) self._libraries = [] self._coverage_files = set() @@ -98,12 +98,12 @@ def _create_modelsim_ini(self): """ Create the modelsim.ini file """ - parent = dirname(self._sim_cfg_file_name) + parent = str(Path(self._sim_cfg_file_name).parent) if not file_exists(parent): os.makedirs(parent) original_modelsim_ini = os.environ.get( - "VUNIT_MODELSIM_INI", join(self._prefix, "..", "modelsim.ini") + "VUNIT_MODELSIM_INI", str(Path(self._prefix).parent / "modelsim.ini") ) with open(original_modelsim_ini, "rb") as fread: with open(self._sim_cfg_file_name, "wb") as fwrite: @@ -157,7 +157,7 @@ def compile_vhdl_file_command(self, source_file): """ return ( [ - join(self._prefix, "vcom"), + str(Path(self._prefix) / "vcom"), "-quiet", "-modelsimini", self._sim_cfg_file_name, @@ -176,7 +176,7 @@ def compile_verilog_file_command(self, source_file): Returns the command to compile a verilog file """ args = [ - join(self._prefix, "vlog"), + str(Path(self._prefix) / "vlog"), "-quiet", "-modelsimini", self._sim_cfg_file_name, @@ -200,12 +200,14 @@ def create_library(self, library_name, path, mapped_libraries=None): """ mapped_libraries = mapped_libraries if mapped_libraries is not None else {} - if not file_exists(dirname(abspath(path))): - os.makedirs(dirname(abspath(path))) + apath = str(Path(path).parent.resolve()) + + if not file_exists(apath): + os.makedirs(apath) if not file_exists(path): proc = Process( - [join(self._prefix, "vlib"), "-unix", path], env=self.get_env() + [str(Path(self._prefix) / "vlib"), "-unix", path], env=self.get_env() ) proc.consume_output(callback=None) @@ -247,7 +249,7 @@ def _create_load_function(self, test_suite_name, config, output_path): architecture_suffix = "(%s)" % config.architecture_name if config.sim_options.get("enable_coverage", False): - coverage_file = join(output_path, "coverage.ucdb") + coverage_file = str(Path(output_path) / "coverage.ucdb") self._coverage_files.add(coverage_file) coverage_save_cmd = ( "coverage save -onexit -testname {%s} -assert -directive -cvg -codeAll {%s}" @@ -259,7 +261,7 @@ def _create_load_function(self, test_suite_name, config, output_path): coverage_args = "" vsim_flags = [ - "-wlf {%s}" % fix_path(join(output_path, "vsim.wlf")), + "-wlf {%s}" % fix_path(str(Path(output_path) / "vsim.wlf")), "-quiet", "-t ps", # for correct handling of verilog fatal/finish @@ -383,9 +385,9 @@ def merge_coverage(self, file_name, args=None): if args is None: args = [] - coverage_files = join(self._output_path, "coverage_files.txt") + coverage_files = str(Path(self._output_path) / "coverage_files.txt") vcover_cmd = ( - [join(self._prefix, "vcover"), "merge", "-inputs"] + [str(Path(self._prefix) / "vcover"), "merge", "-inputs"] + [coverage_files] + args + [file_name] diff --git a/vunit/sim_if/rivierapro.py b/vunit/sim_if/rivierapro.py index 0cb9b1766..2e66ad7ce 100644 --- a/vunit/sim_if/rivierapro.py +++ b/vunit/sim_if/rivierapro.py @@ -8,7 +8,7 @@ Interface towards Aldec Riviera Pro """ -from os.path import join, dirname, abspath +from pathlib import Path import os import re import logging @@ -66,7 +66,7 @@ def find_prefix_from_path(cls): """ def no_avhdl(path): - return not file_exists(join(path, "avhdl.exe")) + return not file_exists(str(Path(path) / "avhdl.exe")) return cls.find_toolchain(["vsim", "vsimsa"], constraints=[no_avhdl]) @@ -75,7 +75,9 @@ def get_osvvm_coverage_api(cls): """ Returns simulator name when OSVVM coverage API is supported, None otherwise. """ - proc = Process([join(cls.find_prefix(), "vcom"), "-version"], env=cls.get_env()) + proc = Process( + [str(Path(cls.find_prefix()) / "vcom"), "-version"], env=cls.get_env() + ) consumer = VersionConsumer() proc.consume_output(consumer) if consumer.year is not None: @@ -96,7 +98,10 @@ def supports_vhdl_package_generics(cls): def __init__(self, prefix, output_path, persistent=False, gui=False): SimulatorInterface.__init__(self, output_path, gui) VsimSimulatorMixin.__init__( - self, prefix, persistent, sim_cfg_file_name=join(output_path, "library.cfg") + self, + prefix, + persistent, + sim_cfg_file_name=str(Path(output_path) / "library.cfg"), ) self._create_library_cfg() self._libraries = [] @@ -151,10 +156,10 @@ def compile_vhdl_file_command(self, source_file): return ( [ - join(self._prefix, "vcom"), + str(Path(self._prefix) / "vcom"), "-quiet", "-j", - dirname(self._sim_cfg_file_name), + str(Path(self._sim_cfg_file_name).parent), ] + source_file.compile_options.get("rivierapro.vcom_flags", []) + [ @@ -169,7 +174,12 @@ def compile_verilog_file_command(self, source_file): """ Returns the command to compile a Verilog file """ - args = [join(self._prefix, "vlog"), "-quiet", "-lc", self._sim_cfg_file_name] + args = [ + str(Path(self._prefix) / "vlog"), + "-quiet", + "-lc", + self._sim_cfg_file_name, + ] if source_file.is_system_verilog: args += ["-sv2k12"] args += source_file.compile_options.get("rivierapro.vlog_flags", []) @@ -188,13 +198,15 @@ def create_library(self, library_name, path, mapped_libraries=None): """ mapped_libraries = mapped_libraries if mapped_libraries is not None else {} - if not file_exists(dirname(abspath(path))): - os.makedirs(dirname(abspath(path))) + apath = str(Path(path).parent.resolve()) + + if not file_exists(apath): + os.makedirs(apath) if not file_exists(path): proc = Process( - [join(self._prefix, "vlib"), library_name, path], - cwd=dirname(self._sim_cfg_file_name), + [str(Path(self._prefix) / "vlib"), library_name, path], + cwd=str(Path(self._sim_cfg_file_name).parent), env=self.get_env(), ) proc.consume_output(callback=None) @@ -203,8 +215,8 @@ def create_library(self, library_name, path, mapped_libraries=None): return proc = Process( - [join(self._prefix, "vmap"), library_name, path], - cwd=dirname(self._sim_cfg_file_name), + [str(Path(self._prefix) / "vmap"), library_name, path], + cwd=str(Path(self._sim_cfg_file_name).parent), env=self.get_env(), ) proc.consume_output(callback=None) @@ -221,7 +233,7 @@ def _create_library_cfg(self): @property def _builtin_library_cfg(self): - return join(self._prefix, "..", "vlib", "library.cfg") + return str(Path(self._prefix).parent / "vlib" / "library.cfg") _library_re = re.compile(r"([a-zA-Z_0-9]+)\s=\s(.*)") @@ -230,7 +242,9 @@ def _get_mapped_libraries(self, library_cfg_file): Get mapped libraries by running vlist on the working directory """ lines = [] - proc = Process([join(self._prefix, "vlist")], cwd=dirname(library_cfg_file)) + proc = Process( + [str(Path(self._prefix) / "vlist")], cwd=str(Path(library_cfg_file).parent) + ) proc.consume_output(callback=lines.append) libraries = {} @@ -240,7 +254,9 @@ def _get_mapped_libraries(self, library_cfg_file): continue key = match.group(1) value = match.group(2) - libraries[key] = abspath(join(dirname(library_cfg_file), dirname(value))) + libraries[key] = str( + (Path(library_cfg_file).parent / (Path(value).parent)).resolve() + ) return libraries def _create_load_function( @@ -260,13 +276,13 @@ def _create_load_function( ) vsim_flags = [ - "-dataset {%s}" % fix_path(join(output_path, "dataset.asdb")), + "-dataset {%s}" % fix_path(str(Path(output_path) / "dataset.asdb")), pli_str, set_generic_str, ] if config.sim_options.get("enable_coverage", False): - coverage_file_path = join(output_path, "coverage.acdb") + coverage_file_path = str(Path(output_path) / "coverage.acdb") self._coverage_files.add(coverage_file_path) vsim_flags += ["-acdb_file {%s}" % coverage_file_path] @@ -384,12 +400,12 @@ def merge_coverage(self, file_name, args=None): merge_command += " -o {%s}" % file_name.replace("\\", "/") - merge_script_name = join(self._output_path, "acdb_merge.tcl") + merge_script_name = str(Path(self._output_path) / "acdb_merge.tcl") with open(merge_script_name, "w") as fptr: fptr.write(merge_command + "\n") vcover_cmd = [ - join(self._prefix, "vsim"), + str(Path(self._prefix) / "vsim"), "-c", "-do", "source %s; quit;" % merge_script_name.replace("\\", "/"), diff --git a/vunit/sim_if/vsim_simulator_mixin.py b/vunit/sim_if/vsim_simulator_mixin.py index f5eee9a07..81d0cad3a 100644 --- a/vunit/sim_if/vsim_simulator_mixin.py +++ b/vunit/sim_if/vsim_simulator_mixin.py @@ -11,7 +11,7 @@ import sys import os -from os.path import join, dirname, abspath +from pathlib import Path from ..ostools import write_file, Process from ..test.suites import get_result_file_name from ..persistent_tcl_shell import PersistentTclShell @@ -25,7 +25,7 @@ class VsimSimulatorMixin(object): def __init__(self, prefix, persistent, sim_cfg_file_name): self._prefix = prefix - sim_cfg_file_name = abspath(sim_cfg_file_name) + sim_cfg_file_name = str(Path(sim_cfg_file_name).resolve()) self._sim_cfg_file_name = sim_cfg_file_name prefix = ( @@ -36,14 +36,14 @@ def __init__(self, prefix, persistent, sim_cfg_file_name): def create_process(ident): return Process( [ - join(prefix, "vsim"), + str(Path(prefix) / "vsim"), "-c", "-l", - join(dirname(sim_cfg_file_name), "transcript%i" % ident), + str(Path(sim_cfg_file_name).parent / ("transcript%i" % ident)), "-do", - abspath(join(dirname(__file__), "tcl_read_eval_loop.tcl")), + str((Path(__file__).parent / "tcl_read_eval_loop.tcl").resolve()), ], - cwd=dirname(sim_cfg_file_name), + cwd=str(Path(sim_cfg_file_name).parent), env=env, ) @@ -91,7 +91,7 @@ def _create_restart_function(): "stdout=sys.stdout, " "stderr=sys.stdout))" ) - % (recompile_command, abspath(os.getcwd())), + % (recompile_command, str(Path(os.getcwd()).resolve())), ] recompile_command_eval_tcl = " ".join( ["{%s}" % part for part in recompile_command_eval] @@ -254,8 +254,8 @@ def _source_tcl_file(file_name, config, message): } """ tcl = template % ( - fix_path(abspath(config.tb_path)), - fix_path(abspath(file_name)), + fix_path(str(Path(config.tb_path).resolve())), + fix_path(str(Path(file_name).resolve())), message, ) return tcl @@ -279,15 +279,15 @@ def _run_batch_file(self, batch_file_name, gui=False): try: args = [ - join(self._prefix, "vsim"), + str(Path(self._prefix) / "vsim"), "-gui" if gui else "-c", "-l", - join(dirname(batch_file_name), "transcript"), + str(Path(batch_file_name).parent / "transcript"), "-do", 'source "%s"' % fix_path(batch_file_name), ] - proc = Process(args, cwd=dirname(self._sim_cfg_file_name)) + proc = Process(args, cwd=str(Path(self._sim_cfg_file_name).parent)) proc.consume_output() except Process.NonZeroExitCode: return False @@ -316,30 +316,33 @@ def simulate(self, output_path, test_suite_name, config, elaborate_only): """ Run a test bench """ - script_path = join(output_path, self.name) + script_path = Path(output_path) / self.name - common_file_name = join(script_path, "common.do") - gui_file_name = join(script_path, "gui.do") - batch_file_name = join(script_path, "batch.do") + common_file_name = script_path / "common.do" + gui_file_name = script_path / "gui.do" + batch_file_name = script_path / "batch.do" write_file( - common_file_name, + str(common_file_name), self._create_common_script( test_suite_name, config, script_path, output_path ), ) - write_file(gui_file_name, self._create_gui_script(common_file_name, config)) write_file( - batch_file_name, self._create_batch_script(common_file_name, elaborate_only) + str(gui_file_name), self._create_gui_script(str(common_file_name), config) + ) + write_file( + str(batch_file_name), + self._create_batch_script(str(common_file_name), elaborate_only), ) if self._gui: - return self._run_batch_file(gui_file_name, gui=True) + return self._run_batch_file(str(gui_file_name), gui=True) if self._persistent_shell is not None: - return self._run_persistent(common_file_name, load_only=elaborate_only) + return self._run_persistent(str(common_file_name), load_only=elaborate_only) - return self._run_batch_file(batch_file_name) + return self._run_batch_file(str(batch_file_name)) def fix_path(path): diff --git a/vunit/source_file.py b/vunit/source_file.py index 4dc763ba8..81731bba2 100644 --- a/vunit/source_file.py +++ b/vunit/source_file.py @@ -9,7 +9,6 @@ """ from pathlib import Path from typing import Union -from os.path import splitext import logging from copy import copy import traceback @@ -369,7 +368,7 @@ def file_type_of(file_name): """ Return the file type of file_name based on the file ending """ - _, ext = splitext(file_name) + ext = str(Path(file_name).suffix) if ext.lower() in VHDL_EXTENSIONS: return "vhdl" diff --git a/vunit/test/bench.py b/vunit/test/bench.py index f3035ef92..d411c60ea 100644 --- a/vunit/test/bench.py +++ b/vunit/test/bench.py @@ -9,7 +9,7 @@ """ import logging -from os.path import basename +from pathlib import Path import re import bisect import collections @@ -98,7 +98,7 @@ def _check_architectures(design_unit): % ( design_unit.name, ", ".join( - "%s:%s" % (name, basename(fname)) + "%s:%s" % (name, str(Path(fname).name)) for name, fname in sorted( design_unit.architecture_names.items() ) diff --git a/vunit/test/report.py b/vunit/test/report.py index 9efb026e0..19900965e 100644 --- a/vunit/test/report.py +++ b/vunit/test/report.py @@ -13,7 +13,7 @@ import os import socket import re -from os.path import dirname +from pathlib import Path from vunit.color_printer import COLOR_PRINTER from vunit.ostools import read_file @@ -325,5 +325,5 @@ def to_dict(self): return { "status": self._status.name, "time": self.time, - "path": dirname(self._output_file_name), + "path": str(Path(self._output_file_name).parent), } diff --git a/vunit/test/runner.py b/vunit/test/runner.py index 5ab718e0a..bc514ff47 100644 --- a/vunit/test/runner.py +++ b/vunit/test/runner.py @@ -9,7 +9,8 @@ """ import os -from os.path import join, exists, abspath, basename, relpath +from os.path import relpath +from pathlib import Path import traceback import threading import sys @@ -77,7 +78,7 @@ def run(self, test_suites): Run a list of test suites """ - if not exists(self._output_path): + if not Path(self._output_path).exists(): os.makedirs(self._output_path) self._create_test_mapping_file(test_suites) @@ -145,7 +146,7 @@ def _run_thread(self, write_stdout, scheduler, num_tests, is_main): test_suite = scheduler.next() output_path = create_output_path(self._output_path, test_suite.name) - output_file_name = join(output_path, "output.txt") + output_file_name = str(Path(output_path) / "output.txt") with self._stdout_lock(): for test_name in test_suite.test_names: @@ -187,7 +188,7 @@ def _run_test_suite( """ Run the actual test suite """ - color_output_file_name = join(output_path, "output_with_color.txt") + color_output_file_name = str(Path(output_path) / "output_with_color.txt") output_file = None color_output_file = None @@ -263,11 +264,13 @@ def _create_test_mapping_file(self, test_suites): Create a file mapping test name to test output folder. This is to allow the user to find the test output folder when it is hashed """ - mapping_file_name = join(self._output_path, "test_name_to_path_mapping.txt") + mapping_file_name = str( + Path(self._output_path) / "test_name_to_path_mapping.txt" + ) # Load old mapping to remember non-deleted test folders as well # even when re-running only a single test case - if exists(mapping_file_name): + if Path(mapping_file_name).exists(): with open(mapping_file_name, "r") as fptr: mapping = set(fptr.read().splitlines()) else: @@ -275,7 +278,7 @@ def _create_test_mapping_file(self, test_suites): for test_suite in test_suites: test_output = create_output_path(self._output_path, test_suite.name) - mapping.add("%s %s" % (basename(test_output), test_suite.name)) + mapping.add("%s %s" % (Path(test_output).name, test_suite.name)) # Sort by everything except hash mapping = sorted(mapping, key=lambda value: value[value.index(" ") :]) @@ -288,7 +291,7 @@ def _print_output(self, output_file_name): """ Print contents of output file if it exists """ - with open(output_file_name, "r") as fread: + with Path(output_file_name).open("r") as fread: for line in fread.readlines(): self._stdout_ansi.write(line) @@ -433,7 +436,7 @@ def create_output_path(output_path, test_suite_name): Create the full output path of a test case. Ensure no bad characters and no long path names. """ - output_path = abspath(output_path) + output_path = str(Path(output_path).resolve()) safe_name = ( "".join(char if _is_legal(char) else "_" for char in test_suite_name) + "_" ) @@ -454,7 +457,7 @@ def create_output_path(output_path, test_suite_name): else: full_name = safe_name + hash_name - return join(output_path, full_name) + return str(Path(output_path) / full_name) def wrap(file_obj, use_color=True): diff --git a/vunit/test/suites.py b/vunit/test/suites.py index 846ff06af..e7d069937 100644 --- a/vunit/test/suites.py +++ b/vunit/test/suites.py @@ -8,7 +8,7 @@ Contains different kinds of test suites """ -from os.path import join +from pathlib import Path from .. import ostools from .report import PASSED, SKIPPED, FAILED @@ -345,4 +345,4 @@ def _full_name(test_suite_name, test_case_name): def get_result_file_name(output_path): - return join(output_path, "vunit_results") + return str(Path(output_path) / "vunit_results") diff --git a/vunit/ui/__init__.py b/vunit/ui/__init__.py index d6dca10ed..97698591c 100644 --- a/vunit/ui/__init__.py +++ b/vunit/ui/__init__.py @@ -17,7 +17,7 @@ import json import os from typing import Optional, Set -from os.path import exists, abspath, join, basename, normpath, dirname +from pathlib import Path from fnmatch import fnmatch from ..database import PickledDataBase, DataBase from .. import ostools @@ -106,7 +106,7 @@ def __init__( ): self._args = args self._configure_logging(args.log_level) - self._output_path = abspath(args.output_path) + self._output_path = str(Path(args.output_path).resolve()) if args.no_color: self._printer = NO_COLOR_PRINTER @@ -135,10 +135,12 @@ def test_filter(name, attribute_names): # Use default simulator options if no simulator was present if self._simulator_class is None: simulator_class = SimulatorInterface - self._simulator_output_path = join(self._output_path, "none") + self._simulator_output_path = str(Path(self._output_path) / "none") else: simulator_class = self._simulator_class - self._simulator_output_path = join(self._output_path, simulator_class.name) + self._simulator_output_path = str( + Path(self._output_path) / simulator_class.name + ) self._create_output_path(args.clean) @@ -161,7 +163,7 @@ def _create_database(self): Check for Python version used to create the database is the same as the running python instance or re-create """ - project_database_file_name = join(self._output_path, "project_database") + project_database_file_name = str(Path(self._output_path) / "project_database") create_new = False key = b"version" version = str((9, sys.version)).encode() @@ -223,7 +225,7 @@ def add_external_library( self._project.add_library( library_name, - abspath(path), + str(Path(path).resolve()), self._which_vhdl_standard(vhdl_standard), is_external=True, ) @@ -252,8 +254,8 @@ def add_source_files_from_csv( if len(row) == 2: lib_name = row[0].strip() no_normalized_file = row[1].strip() - file_name_ = normpath( - join(dirname(project_csv_path), no_normalized_file) + file_name_ = str( + (Path(project_csv_path).parent / no_normalized_file).resolve() ) lib = ( self.library(lib_name) @@ -291,10 +293,9 @@ def add_library( """ standard = self._which_vhdl_standard(vhdl_standard) - - path = join(self._simulator_output_path, "libraries", library_name) + path = Path(self._simulator_output_path) / "libraries" / library_name if not self._project.has_library(library_name): - self._project.add_library(library_name, abspath(path), standard) + self._project.add_library(library_name, str(path.resolve()), standard) elif not allow_duplicate: raise ValueError( "Library %s already added. Use allow_duplicate to ignore this error." @@ -487,7 +488,7 @@ def get_source_files(self, pattern="*", library_name=None, allow_empty=False): continue if not ( - fnmatch(abspath(source_file.name), pattern) + fnmatch(str(Path(source_file.name).resolve()), pattern) or fnmatch(ostools.simplify_path(source_file.name), pattern) ): continue @@ -607,10 +608,12 @@ def _preprocess(self, library_name, file_name, preprocessors): if not preprocessors: return file_name + fname = str(Path(file_name).name) + try: code = ostools.read_file(file_name, encoding=HDL_FILE_ENCODING) for preprocessor in preprocessors: - code = preprocessor.run(code, basename(file_name)) + code = preprocessor.run(code, fname) except KeyboardInterrupt: raise KeyboardInterrupt except: # pylint: disable=bare-except @@ -618,19 +621,17 @@ def _preprocess(self, library_name, file_name, preprocessors): LOGGER.error("Failed to preprocess %s", file_name) return file_name else: - pp_file_name = join( - self._preprocessed_path, library_name, basename(file_name) - ) + pp_file_name = str(Path(self._preprocessed_path) / library_name / fname) idx = 1 while ostools.file_exists(pp_file_name): LOGGER.debug( "Preprocessed file exists '%s', adding prefix", pp_file_name ) - pp_file_name = join( - self._preprocessed_path, - library_name, - "%i_%s" % (idx, basename(file_name)), + pp_file_name = str( + Path(self._preprocessed_path) + / library_name + / ("%i_%s" % (idx, fname)), ) idx += 1 @@ -749,7 +750,7 @@ def _create_simulator_if(self): ) sys.exit(1) - if not exists(self._simulator_output_path): + if not Path(self._simulator_output_path).exists(): os.makedirs(self._simulator_output_path) return self._simulator_class.from_args( @@ -810,7 +811,7 @@ def _main_export_json(self, json_file_name): # pylint: disable=too-many-locals for source_file in file_objects: files.append( dict( - file_name=abspath(source_file.name), + file_name=str(Path(source_file.name).resolve()), library_name=source_file.library.name, ) ) @@ -879,7 +880,7 @@ def _create_output_path(self, clean): """ if clean: ostools.renew_path(self._output_path) - elif not exists(self._output_path): + elif not Path(self._output_path).exists(): os.makedirs(self._output_path) ostools.renew_path(self._preprocessed_path) @@ -890,11 +891,11 @@ def vhdl_standard(self) -> str: @property def _preprocessed_path(self): - return join(self._output_path, "preprocessed") + return str(Path(self._output_path) / "preprocessed") @property def codecs_path(self): - return join(self._output_path, "codecs") + return str(Path(self._output_path) / "codecs") def _compile(self, simulator_if): """ @@ -940,7 +941,7 @@ def _run_test(self, test_cases, report): runner = TestRunner( report, - join(self._output_path, TEST_OUTPUT_PATH), + str(Path(self._output_path) / TEST_OUTPUT_PATH), verbosity=verbosity, num_threads=self._args.num_threads, fail_fast=self._args.fail_fast, diff --git a/vunit/ui/packagefacade.py b/vunit/ui/packagefacade.py index 379e777b1..9637beb65 100644 --- a/vunit/ui/packagefacade.py +++ b/vunit/ui/packagefacade.py @@ -8,7 +8,7 @@ UI class PackageFacade """ -from os.path import join, splitext +from pathlib import Path from ..com import codec_generator @@ -33,9 +33,9 @@ def generate_codecs( codec_package_name = self._package_name + "_codecs" if output_file_name is None: - codecs_path = join(self._parent.codecs_path, self._library_name) - file_extension = splitext(self._design_unit.source_file.name)[1] - output_file_name = join(codecs_path, codec_package_name + file_extension) + codecs_path = Path(self._parent.codecs_path) / self._library_name + file_extension = Path(self._design_unit.source_file.name).suffix + output_file_name = codecs_path / (codec_package_name + file_extension) codec_generator.generate_codecs( self._design_unit, codec_package_name, used_packages, output_file_name diff --git a/vunit/ui/results.py b/vunit/ui/results.py index eb3057ca4..9e63322ce 100644 --- a/vunit/ui/results.py +++ b/vunit/ui/results.py @@ -8,7 +8,8 @@ UI class Results """ -from os.path import join, basename, normpath +from pathlib import Path +from typing import Dict, Union from .common import TEST_OUTPUT_PATH @@ -45,7 +46,7 @@ def get_report(self): report.tests.update( { test.name: TestResult( - join(self._output_path, TEST_OUTPUT_PATH), + Path(self._output_path) / TEST_OUTPUT_PATH, obj["status"], obj["time"], obj["path"], @@ -63,9 +64,9 @@ class Report(object): :data tests: Dictionary of :class:`TestResult` objects """ - def __init__(self, output_path): - self.output_path = output_path - self.tests = {} + def __init__(self, output_path: Union[str, Path]): + self.output_path = Path(output_path) + self.tests: Dict[str, TestResult] = {} class TestResult(object): @@ -93,20 +94,22 @@ def post_func(results): vu.main(post_run=post_func) """ - def __init__(self, test_output_path, status, time, path): - self._test_output_path = test_output_path + def __init__( + self, test_output_path: Union[str, Path], status, time, path: Union[str, Path] + ): + self._test_output_path = Path(test_output_path) self.status = status self.time = time - self.path = path + self.path = Path(path) @property - def relpath(self): + def relpath(self) -> str: """ If the path is a subdir to the default TEST_OUTPUT_PATH, return the subdir only """ - base = basename(self.path) - return ( + base = self.path.name + return str( base - if normpath(join(self._test_output_path, base)) == normpath(self.path) + if (self._test_output_path / base).resolve() == self.path.resolve() else self.path ) diff --git a/vunit/vhdl/check/tools/generate_check_equal.py b/vunit/vhdl/check/tools/generate_check_equal.py index aeb244305..e8957e385 100644 --- a/vunit/vhdl/check/tools/generate_check_equal.py +++ b/vunit/vhdl/check/tools/generate_check_equal.py @@ -4,7 +4,7 @@ # # Copyright (c) 2014-2020, Lars Asplund lars.anders.asplund@gmail.com -from os.path import join, dirname +from pathlib import Path from string import Template api_template = """ procedure check_equal( @@ -767,14 +767,14 @@ def replace_region(region_name, file_name, new_contents): def main(): - check_api_file_name = join(dirname(__file__), "..", "src", "check_api.vhd") + check_api_file_name = str(Path(__file__).parent.parent / "src" / "check_api.vhd") replace_region("check_equal", check_api_file_name, generate_api()) - check_file_name = join(dirname(__file__), "..", "src", "check.vhd") + check_file_name = str(Path(__file__).parent.parent / "src" / "check.vhd") replace_region("check_equal", check_file_name, generate_impl()) - with open( - join(dirname(__file__), "..", "test", "tb_check_equal.vhd"), "wb" + with (Path(__file__).parent.parent / "test" / "tb_check_equal.vhd").open( + "wb" ) as fptr: fptr.write(generate_test().encode()) diff --git a/vunit/vhdl/check/tools/generate_check_match.py b/vunit/vhdl/check/tools/generate_check_match.py index 4229da772..129755b50 100644 --- a/vunit/vhdl/check/tools/generate_check_match.py +++ b/vunit/vhdl/check/tools/generate_check_match.py @@ -4,7 +4,7 @@ # # Copyright (c) 2014-2020, Lars Asplund lars.anders.asplund@gmail.com -from os.path import join, dirname +from pathlib import Path from string import Template from generate_check_equal import replace_region @@ -448,14 +448,14 @@ def generate_test(): def main(): - check_api_file_name = join(dirname(__file__), "..", "src", "check_api.vhd") + check_api_file_name = str(Path(__file__).parent.parent / "src" / "check_api.vhd") replace_region("check_match", check_api_file_name, generate_api()) - check_file_name = join(dirname(__file__), "..", "src", "check.vhd") + check_file_name = str(Path(__file__).parent.parent / "src" / "check.vhd") replace_region("check_match", check_file_name, generate_impl()) - with open( - join(dirname(__file__), "..", "test", "tb_check_match.vhd"), "wb" + with (Path(__file__).parent.parent / "test" / "tb_check_match.vhd").open( + "wb" ) as fptr: fptr.write(generate_test().encode()) diff --git a/vunit/vhdl/com/run.py b/vunit/vhdl/com/run.py index c1e83b7ec..f55e7ab28 100644 --- a/vunit/vhdl/com/run.py +++ b/vunit/vhdl/com/run.py @@ -4,17 +4,16 @@ # # Copyright (c) 2014-2020, Lars Asplund lars.anders.asplund@gmail.com -from os.path import join, dirname +from pathlib import Path from vunit import VUnit -root = dirname(__file__) +ROOT = Path(__file__).parent -prj = VUnit.from_argv() -prj.add_com() -tb_com_lib = prj.add_library("tb_com_lib") -tb_com_lib.add_source_files(join(root, "test", "*.vhd")) -pkg = tb_com_lib.package("custom_types_pkg") -pkg.generate_codecs( +UI = VUnit.from_argv() +UI.add_com() +TB_COM_LIB = UI.add_library("tb_com_lib") +TB_COM_LIB.add_source_files(ROOT / "test" / "*.vhd") +TB_COM_LIB.package("custom_types_pkg").generate_codecs( codec_package_name="custom_codec_pkg", used_packages=[ "ieee.std_logic_1164", @@ -22,4 +21,5 @@ "tb_com_lib.more_constants_pkg", ], ) -prj.main() + +UI.main() diff --git a/vunit/vhdl/dictionary/run.py b/vunit/vhdl/dictionary/run.py index 8fbb6da42..a90fc1571 100644 --- a/vunit/vhdl/dictionary/run.py +++ b/vunit/vhdl/dictionary/run.py @@ -4,12 +4,12 @@ # # Copyright (c) 2014-2020, Lars Asplund lars.anders.asplund@gmail.com -from os.path import join, dirname +from pathlib import Path from vunit import VUnit -root = dirname(__file__) +ROOT = Path(__file__).parent -ui = VUnit.from_argv() -lib = ui.add_library("lib") -lib.add_source_files(join(root, "test", "*.vhd")) -ui.main() +UI = VUnit.from_argv() +UI.add_library("lib").add_source_files(ROOT / "test" / "*.vhd") + +UI.main() diff --git a/vunit/vhdl/logging/run.py b/vunit/vhdl/logging/run.py index 5378e3cdd..caaeb8be3 100644 --- a/vunit/vhdl/logging/run.py +++ b/vunit/vhdl/logging/run.py @@ -4,11 +4,12 @@ # # Copyright (c) 2014-2020, Lars Asplund lars.anders.asplund@gmail.com -from os.path import join, dirname +from pathlib import Path from vunit import VUnit -root = dirname(__file__) -ui = VUnit.from_argv() -lib = ui.library("vunit_lib") -lib.add_source_files(join(root, "test", "*.vhd")) -ui.main() +ROOT = Path(__file__).parent + +UI = VUnit.from_argv() +UI.library("vunit_lib").add_source_files(ROOT / "test" / "*.vhd") + +UI.main() diff --git a/vunit/vhdl/path/run.py b/vunit/vhdl/path/run.py index 913f518c3..a90fc1571 100644 --- a/vunit/vhdl/path/run.py +++ b/vunit/vhdl/path/run.py @@ -4,11 +4,12 @@ # # Copyright (c) 2014-2020, Lars Asplund lars.anders.asplund@gmail.com -from os.path import join, dirname +from pathlib import Path from vunit import VUnit -root = dirname(__file__) -ui = VUnit.from_argv() -lib = ui.add_library("lib") -lib.add_source_files(join(root, "test", "*.vhd")) -ui.main() +ROOT = Path(__file__).parent + +UI = VUnit.from_argv() +UI.add_library("lib").add_source_files(ROOT / "test" / "*.vhd") + +UI.main() diff --git a/vunit/vhdl/random/run.py b/vunit/vhdl/random/run.py index 041600ff1..74c1b3f47 100644 --- a/vunit/vhdl/random/run.py +++ b/vunit/vhdl/random/run.py @@ -4,13 +4,13 @@ # # Copyright (c) 2014-2020, Lars Asplund lars.anders.asplund@gmail.com -from os.path import join, dirname +from pathlib import Path from vunit import VUnit -root = dirname(__file__) +ROOT = Path(__file__).parent -ui = VUnit.from_argv() -ui.add_random() -lib = ui.library("vunit_lib") -lib.add_source_files(join(root, "test", "*.vhd")) -ui.main() +UI = VUnit.from_argv() +UI.add_random() +UI.library("vunit_lib").add_source_files(ROOT / "test" / "*.vhd") + +UI.main() diff --git a/vunit/vhdl/run/run.py b/vunit/vhdl/run/run.py index 7135a5b94..e7ee5c0de 100644 --- a/vunit/vhdl/run/run.py +++ b/vunit/vhdl/run/run.py @@ -4,12 +4,12 @@ # # Copyright (c) 2014-2020, Lars Asplund lars.anders.asplund@gmail.com -from os.path import join, dirname +from pathlib import Path from vunit import VUnit -root = dirname(__file__) -ui = VUnit.from_argv() +ROOT = Path(__file__).parent -lib = ui.add_library("tb_run_lib") -lib.add_source_files(join(root, "test", "*.vhd")) -ui.main() +UI = VUnit.from_argv() +UI.add_library("tb_run_lib").add_source_files(ROOT / "test" / "*.vhd") + +UI.main() diff --git a/vunit/vhdl/string_ops/run.py b/vunit/vhdl/string_ops/run.py index cb9bb49c1..a90fc1571 100644 --- a/vunit/vhdl/string_ops/run.py +++ b/vunit/vhdl/string_ops/run.py @@ -4,14 +4,12 @@ # # Copyright (c) 2014-2020, Lars Asplund lars.anders.asplund@gmail.com -from os.path import join, dirname +from pathlib import Path from vunit import VUnit -root = dirname(__file__) -common_path = join(root, "..", "common", "test") +ROOT = Path(__file__).parent -ui = VUnit.from_argv() -lib = ui.add_library("lib") -lib.add_source_files(join(root, "test", "*.vhd")) +UI = VUnit.from_argv() +UI.add_library("lib").add_source_files(ROOT / "test" / "*.vhd") -ui.main() +UI.main() diff --git a/vunit/vhdl/verification_components/run.py b/vunit/vhdl/verification_components/run.py index db7e52409..cc8005c0e 100644 --- a/vunit/vhdl/verification_components/run.py +++ b/vunit/vhdl/verification_components/run.py @@ -4,17 +4,17 @@ # # Copyright (c) 2014-2020, Lars Asplund lars.anders.asplund@gmail.com -from os.path import join, dirname +from pathlib import Path from itertools import product from vunit import VUnit -root = dirname(__file__) +ROOT = Path(__file__).parent -ui = VUnit.from_argv() -ui.add_random() -ui.add_verification_components() -lib = ui.library("vunit_lib") -lib.add_source_files(join(root, "test", "*.vhd")) +UI = VUnit.from_argv() +UI.add_random() +UI.add_verification_components() +LIB = UI.library("vunit_lib") +LIB.add_source_files(ROOT / "test" / "*.vhd") def encode(tb_cfg): @@ -67,12 +67,12 @@ def gen_avalon_master_tests(obj, *args): obj.add_config(name=config_name, generics=dict(encoded_tb_cfg=encode(tb_cfg))) -tb_avalon_slave = lib.test_bench("tb_avalon_slave") +tb_avalon_slave = LIB.test_bench("tb_avalon_slave") for test in tb_avalon_slave.get_tests(): gen_avalon_tests(test, [32], [1, 2, 64], [1.0, 0.3], [0.0, 0.4]) -tb_avalon_master = lib.test_bench("tb_avalon_master") +tb_avalon_master = LIB.test_bench("tb_avalon_master") for test in tb_avalon_master.get_tests(): if test.name == "wr single rd single": @@ -82,19 +82,19 @@ def gen_avalon_master_tests(obj, *args): test, [64], [1.0, 0.3], [0.0, 0.7], [1.0, 0.3], [1.0, 0.3] ) -TB_WISHBONE_SLAVE = lib.test_bench("tb_wishbone_slave") +TB_WISHBONE_SLAVE = LIB.test_bench("tb_wishbone_slave") for test in TB_WISHBONE_SLAVE.get_tests(): # TODO strobe_prob not implemented in slave tb gen_wb_tests(test, [8, 32], [1, 64], [1.0], [0.3, 1.0], [0.4, 0.0]) -TB_WISHBONE_MASTER = lib.test_bench("tb_wishbone_master") +TB_WISHBONE_MASTER = LIB.test_bench("tb_wishbone_master") for test in TB_WISHBONE_MASTER.get_tests(): gen_wb_tests(test, [8, 32], [1, 64], [0.3, 1.0], [0.3, 1.0], [0.4, 0.0]) -TB_AXI_STREAM = lib.test_bench("tb_axi_stream") +TB_AXI_STREAM = LIB.test_bench("tb_axi_stream") for id_length in [0, 8]: for dest_length in [0, 8]: @@ -110,7 +110,7 @@ def gen_avalon_master_tests(obj, *args): ), ) -TB_AXI_STREAM_PROTOCOL_CHECKER = lib.test_bench("tb_axi_stream_protocol_checker") +TB_AXI_STREAM_PROTOCOL_CHECKER = LIB.test_bench("tb_axi_stream_protocol_checker") for data_length in [0, 8]: for test in TB_AXI_STREAM_PROTOCOL_CHECKER.get_tests("*passing*tdata*"): @@ -140,4 +140,4 @@ def gen_avalon_master_tests(obj, *args): name="stall_slave", generics=dict(g_stall_percentage_slave=30) ) -ui.main() +UI.main() diff --git a/vunit/vhdl_parser.py b/vunit/vhdl_parser.py index b4e530cbe..1fb05f994 100644 --- a/vunit/vhdl_parser.py +++ b/vunit/vhdl_parser.py @@ -11,7 +11,7 @@ """ import re -from os.path import abspath +from pathlib import Path import logging from vunit.cached import cached from vunit.parsing.encodings import HDL_FILE_ENCODING @@ -32,7 +32,7 @@ def parse(self, file_name): Parse the VHDL code and return a VHDLDesignFile parse result parse result is re-used if content hash found in database """ - file_name = abspath(file_name) + file_name = str(Path(file_name).resolve()) return cached( "CachedVHDLParser.parse", VHDLDesignFile.parse, diff --git a/vunit/vivado/vivado.py b/vunit/vivado/vivado.py index 530e0b8ee..0978c32b4 100644 --- a/vunit/vivado/vivado.py +++ b/vunit/vivado/vivado.py @@ -10,7 +10,7 @@ from subprocess import check_call from os import makedirs -from os.path import abspath, join, dirname, exists, basename +from pathlib import Path def add_from_compile_order_file( @@ -69,15 +69,16 @@ def create_compile_order_file(project_file, compile_order_file, vivado_path=None """ print( "Generating Vivado project compile order into %s ..." - % abspath(compile_order_file) + % str(Path(compile_order_file).resolve()) ) - if not exists(dirname(compile_order_file)): - makedirs(dirname(compile_order_file)) + fpath = Path(compile_order_file) + if not fpath.parent.exists(): + makedirs(str(fpath.parent)) print("Extracting compile order ...") run_vivado( - join(dirname(__file__), "tcl", "extract_compile_order.tcl"), + str(Path(__file__).parent / "tcl" / "extract_compile_order.tcl"), tcl_args=[project_file, compile_order_file], vivado_path=vivado_path, ) @@ -101,13 +102,13 @@ def _read_compile_order(file_name): # Vivado generates duplicate files for different IP:s # using the same underlying libraries. We remove duplicates here - key = (library_name, basename(file_name)) + key = (library_name, Path(file_name).name) if key in unique: continue unique.add(key) if file_type == "Verilog Header": - include_dirs.add(dirname(file_name)) + include_dirs.add(str(Path(file_name).parent)) else: compile_order.append((library_name, file_name)) @@ -121,10 +122,12 @@ def run_vivado(tcl_file_name, tcl_args=None, cwd=None, vivado_path=None): Note: the shell=True is important in windows where Vivado is just a bat file. """ vivado = ( - "vivado" if vivado_path is None else join(abspath(vivado_path), "bin", "vivado") + "vivado" + if vivado_path is None + else str(Path(vivado_path).resolve() / "bin" / "vivado") ) cmd = "{} -nojournal -nolog -notrace -mode batch -source {}".format( - vivado, abspath(tcl_file_name) + vivado, str(Path(tcl_file_name).resolve()) ) if tcl_args is not None: cmd += " -tclargs " + " ".join([str(val) for val in tcl_args]) diff --git a/vunit/vunit_cli.py b/vunit/vunit_cli.py index 1e9731eb7..1f3003028 100644 --- a/vunit/vunit_cli.py +++ b/vunit/vunit_cli.py @@ -39,7 +39,7 @@ """ import argparse -from os.path import join, abspath +from pathlib import Path import os from vunit.sim_if.factory import SIMULATOR_FACTORY from vunit.about import version @@ -80,7 +80,7 @@ def _create_argument_parser(description=None, for_documentation=False): if for_documentation: default_output_path = "./vunit_out" else: - default_output_path = join(abspath(os.getcwd()), "vunit_out") + default_output_path = str(Path(os.getcwd()).resolve() / "vunit_out") parser = argparse.ArgumentParser(description=description) From af7b89d389e26c45797f6c82dc8bf5a695670519 Mon Sep 17 00:00:00 2001 From: eine Date: Thu, 13 Feb 2020 02:12:39 +0100 Subject: [PATCH 24/79] ui: add type annotations --- vunit/project.py | 23 ++++---- vunit/sim_if/ghdl.py | 4 +- vunit/ui/__init__.py | 121 ++++++++++++++++++++++++++++--------------- 3 files changed, 92 insertions(+), 56 deletions(-) diff --git a/vunit/project.py b/vunit/project.py index dc97cceeb..ea7ac3f9b 100644 --- a/vunit/project.py +++ b/vunit/project.py @@ -9,7 +9,7 @@ """ Functionality to represent and operate on a HDL code project """ -from typing import Optional +from typing import Optional, Union from pathlib import Path import logging from collections import OrderedDict @@ -83,7 +83,7 @@ def add_builtin_library(self, logical_name): def add_library( self, logical_name, - directory, + directory: Union[str, Path], vhdl_standard: VHDLStandard = VHDL.STD_2008, is_external=False, ): @@ -93,19 +93,18 @@ def add_library( """ self._validate_new_library_name(logical_name) + dpath = Path(directory) + dstr = str(directory) + if is_external: - if not exists(directory): - raise ValueError("External library %r does not exist" % directory) + if not dpath.exists(): + raise ValueError("External library %r does not exist" % dstr) - if not isdir(directory): - raise ValueError( - "External library must be a directory. Got %r" % directory - ) + if not dpath.is_dir(): + raise ValueError("External library must be a directory. Got %r" % dstr) - library = Library( - logical_name, directory, vhdl_standard, is_external=is_external - ) - LOGGER.debug("Adding library %s with path %s", logical_name, directory) + library = Library(logical_name, dstr, vhdl_standard, is_external=is_external) + LOGGER.debug("Adding library %s with path %s", logical_name, dstr) self._libraries[logical_name] = library self._lower_library_names_dict[logical_name.lower()] = library.name diff --git a/vunit/sim_if/ghdl.py b/vunit/sim_if/ghdl.py index b32c1604d..ae0a49fd3 100644 --- a/vunit/sim_if/ghdl.py +++ b/vunit/sim_if/ghdl.py @@ -303,7 +303,7 @@ def _get_command(self, config, output_path, elaborate_only, ghdl_e, wave_file): cmd += ["--no-run"] else: try: - os.makedirs(output_path, mode=0o777) + makedirs(output_path, mode=0o777) except OSError: pass with (Path(output_path) / "args.json").open("w") as fname: @@ -337,7 +337,7 @@ def simulate( # pylint: disable=too-many-locals if self._gtkwave_fmt is not None: data_file_name = str(Path(script_path) / ("wave.%s" % self._gtkwave_fmt)) - if Path(data_file_name).exists(): + if Path(data_file_name).exists(): remove(data_file_name) else: data_file_name = None diff --git a/vunit/ui/__init__.py b/vunit/ui/__init__.py index 97698591c..33fc882c0 100644 --- a/vunit/ui/__init__.py +++ b/vunit/ui/__init__.py @@ -16,7 +16,7 @@ import logging import json import os -from typing import Optional, Set +from typing import Optional, Set, Union from pathlib import Path from fnmatch import fnmatch from ..database import PickledDataBase, DataBase @@ -58,7 +58,10 @@ class VUnit( # pylint: disable=too-many-instance-attributes, too-many-public-me @classmethod def from_argv( - cls, argv=None, compile_builtins=True, vhdl_standard: Optional[str] = None + cls, + argv=None, + compile_builtins: Optional[bool] = True, + vhdl_standard: Optional[str] = None, ): """ Create VUnit instance from command line arguments. @@ -84,7 +87,10 @@ def from_argv( @classmethod def from_args( - cls, args, compile_builtins=True, vhdl_standard: Optional[str] = None + cls, + args, + compile_builtins: Optional[bool] = True, + vhdl_standard: Optional[str] = None, ): """ Create VUnit instance from args namespace. @@ -102,7 +108,10 @@ def from_args( return cls(args, compile_builtins=compile_builtins, vhdl_standard=vhdl_standard) def __init__( - self, args, compile_builtins=True, vhdl_standard: Optional[str] = None + self, + args, + compile_builtins: Optional[bool] = True, + vhdl_standard: Optional[str] = None, ): self._args = args self._configure_logging(args.log_level) @@ -204,7 +213,7 @@ def _which_vhdl_standard(self, vhdl_standard: Optional[str]) -> VHDLStandard: return VHDL.standard(vhdl_standard) def add_external_library( - self, library_name, path, vhdl_standard: Optional[str] = None + self, library_name, path: Union[str, Path], vhdl_standard: Optional[str] = None ): """ Add an externally compiled library as a black-box @@ -225,14 +234,14 @@ def add_external_library( self._project.add_library( library_name, - str(Path(path).resolve()), + Path(path).resolve(), self._which_vhdl_standard(vhdl_standard), is_external=True, ) return self.library(library_name) def add_source_files_from_csv( - self, project_csv_path, vhdl_standard: Optional[str] = None + self, project_csv_path: Union[str, Path], vhdl_standard: Optional[str] = None ): """ Add a project configuration, mapping all the libraries and files @@ -249,14 +258,14 @@ def add_source_files_from_csv( libs: Set[str] = set() files = SourceFileList(list()) - with open(project_csv_path) as csv_path_file: + ppath = Path(project_csv_path) + + with ppath.open() as csv_path_file: for row in csv.reader(csv_path_file): if len(row) == 2: lib_name = row[0].strip() no_normalized_file = row[1].strip() - file_name_ = str( - (Path(project_csv_path).parent / no_normalized_file).resolve() - ) + file_name_ = str((ppath.parent / no_normalized_file).resolve()) lib = ( self.library(lib_name) if lib_name in libs @@ -272,7 +281,10 @@ def add_source_files_from_csv( return files def add_library( - self, library_name, vhdl_standard: Optional[str] = None, allow_duplicate=False + self, + library_name: str, + vhdl_standard: Optional[str] = None, + allow_duplicate: Optional[bool] = False, ): """ Add a library managed by VUnit. @@ -303,7 +315,7 @@ def add_library( ) return self.library(library_name) - def library(self, library_name): + def library(self, library_name: str): """ Get a library @@ -314,7 +326,7 @@ def library(self, library_name): raise KeyError(library_name) return Library(library_name, self, self._project, self._test_bench_list) - def set_attribute(self, name, value, allow_empty=False): + def set_attribute(self, name: str, value: str, allow_empty: Optional[bool] = False): """ Set a value of attribute in all |configurations| @@ -337,7 +349,7 @@ def set_attribute(self, name, value, allow_empty=False): ): test_bench.set_attribute(name, value) - def set_generic(self, name, value, allow_empty=False): + def set_generic(self, name: str, value: str, allow_empty: Optional[bool] = False): """ Set a value of generic in all |configurations| @@ -360,7 +372,7 @@ def set_generic(self, name, value, allow_empty=False): ): test_bench.set_generic(name.lower(), value) - def set_parameter(self, name, value, allow_empty=False): + def set_parameter(self, name: str, value: str, allow_empty: Optional[bool] = False): """ Set value of parameter in all |configurations| @@ -383,7 +395,13 @@ def set_parameter(self, name, value, allow_empty=False): ): test_bench.set_generic(name, value) - def set_sim_option(self, name, value, allow_empty=False, overwrite=True): + def set_sim_option( + self, + name: str, + value: str, + allow_empty: Optional[bool] = False, + overwrite: Optional[bool] = True, + ): """ Set simulation option in all |configurations| @@ -407,7 +425,9 @@ def set_sim_option(self, name, value, allow_empty=False, overwrite=True): ): test_bench.set_sim_option(name, value, overwrite) - def set_compile_option(self, name, value, allow_empty=False): + def set_compile_option( + self, name: str, value: str, allow_empty: Optional[bool] = False + ): """ Set compile option of all files @@ -431,7 +451,9 @@ def set_compile_option(self, name, value, allow_empty=False): ): source_file.set_compile_option(name, value) - def add_compile_option(self, name, value, allow_empty=False): + def add_compile_option( + self, name: str, value: str, allow_empty: Optional[bool] = False + ): """ Add compile option to all files @@ -448,7 +470,9 @@ def add_compile_option(self, name, value, allow_empty=False): ): source_file.add_compile_option(name, value) - def get_source_file(self, file_name, library_name=None): + def get_source_file( + self, file_name: Union[str, Path], library_name: Optional[str] = None + ): """ Get a source file @@ -457,22 +481,29 @@ def get_source_file(self, file_name, library_name=None): :returns: A :class:`.SourceFile` object """ - files = self.get_source_files(file_name, library_name, allow_empty=True) + fstr = str(file_name) + + files = self.get_source_files(fstr, library_name, allow_empty=True) if len(files) > 1: raise ValueError( "Found file named '%s' in multiple-libraries, " - "add explicit library_name." % file_name + "add explicit library_name." % fstr ) if not files: if library_name is None: - raise ValueError("Found no file named '%s'" % file_name) + raise ValueError("Found no file named '%s'" % fstr) raise ValueError( - "Found no file named '%s' in library '%s'" % (file_name, library_name) + "Found no file named '%s' in library '%s'" % (fstr, library_name) ) return files[0] - def get_source_files(self, pattern="*", library_name=None, allow_empty=False): + def get_source_files( + self, + pattern="*", + library_name: Optional[str] = None, + allow_empty: Optional[bool] = False, + ): """ Get a list of source files @@ -509,13 +540,13 @@ def get_source_files(self, pattern="*", library_name=None, allow_empty=False): def add_source_files( # pylint: disable=too-many-arguments self, pattern, - library_name, + library_name: str, preprocessors=None, include_dirs=None, defines=None, - allow_empty=False, + allow_empty: Optional[bool] = False, vhdl_standard: Optional[str] = None, - no_parse=False, + no_parse: Optional[bool] = False, file_type=None, ): """ @@ -553,13 +584,13 @@ def add_source_files( # pylint: disable=too-many-arguments def add_source_file( # pylint: disable=too-many-arguments self, - file_name, - library_name, + file_name: Union[str, Path], + library_name: str, preprocessors=None, include_dirs=None, defines=None, vhdl_standard: Optional[str] = None, - no_parse=False, + no_parse: Optional[bool] = False, file_type=None, ): """ @@ -584,7 +615,7 @@ def add_source_file( # pylint: disable=too-many-arguments """ return self.library(library_name).add_source_file( - file_name=file_name, + file_name=str(file_name), preprocessors=preprocessors, include_dirs=include_dirs, defines=defines, @@ -593,7 +624,9 @@ def add_source_file( # pylint: disable=too-many-arguments file_type=file_type, ) - def _preprocess(self, library_name, file_name, preprocessors): + def _preprocess( + self, library_name: str, file_name: Union[str, Path], preprocessors + ): """ Preprocess file_name within library_name using explicit preprocessors if preprocessors is None then use implicit globally defined processors @@ -605,8 +638,10 @@ def _preprocess(self, library_name, file_name, preprocessors): preprocessors = [p for p in preprocessors if p is not None] preprocessors = self._external_preprocessors + preprocessors + fstr = str(file_name) + if not preprocessors: - return file_name + return fstr fname = str(Path(file_name).name) @@ -618,8 +653,8 @@ def _preprocess(self, library_name, file_name, preprocessors): raise KeyboardInterrupt except: # pylint: disable=bare-except traceback.print_exc() - LOGGER.error("Failed to preprocess %s", file_name) - return file_name + LOGGER.error("Failed to preprocess %s", fstr) + return fstr else: pp_file_name = str(Path(self._preprocessed_path) / library_name / fname) @@ -706,7 +741,7 @@ def main(self, post_run=None): sys.exit(0) - def _create_tests(self, simulator_if): + def _create_tests(self, simulator_if: Union[None, SimulatorInterface]): """ Create the test cases """ @@ -801,7 +836,9 @@ def _main_list_only(self): print("Listed %i tests" % test_list.num_tests) return True - def _main_export_json(self, json_file_name): # pylint: disable=too-many-locals + def _main_export_json( + self, json_file_name: Union[str, Path] + ): # pylint: disable=too-many-locals """ Main function when exporting to JSON """ @@ -851,7 +888,7 @@ def _main_export_json(self, json_file_name): # pylint: disable=too-many-locals tests=tests, ) - with open(json_file_name, "w") as fptr: + with Path(json_file_name).open("w") as fptr: json.dump(json_data, fptr, sort_keys=True, indent=4, separators=(",", ": ")) return True @@ -874,7 +911,7 @@ def _main_compile_only(self): self._compile(simulator_if) return True - def _create_output_path(self, clean): + def _create_output_path(self, clean: bool): """ Create or re-create the output path if necessary """ @@ -897,7 +934,7 @@ def _preprocessed_path(self): def codecs_path(self): return str(Path(self._output_path) / "codecs") - def _compile(self, simulator_if): + def _compile(self, simulator_if: SimulatorInterface): """ Compile entire project """ @@ -914,7 +951,7 @@ def _compile(self, simulator_if): target_files=target_files, ) - def _get_testbench_files(self, simulator_if): + def _get_testbench_files(self, simulator_if: Union[None, SimulatorInterface]): """ Return the list of all test bench files for the currently selected tests to run """ From bcdabe4ae13e5e49614f9572232f97c516ca261d Mon Sep 17 00:00:00 2001 From: Lukas Vik <10241915+LukasVik@users.noreply.github.com> Date: Wed, 4 Mar 2020 08:58:55 +0100 Subject: [PATCH 25/79] Fix "prefix of array attribute must be an object name" --- vunit/vhdl/data_types/src/codec-2008p.vhd | 21 ++++++++++++++------- vunit/vhdl/data_types/src/codec.vhd | 21 ++++++++++++++------- vunit/vhdl/logging/src/logger_pkg-body.vhd | 6 ++++-- 3 files changed, 32 insertions(+), 16 deletions(-) diff --git a/vunit/vhdl/data_types/src/codec-2008p.vhd b/vunit/vhdl/data_types/src/codec-2008p.vhd index 58dd63e95..405edae0f 100644 --- a/vunit/vhdl/data_types/src/codec-2008p.vhd +++ b/vunit/vhdl/data_types/src/codec-2008p.vhd @@ -110,7 +110,8 @@ package body codec_2008p_pkg is function decode ( constant code : string) return boolean_vector is - variable ret_val : boolean_vector(get_range(code)'range) := (others => false); + constant ret_range : range_t := get_range(code); + variable ret_val : boolean_vector(ret_range'range) := (others => false); variable index : positive := code'left; begin decode(code, index, ret_val); @@ -136,7 +137,8 @@ package body codec_2008p_pkg is function decode ( constant code : string) return integer_vector is - variable ret_val : integer_vector(get_range(code)'range) := (others => integer'left); + constant ret_range : range_t := get_range(code); + variable ret_val : integer_vector(ret_range'range) := (others => integer'left); variable index : positive := code'left; begin decode(code, index, ret_val); @@ -162,7 +164,8 @@ package body codec_2008p_pkg is function decode ( constant code : string) return real_vector is - variable ret_val : real_vector(get_range(code)'range) := (others => real'left); + constant ret_range : range_t := get_range(code); + variable ret_val : real_vector(ret_range'range) := (others => real'left); variable index : positive := code'left; begin decode(code, index, ret_val); @@ -188,7 +191,8 @@ package body codec_2008p_pkg is function decode ( constant code : string) return time_vector is - variable ret_val : time_vector(get_range(code)'range) := (others => time'left); + constant ret_range : range_t := get_range(code); + variable ret_val : time_vector(ret_range'range) := (others => time'left); variable index : positive := code'left; begin decode(code, index, ret_val); @@ -206,7 +210,8 @@ package body codec_2008p_pkg is function decode ( constant code : string) return ufixed is - variable ret_val : ufixed(get_range(code)'range); + constant ret_range : range_t := get_range(code); + variable ret_val : ufixed(ret_range'range); variable index : positive := code'left; begin decode(code, index, ret_val); @@ -224,7 +229,8 @@ package body codec_2008p_pkg is function decode ( constant code : string) return sfixed is - variable ret_val : sfixed(get_range(code)'range); + constant ret_range : range_t := get_range(code); + variable ret_val : sfixed(ret_range'range); variable index : positive := code'left; begin decode(code, index, ret_val); @@ -242,7 +248,8 @@ package body codec_2008p_pkg is function decode ( constant code : string) return float is - variable ret_val : float(get_range(code)'range); + constant ret_range : range_t := get_range(code); + variable ret_val : float(ret_range'range); variable index : positive := code'left; begin decode(code, index, ret_val); diff --git a/vunit/vhdl/data_types/src/codec.vhd b/vunit/vhdl/data_types/src/codec.vhd index 2b0e25877..f441b18dd 100644 --- a/vunit/vhdl/data_types/src/codec.vhd +++ b/vunit/vhdl/data_types/src/codec.vhd @@ -517,7 +517,8 @@ package body codec_pkg is function decode ( constant code : string) return string is - variable ret_val : string(get_range(code)'range) := (others => NUL); + constant ret_range : range_t := get_range(code); + variable ret_val : string(ret_range'range) := (others => NUL); variable index : positive := code'left; begin decode(code, index, ret_val); @@ -542,7 +543,8 @@ package body codec_pkg is function decode ( constant code : string) return bit_vector is - variable ret_val : bit_vector(get_range(code)'range) := (others => '0'); + constant ret_range : range_t := get_range(code); + variable ret_val : bit_vector(ret_range'range) := (others => '0'); variable index : positive := code'left; begin decode(code, index, ret_val); @@ -560,7 +562,8 @@ package body codec_pkg is function decode ( constant code : string) return std_ulogic_vector is - variable ret_val : std_ulogic_vector(get_range(code)'range) := (others => 'U'); + constant ret_range : range_t := get_range(code); + variable ret_val : std_ulogic_vector(ret_range'range) := (others => 'U'); variable index : positive := code'left; begin decode(code, index, ret_val); @@ -614,7 +617,8 @@ package body codec_pkg is function decode ( constant code : string) return ieee.numeric_bit.unsigned is - variable ret_val : ieee.numeric_bit.unsigned(get_range(code)'range) := (others => '0'); + constant ret_range : range_t := get_range(code); + variable ret_val : ieee.numeric_bit.unsigned(ret_range'range) := (others => '0'); variable index : positive := code'left; begin decode(code, index, ret_val); @@ -632,7 +636,8 @@ package body codec_pkg is function decode ( constant code : string) return ieee.numeric_bit.signed is - variable ret_val : ieee.numeric_bit.signed(get_range(code)'range) := (others => '0'); + constant ret_range : range_t := get_range(code); + variable ret_val : ieee.numeric_bit.signed(ret_range'range) := (others => '0'); variable index : positive := code'left; begin decode(code, index, ret_val); @@ -650,7 +655,8 @@ package body codec_pkg is function decode ( constant code : string) return ieee.numeric_std.unsigned is - variable ret_val : ieee.numeric_std.unsigned(get_range(code)'range) := (others => 'U'); + constant ret_range : range_t := get_range(code); + variable ret_val : ieee.numeric_std.unsigned(ret_range'range) := (others => 'U'); variable index : positive := code'left; begin decode(code, index, ret_val); @@ -668,7 +674,8 @@ package body codec_pkg is function decode ( constant code : string) return ieee.numeric_std.signed is - variable ret_val : ieee.numeric_std.signed(get_range(code)'range) := (others => 'U'); + constant ret_range : range_t := get_range(code); + variable ret_val : ieee.numeric_std.signed(ret_range'range) := (others => 'U'); variable index : positive := code'left; begin decode(code, index, ret_val); diff --git a/vunit/vhdl/logging/src/logger_pkg-body.vhd b/vunit/vhdl/logging/src/logger_pkg-body.vhd index 9a51780ec..ca91aa00d 100644 --- a/vunit/vhdl/logging/src/logger_pkg-body.vhd +++ b/vunit/vhdl/logging/src/logger_pkg-body.vhd @@ -92,12 +92,13 @@ package body logger_pkg is procedure p_set_log_handlers(logger : logger_t; log_handlers : log_handler_vec_t) is constant handlers : integer_vector_ptr_t := to_integer_vector_ptr(get(logger.p_data, handlers_idx)); + constant full_logger_name : string := get_full_name(logger); begin resize(handlers, log_handlers'length); for i in log_handlers'range loop set(handlers, i, to_integer(log_handlers(i).p_data)); - update_max_logger_name_length(log_handlers(i), get_full_name(logger)'length); + update_max_logger_name_length(log_handlers(i), full_logger_name'length); end loop; end; @@ -262,11 +263,12 @@ package body logger_pkg is end; impure function get_max_name_length(logger : logger_t) return natural is + constant full_name : string := get_full_name(logger); variable result : natural := 0; variable child_result : natural; begin if num_children(logger) = 0 then - return get_full_name(logger)'length; + return full_name'length; end if; for i in 0 to num_children(logger)-1 loop From bb53c031ac819fd118e3cfa4edd2c2262a83a02c Mon Sep 17 00:00:00 2001 From: eine Date: Sat, 7 Mar 2020 16:35:36 +0100 Subject: [PATCH 26/79] fix pathlib conversion (#632) --- vunit/sim_if/modelsim.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vunit/sim_if/modelsim.py b/vunit/sim_if/modelsim.py index e14e22f37..26d99e188 100644 --- a/vunit/sim_if/modelsim.py +++ b/vunit/sim_if/modelsim.py @@ -70,7 +70,7 @@ def find_prefix_from_path(cls): """ def has_modelsim_ini(path): - return os.path.isfile(str(Path(path) / "modelsim.ini")) + return os.path.isfile(str(Path(path).parent / "modelsim.ini")) return cls.find_toolchain(["vsim"], constraints=[has_modelsim_ini]) From f1c4102b4e066c18f1f570b620f1e3dbbd0b44a8 Mon Sep 17 00:00:00 2001 From: ludli505 Date: Tue, 24 Mar 2020 16:12:39 +0100 Subject: [PATCH 27/79] add code coverage support for ghdl (#627) * sim_if: add supports_coverage method * Expose supports_coverage to user * Add code coverage support for ghdl Simulation now puts the compile-time gcno with the compiled .o-files, and run-time gcda folders within the output folder for each test Each test case produces a separate .gcda file. * Hopefully make ghdl coverage work when using multiple threads * Add merge_coverage method for GHDLInterface * Update coverage example for GHDL * Adapt for usage of Pathlib * Better handling of empty glob results * Correct comment * Minor fixes after review by LukasVik * Add enable_coverage for source files * Fix linting * Add coverage example to acceptance tests * Updates after review * Add documentation for the enable_coverage flags for GHDL Co-authored-by: Lukas Vik <10241915+LukasVik@users.noreply.github.com> --- docs/py/opts.rst | 16 +++- examples/vhdl/coverage/run.py | 7 +- tests/acceptance/test_external_run_scripts.py | 7 ++ vunit/sim_if/__init__.py | 13 ++- vunit/sim_if/activehdl.py | 7 ++ vunit/sim_if/factory.py | 2 +- vunit/sim_if/ghdl.py | 87 ++++++++++++++++++- vunit/sim_if/modelsim.py | 7 ++ vunit/sim_if/rivierapro.py | 7 ++ vunit/ui/__init__.py | 10 +++ 10 files changed, 151 insertions(+), 12 deletions(-) diff --git a/docs/py/opts.rst b/docs/py/opts.rst index 3cce2ea15..8646c401c 100644 --- a/docs/py/opts.rst +++ b/docs/py/opts.rst @@ -46,6 +46,11 @@ The following compilation options are known. Extra arguments passed to Active HDL ``vcom`` command. Must be a list of strings. +``enable_coverage`` + Enables compilation flags needed for code coverage and tells VUnit to handle + the coverage files created at compilation. Only used for coverage with GHDL. + Must be a boolean value. Default is False. + .. note:: Only affects source files added *before* the option is set. @@ -73,17 +78,22 @@ The following simulation options are known. Must be a boolean value. Default is False. When coverage is enabled VUnit only takes the minimal steps required - to make the simulator creates an unique coverage file for the - simulation run. The VUnit users must still set :ref:`sim + to make the simulator create a unique coverage file for the + simulation run. + + For RiverieraPRO and Modelsim/Questa, the VUnit users must still set :ref:`sim ` and :ref:`compile ` options to configure the simulator specific coverage options they want. The reason for this to allow the VUnit users maximum control of their coverage settings. + For GHDL with GCC backend there is less configurability for coverage, and all + necessary flags are set by the the ``enable_coverage`` sim and compile options. + An example of a ``run.py`` file using coverage can be found :vunit_example:`here `. - .. note: Supported by RivieraPRO and Modelsim/Questa simulators. + .. note: Supported by GHDL with GCC backend, RivieraPRO and Modelsim/Questa simulators. ``pli`` diff --git a/examples/vhdl/coverage/run.py b/examples/vhdl/coverage/run.py index ca598a881..9fe002907 100644 --- a/examples/vhdl/coverage/run.py +++ b/examples/vhdl/coverage/run.py @@ -6,10 +6,13 @@ from pathlib import Path from vunit import VUnit +from subprocess import call def post_run(results): results.merge_coverage(file_name="coverage_data") + if VU.get_simulator_name() == "ghdl": + call(["gcovr", "coverage_data"]) VU = VUnit.from_argv() @@ -17,10 +20,12 @@ def post_run(results): LIB = VU.add_library("lib") LIB.add_source_files(Path(__file__).parent / "*.vhd") +LIB.set_sim_option("enable_coverage", True) + LIB.set_compile_option("rivierapro.vcom_flags", ["-coverage", "bs"]) LIB.set_compile_option("rivierapro.vlog_flags", ["-coverage", "bs"]) LIB.set_compile_option("modelsim.vcom_flags", ["+cover=bs"]) LIB.set_compile_option("modelsim.vlog_flags", ["+cover=bs"]) -LIB.set_sim_option("enable_coverage", True) +LIB.set_compile_option("enable_coverage", True) VU.main(post_run=post_run) diff --git a/tests/acceptance/test_external_run_scripts.py b/tests/acceptance/test_external_run_scripts.py index 3fdba5a69..17a90a49a 100644 --- a/tests/acceptance/test_external_run_scripts.py +++ b/tests/acceptance/test_external_run_scripts.py @@ -121,6 +121,13 @@ def test_vhdl_third_party_integration_example_project(self): def test_vhdl_check_example_project(self): self.check(ROOT / "examples" / "vhdl" / "check" / "run.py") + @unittest.skipIf( + simulator_check(lambda simclass: not simclass.supports_coverage()), + "This simulator/backend does not support coverage", + ) + def test_vhdl_coverage_example_project(self): + self.check(join(ROOT, "examples", "vhdl", "coverage", "run.py")) + def test_vhdl_generate_tests_example_project(self): self.check(ROOT / "examples" / "vhdl" / "generate_tests" / "run.py") check_report( diff --git a/vunit/sim_if/__init__.py b/vunit/sim_if/__init__.py index 6709b758f..7104721f8 100644 --- a/vunit/sim_if/__init__.py +++ b/vunit/sim_if/__init__.py @@ -172,7 +172,14 @@ def has_valid_exit_code(): @staticmethod def supports_vhpi(): """ - Return if the simulator supports VHPI + Returns True when the simulator supports VHPI + """ + return False + + @staticmethod + def supports_coverage(): + """ + Returns True when the simulator supports coverage """ return False @@ -216,7 +223,7 @@ def setup_library_mapping(self, project): Implemented by specific simulators """ - def __compile_source_file(self, source_file, printer): + def _compile_source_file(self, source_file, printer): """ Compiles a single source file and prints status information """ @@ -297,7 +304,7 @@ def compile_source_files( printer.write("\n") continue - if self.__compile_source_file(source_file, printer): + if self._compile_source_file(source_file, printer): project.update(source_file) else: source_files_to_skip.update( diff --git a/vunit/sim_if/activehdl.py b/vunit/sim_if/activehdl.py index d5af99616..c3c790461 100644 --- a/vunit/sim_if/activehdl.py +++ b/vunit/sim_if/activehdl.py @@ -68,6 +68,13 @@ def supports_vhdl_package_generics(cls): return False + @staticmethod + def supports_coverage(): + """ + Returns True when the simulator supports coverage + """ + return True + def __init__(self, prefix, output_path, gui=False): SimulatorInterface.__init__(self, output_path, gui) self._library_cfg = str(Path(output_path) / "library.cfg") diff --git a/vunit/sim_if/factory.py b/vunit/sim_if/factory.py index 16cd0a636..5291f2fda 100644 --- a/vunit/sim_if/factory.py +++ b/vunit/sim_if/factory.py @@ -39,7 +39,7 @@ def _extract_compile_options(self): """ Return all supported compile options """ - result = dict() + result = dict((opt.name, opt) for opt in [BooleanOption("enable_coverage")]) for sim_class in self.supported_simulators(): for opt in sim_class.compile_options: assert hasattr(opt, "name") diff --git a/vunit/sim_if/ghdl.py b/vunit/sim_if/ghdl.py index ae0a49fd3..e459266e5 100644 --- a/vunit/sim_if/ghdl.py +++ b/vunit/sim_if/ghdl.py @@ -14,6 +14,7 @@ import subprocess import shlex import re +import shutil from json import dump from sys import stdout # To avoid output catched in non-verbose mode from warnings import warn @@ -25,7 +26,7 @@ LOGGER = logging.getLogger(__name__) -class GHDLInterface(SimulatorInterface): +class GHDLInterface(SimulatorInterface): # pylint: disable=too-many-instance-attributes """ Interface for GHDL simulator """ @@ -108,6 +109,7 @@ def __init__( # pylint: disable=too-many-arguments self._gtkwave_args = gtkwave_args self._backend = backend self._vhdl_standard = None + self._coverage_test_dirs = set() def has_valid_exit_code(self): """ @@ -164,12 +166,19 @@ def determine_version(cls, prefix): @classmethod def supports_vhpi(cls): """ - Return if the simulator supports VHPI + Returns True when the simulator supports VHPI """ return (cls.determine_backend(cls.find_prefix_from_path()) != "mcode") or ( cls.determine_version(cls.find_prefix_from_path()) > 0.36 ) + @classmethod + def supports_coverage(cls): + """ + Returns True when the simulator supports coverage + """ + return cls.determine_backend(cls.find_prefix_from_path()) == "gcc" + def _has_output_flag(self): """ Returns if backend supports output flag @@ -254,10 +263,18 @@ def compile_vhdl_file_command(self, source_file): a_flags += flags cmd += a_flags + + if source_file.compile_options.get("enable_coverage", False): + # Add gcc compilation flags for coverage + # -ftest-coverages creates .gcno notes files needed by gcov + # -fprofile-arcs creates branch profiling in .gcda database files + cmd += ["-fprofile-arcs", "-ftest-coverage"] cmd += [source_file.name] return cmd - def _get_command(self, config, output_path, elaborate_only, ghdl_e, wave_file): + def _get_command( # pylint: disable=too-many-branches + self, config, output_path, elaborate_only, ghdl_e, wave_file + ): """ Return GHDL simulation command """ @@ -282,6 +299,9 @@ def _get_command(self, config, output_path, elaborate_only, ghdl_e, wave_file): if self._has_output_flag(): cmd += ["-o", bin_path] cmd += config.sim_options.get("ghdl.elab_flags", []) + if config.sim_options.get("enable_coverage", False): + # Enable coverage in linker + cmd += ["-Wl,-lgcov"] cmd += [config.entity_name, config.architecture_name] sim = config.sim_options.get("ghdl.sim_flags", []) @@ -347,8 +367,16 @@ def simulate( # pylint: disable=too-many-locals ) status = True + + gcov_env = environ.copy() + if config.sim_options.get("enable_coverage", False): + # Set environment variable to put the coverage output in the test_output folder + coverage_dir = str(Path(output_path) / "coverage") + gcov_env["GCOV_PREFIX"] = coverage_dir + self._coverage_test_dirs.add(coverage_dir) + try: - proc = Process(cmd) + proc = Process(cmd, env=gcov_env) proc.consume_output() except Process.NonZeroExitCode: status = False @@ -364,3 +392,54 @@ def simulate( # pylint: disable=too-many-locals subprocess.call(cmd) return status + + def _compile_source_file(self, source_file, printer): + """ + Runs parent command for compilation, and moves any .gcno files to the compilation output + """ + compilation_ok = super()._compile_source_file(source_file, printer) + + if source_file.compile_options.get("enable_coverage", False): + # GCOV gcno files are output to where the command is run, + # move it back to the compilation folder + source_path = Path(source_file.name) + gcno_file = Path(source_path.stem + ".gcno") + if Path(gcno_file).exists(): + new_path = Path(source_file.library.directory) / gcno_file + gcno_file.rename(new_path) + + return compilation_ok + + def merge_coverage(self, file_name, args=None): + """ + Merge coverage from all test cases + """ + output_dir = file_name + + # Loop over each .gcda output folder and merge them two at a time + first_input = True + for coverage_dir in self._coverage_test_dirs: + if Path(coverage_dir).exists(): + merge_command = [ + "gcov-tool", + "merge", + "-o", + output_dir, + coverage_dir if first_input else output_dir, + coverage_dir, + ] + subprocess.call(merge_command) + first_input = False + else: + LOGGER.warning("Missing coverage directory: %s", coverage_dir) + + # Find actual output path of the .gcda files (they are deep in hierarchy) + dir_path = Path(output_dir) + gcda_dirs = {x.parent for x in dir_path.glob("**/*.gcda")} + assert len(gcda_dirs) == 1, "Expected exactly one folder with gcda files" + gcda_dir = gcda_dirs.pop() + + # Add compile-time .gcno files as well, they are needed for the report + for library in self._project.get_libraries(): + for gcno_file in Path(library.directory).glob("*.gcno"): + shutil.copy(gcno_file, gcda_dir) diff --git a/vunit/sim_if/modelsim.py b/vunit/sim_if/modelsim.py index 26d99e188..96bdbfdd4 100644 --- a/vunit/sim_if/modelsim.py +++ b/vunit/sim_if/modelsim.py @@ -81,6 +81,13 @@ def supports_vhdl_package_generics(cls): """ return True + @staticmethod + def supports_coverage(): + """ + Returns True when the simulator supports coverage + """ + return True + def __init__(self, prefix, output_path, persistent=False, gui=False): SimulatorInterface.__init__(self, output_path, gui) VsimSimulatorMixin.__init__( diff --git a/vunit/sim_if/rivierapro.py b/vunit/sim_if/rivierapro.py index 2e66ad7ce..bca118986 100644 --- a/vunit/sim_if/rivierapro.py +++ b/vunit/sim_if/rivierapro.py @@ -95,6 +95,13 @@ def supports_vhdl_package_generics(cls): """ return True + @staticmethod + def supports_coverage(): + """ + Returns True when the simulator supports coverage + """ + return True + def __init__(self, prefix, output_path, persistent=False, gui=False): SimulatorInterface.__init__(self, output_path, gui) VsimSimulatorMixin.__init__( diff --git a/vunit/ui/__init__.py b/vunit/ui/__init__.py index 33fc882c0..83e16b331 100644 --- a/vunit/ui/__init__.py +++ b/vunit/ui/__init__.py @@ -1099,3 +1099,13 @@ def get_simulator_name(self): if self._simulator_class is None: return None return self._simulator_class.name + + def simulator_supports_coverage(self): + """ + Returns True when the simulator supports coverage + + Will return None if no simulator was found. + """ + if self._simulator_class is None: + return None + return self._simulator_class.supports_coverage From 3124d974d5b23718edf14fb71b1ce0ad7fa958c1 Mon Sep 17 00:00:00 2001 From: eine <6628437+eine@users.noreply.github.com> Date: Thu, 26 Mar 2020 19:01:38 +0100 Subject: [PATCH 28/79] codec: fix "prefix of array attribute must be an object name" (#635) --- vunit/com/codec_vhdl_array_type.py | 26 +++--- vunit/com/codec_vhdl_package.py | 7 +- vunit/vhdl/data_types/test/tb_codec-2008p.vhd | 72 +++++++++------ vunit/vhdl/data_types/test/tb_codec.vhd | 92 ++++++++++++------- 4 files changed, 120 insertions(+), 77 deletions(-) diff --git a/vunit/com/codec_vhdl_array_type.py b/vunit/com/codec_vhdl_array_type.py index 95714046f..62a32ce95 100644 --- a/vunit/com/codec_vhdl_array_type.py +++ b/vunit/com/codec_vhdl_array_type.py @@ -172,7 +172,7 @@ class ArrayCodecTemplate(DatatypeCodecTemplate): function encode ( constant data : $type) return string is - constant length : positive := encode(data(data'left))'length; + constant length : positive := get_encoded_length(encode(data(data'left))); variable index : positive := 1; variable ret_val : string(1 to data'length * length); begin @@ -233,7 +233,7 @@ class ArrayCodecTemplate(DatatypeCodecTemplate): function encode ( constant data : $type) return string is - constant length : positive := encode(data(data'left(1), data'left(2)))'length; + constant length : positive := get_encoded_length(encode(data(data'left(1), data'left(2)))); variable index : positive := 1; variable ret_val : string(1 to data'length(1) * data'length(2) * length); begin @@ -305,11 +305,11 @@ class ArrayCodecTemplate(DatatypeCodecTemplate): if data'length = 0 then return 0; else - return encode(data(data'left))'length; + return get_encoded_length(encode(data(data'left))); end if; end; constant length : natural := element_length(data); - constant range_length : positive := encode(data'left)'length; + constant range_length : positive := get_encoded_length(encode(data'left)); variable index : positive := 2 + 2 * range_length; variable ret_val : string(1 to 1 + 2 * range_length + data'length * length); begin @@ -328,7 +328,7 @@ class ArrayCodecTemplate(DatatypeCodecTemplate): constant code : string; variable index : inout positive; variable result : out $array_type) is - constant range_length : positive := encode($range_type'left)'length; + constant range_length : positive := get_encoded_length(encode($range_type'left)); begin index := index + 1 + 2 * range_length; for i in result'range loop @@ -339,7 +339,7 @@ class ArrayCodecTemplate(DatatypeCodecTemplate): function decode ( constant code : string) return $array_type is - constant range_length : positive := encode($range_type'left)'length; + constant range_length : positive := get_encoded_length(encode($range_type'left)); function ret_val_range ( constant code : string) return $array_type is @@ -401,12 +401,12 @@ class ArrayCodecTemplate(DatatypeCodecTemplate): if data'length(1) * data'length(2) = 0 then return 0; else - return encode(data(data'left(1), data'left(2)))'length; + return get_encoded_length(encode(data(data'left(1), data'left(2)))); end if; end; constant length : natural := element_length(data); - constant range1_length : positive := encode(data'left(1))'length; - constant range2_length : positive := encode(data'left(2))'length; + constant range1_length : positive := get_encoded_length(encode(data'left(1))); + constant range2_length : positive := get_encoded_length(encode(data'left(2))); variable index : positive := 3 + 2 * range1_length + 2 * range2_length; variable ret_val : string(1 to 2 + 2 * range1_length + 2 * range2_length + data'length(1) * data'length(2) * length); @@ -428,8 +428,8 @@ class ArrayCodecTemplate(DatatypeCodecTemplate): constant code : string; variable index : inout positive; variable result : out $array_type) is - constant range1_length : positive := encode($range_type1'left)'length; - constant range2_length : positive := encode($range_type2'left)'length; + constant range1_length : positive := get_encoded_length(encode($range_type1'left)); + constant range2_length : positive := get_encoded_length(encode($range_type2'left)); begin index := index + 2 + 2 * range1_length + 2 * range2_length; for i in result'range(1) loop @@ -442,8 +442,8 @@ class ArrayCodecTemplate(DatatypeCodecTemplate): function decode ( constant code : string) return $array_type is - constant range1_length : positive := encode($range_type1'left)'length; - constant range2_length : positive := encode($range_type2'left)'length; + constant range1_length : positive := get_encoded_length(encode($range_type1'left)); + constant range2_length : positive := get_encoded_length(encode($range_type2'left)); function ret_val_range ( constant code : string) return $array_type is diff --git a/vunit/com/codec_vhdl_package.py b/vunit/com/codec_vhdl_package.py index 876e2b0d8..536b5dcac 100644 --- a/vunit/com/codec_vhdl_package.py +++ b/vunit/com/codec_vhdl_package.py @@ -128,7 +128,12 @@ def _generate_array_codec_and_to_string_functions(self): """Generate codecs and to_string functions for all array data types.""" declarations = "" - definitions = "" + definitions = """ + -- Helper function to make tests pass GHDL v0.37 + function get_encoded_length ( constant vec: string ) return integer is + begin return vec'length; end; + +""" for array in self.array_types: ( new_declarations, diff --git a/vunit/vhdl/data_types/test/tb_codec-2008p.vhd b/vunit/vhdl/data_types/test/tb_codec-2008p.vhd index a0f152c9a..f6f1c5acc 100644 --- a/vunit/vhdl/data_types/test/tb_codec-2008p.vhd +++ b/vunit/vhdl/data_types/test/tb_codec-2008p.vhd @@ -53,8 +53,30 @@ begin variable real_vector_5_downto_3 : real_vector(5 downto 3); variable time_vector_5_downto_3 : time_vector(5 downto 3); - -- Temp variables to make test case pass Riviera-PRO 2016.10 - variable range_left, range_right : integer; + -- Helper functions to make tests pass GHDL v0.37 and Riviera-PRO 2016.10 + function get_decoded_range_left ( constant vec: boolean_vector ) return integer is + begin return vec'left; end; + + function get_decoded_range_right ( constant vec: boolean_vector ) return integer is + begin return vec'right; end; + + function get_decoded_range_left ( constant vec: integer_vector ) return integer is + begin return vec'left; end; + + function get_decoded_range_right ( constant vec: integer_vector ) return integer is + begin return vec'right; end; + + function get_decoded_range_left ( constant vec: real_vector ) return integer is + begin return vec'left; end; + + function get_decoded_range_right ( constant vec: real_vector ) return integer is + begin return vec'right; end; + + function get_decoded_range_left ( constant vec: time_vector ) return integer is + begin return vec'left; end; + + function get_decoded_range_right ( constant vec: time_vector ) return integer is + begin return vec'right; end; begin test_runner_setup(runner, runner_cfg); @@ -63,57 +85,49 @@ begin if run("Test that boolean_vector can be encoded and decoded") then boolean_vector_5_downto_3 := (true, false, true); check_relation(decode_boolean_vector(encode_boolean_vector((true, false, true))) = boolean_vector'(true, false, true)); - check_relation(decode_boolean_vector(encode_boolean_vector((0 => true))) = boolean_vector'(0 => true)); + check_relation(decode_boolean_vector(encode_boolean_vector((0 => true))) = boolean_vector'(0 => true)); check_relation(decode_boolean_vector(encode_boolean_vector(null_boolean_vector)) = null_boolean_vector); check_relation(decode_boolean_vector(encode_boolean_vector(boolean_vector_5_downto_3)) = boolean_vector'(true, false, true)); - range_left := decode_boolean_vector(encode_boolean_vector(boolean_vector_5_downto_3))'left; - range_right := decode_boolean_vector(encode_boolean_vector(boolean_vector_5_downto_3))'right; - check_relation(range_left = 5); - check_relation(range_right = 3); + check_relation(get_decoded_range_left(decode_boolean_vector(encode_boolean_vector(boolean_vector_5_downto_3))) = 5); + check_relation(get_decoded_range_right(decode_boolean_vector(encode_boolean_vector(boolean_vector_5_downto_3))) = 3); elsif run("Test that integer_vector can be encoded and decoded") then integer_vector_5_downto_3 := (-42, 0, 17); check_relation(decode_integer_vector(encode_integer_vector((-2147483648, -2147483648, -2147483648))) = integer_vector'(-2147483648, -2147483648, -2147483648)); check_relation(decode_integer_vector(encode_integer_vector((-42, 0, 17))) = integer_vector'(-42, 0, 17)); - check_relation(decode_integer_vector(encode_integer_vector((0 => -42))) = integer_vector'(0 => -42)); + check_relation(decode_integer_vector(encode_integer_vector((0 => -42))) = integer_vector'(0 => -42)); check_relation(decode_integer_vector(encode_integer_vector(null_integer_vector)) = null_integer_vector); check_relation(decode_integer_vector(encode_integer_vector(integer_vector_5_downto_3)) = integer_vector'(-42, 0, 17)); - range_left := decode_integer_vector(encode_integer_vector(integer_vector_5_downto_3))'left; - range_right := decode_integer_vector(encode_integer_vector(integer_vector_5_downto_3))'right; - check_relation(range_left = 5); - check_relation(range_right = 3); + check_relation(get_decoded_range_left(decode_integer_vector(encode_integer_vector(integer_vector_5_downto_3))) = 5); + check_relation(get_decoded_range_right(decode_integer_vector(encode_integer_vector(integer_vector_5_downto_3))) = 3); elsif run("Test that real_vector can be encoded and decoded") then real_vector_5_downto_3 := (-42.42, 0.001, 17.17); check_relation(decode_real_vector(encode_real_vector((-42.42, 0.001, 17.17))) = real_vector'(-42.42, 0.001, 17.17)); check_relation(decode_real_vector(encode_real_vector((0 => -42.42))) = real_vector'(0 => -42.42)); check_relation(decode_real_vector(encode_real_vector(null_real_vector)) = null_real_vector); check_relation(decode_real_vector(encode_real_vector(real_vector_5_downto_3)) = real_vector'(-42.42, 0.001, 17.17)); - range_left := decode_real_vector(encode_real_vector(real_vector_5_downto_3))'left; - range_right := decode_real_vector(encode_real_vector(real_vector_5_downto_3))'right; - check_relation(range_left = 5); - check_relation(range_right = 3); + check_relation(get_decoded_range_left(decode_real_vector(encode_real_vector(real_vector_5_downto_3))) = 5); + check_relation(get_decoded_range_right(decode_real_vector(encode_real_vector(real_vector_5_downto_3))) = 3); elsif run("Test that time_vector can be encoded and decoded") then time_vector_5_downto_3 := (-42 ms, 0 sec, 17 min); check_relation(decode_time_vector(encode_time_vector((-42 ms, 0 sec, 17 min))) = time_vector'(-42 ms, 0 sec, 17 min)); - check_relation(decode_time_vector(encode_time_vector((0 => -42 ms))) = time_vector'(0 => -42 ms)); + check_relation(decode_time_vector(encode_time_vector((0 => -42 ms))) = time_vector'(0 => -42 ms)); check_relation(decode_time_vector(encode_time_vector(null_time_vector)) = null_time_vector); check_relation(decode_time_vector(encode_time_vector(time_vector_5_downto_3)) = time_vector'(-42 ms, 0 sec, 17 min)); - range_left := decode_time_vector(encode_time_vector(time_vector_5_downto_3))'left; - range_right := decode_time_vector(encode_time_vector(time_vector_5_downto_3))'right; - check_relation(range_left = 5); - check_relation(range_right = 3); + check_relation(get_decoded_range_left(decode_time_vector(encode_time_vector(time_vector_5_downto_3))) = 5); + check_relation(get_decoded_range_right(decode_time_vector(encode_time_vector(time_vector_5_downto_3))) = 3); elsif run("Test that ufixed can be encoded and decoded") then - check_relation(decode_ufixed(encode_ufixed(to_ufixed(6.5, 3, -3))) = to_ufixed(6.5, 3, -3)); - check_relation(decode_ufixed(encode_ufixed(to_ufixed(8.0, 3, 1))) = to_ufixed(8.0, 3, 1)); + check_relation(decode_ufixed(encode_ufixed(to_ufixed( 6.5, 3, -3))) = to_ufixed(6.5, 3, -3)); + check_relation(decode_ufixed(encode_ufixed(to_ufixed( 8.0, 3, 1))) = to_ufixed(8.0, 3, 1)); check_relation(decode_ufixed(encode_ufixed(to_ufixed(0.25, -2, -4))) = to_ufixed(0.25, -2, -4)); elsif run("Test that sfixed can be encoded and decoded") then - check_relation(decode_sfixed(encode_sfixed(to_sfixed(6.5, 3, -3))) = to_sfixed(6.5, 3, -3)); - check_relation(decode_sfixed(encode_sfixed(to_sfixed(8.0, 4, 1))) = to_sfixed(8.0, 4, 1)); - check_relation(decode_sfixed(encode_sfixed(to_sfixed(0.25, -1, -4))) = to_sfixed(0.25, -1, -4)); - check_relation(decode_sfixed(encode_sfixed(to_sfixed(-6.5, 3, -3))) = to_sfixed(-6.5, 3, -3)); - check_relation(decode_sfixed(encode_sfixed(to_sfixed(-8.0, 4, 1))) = to_sfixed(-8.0, 4, 1)); + check_relation(decode_sfixed(encode_sfixed(to_sfixed( 6.5, 3, -3))) = to_sfixed(6.5, 3, -3)); + check_relation(decode_sfixed(encode_sfixed(to_sfixed( 8.0, 4, 1))) = to_sfixed(8.0, 4, 1)); + check_relation(decode_sfixed(encode_sfixed(to_sfixed(0.25, -1, -4))) = to_sfixed(0.25, -1, -4)); + check_relation(decode_sfixed(encode_sfixed(to_sfixed(-6.5, 3, -3))) = to_sfixed(-6.5, 3, -3)); + check_relation(decode_sfixed(encode_sfixed(to_sfixed(-8.0, 4, 1))) = to_sfixed(-8.0, 4, 1)); check_relation(decode_sfixed(encode_sfixed(to_sfixed(-0.25, -1, -4))) = to_sfixed(-0.25, -1, -4)); elsif run("Test that float can be encoded and decoded") then - check_relation(decode_float(encode_float(to_float(real'low, 11, 52))) = to_float(real'low, 11, 52)); + check_relation(decode_float(encode_float(to_float(real'low, 11, 52))) = to_float(real'low, 11, 52)); check_relation(decode_float(encode_float(to_float(real'high, 11, 52))) = to_float(real'high, 11, 52)); check_relation(to_string(decode_float(encode_float(positive_zero))) = to_string(positive_zero)); diff --git a/vunit/vhdl/data_types/test/tb_codec.vhd b/vunit/vhdl/data_types/test/tb_codec.vhd index a4ee84d41..9fc354023 100644 --- a/vunit/vhdl/data_types/test/tb_codec.vhd +++ b/vunit/vhdl/data_types/test/tb_codec.vhd @@ -101,8 +101,48 @@ begin variable numeric_std_unsigned_5_downto_3 : ieee.numeric_std.unsigned(5 downto 3); variable numeric_std_signed_5_downto_3 : ieee.numeric_std.signed(5 downto 3); - -- Temp variables to make test case pass Riviera-PRO 2016.10 - variable range_left, range_right : integer; + -- Helper functions to make tests pass GHDL v0.37 and Riviera-PRO 2016.10 + function get_decoded_range_left ( constant vec: string ) return integer is + begin return vec'left; end; + + function get_decoded_range_right ( constant vec: string ) return integer is + begin return vec'right; end; + + function get_decoded_range_left ( constant vec: bit_vector ) return integer is + begin return vec'left; end; + + function get_decoded_range_right ( constant vec: bit_vector ) return integer is + begin return vec'right; end; + + function get_decoded_range_left ( constant vec: std_ulogic_vector ) return integer is + begin return vec'left; end; + + function get_decoded_range_right ( constant vec: std_ulogic_vector ) return integer is + begin return vec'right; end; + + function get_decoded_range_left ( constant vec: ieee.numeric_bit.unsigned ) return integer is + begin return vec'left; end; + + function get_decoded_range_right ( constant vec: ieee.numeric_bit.unsigned ) return integer is + begin return vec'right; end; + + function get_decoded_range_left ( constant vec: ieee.numeric_bit.signed ) return integer is + begin return vec'left; end; + + function get_decoded_range_right ( constant vec: ieee.numeric_bit.signed ) return integer is + begin return vec'right; end; + + function get_decoded_range_left ( constant vec: ieee.numeric_std.unsigned ) return integer is + begin return vec'left; end; + + function get_decoded_range_right ( constant vec: ieee.numeric_std.unsigned ) return integer is + begin return vec'right; end; + + function get_decoded_range_left ( constant vec: ieee.numeric_std.signed ) return integer is + begin return vec'left; end; + + function get_decoded_range_right ( constant vec: ieee.numeric_std.signed ) return integer is + begin return vec'right; end; begin test_runner_setup(runner, runner_cfg); @@ -174,35 +214,27 @@ begin string_15_downto_4 := "Hello world!"; check_relation(decode_string(encode_string("The quick brown fox jumps over the lazy dog")) = string'("The quick brown fox jumps over the lazy dog")); check_relation(decode_string(encode_string(special_chars)) = string'(special_chars)); - range_left := decode_string(encode_string(null_string))'left; - range_right := decode_string(encode_string(null_string))'right; - check_relation(range_left = 10); - check_relation(range_right = 9); + check_relation(get_decoded_range_left(decode_string(encode_string(null_string))) = 10); + check_relation(get_decoded_range_right(decode_string(encode_string(null_string))) = 9); check_relation(decode_string(encode_string(string_15_downto_4)) = string'("Hello world!")); - range_left := decode_string(encode_string(string_15_downto_4))'left; - range_right := decode_string(encode_string(string_15_downto_4))'right; - check_relation(range_left = 15); - check_relation(range_right = 4); + check_relation(get_decoded_range_left(decode_string(encode_string(string_15_downto_4))) = 15); + check_relation(get_decoded_range_right(decode_string(encode_string(string_15_downto_4))) = 4); elsif run("Test that bit_vector can be encoded and decoded") then bit_vector_5_downto_3 := "101"; check_relation(decode_bit_vector(encode_bit_vector("101")) = bit_vector'("101")); check_relation(decode_bit_vector(encode_bit_vector("1")) = bit_vector'("1")); check_relation(decode_bit_vector(encode_bit_vector("")) = bit_vector'("")); check_relation(decode_bit_vector(encode_bit_vector(bit_vector_5_downto_3)) = bit_vector'("101")); - range_left := decode_bit_vector(encode_bit_vector(bit_vector_5_downto_3))'left; - range_right := decode_bit_vector(encode_bit_vector(bit_vector_5_downto_3))'right; - check_relation(range_left = 5); - check_relation(range_right = 3); + check_relation(get_decoded_range_left(decode_bit_vector(encode_bit_vector(bit_vector_5_downto_3))) = 5); + check_relation(get_decoded_range_right(decode_bit_vector(encode_bit_vector(bit_vector_5_downto_3))) = 3); elsif run("Test that std_ulogic_vector can be encoded and decoded") then std_ulogic_vector_5_downto_3 := "XU1"; check_relation(decode_std_ulogic_vector(encode_std_ulogic_vector("XU1")) = std_ulogic_vector'("XU1")); check_relation(decode_std_ulogic_vector(encode_std_ulogic_vector("X")) = std_ulogic_vector'("X")); check_relation(decode_std_ulogic_vector(encode_std_ulogic_vector("")) = std_ulogic_vector'("")); check_relation(decode_std_ulogic_vector(encode_std_ulogic_vector(std_ulogic_vector_5_downto_3)) = std_ulogic_vector'("XU1")); - range_left := decode_std_ulogic_vector(encode_std_ulogic_vector(std_ulogic_vector_5_downto_3))'left; - range_right := decode_std_ulogic_vector(encode_std_ulogic_vector(std_ulogic_vector_5_downto_3))'right; - check_relation(range_left = 5); - check_relation(range_right = 3); + check_relation(get_decoded_range_left(decode_std_ulogic_vector(encode_std_ulogic_vector(std_ulogic_vector_5_downto_3))) = 5); + check_relation(get_decoded_range_right(decode_std_ulogic_vector(encode_std_ulogic_vector(std_ulogic_vector_5_downto_3))) = 3); elsif run("Test that complex can be encoded and decoded") then check_relation(decode_complex(encode_complex((-17.17, 42.42))) = complex'(-17.17, 42.42)); elsif run("Test that complex_polar can be encoded and decoded") then @@ -213,40 +245,32 @@ begin check_relation(decode_numeric_bit_unsigned(encode_numeric_bit_unsigned("1")) = ieee.numeric_bit.unsigned'("1")); check_relation(decode_numeric_bit_unsigned(encode_numeric_bit_unsigned("")) = ieee.numeric_bit.unsigned'("")); check_relation(decode_numeric_bit_unsigned(encode_numeric_bit_unsigned(numeric_bit_unsigned_5_downto_3)) = ieee.numeric_bit.unsigned'("101")); - range_left := decode_numeric_bit_unsigned(encode_numeric_bit_unsigned(numeric_bit_unsigned_5_downto_3))'left; - range_right := decode_numeric_bit_unsigned(encode_numeric_bit_unsigned(numeric_bit_unsigned_5_downto_3))'right; - check_relation(range_left = 5); - check_relation(range_right = 3); + check_relation(get_decoded_range_left(decode_numeric_bit_unsigned(encode_numeric_bit_unsigned(numeric_bit_unsigned_5_downto_3))) = 5); + check_relation(get_decoded_range_right(decode_numeric_bit_unsigned(encode_numeric_bit_unsigned(numeric_bit_unsigned_5_downto_3))) = 3); elsif run("Test that signed from numeric_bit can be encoded and decoded") then numeric_bit_signed_5_downto_3 := "101"; check_relation(decode_numeric_bit_signed(encode_numeric_bit_signed("101")) = ieee.numeric_bit.signed'("101")); check_relation(decode_numeric_bit_signed(encode_numeric_bit_signed("1")) = ieee.numeric_bit.signed'("1")); check_relation(decode_numeric_bit_signed(encode_numeric_bit_signed("")) = ieee.numeric_bit.signed'("")); check_relation(decode_numeric_bit_signed(encode_numeric_bit_signed(numeric_bit_signed_5_downto_3)) = ieee.numeric_bit.signed'("101")); - range_left := decode_numeric_bit_signed(encode_numeric_bit_signed(numeric_bit_signed_5_downto_3))'left; - range_right := decode_numeric_bit_signed(encode_numeric_bit_signed(numeric_bit_signed_5_downto_3))'right; - check_relation(range_left = 5); - check_relation(range_right = 3); + check_relation(get_decoded_range_left(decode_numeric_bit_signed(encode_numeric_bit_signed(numeric_bit_signed_5_downto_3))) = 5); + check_relation(get_decoded_range_right(decode_numeric_bit_signed(encode_numeric_bit_signed(numeric_bit_signed_5_downto_3))) = 3); elsif run("Test that unsigned from numeric_std can be encoded and decoded") then numeric_std_unsigned_5_downto_3 := "101"; check_relation(decode_numeric_std_unsigned(encode_numeric_std_unsigned("101")) = ieee.numeric_std.unsigned'("101")); check_relation(decode_numeric_std_unsigned(encode_numeric_std_unsigned("1")) = ieee.numeric_std.unsigned'("1")); check_relation(decode_numeric_std_unsigned(encode_numeric_std_unsigned("")) = ieee.numeric_std.unsigned'("")); check_relation(decode_numeric_std_unsigned(encode_numeric_std_unsigned(numeric_std_unsigned_5_downto_3)) = ieee.numeric_std.unsigned'("101")); - range_left := decode_numeric_std_unsigned(encode_numeric_std_unsigned(numeric_std_unsigned_5_downto_3))'left; - range_right := decode_numeric_std_unsigned(encode_numeric_std_unsigned(numeric_std_unsigned_5_downto_3))'right; - check_relation(range_left = 5); - check_relation(range_right = 3); + check_relation(get_decoded_range_left(decode_numeric_std_unsigned(encode_numeric_std_unsigned(numeric_std_unsigned_5_downto_3))) = 5); + check_relation(get_decoded_range_right(decode_numeric_std_unsigned(encode_numeric_std_unsigned(numeric_std_unsigned_5_downto_3))) = 3); elsif run("Test that signed from numeric_std can be encoded and decoded") then numeric_std_signed_5_downto_3 := "101"; check_relation(decode_numeric_std_signed(encode_numeric_std_signed("101")) = ieee.numeric_std.signed'("101")); check_relation(decode_numeric_std_signed(encode_numeric_std_signed("1")) = ieee.numeric_std.signed'("1")); check_relation(decode_numeric_std_signed(encode_numeric_std_signed("")) = ieee.numeric_std.signed'("")); check_relation(decode_numeric_std_signed(encode_numeric_std_signed(numeric_std_signed_5_downto_3)) = ieee.numeric_std.signed'("101")); - range_left := decode_numeric_std_signed(encode_numeric_std_signed(numeric_std_signed_5_downto_3))'left; - range_right := decode_numeric_std_signed(encode_numeric_std_signed(numeric_std_signed_5_downto_3))'right; - check_relation(range_left = 5); - check_relation(range_right = 3); + check_relation(get_decoded_range_left(decode_numeric_std_signed(encode_numeric_std_signed(numeric_std_signed_5_downto_3))) = 5); + check_relation(get_decoded_range_right(decode_numeric_std_signed(encode_numeric_std_signed(numeric_std_signed_5_downto_3))) = 3); end if; end loop; From aad93a50457b65b650d4608b1b939ecdb53af3b8 Mon Sep 17 00:00:00 2001 From: eine Date: Fri, 6 Mar 2020 04:43:38 +0100 Subject: [PATCH 29/79] Release 4.4.0 --- docs/release_notes/4.4.0.rst | 20 ++++++++++++++++++++ vunit/about.py | 2 +- 2 files changed, 21 insertions(+), 1 deletion(-) create mode 100644 docs/release_notes/4.4.0.rst diff --git a/docs/release_notes/4.4.0.rst b/docs/release_notes/4.4.0.rst new file mode 100644 index 000000000..6cd015521 --- /dev/null +++ b/docs/release_notes/4.4.0.rst @@ -0,0 +1,20 @@ +- Update year and update license test to 2020. +- Bump OSVVM to latest version. +- Add possibility to configure random stalls for AXI Stream. :vunit_issue:`557` +- JSON-for-VHDL: use base16 encodings :vunit_issue:`595` +- First release requiring Python 3.6 or higher. Python 2.7, 3.4 and 3.5 are not supported anymore. :vunit_issue:`596` :vunit_issue:`601` +- Start adding type annotations to the Python sources; add mypy (a static type checker) to the list of linters. :vunit_issue:`601` :vunit_issue:`626` +- Move co-simulation (VHPIDIRECT) sources (implementation and example) to `VUnit/cosim `_ :vunit_issue:`606` +- ghdl interface: with ``ghdl_e``, save runtime args to JSON file :vunit_issue:`606` +- Add missing mode assertions to ``-93`` sources of ``integer_vector_ptr`` and ``string_ptr``. :vunit_issue:`607` +- Add method ``get_simulator_name()`` to public Python API. :vunit_issue:`610` +- Start replacing ``join``, ``dirname``, etc. with ``pathlib`` :vunit_issue:`612` :vunit_issue:`626` :vunit_issue:`632` +- Fix parsing adjacent hyphens in a literal. :vunit_issue:`616` +- Fix ``ghdl.flags`` error in documentation. :vunit_issue:`620` +- Rename compile option 'ghdl.flags' to 'ghdl.a_flags' :vunit_issue:`624` +- Move ``project.Library`` to separate file +- Remove Travis CI and AppVeyor, use GitHub Actions only +- Remove Sphinx extension ABlog; handle posts as regular pages in subdir ``blog`` +- Update GHDL to v0.37 in Windows CI jobs. +- Fix regression in GHDL (``prefix of array attribute must be an object name``) :vunit_issue:`631` :vunit_issue:`635` +- Add code coverage support for GHDL :vunit_issue:`627` diff --git a/vunit/about.py b/vunit/about.py index 2748dae50..4229798e4 100644 --- a/vunit/about.py +++ b/vunit/about.py @@ -68,4 +68,4 @@ def version(): return VERSION -VERSION = "4.3.1rc0" +VERSION = "4.4.0" From 2bd9b7588726409787935135161c5c7f17a46641 Mon Sep 17 00:00:00 2001 From: eine Date: Fri, 6 Mar 2020 04:43:39 +0100 Subject: [PATCH 30/79] Start of next release candidate 4.4.1rc0 --- vunit/about.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vunit/about.py b/vunit/about.py index 4229798e4..87eaed5b8 100644 --- a/vunit/about.py +++ b/vunit/about.py @@ -68,4 +68,4 @@ def version(): return VERSION -VERSION = "4.4.0" +VERSION = "4.4.1rc0" From 55672f36a5cacbe93ec0ab9b3bbca8763112b6af Mon Sep 17 00:00:00 2001 From: eine Date: Thu, 26 Mar 2020 20:48:24 +0100 Subject: [PATCH 31/79] style: doc/release_notes/4.4.0.rst --- docs/release_notes/4.4.0.rst | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/docs/release_notes/4.4.0.rst b/docs/release_notes/4.4.0.rst index 6cd015521..078eea7ec 100644 --- a/docs/release_notes/4.4.0.rst +++ b/docs/release_notes/4.4.0.rst @@ -1,20 +1,20 @@ - Update year and update license test to 2020. - Bump OSVVM to latest version. - Add possibility to configure random stalls for AXI Stream. :vunit_issue:`557` -- JSON-for-VHDL: use base16 encodings :vunit_issue:`595` +- JSON-for-VHDL: use base16 encodings. :vunit_issue:`595` - First release requiring Python 3.6 or higher. Python 2.7, 3.4 and 3.5 are not supported anymore. :vunit_issue:`596` :vunit_issue:`601` - Start adding type annotations to the Python sources; add mypy (a static type checker) to the list of linters. :vunit_issue:`601` :vunit_issue:`626` -- Move co-simulation (VHPIDIRECT) sources (implementation and example) to `VUnit/cosim `_ :vunit_issue:`606` -- ghdl interface: with ``ghdl_e``, save runtime args to JSON file :vunit_issue:`606` +- Move co-simulation (VHPIDIRECT) sources (implementation and example) to `VUnit/cosim `_. :vunit_issue:`606` +- ghdl interface: with ``ghdl_e``, save runtime args to JSON file. :vunit_issue:`606` - Add missing mode assertions to ``-93`` sources of ``integer_vector_ptr`` and ``string_ptr``. :vunit_issue:`607` - Add method ``get_simulator_name()`` to public Python API. :vunit_issue:`610` -- Start replacing ``join``, ``dirname``, etc. with ``pathlib`` :vunit_issue:`612` :vunit_issue:`626` :vunit_issue:`632` +- Start replacing ``join``, ``dirname``, etc. with ``pathlib``. :vunit_issue:`612` :vunit_issue:`626` :vunit_issue:`632` - Fix parsing adjacent hyphens in a literal. :vunit_issue:`616` - Fix ``ghdl.flags`` error in documentation. :vunit_issue:`620` -- Rename compile option 'ghdl.flags' to 'ghdl.a_flags' :vunit_issue:`624` -- Move ``project.Library`` to separate file -- Remove Travis CI and AppVeyor, use GitHub Actions only -- Remove Sphinx extension ABlog; handle posts as regular pages in subdir ``blog`` +- Rename compile option ``ghdl.flags`` to ``ghdl.a_flags``. :vunit_issue:`624` +- Move ``project.Library`` to separate file. +- Remove Travis CI and AppVeyor, use GitHub Actions only. +- Remove Sphinx extension ABlog; handle posts as regular pages in subdir ``blog``. - Update GHDL to v0.37 in Windows CI jobs. -- Fix regression in GHDL (``prefix of array attribute must be an object name``) :vunit_issue:`631` :vunit_issue:`635` -- Add code coverage support for GHDL :vunit_issue:`627` +- Fix regression in GHDL (``prefix of array attribute must be an object name``). :vunit_issue:`631` :vunit_issue:`635` +- Add code coverage support for GHDL. :vunit_issue:`627` From fe11736e0d04103ecdf194c97734b2550f6b1911 Mon Sep 17 00:00:00 2001 From: eine Date: Thu, 26 Mar 2020 21:04:27 +0100 Subject: [PATCH 32/79] fix: release trigger condition in workflow 'push' --- .github/workflows/push.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml index 424c36664..ebbc35b91 100644 --- a/.github/workflows/push.yml +++ b/.github/workflows/push.yml @@ -116,7 +116,7 @@ jobs: deploy: runs-on: ubuntu-latest needs: [ fmt, lin, docker, win ] - if: github.event_name == 'release' && github.event.action == 'created' + if: github.event_name == 'push' && github.event.created == 'true' steps: - uses: actions/checkout@v1 with: From eb5aa86f9ea0440a81b17286290a140e6453d778 Mon Sep 17 00:00:00 2001 From: eine Date: Thu, 26 Mar 2020 21:50:55 +0100 Subject: [PATCH 33/79] fix: make tools/release.py executable --- tools/release.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 tools/release.py diff --git a/tools/release.py b/tools/release.py old mode 100644 new mode 100755 From e5d4595d0d12c5ac7c7d795d46e306ef66d82609 Mon Sep 17 00:00:00 2001 From: eine Date: Thu, 26 Mar 2020 21:02:36 +0100 Subject: [PATCH 34/79] docs: Travis is not used for releases --- docs/contributing.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/contributing.rst b/docs/contributing.rst index 587dc650c..674ea752e 100644 --- a/docs/contributing.rst +++ b/docs/contributing.rst @@ -205,7 +205,7 @@ To create a new tagged release commit: commits have to be merged into origin/master. -Travic CI makes a release by uploading a new package to PyPI when a tag +The CI service makes a release by uploading a new package to PyPI when a tag named ``vX.Y.Z`` is found in Git. A new release will not be made if: - The ``X.Y.Z`` release is already on PyPI. From 15669a9fd67d35e052b4185c5da5d640dec66243 Mon Sep 17 00:00:00 2001 From: Lukas Vik <10241915+LukasVik@users.noreply.github.com> Date: Tue, 31 Mar 2020 21:50:50 +0200 Subject: [PATCH 35/79] call supports_coverage() rather than returning method object (#638) Previously the method was returned rather than a boolean. So doing "vunit_proj.simulator_supports_coverage()" in run.py would always evaluate as True. Co-authored-by: Lukas Vik <2767848-LukasVik@users.noreply.gitlab.com> --- vunit/ui/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vunit/ui/__init__.py b/vunit/ui/__init__.py index 83e16b331..304d01549 100644 --- a/vunit/ui/__init__.py +++ b/vunit/ui/__init__.py @@ -1108,4 +1108,4 @@ def simulator_supports_coverage(self): """ if self._simulator_class is None: return None - return self._simulator_class.supports_coverage + return self._simulator_class.supports_coverage() From d93adf72c928cb173ed2fcef48ddcdcc8c670f2c Mon Sep 17 00:00:00 2001 From: eine Date: Mon, 6 Apr 2020 15:50:29 +0200 Subject: [PATCH 36/79] fix: use 'str' for params to 'self.check' --- tests/acceptance/test_external_run_scripts.py | 88 ++++++++++--------- 1 file changed, 46 insertions(+), 42 deletions(-) diff --git a/tests/acceptance/test_external_run_scripts.py b/tests/acceptance/test_external_run_scripts.py index 17a90a49a..12e07d3b7 100644 --- a/tests/acceptance/test_external_run_scripts.py +++ b/tests/acceptance/test_external_run_scripts.py @@ -36,15 +36,15 @@ class TestExternalRunScripts(unittest.TestCase): """ def test_vhdl_uart_example_project(self): - self.check(ROOT / "examples" / "vhdl" / "uart" / "run.py") + self.check(str(ROOT / "examples" / "vhdl" / "uart" / "run.py")) @unittest.skipUnless(simulator_supports_verilog(), "Verilog") def test_verilog_uart_example_project(self): - self.check(ROOT / "examples" / "verilog" / "uart" / "run.py") + self.check(str(ROOT / "examples" / "verilog" / "uart" / "run.py")) @unittest.skipUnless(simulator_supports_verilog(), "Verilog") def test_verilog_ams_example(self): - self.check(ROOT / "examples" / "verilog" / "verilog_ams" / "run.py") + self.check(str(ROOT / "examples" / "verilog" / "verilog_ams" / "run.py")) check_report( self.report_file, [ @@ -54,10 +54,10 @@ def test_verilog_ams_example(self): ) def test_vhdl_logging_example_project(self): - self.check(ROOT / "examples" / "vhdl" / "logging" / "run.py") + self.check(str(ROOT / "examples" / "vhdl" / "logging" / "run.py")) def test_vhdl_run_example_project(self): - self.check(ROOT / "examples" / "vhdl" / "run" / "run.py", exit_code=1) + self.check(str(ROOT / "examples" / "vhdl" / "run" / "run.py"), exit_code=1) check_report( self.report_file, [ @@ -100,7 +100,7 @@ def test_vhdl_run_example_project(self): def test_vhdl_third_party_integration_example_project(self): self.check( - ROOT / "examples" / "vhdl" / "third_party_integration" / "run.py", + str(ROOT / "examples" / "vhdl" / "third_party_integration" / "run.py"), exit_code=1, ) check_report( @@ -119,17 +119,17 @@ def test_vhdl_third_party_integration_example_project(self): ) def test_vhdl_check_example_project(self): - self.check(ROOT / "examples" / "vhdl" / "check" / "run.py") + self.check(str(ROOT / "examples" / "vhdl" / "check" / "run.py")) @unittest.skipIf( simulator_check(lambda simclass: not simclass.supports_coverage()), "This simulator/backend does not support coverage", ) def test_vhdl_coverage_example_project(self): - self.check(join(ROOT, "examples", "vhdl", "coverage", "run.py")) + self.check(str(ROOT / "examples" / "vhdl" / "coverage" / "run.py")) def test_vhdl_generate_tests_example_project(self): - self.check(ROOT / "examples" / "vhdl" / "generate_tests" / "run.py") + self.check(str(ROOT / "examples" / "vhdl" / "generate_tests" / "run.py")) check_report( self.report_file, [ @@ -146,7 +146,7 @@ def test_vhdl_generate_tests_example_project(self): ) def test_vhdl_composite_generics_example_project(self): - self.check(ROOT / "examples" / "vhdl" / "composite_generics" / "run.py") + self.check(str(ROOT / "examples" / "vhdl" / "composite_generics" / "run.py")) check_report( self.report_file, [ @@ -159,19 +159,21 @@ def test_vhdl_composite_generics_example_project(self): simulator_is("ghdl"), "Support complex JSON strings as generic" ) def test_vhdl_json4vhdl_example_project(self): - self.check(ROOT / "examples" / "vhdl" / "json4vhdl" / "run.py") + self.check(str(ROOT / "examples" / "vhdl" / "json4vhdl" / "run.py")) def test_vhdl_array_example_project(self): - self.check(ROOT / "examples" / "vhdl" / "array" / "run.py") + self.check(str(ROOT / "examples" / "vhdl" / "array" / "run.py")) def test_vhdl_array_axis_vcs_example_project(self): - self.check(ROOT / "examples" / "vhdl" / "array_axis_vcs" / "run.py") + self.check(str(ROOT / "examples" / "vhdl" / "array_axis_vcs" / "run.py")) def test_vhdl_axi_dma_example_project(self): - self.check(ROOT / "examples" / "vhdl" / "axi_dma" / "run.py") + self.check(str(ROOT / "examples" / "vhdl" / "axi_dma" / "run.py")) def test_vhdl_user_guide_example_project(self): - self.check(ROOT / "examples" / "vhdl" / "user_guide" / "run.py", exit_code=1) + self.check( + str(ROOT / "examples" / "vhdl" / "user_guide" / "run.py"), exit_code=1 + ) check_report( self.report_file, [ @@ -183,7 +185,9 @@ def test_vhdl_user_guide_example_project(self): @unittest.skipUnless(simulator_supports_verilog(), "Verilog") def test_verilog_user_guide_example_project(self): - self.check(ROOT / "examples" / "verilog" / "user_guide" / "run.py", exit_code=1) + self.check( + str(ROOT / "examples" / "verilog" / "user_guide" / "run.py"), exit_code=1 + ) check_report( self.report_file, [ @@ -201,79 +205,79 @@ def test_verilog_user_guide_example_project(self): ) def test_vhdl_com_example_project(self): - self.check(ROOT / "examples" / "vhdl" / "com" / "run.py") + self.check(str(ROOT / "examples" / "vhdl" / "com" / "run.py")) def test_array_vhdl_2008(self): - self.check(VHDL_PATH / "array" / "run.py") + self.check(str(VHDL_PATH / "array" / "run.py")) def test_data_types_vhdl_2008(self): - self.check(VHDL_PATH / "data_types" / "run.py") + self.check(str(VHDL_PATH / "data_types" / "run.py")) def test_data_types_vhdl_2002(self): - self.check(VHDL_PATH / "data_types" / "run.py", vhdl_standard="2002") + self.check(str(VHDL_PATH / "data_types" / "run.py"), vhdl_standard="2002") def test_data_types_vhdl_93(self): - self.check(VHDL_PATH / "data_types" / "run.py", vhdl_standard="93") + self.check(str(VHDL_PATH / "data_types" / "run.py"), vhdl_standard="93") def test_random_vhdl_2008(self): - self.check(VHDL_PATH / "random" / "run.py") + self.check(str(VHDL_PATH / "random" / "run.py")) def test_check_vhdl_2008(self): - self.check(VHDL_PATH / "check" / "run.py") + self.check(str(VHDL_PATH / "check" / "run.py")) def test_check_vhdl_2002(self): - self.check(VHDL_PATH / "check" / "run.py", vhdl_standard="2002") + self.check(str(VHDL_PATH / "check" / "run.py"), vhdl_standard="2002") def test_check_vhdl_93(self): - self.check(VHDL_PATH / "check" / "run.py", vhdl_standard="93") + self.check(str(VHDL_PATH / "check" / "run.py"), vhdl_standard="93") def test_logging_vhdl_2008(self): - self.check(VHDL_PATH / "logging" / "run.py") + self.check(str(VHDL_PATH / "logging" / "run.py")) def test_logging_vhdl_2002(self): - self.check(VHDL_PATH / "logging" / "run.py", vhdl_standard="2002") + self.check(str(VHDL_PATH / "logging" / "run.py"), vhdl_standard="2002") def test_logging_vhdl_93(self): - self.check(VHDL_PATH / "logging" / "run.py", vhdl_standard="93") + self.check(str(VHDL_PATH / "logging" / "run.py"), vhdl_standard="93") def test_run_vhdl_2008(self): - self.check(VHDL_PATH / "run" / "run.py") + self.check(str(VHDL_PATH / "run" / "run.py")) def test_run_vhdl_2002(self): - self.check(VHDL_PATH / "run" / "run.py", vhdl_standard="2002") + self.check(str(VHDL_PATH / "run" / "run.py"), vhdl_standard="2002") def test_run_vhdl_93(self): - self.check(VHDL_PATH / "run" / "run.py", vhdl_standard="93") + self.check(str(VHDL_PATH / "run" / "run.py"), vhdl_standard="93") def test_string_ops_vhdl_2008(self): - self.check(VHDL_PATH / "string_ops" / "run.py") + self.check(str(VHDL_PATH / "string_ops" / "run.py")) def test_string_ops_vhdl_2002(self): - self.check(VHDL_PATH / "string_ops" / "run.py", vhdl_standard="2002") + self.check(str(VHDL_PATH / "string_ops" / "run.py"), vhdl_standard="2002") def test_string_ops_vhdl_93(self): - self.check(VHDL_PATH / "string_ops" / "run.py", vhdl_standard="93") + self.check(str(VHDL_PATH / "string_ops" / "run.py"), vhdl_standard="93") def test_dictionary_vhdl_2008(self): - self.check(VHDL_PATH / "dictionary" / "run.py") + self.check(str(VHDL_PATH / "dictionary" / "run.py")) def test_dictionary_vhdl_2002(self): - self.check(VHDL_PATH / "dictionary" / "run.py", vhdl_standard="2002") + self.check(str(VHDL_PATH / "dictionary" / "run.py"), vhdl_standard="2002") def test_dictionary_vhdl_93(self): - self.check(VHDL_PATH / "dictionary" / "run.py", vhdl_standard="93") + self.check(str(VHDL_PATH / "dictionary" / "run.py"), vhdl_standard="93") def test_path_vhdl_2008(self): - self.check(VHDL_PATH / "path" / "run.py") + self.check(str(VHDL_PATH / "path" / "run.py")) def test_path_vhdl_2002(self): - self.check(VHDL_PATH / "path" / "run.py", vhdl_standard="2002") + self.check(str(VHDL_PATH / "path" / "run.py"), vhdl_standard="2002") def test_path_vhdl_93(self): - self.check(VHDL_PATH / "path" / "run.py", vhdl_standard="93") + self.check(str(VHDL_PATH / "path" / "run.py"), vhdl_standard="93") def test_com_vhdl_2008(self): - self.check(VHDL_PATH / "com" / "run.py") + self.check(str(VHDL_PATH / "com" / "run.py")) def setUp(self): self.output_path = str(Path(__file__).parent / "external_run_out") @@ -289,7 +293,7 @@ def check(self, run_file: Path, args=None, vhdl_standard="2008", exit_code=0): retcode = call( [ sys.executable, - run_file, + str(run_file), "--clean", "--output-path=%s" % self.output_path, "--xunit-xml=%s" % self.report_file, From 843fd1269da6ba83251dcda258eaea2e62c6e363 Mon Sep 17 00:00:00 2001 From: eine Date: Mon, 6 Apr 2020 16:56:31 +0200 Subject: [PATCH 37/79] docs: refs to methods are not found --- docs/cli.rst | 6 ++---- docs/py/vunit.rst | 2 +- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/docs/cli.rst b/docs/cli.rst index 944caa3cc..5f0ef5386 100644 --- a/docs/cli.rst +++ b/docs/cli.rst @@ -3,12 +3,10 @@ Command Line Interface ====================== A :class:`VUnit ` object can be created from command -line arguments by using the :meth:`from_argv -` method effectively creating a custom +line arguments by using the **from_argv** method effectively creating a custom command line tool for running tests in the user project. Source files and libraries are added to the project by using methods on the VUnit -object. The configuration is followed by a call to the :meth:`main -` method which will execute the function +object. The configuration is followed by a call to the **main** method which will execute the function specified by the command line arguments and exit the script. The added source files are automatically scanned for test cases. diff --git a/docs/py/vunit.rst b/docs/py/vunit.rst index 230788481..96256f6e7 100644 --- a/docs/py/vunit.rst +++ b/docs/py/vunit.rst @@ -1,5 +1,5 @@ vunit.ui -============== +======== .. autoclass:: vunit.ui.VUnit() :exclude-members: add_preprocessor, From 449232663fe14502addba198bb6166a40d00bdb0 Mon Sep 17 00:00:00 2001 From: eine Date: Tue, 7 Apr 2020 17:50:57 +0200 Subject: [PATCH 38/79] update .gitignore (#641) --- .gitignore | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/.gitignore b/.gitignore index c8fb98bd3..fc6e29d05 100644 --- a/.gitignore +++ b/.gitignore @@ -7,16 +7,19 @@ .devcontainer .tox .vscode -docs/_build -docs/examples.rst -docs/release_notes.rst env/ -tests/acceptance/*_out -tests/unit/test_report_output.txt venv/ -vunit/vhdl/check/test/tb_check_equal.vhd -vunit/vhdl/check/test/tb_check_match.vhd error.csv log.csv my_logger.csv -examples/vhdl/array_axis_vcs/src/test/data/out.csv +/build/ +/dist/ +/docs/_build +/docs/_theme +/docs/examples.rst +/docs/release_notes.rst +/examples/vhdl/array_axis_vcs/src/test/data/out.csv +/tests/acceptance/*_out +/tests/unit/test_report_output.txt +/vunit/vhdl/check/test/tb_check_equal.vhd +/vunit/vhdl/check/test/tb_check_match.vhd From 9bd524671e07b30e11de653205c34f285d9cf71b Mon Sep 17 00:00:00 2001 From: eine Date: Thu, 9 Apr 2020 16:45:19 +0200 Subject: [PATCH 39/79] tools: raise exception if git not available when creating release notes --- tools/create_release_notes.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/tools/create_release_notes.py b/tools/create_release_notes.py index e4f35d8ab..937133bce 100644 --- a/tools/create_release_notes.py +++ b/tools/create_release_notes.py @@ -120,10 +120,9 @@ def _get_date(commit): """ Get date """ - date_str = ( - check_output([which("git"), "log", "-1", "--format=%ci", commit]) - .decode() - .strip() - ) + git = which("git") + if git is None: + raise BaseException("'git' is required!") + date_str = check_output([git, "log", "-1", "--format=%ci", commit]).decode().strip() date_str = " ".join(date_str.split(" ")[0:2]) return datetime.datetime.strptime(date_str, "%Y-%m-%d %H:%M:%S") From 0b3bf191acd568cd09c7654341483b6d155aa848 Mon Sep 17 00:00:00 2001 From: eine Date: Thu, 9 Apr 2020 18:09:01 +0200 Subject: [PATCH 40/79] docs: use autodoc_default_options instead of (deprecated) autodoc_default_flags --- docs/conf.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/conf.py b/docs/conf.py index 4787dada8..03c50a0b5 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -27,7 +27,9 @@ "sphinxarg.ext", # Automatic argparse command line argument documentation ] -autodoc_default_flags = ["members"] +autodoc_default_options = { + "members": True, +} # The suffix(es) of source filenames. source_suffix = ".rst" From ea1a5f557f7c3f41ef66c002892ea56604840aaf Mon Sep 17 00:00:00 2001 From: eine Date: Thu, 9 Apr 2020 18:08:01 +0200 Subject: [PATCH 41/79] docs: fix duplicated content and index of vunit_cli --- vunit/vunit_cli.py | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/vunit/vunit_cli.py b/vunit/vunit_cli.py index 1f3003028..1feea021c 100644 --- a/vunit/vunit_cli.py +++ b/vunit/vunit_cli.py @@ -10,13 +10,9 @@ Adding Custom Command Line Arguments ------------------------------------ It is possible to add custom command line arguments to your ``run.py`` -scripts using the :class:`.VUnitCLI` class. - -.. autoclass:: vunit.vunit_cli.VUnitCLI - :members: - -A :class:`.VUnitCLI` object has a ``parser`` field which is an -`ArgumentParser` object of the `argparse`_ library. +scripts using the ``VUnitCLI`` class. A ``VUnitCLI`` object +has a ``parser`` field which is an `ArgumentParser` object of the +`argparse`_ library. .. _argparse: https://docs.python.org/3/library/argparse.html From 10b4daa99cb2732c968ae03e06923db4c1f001af Mon Sep 17 00:00:00 2001 From: eine Date: Thu, 9 Apr 2020 18:09:27 +0200 Subject: [PATCH 42/79] docs: add intersphinx mapping to docs.python.org --- docs/conf.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/conf.py b/docs/conf.py index 03c50a0b5..5164e2386 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -79,6 +79,12 @@ # Output file base name for HTML help builder. htmlhelp_basename = "vunitdoc" +# -- InterSphinx ---------------------------------------------------------- + +intersphinx_mapping = { + "python": ("https://docs.python.org/3.8/", None), +} + # -- ExtLinks ------------------------------------------------------------- extlinks = { From 315d0969589e292b4eec690cc27edae1bc74bcda Mon Sep 17 00:00:00 2001 From: eine Date: Thu, 9 Apr 2020 18:25:25 +0200 Subject: [PATCH 43/79] revert "docs: refs to methods are not found" This (partially) reverts commit 843fd1269da6ba83251dcda258eaea2e62c6e363. --- docs/cli.rst | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/cli.rst b/docs/cli.rst index 5f0ef5386..944caa3cc 100644 --- a/docs/cli.rst +++ b/docs/cli.rst @@ -3,10 +3,12 @@ Command Line Interface ====================== A :class:`VUnit ` object can be created from command -line arguments by using the **from_argv** method effectively creating a custom +line arguments by using the :meth:`from_argv +` method effectively creating a custom command line tool for running tests in the user project. Source files and libraries are added to the project by using methods on the VUnit -object. The configuration is followed by a call to the **main** method which will execute the function +object. The configuration is followed by a call to the :meth:`main +` method which will execute the function specified by the command line arguments and exit the script. The added source files are automatically scanned for test cases. From 89957a9b227ace890bfbfab0d50ff59940560b5c Mon Sep 17 00:00:00 2001 From: Lars Asplund Date: Thu, 16 Apr 2020 20:57:50 +0200 Subject: [PATCH 44/79] Updated check_stable to handle longer time frames. Closes #636. --- vunit/vhdl/check/src/check.vhd | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/vunit/vhdl/check/src/check.vhd b/vunit/vhdl/check/src/check.vhd index de4987c25..8b99ce414 100644 --- a/vunit/vhdl/check/src/check.vhd +++ b/vunit/vhdl/check/src/check.vhd @@ -132,8 +132,8 @@ package body check_pkg is end if; end; - function to_ordinal_number (num : natural) return string is - constant num_str : string := natural'image(num); + function to_ordinal_number (num : unsigned) return string is + constant num_str : string := to_integer_string(num); variable ordinal_unit : string(1 to 2); begin case num_str(num_str'right) is @@ -758,7 +758,7 @@ package body check_pkg is variable state : inout check_stable_fsm_state_t; variable ref : inout std_logic_vector; - variable clock_edge_counter : inout natural; + variable clock_edge_counter : inout unsigned(63 downto 0); variable is_stable : inout boolean; variable exit_stability_check : out boolean) is @@ -779,7 +779,7 @@ package body check_pkg is procedure open_window (variable open_ok : out boolean) is begin - clock_edge_counter := 1; + clock_edge_counter := x"0000000000000001"; ref := to_x01(expr); open_ok := true; if is_x(start_event) then @@ -798,7 +798,7 @@ package body check_pkg is end if; end procedure; - procedure close_window(cycle : natural; is_ok : boolean) is + procedure close_window(cycle : unsigned; is_ok : boolean) is variable close_ok : boolean := is_ok; variable pass_msg_en : boolean; begin @@ -815,7 +815,7 @@ package body check_pkg is passing_check(checker, std_msg("Stability check passed", msg, "Got " & format(ref) & - " for " & positive'image(cycle) & + " for " & to_integer_string(cycle) & " active and enabled clock edges."), line_num, file_name); else @@ -891,7 +891,7 @@ package body check_pkg is variable state : check_stable_fsm_state_t := idle; variable ref : std_logic_vector(expr'range); - variable clock_edge_counter : natural; + variable clock_edge_counter : unsigned(63 downto 0); variable is_stable : boolean := true; variable exit_stability_check : boolean; begin @@ -942,7 +942,7 @@ package body check_pkg is variable state : check_stable_fsm_state_t := idle; variable ref : std_logic_vector(0 to 0); - variable clock_edge_counter : natural; + variable clock_edge_counter : unsigned(63 downto 0); variable is_stable : boolean := true; variable exit_stability_check : boolean; begin @@ -1608,7 +1608,7 @@ package body check_pkg is failing_check(checker, std_msg("Next check failed", msg, "Got " & std_logic'image(expr)(2) & - " at the " & to_ordinal_number(num_cks) & + " at the " & to_ordinal_number(to_unsigned(num_cks, 32)) & " active and enabled clock edge."), level, line_num, file_name); end if; @@ -1625,7 +1625,7 @@ package body check_pkg is failing_check(checker, std_msg("Next check failed", msg, "Got overlapping start event at the " & - to_ordinal_number(clock_cycles_after_start_event) & + to_ordinal_number(to_unsigned(clock_cycles_after_start_event, 32)) & " active and enabled clock edge."), level, line_num, file_name); else @@ -1760,7 +1760,7 @@ package body check_pkg is failing_check(checker, std_msg("Sequence check failed", msg, "Missing required event at " & - to_ordinal_number(i) & + to_ordinal_number(to_unsigned(i, 32)) & " active and enabled clock edge."), level, line_num, file_name); elsif i = seq'right then From 209e27d28cf9abb93b2d4f7ccc9e26882df11859 Mon Sep 17 00:00:00 2001 From: umarcor <38422348+umarcor@users.noreply.github.com> Date: Sat, 2 May 2020 03:42:46 +0200 Subject: [PATCH 45/79] update example 'vhdl/array_axis_vcs' (#648) --- examples/vhdl/array_axis_vcs/src/fifo.vhd | 26 ++---- .../array_axis_vcs/src/test/tb_axis_loop.vhd | 55 ++----------- .../vhdl/array_axis_vcs/src/test/vc_axis.vhd | 82 +++++++++++++++++++ 3 files changed, 96 insertions(+), 67 deletions(-) create mode 100644 examples/vhdl/array_axis_vcs/src/test/vc_axis.vhd diff --git a/examples/vhdl/array_axis_vcs/src/fifo.vhd b/examples/vhdl/array_axis_vcs/src/fifo.vhd index 84a204d81..61e6ccb3d 100644 --- a/examples/vhdl/array_axis_vcs/src/fifo.vhd +++ b/examples/vhdl/array_axis_vcs/src/fifo.vhd @@ -35,27 +35,17 @@ architecture arch of fifo is begin --- Assertions - process(clkw, clkr) + PslChecks : block is constant dx : std_logic_vector(d'left downto 0) := (others => 'X'); constant du : std_logic_vector(d'left downto 0) := (others => 'U'); begin - if rising_edge(clkw) then - if ( wr and ( d?=dx or d?=du ) ) then - assert false report "wrote X|U to fIfO" severity failure; - end if; - if (f and wr) then - assert false report "wrote to fIfO while full" severity failure; - end if; - end if; - if rising_edge(clkr) then - if (e and rd) then - assert false report "Read from fIfO while empty" severity failure; - end if; - end if; - end process; - --- + assert always (not rst and wr -> not (d ?= dx or d ?= du))@rising_edge(clkw) + report "wrote X|U to FIFO"; + assert always (not rst and f -> not wr)@rising_edge(clkw) + report "Wrote to FIFO while full"; + assert always (not rst and e -> not rd)@rising_edge(clkr) + report "Read from FIFO while empty"; + end block PslChecks; process(clkw) begin if rising_edge(clkw) then diff --git a/examples/vhdl/array_axis_vcs/src/test/tb_axis_loop.vhd b/examples/vhdl/array_axis_vcs/src/test/tb_axis_loop.vhd index 301c8a38c..51379e104 100644 --- a/examples/vhdl/array_axis_vcs/src/test/tb_axis_loop.vhd +++ b/examples/vhdl/array_axis_vcs/src/test/tb_axis_loop.vhd @@ -39,11 +39,6 @@ architecture tb of tb_axis_loop is constant master_axi_stream : axi_stream_master_t := new_axi_stream_master(data_length => data_width); constant slave_axi_stream : axi_stream_slave_t := new_axi_stream_slave(data_length => data_width); - -- Signals to/from the UUT from/to the verification components - - signal m_valid, m_ready, m_last, s_valid, s_ready, s_last : std_logic; - signal m_data, s_data : std_logic_vector(data_length(master_axi_stream)-1 downto 0); - -- tb signals and variables signal clk, rst, rstn : std_logic := '0'; @@ -134,53 +129,15 @@ begin -- - vunit_axism: entity vunit_lib.axi_stream_master - generic map ( - master => master_axi_stream - ) - port map ( - aclk => clk, - tvalid => m_valid, - tready => m_ready, - tdata => m_data, - tlast => m_last - ); - - vunit_axiss: entity vunit_lib.axi_stream_slave - generic map ( - slave => slave_axi_stream - ) - port map ( - aclk => clk, - tvalid => s_valid, - tready => s_ready, - tdata => s_data, - tlast => s_last - ); - --- - - uut: entity work.axis_buffer + uut_vc: entity work.vc_axis generic map ( - data_width => data_width, - fifo_depth => 4 + m_axis => master_axi_stream, + s_axis => slave_axi_stream, + data_width => data_width ) port map ( - s_axis_clk => clk, - s_axis_rstn => rstn, - s_axis_rdy => m_ready, - s_axis_data => m_data, - s_axis_valid => m_valid, - s_axis_strb => "1111", - s_axis_last => m_last, - - m_axis_clk => clk, - m_axis_rstn => rstn, - m_axis_valid => s_valid, - m_axis_data => s_data, - m_axis_rdy => s_ready, - m_axis_strb => open, - m_axis_last => s_last + clk => clk, + rstn => rstn ); end architecture; diff --git a/examples/vhdl/array_axis_vcs/src/test/vc_axis.vhd b/examples/vhdl/array_axis_vcs/src/test/vc_axis.vhd new file mode 100644 index 000000000..4497eb3f0 --- /dev/null +++ b/examples/vhdl/array_axis_vcs/src/test/vc_axis.vhd @@ -0,0 +1,82 @@ +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2020, Lars Asplund lars.anders.asplund@gmail.com + +library ieee; +context ieee.ieee_std_context; + +library vunit_lib; +context vunit_lib.vunit_context; +context vunit_lib.vc_context; + +entity vc_axis is + generic ( + m_axis : axi_stream_master_t; + s_axis : axi_stream_slave_t; + data_width : natural := 32; + fifo_depth : natural := 4 + ); + port ( + clk, rstn: in std_logic + ); +end entity; + +architecture arch of vc_axis is + + signal m_valid, m_ready, m_last, s_valid, s_ready, s_last : std_logic; + signal m_data, s_data : std_logic_vector(data_length(m_axis)-1 downto 0); + +begin + + vunit_axism: entity vunit_lib.axi_stream_master + generic map ( + master => m_axis + ) + port map ( + aclk => clk, + tvalid => m_valid, + tready => m_ready, + tdata => m_data, + tlast => m_last + ); + + vunit_axiss: entity vunit_lib.axi_stream_slave + generic map ( + slave => s_axis + ) + port map ( + aclk => clk, + tvalid => s_valid, + tready => s_ready, + tdata => s_data, + tlast => s_last + ); + +-- + + uut: entity work.axis_buffer + generic map ( + data_width => data_width, + fifo_depth => fifo_depth + ) + port map ( + s_axis_clk => clk, + s_axis_rstn => rstn, + s_axis_rdy => m_ready, + s_axis_data => m_data, + s_axis_valid => m_valid, + s_axis_strb => "1111", + s_axis_last => m_last, + + m_axis_clk => clk, + m_axis_rstn => rstn, + m_axis_valid => s_valid, + m_axis_data => s_data, + m_axis_rdy => s_ready, + m_axis_strb => open, + m_axis_last => s_last + ); + +end architecture; From aab59a9c403ccfc40550698c55fee05f0400bb4e Mon Sep 17 00:00:00 2001 From: David Medina <40488791+davidmedinasigasi@users.noreply.github.com> Date: Mon, 29 Jun 2020 18:24:02 +0200 Subject: [PATCH 46/79] do not use 'relpath' when printing output file (#661) This was causing issues when trying to output a XML file in a different drive than the project. --- vunit/test/runner.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/vunit/test/runner.py b/vunit/test/runner.py index bc514ff47..5edae0529 100644 --- a/vunit/test/runner.py +++ b/vunit/test/runner.py @@ -9,7 +9,6 @@ """ import os -from os.path import relpath from pathlib import Path import traceback import threading @@ -151,7 +150,7 @@ def _run_thread(self, write_stdout, scheduler, num_tests, is_main): with self._stdout_lock(): for test_name in test_suite.test_names: print("Starting %s" % test_name) - print("Output file: %s" % relpath(output_file_name)) + print("Output file: %s" % output_file_name) self._run_test_suite( test_suite, write_stdout, num_tests, output_path, output_file_name From 43570944462cc756c73447b73acb99364a2d6491 Mon Sep 17 00:00:00 2001 From: Lars Asplund Date: Thu, 25 Jun 2020 18:01:17 +0200 Subject: [PATCH 47/79] Resolved ambiguity between VUnit's line_vector type and the new standard line_vector type in VHDL-2019. --- vunit/vhdl/com/src/com_debug_codec_builder.vhd | 2 +- vunit/vhdl/path/src/path.vhd | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/vunit/vhdl/com/src/com_debug_codec_builder.vhd b/vunit/vhdl/com/src/com_debug_codec_builder.vhd index dd592e855..c6f23c7bd 100644 --- a/vunit/vhdl/com/src/com_debug_codec_builder.vhd +++ b/vunit/vhdl/com/src/com_debug_codec_builder.vhd @@ -205,7 +205,7 @@ package body com_debug_codec_builder_pkg is return; end if; - elements := new line_vector(0 to max_num_of_elements - 1); + elements := new work.string_ops.line_vector(0 to max_num_of_elements - 1); element_start := grp'left + 1; for i in grp'left + 1 to grp'right loop if length = max_num_of_elements then diff --git a/vunit/vhdl/path/src/path.vhd b/vunit/vhdl/path/src/path.vhd index 86b04f608..fd422d430 100644 --- a/vunit/vhdl/path/src/path.vhd +++ b/vunit/vhdl/path/src/path.vhd @@ -37,7 +37,7 @@ package body path is constant p9 : string := ""; constant p10 : string := "") return string is - variable inputs : line_vector(1 to 10); + variable inputs : work.string_ops.line_vector(1 to 10); variable result : line; begin write(inputs(1), p1); From c00f03dca7d8635dbdb73a7ac47965c38a6dd60f Mon Sep 17 00:00:00 2001 From: Lars Asplund Date: Fri, 26 Jun 2020 07:44:07 +0200 Subject: [PATCH 48/79] Updated VHDL version option in Riviera-PRO's command line interface to work with version 2020.04 and above. --- tests/unit/test_rivierapro_interface.py | 56 ++++++++++++++++++++----- vunit/sim_if/rivierapro.py | 32 ++++++++++---- 2 files changed, 68 insertions(+), 20 deletions(-) diff --git a/tests/unit/test_rivierapro_interface.py b/tests/unit/test_rivierapro_interface.py index d0a42eaae..03574e9e4 100644 --- a/tests/unit/test_rivierapro_interface.py +++ b/tests/unit/test_rivierapro_interface.py @@ -27,7 +27,10 @@ class TestRivieraProInterface(unittest.TestCase): @mock.patch("vunit.sim_if.check_output", autospec=True, return_value="") @mock.patch("vunit.sim_if.rivierapro.Process", autospec=True) - def test_compile_project_vhdl_2019(self, process, check_output): + @mock.patch( + "vunit.sim_if.rivierapro.RivieraProInterface.find_prefix", return_value="prefix" + ) + def test_compile_project_vhdl_2019(self, _find_prefix, process, check_output): simif = RivieraProInterface(prefix="prefix", output_path=self.output_path) project = Project() project.add_library("lib", "lib_path") @@ -52,7 +55,7 @@ def test_compile_project_vhdl_2019(self, process, check_output): "-quiet", "-j", self.output_path, - "-2018", + "-2019", "-work", "lib", "file.vhd", @@ -62,7 +65,10 @@ def test_compile_project_vhdl_2019(self, process, check_output): @mock.patch("vunit.sim_if.check_output", autospec=True, return_value="") @mock.patch("vunit.sim_if.rivierapro.Process", autospec=True) - def test_compile_project_vhdl_2008(self, process, check_output): + @mock.patch( + "vunit.sim_if.rivierapro.RivieraProInterface.find_prefix", return_value="prefix" + ) + def test_compile_project_vhdl_2008(self, _find_prefix, process, check_output): simif = RivieraProInterface(prefix="prefix", output_path=self.output_path) project = Project() project.add_library("lib", "lib_path") @@ -97,7 +103,10 @@ def test_compile_project_vhdl_2008(self, process, check_output): @mock.patch("vunit.sim_if.check_output", autospec=True, return_value="") @mock.patch("vunit.sim_if.rivierapro.Process", autospec=True) - def test_compile_project_vhdl_2002(self, process, check_output): + @mock.patch( + "vunit.sim_if.rivierapro.RivieraProInterface.find_prefix", return_value="prefix" + ) + def test_compile_project_vhdl_2002(self, _find_prefix, process, check_output): simif = RivieraProInterface(prefix="prefix", output_path=self.output_path) project = Project() project.add_library("lib", "lib_path") @@ -132,7 +141,10 @@ def test_compile_project_vhdl_2002(self, process, check_output): @mock.patch("vunit.sim_if.check_output", autospec=True, return_value="") @mock.patch("vunit.sim_if.rivierapro.Process", autospec=True) - def test_compile_project_vhdl_93(self, process, check_output): + @mock.patch( + "vunit.sim_if.rivierapro.RivieraProInterface.find_prefix", return_value="prefix" + ) + def test_compile_project_vhdl_93(self, _find_prefix, process, check_output): simif = RivieraProInterface(prefix="prefix", output_path=self.output_path) project = Project() project.add_library("lib", "lib_path") @@ -167,7 +179,12 @@ def test_compile_project_vhdl_93(self, process, check_output): @mock.patch("vunit.sim_if.check_output", autospec=True, return_value="") @mock.patch("vunit.sim_if.rivierapro.Process", autospec=True) - def test_compile_project_vhdl_extra_flags(self, process, check_output): + @mock.patch( + "vunit.sim_if.rivierapro.RivieraProInterface.find_prefix", return_value="prefix" + ) + def test_compile_project_vhdl_extra_flags( + self, _find_prefix, process, check_output + ): simif = RivieraProInterface(prefix="prefix", output_path=self.output_path) project = Project() project.add_library("lib", "lib_path") @@ -203,7 +220,10 @@ def test_compile_project_vhdl_extra_flags(self, process, check_output): @mock.patch("vunit.sim_if.check_output", autospec=True, return_value="") @mock.patch("vunit.sim_if.rivierapro.Process", autospec=True) - def test_compile_project_verilog(self, process, check_output): + @mock.patch( + "vunit.sim_if.rivierapro.RivieraProInterface.find_prefix", return_value="prefix" + ) + def test_compile_project_verilog(self, _find_prefix, process, check_output): library_cfg = str(Path(self.output_path) / "library.cfg") simif = RivieraProInterface(prefix="prefix", output_path=self.output_path) project = Project() @@ -238,7 +258,10 @@ def test_compile_project_verilog(self, process, check_output): @mock.patch("vunit.sim_if.check_output", autospec=True, return_value="") @mock.patch("vunit.sim_if.rivierapro.Process", autospec=True) - def test_compile_project_system_verilog(self, process, check_output): + @mock.patch( + "vunit.sim_if.rivierapro.RivieraProInterface.find_prefix", return_value="prefix" + ) + def test_compile_project_system_verilog(self, _find_prefix, process, check_output): library_cfg = str(Path(self.output_path) / "library.cfg") simif = RivieraProInterface(prefix="prefix", output_path=self.output_path) project = Project() @@ -274,7 +297,12 @@ def test_compile_project_system_verilog(self, process, check_output): @mock.patch("vunit.sim_if.check_output", autospec=True, return_value="") @mock.patch("vunit.sim_if.rivierapro.Process", autospec=True) - def test_compile_project_verilog_extra_flags(self, process, check_output): + @mock.patch( + "vunit.sim_if.rivierapro.RivieraProInterface.find_prefix", return_value="prefix" + ) + def test_compile_project_verilog_extra_flags( + self, _find_prefix, process, check_output + ): library_cfg = str(Path(self.output_path) / "library.cfg") simif = RivieraProInterface(prefix="prefix", output_path=self.output_path) project = Project() @@ -312,7 +340,10 @@ def test_compile_project_verilog_extra_flags(self, process, check_output): @mock.patch("vunit.sim_if.check_output", autospec=True, return_value="") @mock.patch("vunit.sim_if.rivierapro.Process", autospec=True) - def test_compile_project_verilog_include(self, process, check_output): + @mock.patch( + "vunit.sim_if.rivierapro.RivieraProInterface.find_prefix", return_value="prefix" + ) + def test_compile_project_verilog_include(self, _find_prefix, process, check_output): library_cfg = str(Path(self.output_path) / "library.cfg") simif = RivieraProInterface(prefix="prefix", output_path=self.output_path) project = Project() @@ -350,7 +381,10 @@ def test_compile_project_verilog_include(self, process, check_output): @mock.patch("vunit.sim_if.check_output", autospec=True, return_value="") @mock.patch("vunit.sim_if.rivierapro.Process", autospec=True) - def test_compile_project_verilog_define(self, process, check_output): + @mock.patch( + "vunit.sim_if.rivierapro.RivieraProInterface.find_prefix", return_value="prefix" + ) + def test_compile_project_verilog_define(self, _find_prefix, process, check_output): library_cfg = str(Path(self.output_path) / "library.cfg") simif = RivieraProInterface(prefix="prefix", output_path=self.output_path) project = Project() diff --git a/vunit/sim_if/rivierapro.py b/vunit/sim_if/rivierapro.py index bca118986..fa6fe222b 100644 --- a/vunit/sim_if/rivierapro.py +++ b/vunit/sim_if/rivierapro.py @@ -71,19 +71,26 @@ def no_avhdl(path): return cls.find_toolchain(["vsim", "vsimsa"], constraints=[no_avhdl]) @classmethod - def get_osvvm_coverage_api(cls): + def _get_version(cls): """ - Returns simulator name when OSVVM coverage API is supported, None otherwise. + Return a VersionConsumer object containing the simulator version. """ proc = Process( [str(Path(cls.find_prefix()) / "vcom"), "-version"], env=cls.get_env() ) consumer = VersionConsumer() proc.consume_output(consumer) - if consumer.year is not None: - if (consumer.year == 2016 and consumer.month >= 10) or ( - consumer.year > 2016 - ): + + return consumer + + @classmethod + def get_osvvm_coverage_api(cls): + """ + Returns simulator name when OSVVM coverage API is supported, None otherwise. + """ + version = cls._get_version() + if version.year is not None: + if (version.year == 2016 and version.month >= 10) or (version.year > 2016): return cls.name return None @@ -113,6 +120,7 @@ def __init__(self, prefix, output_path, persistent=False, gui=False): self._create_library_cfg() self._libraries = [] self._coverage_files = set() + self._version = self._get_version() def add_simulator_specific(self, project): """ @@ -147,13 +155,19 @@ def compile_source_file_command(self, source_file): LOGGER.error("Unknown file type: %s", source_file.file_type) raise CompileError - @staticmethod - def _std_str(vhdl_standard): + def _std_str(self, vhdl_standard): """ Convert standard to format of Riviera-PRO command line flag """ if vhdl_standard == VHDL.STD_2019: - return "-2018" + if self._version.year is not None: + if (self._version.year == 2020 and self._version.month < 4) or ( + self._version.year < 2020 + ): + return "-2018" + + return "-2019" + return "-%s" % vhdl_standard def compile_vhdl_file_command(self, source_file): From ff997a4bfda28844253e1a6b4f3f91f0e552261d Mon Sep 17 00:00:00 2001 From: eine Date: Tue, 7 Jul 2020 14:38:25 +0200 Subject: [PATCH 49/79] ui: fix not serializable path when exporting JSON (#657) --- vunit/ui/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vunit/ui/__init__.py b/vunit/ui/__init__.py index 304d01549..21ffd256d 100644 --- a/vunit/ui/__init__.py +++ b/vunit/ui/__init__.py @@ -871,7 +871,7 @@ def _main_export_json( dict( name=name, location=dict( - file_name=info.location.file_name, + file_name=str(info.location.file_name), offset=info.location.offset, length=info.location.length, ), From b34a4f3c341f2b4175db678ac7647c32ce48bfae Mon Sep 17 00:00:00 2001 From: eine Date: Tue, 7 Jul 2020 15:46:48 +0200 Subject: [PATCH 50/79] docs: update 'Credits' and 'License' --- docs/about.rst | 49 +++++++++++++++++++++++++++++++++---------------- 1 file changed, 33 insertions(+), 16 deletions(-) diff --git a/docs/about.rst b/docs/about.rst index c07ba155f..e067de899 100644 --- a/docs/about.rst +++ b/docs/about.rst @@ -138,16 +138,27 @@ can be made by creating a `new issue`_. Credits ------- -Founders -******** -- `Lars Asplund `_ -- `Olof Kraigher `_ +- Founders: -Notable contributors -******************** -- `Colin Marquardt `_: Cadence Incisive support -- `Sławomir Siluk `_: Verification Components such as Avalon and Wishbone + - `Lars Asplund `_ + - `Olof Kraigher `_ + +- Notable contributors: + + - `Colin Marquardt `_: + + - Cadence Incisive support + + - `Sławomir Siluk `_: + + - Verification Components (such as Avalon and Wishbone) + + - `Unai Martinez-Corral `_: + + - Co-simulation with GHDL's VHPIDIRECT interface (`VUnit/cosim `_, based on `ghdl/ghdl-cosim `_) + + - Continuous Integration (CI) License ------- @@ -157,18 +168,26 @@ License VUnit ***** -VUnit except for OSVVM (see below) is released under the terms of +VUnit, except for the projects below, is released under the terms of `Mozilla Public License, v. 2.0`_. -|copy| 2014-2018 Lars Asplund, lars.anders.asplund@gmail.com. +|copy| 2014-2020 Lars Asplund, lars.anders.asplund@gmail.com. OSVVM ***** -OSVVM is `redistributed`_ with VUnit for your convenience. These -files are licensed under the terms of `ARTISTIC License`_. +OSVVM is redistributed (`vunit/vhdl/osvvm `_) with VUnit for your convenience. These +files are licensed under the terms of `Apache License, v 2.0`_. + +|copy| 2010 - 2020 by SynthWorks Design Inc. All rights reserved. + +JSON-for-VHDL +************* + +JSON-for-VHDL is redistributed (`vunit/vhdl/JSON-for-VHDL `_) with VUnit for your convenience. These +files are licensed under the terms of `Apache License, v 2.0`_. -|copy| 2010 - 2017 by SynthWorks Design Inc. All rights reserved. +|copy| 2015 - 2020 Patrick Lehmann. .. _xUnit: http://en.wikipedia.org/wiki/XUnit .. _Jenkins: http://jenkins-ci.org/ @@ -181,9 +200,7 @@ files are licensed under the terms of `ARTISTIC License`_. .. _YouTube: https://www.youtube.com/channel/UCCPVCaeWkz6C95aRUTbIwdg .. _an introduction to unit testing (6 min): https://www.youtube.com/watch?v=PZuBqcxS8t4 .. _short introduction to VUnit (12 min): https://www.youtube.com/watch?v=D8s_VLD91tw -.. _Development document: https://github.com/VUnit/vunit/blob/master/developing.md .. _new issue: https://github.com/VUnit/vunit/issues/new .. _Mozilla Public License, v. 2.0: http://mozilla.org/MPL/2.0/ -.. _redistributed: https://github.com/VUnit/vunit/blob/master/vunit/vhdl/osvvm -.. _modifications: https://github.com/VUnit/vunit/commit/25fce1b3700e746c3fa23bd7157777dd4f20f0d6 .. _ARTISTIC License: http://www.perlfoundation.org/artistic_license_2_0 +.. _Apache License, v 2.0: http://www.apache.org/licenses/LICENSE-2.0 From 9d9843c6dc624b8d8c7909b04946c28b85c59f28 Mon Sep 17 00:00:00 2001 From: eine Date: Mon, 3 Aug 2020 12:05:38 +0200 Subject: [PATCH 51/79] ci: fix images.sh URL from ghdl/docker --- .github/images.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/images.sh b/.github/images.sh index 736647899..0b01839e7 100755 --- a/.github/images.sh +++ b/.github/images.sh @@ -10,7 +10,7 @@ docker build \ --target vunit \ -t "vunit/dev:${TAG}" \ - <<-EOF -$(curl -fsSL https://raw.githubusercontent.com/ghdl/docker/master/dockerfiles/run_debian) +$(curl -fsSL https://raw.githubusercontent.com/ghdl/docker/master/run_debian.dockerfile) FROM $TAG AS vunit COPY --from=ghdl/pkg:buster-$PKG / / From cd7dd772ae12aea00caea59d1aca7d0d6271a019 Mon Sep 17 00:00:00 2001 From: eine Date: Mon, 27 Jul 2020 20:38:37 +0200 Subject: [PATCH 52/79] ci: update GHA workflows --- .github/workflows/coverage.yml | 15 +++-- .github/workflows/docs.yml | 19 ++++-- .github/workflows/images.yml | 15 +++-- .github/workflows/push.yml | 102 ++++++++++++++++++++++----------- 4 files changed, 104 insertions(+), 47 deletions(-) diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index 4fc0d2801..4bb3fb844 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -13,11 +13,15 @@ jobs: DOCKER_REGISTRY: docker.pkg.github.com IMAGE: docker.pkg.github.com/vunit/vunit/dev:llvm steps: - - uses: actions/checkout@v1 - - run: git submodule update --init --recursive - - uses: actions/setup-python@v1 + + - uses: actions/checkout@v2 + with: + submodules: recursive + + - uses: actions/setup-python@v2 with: python-version: 3.7 + - name: Docker login env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} @@ -25,13 +29,16 @@ jobs: echo "$GITHUB_TOKEN" | docker login -u vunit-gha --password-stdin "$DOCKER_REGISTRY" docker pull $IMAGE docker logout "$DOCKER_REGISTRY" + - name: Run coverage run: | ./.github/run.sh tox -e coverage ./.github/run.sh coverage html --directory=htmlcov + - name: Report coverage run: ./.github/run.sh coverage report -m --skip-covered - - uses: actions/upload-artifact@master + + - uses: actions/upload-artifact@v2 with: name: VUnit_coverage path: htmlcov diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 45d8e7153..86ccd1b8e 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -11,23 +11,30 @@ jobs: docs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v1 + + - uses: actions/checkout@v2 with: submodules: recursive - - uses: actions/setup-python@v1 + fetch-depth: 0 + + - uses: actions/setup-python@v2 with: python-version: 3.8 - - name: install dependencies + + - name: Install dependencies run: | pip install -U pip --progress-bar off pip install -U virtualenv tox --progress-bar off - - name: build docs + + - name: Build docs run: tox -e py38-docs -- --color - - uses: actions/upload-artifact@master + + - uses: Actions/upload-artifact@v2 with: name: VUnit-site path: .tox/py38-docs/tmp/docsbuild/ - - name: 'publish site to gh-pages' + + - name: Publish site to gh-pages if: github.event_name != 'pull_request' && github.repository == 'VUnit/vunit' env: GH_DEPKEY: ${{ secrets.VUNIT_GITHUB_IO_DEPLOY_KEY }} diff --git a/.github/workflows/images.yml b/.github/workflows/images.yml index 9f77892a2..cc3d0df25 100644 --- a/.github/workflows/images.yml +++ b/.github/workflows/images.yml @@ -25,22 +25,27 @@ jobs: env: DOCKER_REGISTRY: docker.pkg.github.com steps: - - uses: actions/checkout@v1 - - name: build image + + - uses: actions/checkout@v2 + + - name: Build image env: TAG: ${{ matrix.task.tag }} PKG: ${{ matrix.task.pkg }} run: | ./.github/images.sh - - name: docker login + + - name: Docker login run: echo "${{ secrets.GITHUB_TOKEN }}" | docker login -u vunit-gha --password-stdin "$DOCKER_REGISTRY" - - name: docker push + + - name: Docker push if: github.repository == 'VUnit/vunit' run: | DIMG="vunit/dev:${{ matrix.task.tag }}" GHIMG="${DOCKER_REGISTRY}/vunit/$DIMG" docker tag "$DIMG" "$GHIMG" docker push "$GHIMG" - - name: docker logout + + - name: Docker logout run: docker logout "$DOCKER_REGISTRY" if: always() diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml index ebbc35b91..f04f7d6f0 100644 --- a/.github/workflows/push.yml +++ b/.github/workflows/push.yml @@ -13,21 +13,34 @@ env: jobs: +# +# Python code format +# + fmt: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v1 - - uses: actions/setup-python@v1 + + - uses: actions/checkout@v2 + + - uses: actions/setup-python@v2 with: python-version: 3.8 - - name: install dependencies + + - name: Install dependencies run: | pip install -U pip --progress-bar off pip install -U virtualenv tox --progress-bar off - - name: run 'black' + + - name: Run 'black' run: tox -e py38-fmt -- --check +# +# Linux linting and unit tests +# + lin: + runs-on: ubuntu-latest strategy: fail-fast: false max-parallel: 2 @@ -37,21 +50,28 @@ jobs: 36-unit, 38-unit, ] - runs-on: ubuntu-latest steps: - - uses: actions/checkout@v1 - - uses: actions/setup-python@v1 + + - uses: actions/checkout@v2 + + - uses: actions/setup-python@v2 with: python-version: 3.8 - - name: install dependencies + + - name: Install dependencies run: | pip install -U pip --progress-bar off pip install -U virtualenv tox --progress-bar off - - name: run job - run: | - tox -e py${{ matrix.task }} -- --color=yes + + - name: Run job + run: tox -e py${{ matrix.task }} -- --color=yes + +# +# Docker (Linux) acceptance tests +# docker: + runs-on: ubuntu-latest strategy: fail-fast: false max-parallel: 2 @@ -60,24 +80,31 @@ jobs: {do: 38-acceptance, tag: llvm}, {do: 38-vcomponents, tag: mcode}, ] - runs-on: ubuntu-latest steps: - - uses: actions/checkout@v1 + + - uses: actions/checkout@v2 with: submodules: recursive - - name: docker login + + - name: Docker login run: echo "${{ secrets.GITHUB_TOKEN }}" | docker login -u vunit-gha --password-stdin "$DOCKER_REGISTRY" - - name: run job - run: | - docker run --rm -tv $(pwd):/src -w /src "$DOCKER_REGISTRY"/vunit/vunit/dev:${{ matrix.task.tag }} tox -e py${{ matrix.task.do }}-ghdl - - name: docker logout + + - name: Run job + run: docker run --rm -tv $(pwd):/src -w /src "$DOCKER_REGISTRY"/vunit/vunit/dev:${{ matrix.task.tag }} tox -e py${{ matrix.task.do }}-ghdl + + - name: Docker logout run: docker logout "$DOCKER_REGISTRY" if: always() +# +# Windows with latest stable GHDL +# + win: + runs-on: windows-latest strategy: fail-fast: false - max-parallel: 4 + max-parallel: 3 matrix: task: [ 38-acceptance-ghdl, @@ -86,20 +113,22 @@ jobs: 36-unit, 38-unit, ] - runs-on: windows-latest steps: - - uses: actions/checkout@v1 - - name: git submodule update - run: git submodule update --init --recursive - if: (endsWith( matrix.task, '-lint' ) || endsWith( matrix.task, '-unit' )) == false - - uses: actions/setup-python@v1 + + - uses: actions/checkout@v2 + with: + submodules: recursive + + - uses: actions/setup-python@v2 with: python-version: 3.8 - - name: install dependencies + + - name: Install dependencies run: | pip install -U pip --progress-bar off pip install -U virtualenv tox --progress-bar off - - name: install GHDL + + - name: Install GHDL if: endsWith( matrix.task, '-ghdl' ) shell: bash run: | @@ -107,28 +136,37 @@ jobs: 7z x ghdl.zip "-o../ghdl" -y mv ../ghdl/GHDL/0.37-mingw32-mcode/ ../ghdl-v0.37 rm -rf ../ghdl ghdl.zip - - name: run job + + - name: Run job shell: bash run: | export PATH=$PATH:$(pwd)/../ghdl-v0.37/bin tox -e py${{ matrix.task }} -- --color=yes +# +# Deploy to PyPI +# + deploy: runs-on: ubuntu-latest needs: [ fmt, lin, docker, win ] if: github.event_name == 'push' && github.event.created == 'true' steps: - - uses: actions/checkout@v1 + + - uses: actions/checkout@v2 with: submodules: recursive - - uses: actions/setup-python@v1 + + - uses: actions/setup-python@v2 with: python-version: 3.8 - - name: install dependencies + + - name: Install dependencies run: | pip install -U pip pip install -U setuptools wheel twine - - name: build and deploy to PyPI + + - name: Build and deploy to PyPI env: TWINE_USERNAME: __token__ TWINE_PASSWORD: ${{ secrets.VUNIT_HDL_PYPI_DEPLOY_TOKEN }} From 1f45cc006b406d8cfac13dda5f85a96f2f44ae01 Mon Sep 17 00:00:00 2001 From: eine Date: Sun, 5 Jan 2020 04:26:29 +0100 Subject: [PATCH 53/79] doc: use buildthedocs/sphinx.theme --- docs/_static/VUnit_logo_175x175.png | Bin 0 -> 12744 bytes docs/about.rst | 2 +- docs/conf.py | 49 +++++++++++++--------------- tools/build_docs.py | 6 +++- tools/create_release_notes.py | 18 +++++----- tools/docs_utils.py | 19 ++++++++++- tox.ini | 1 - 7 files changed, 54 insertions(+), 41 deletions(-) create mode 100644 docs/_static/VUnit_logo_175x175.png diff --git a/docs/_static/VUnit_logo_175x175.png b/docs/_static/VUnit_logo_175x175.png new file mode 100644 index 0000000000000000000000000000000000000000..3836371faf62f1be2df616d4c8f86d8132c411ea GIT binary patch literal 12744 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%0Dsc;iVI#=IDCs=J5R zhWtS5K%j>Tse#d((Xw zyq{3NJUxo3s{vJgW`8h(AN*l3-nWjyYccTXyr{0ILs!oz44lm1jrRrM0311OSJh{J zyI~MI4h&wl1i(8TZcJbGtKzfH`$DK~9b=ou;QiCj@MI9ZgI84XS)IevsC9VRrZIT` zGBGiDnV1;7 zOblKoCI&AP6N8tD>C?f>g5pdng;&(E&^S}AW1TKNs^^tFml}sHf^vW)x=6pm|~|SqkyUnw(I^L3`QVHvc&rXAveE3 z(nng71uuMaGd$iXTScZtm&X^yFTSw}>B-jX-35VzKM->BJp+@+e7?|vZ*cdvA{_g1 z4HAt4A~L|d2qsF&03tGwXb^DZscqQ1y?DXb`FtVt3``yqeg4R64%gHpR*Mlj-3Q$S zjsx!AT7;H0nK*aBg}$L_M8fP&%tZ8XCJHFbO+@AT>;><*01ysG;c!jC=a0O`)iw0! zox`&&ySEpYrll@rE6Aj1O}MY(cwJ9tPS&!G{PybNkbBB|uy4>EnV#{m)nQVsre{3p z8+1q9Q{IEytBXSd031K^vVCpEfxxl^!<|Nh2$Mi5gxRk|az1K*-%QFnP@9kGxh@p7rkb>f-C-x&IGGz?ol4oY@=z O0000`_. VUnit maintainers does not have access to this simulator to verify the functionality. - Run ``incisive_vhdl_fixup.py`` to remove VHDL constructs that are - not compatible with Incisive + not compatible with Incisive Getting Started --------------- diff --git a/docs/conf.py b/docs/conf.py index 5164e2386..2de32d8bf 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -4,20 +4,10 @@ import sys from pathlib import Path -# blog_title = u"VUnit Blog" -# blog_baseurl = "http://vunit.github.io" -# blog_authors = {"Olof Kraigher": ("kraigher", None), "Lars Asplund": ("lasplund", None)} - -# -- Disqus Integration ------------------------------------------------------- - -# You can enable Disqus_ by setting ``disqus_shortname`` variable. -# Disqus_ short name for the blog. -disqus_shortname = "vunitframework" - # -- Sphinx Options ----------------------------------------------------------- # If your project needs a minimal Sphinx version, state it here. -needs_sphinx = "1.2" +needs_sphinx = "3.0" extensions = [ "sphinx.ext.autodoc", @@ -32,13 +22,17 @@ } # The suffix(es) of source filenames. -source_suffix = ".rst" +source_suffix = { + ".rst": "restructuredtext", + # '.txt': 'markdown', + # '.md': 'markdown', +} master_doc = "index" project = u"VUnit" copyright = u"2014-2020, Lars Asplund" -author = u"lasplund" +author = u"LarsAsplund, kraigher and contributors" version = "" release = "" @@ -53,31 +47,32 @@ # -- Options for HTML output ---------------------------------------------- -# html_theme_path = [] - -# html_theme = "sphinx_rtd_theme" +html_theme_path = ["."] +html_theme = "_theme" html_theme_options = { "analytics_id": "UA-112393863-1", - # "github_button": True, - # "github_type": "star", - # "github_user": "VUnit", - # "github_repo": "vunit", - # "description": "A test framework for HDL", - # "logo": "VUnit_logo_420x420.png", - # "logo_name": True, - # "travis_button": True, - # "page_width": "75%", + "logo_only": True, + "vcs_pageview_mode": "blob", + "style_nav_header_background": "#0c479d", + "home_breadcrumbs": False, +} +html_context = { + "conf_py_path": "%s/" % Path(__file__).parent.name, + "display_github": True, + "github_user": "VUnit", + "github_repo": "vunit", + "github_version": "master/", } html_static_path = ["_static"] -html_logo = str(Path(html_static_path[0]) / "VUnit_logo_420x420.png") +html_logo = str(Path(html_static_path[0]) / "VUnit_logo_175x175.png") html_favicon = str(Path(html_static_path[0]) / "vunit.ico") # Output file base name for HTML help builder. -htmlhelp_basename = "vunitdoc" +htmlhelp_basename = "VUnitDoc" # -- InterSphinx ---------------------------------------------------------- diff --git a/tools/build_docs.py b/tools/build_docs.py index 1211b76ae..284b5c450 100644 --- a/tools/build_docs.py +++ b/tools/build_docs.py @@ -13,7 +13,7 @@ import sys from sys import argv from create_release_notes import create_release_notes -from docs_utils import examples +from docs_utils import examples, get_theme def main(): @@ -22,6 +22,10 @@ def main(): """ create_release_notes() examples() + get_theme( + Path(__file__).parent.parent / "docs", + "https://codeload.github.com/buildthedocs/sphinx.theme/tar.gz/v0" + ) check_call( [ sys.executable, diff --git a/tools/create_release_notes.py b/tools/create_release_notes.py index 937133bce..ed796ed1b 100644 --- a/tools/create_release_notes.py +++ b/tools/create_release_notes.py @@ -38,9 +38,6 @@ def create_release_notes(): releases = get_releases(source_path) latest_release = releases[0] - def banner(fptr): - fptr.write("\n" + ("-" * 80) + "\n\n") - with (source_path / "release_notes.rst").open("w") as fptr: fptr.write( """ @@ -49,7 +46,7 @@ def banner(fptr): Release notes ============= -For installation instructions read :ref:`this `. +.. NOTE:: For installation instructions read :ref:`this `. `Commits since last release `__ @@ -57,7 +54,7 @@ def banner(fptr): % latest_release.tag ) - banner(fptr) + fptr.write("\n\n") for idx, release in enumerate(releases): is_last = idx == len(releases) - 1 @@ -75,19 +72,20 @@ def banner(fptr): fptr.write(title + "\n") fptr.write("-" * len(title) + "\n\n") - fptr.write(".. include:: %s\n" % relpath(release.file_name, source_path)) - fptr.write( - "\n`Download from PyPI `__\n" + "\n`Download from PyPI `__" % release.name ) if not is_last: fptr.write( - "\n`Commits since previous release `__\n" + " | `Commits since previous release `__" % (releases[idx + 1].tag, release.tag) ) - banner(fptr) + + fptr.write("\n\n") + + fptr.write(".. include:: %s\n" % relpath(release.file_name, source_path)) class Release(object): diff --git a/tools/docs_utils.py b/tools/docs_utils.py index aa394fa87..ff2db2c20 100644 --- a/tools/docs_utils.py +++ b/tools/docs_utils.py @@ -11,8 +11,10 @@ import sys import inspect -from pathlib import Path from os import listdir, remove +from pathlib import Path +from subprocess import check_call + ROOT = Path(__file__).parent.parent / "docs" @@ -77,3 +79,18 @@ def _get_eg_doc(location: Path, ref): title = "`%s <%s/>`_" % (eg_doc.split("---", 1)[0][0:-1], ref) return "\n".join([title, "-" * len(title), eg_doc.split("---\n", 1)[1], "\n"]) + + +def get_theme(path: Path, url: str): + """ + Check if the theme is available locally, retrieve it with curl and tar otherwise + """ + tpath = path / "_theme" + if not tpath.is_dir() or not (tpath / "theme.conf").is_file(): + if not tpath.is_dir(): + tpath.mkdir() + zpath = path / "theme.tgz" + if not zpath.is_file(): + check_call(["curl", "-fsSL", url, "-o", str(zpath)]) + tar_cmd = ["tar", "--strip-components=1", "-C", str(tpath), "-xvzf", str(zpath)] + check_call(tar_cmd) diff --git a/tox.ini b/tox.ini index 7495f4a7d..1777d94b0 100644 --- a/tox.ini +++ b/tox.ini @@ -19,7 +19,6 @@ deps= docs: docutils docs: sphinx docs: sphinx-argparse - docs: sphinx_rtd_theme setenv= acceptance-activehdl: VUNIT_SIMULATOR=activehdl From a732ff901c90e893ade5afb68b6e76b7051949c6 Mon Sep 17 00:00:00 2001 From: eine Date: Sat, 8 Aug 2020 06:54:44 +0200 Subject: [PATCH 54/79] docs: replace LICENSE.txt with LICENSE.rst --- LICENSE.rst | 15 +++++++++++++++ LICENSE.txt | 15 --------------- docs/about.rst | 36 +++--------------------------------- tests/lint/test_license.py | 2 +- tools/build_docs.py | 7 ++++++- vunit/about.py | 24 +++++++++++------------- 6 files changed, 36 insertions(+), 63 deletions(-) create mode 100644 LICENSE.rst delete mode 100644 LICENSE.txt diff --git a/LICENSE.rst b/LICENSE.rst new file mode 100644 index 000000000..1fd49f8e4 --- /dev/null +++ b/LICENSE.rst @@ -0,0 +1,15 @@ +**VUnit**, except for the projects below, is released under the terms of +`Mozilla Public License, v. 2.0`_. |copy| 2014-2020 Lars Asplund, lars.anders.asplund@gmail.com. + +The following libraries are `redistributed`_ with VUnit for your convenience: + +* **OSVVM** (``vunit/vhdl/osvvm``): these files are licensed under the terms of `Apache License, v 2.0`_, |copy| 2010 - 2020 by `SynthWorks Design Inc`_. All rights reserved. + +* **JSON-for-VHDL** (``vunit/vhdl/JSON-for-VHDL``): these files are licensed under the terms of `Apache License, v 2.0`_, |copy| 2015 - 2020 Patrick Lehmann. + +.. |copy| unicode:: U+000A9 .. COPYRIGHT SIGN +.. _redistributed: https://github.com/VUnit/vunit/blob/master/.gitmodules +.. _Mozilla Public License, v. 2.0: http://mozilla.org/MPL/2.0/ +.. _ARTISTIC License: http://www.perlfoundation.org/artistic_license_2_0 +.. _Apache License, v 2.0: http://www.apache.org/licenses/LICENSE-2.0 +.. _SynthWorks Design Inc: http://www.synthworks.com diff --git a/LICENSE.txt b/LICENSE.txt deleted file mode 100644 index bcdca799e..000000000 --- a/LICENSE.txt +++ /dev/null @@ -1,15 +0,0 @@ -VUnit ------ - -VUnit except for OSVVM (see below) is released under the terms of -Mozilla Public License, v. 2.0. - -Copyright (c) 2014-2020, Lars Asplund lars.anders.asplund@gmail.com - -OSVVM ------ - -OSVVM is redistributed as a submodule to VUnit for your convenience. OSVVM and derivative work -located under examples/vhdl/osvvm_integration/src are licensed under the terms of Artistic License 2.0. - -Copyright (c) 2006-2016, SynthWorks Design Inc http://www.synthworks.com diff --git a/docs/about.rst b/docs/about.rst index c4bf3aa32..001be216f 100644 --- a/docs/about.rst +++ b/docs/about.rst @@ -135,8 +135,8 @@ Support Any bug reports, feature requests or questions about the usage of VUnit can be made by creating a `new issue`_. -Credits -------- +Credits and license +------------------- - Founders: @@ -160,34 +160,7 @@ Credits - Continuous Integration (CI) -License -------- - -.. |copy| unicode:: U+000A9 .. COPYRIGHT SIGN - -VUnit -***** - -VUnit, except for the projects below, is released under the terms of -`Mozilla Public License, v. 2.0`_. - -|copy| 2014-2020 Lars Asplund, lars.anders.asplund@gmail.com. - -OSVVM -***** - -OSVVM is redistributed (`vunit/vhdl/osvvm `_) with VUnit for your convenience. These -files are licensed under the terms of `Apache License, v 2.0`_. - -|copy| 2010 - 2020 by SynthWorks Design Inc. All rights reserved. - -JSON-for-VHDL -************* - -JSON-for-VHDL is redistributed (`vunit/vhdl/JSON-for-VHDL `_) with VUnit for your convenience. These -files are licensed under the terms of `Apache License, v 2.0`_. - -|copy| 2015 - 2020 Patrick Lehmann. +.. include:: license.rst .. _xUnit: http://en.wikipedia.org/wiki/XUnit .. _Jenkins: http://jenkins-ci.org/ @@ -201,6 +174,3 @@ files are licensed under the terms of `Apache License, v 2.0`_. .. _an introduction to unit testing (6 min): https://www.youtube.com/watch?v=PZuBqcxS8t4 .. _short introduction to VUnit (12 min): https://www.youtube.com/watch?v=D8s_VLD91tw .. _new issue: https://github.com/VUnit/vunit/issues/new -.. _Mozilla Public License, v. 2.0: http://mozilla.org/MPL/2.0/ -.. _ARTISTIC License: http://www.perlfoundation.org/artistic_license_2_0 -.. _Apache License, v 2.0: http://www.apache.org/licenses/LICENSE-2.0 diff --git a/tests/lint/test_license.py b/tests/lint/test_license.py index 8e90c687b..47656010d 100644 --- a/tests/lint/test_license.py +++ b/tests/lint/test_license.py @@ -57,7 +57,7 @@ def test_that_a_valid_license_exists_in_source_files_and_that_global_licensing_i def test_that_license_file_matches_vunit_license_text(self): with catch_warnings(): simplefilter("ignore", category=DeprecationWarning) - with (ROOT / "LICENSE.txt").open("rU") as lic: + with (ROOT / "LICENSE.rst").open("rU") as lic: self.assertEqual(lic.read(), license_text()) def _check_license(self, code, file_name): diff --git a/tools/build_docs.py b/tools/build_docs.py index 284b5c450..d56d565ff 100644 --- a/tools/build_docs.py +++ b/tools/build_docs.py @@ -12,18 +12,23 @@ from pathlib import Path import sys from sys import argv +from shutil import copyfile from create_release_notes import create_release_notes from docs_utils import examples, get_theme +DROOT = Path(__file__).parent.parent / 'docs' + + def main(): """ Build documentation/website """ create_release_notes() examples() + copyfile(str(DROOT / '..' / 'LICENSE.rst'), str(DROOT / 'license.rst')) get_theme( - Path(__file__).parent.parent / "docs", + DROOT, "https://codeload.github.com/buildthedocs/sphinx.theme/tar.gz/v0" ) check_call( diff --git a/vunit/about.py b/vunit/about.py index 87eaed5b8..b7d556351 100644 --- a/vunit/about.py +++ b/vunit/about.py @@ -13,21 +13,21 @@ def license_text(): """ Returns licence text """ - return """VUnit ------ + return """**VUnit**, except for the projects below, is released under the terms of +`Mozilla Public License, v. 2.0`_. |copy| 2014-2020 Lars Asplund, lars.anders.asplund@gmail.com. -VUnit except for OSVVM (see below) is released under the terms of -Mozilla Public License, v. 2.0. +The following libraries are `redistributed`_ with VUnit for your convenience: -Copyright (c) 2014-2020, Lars Asplund lars.anders.asplund@gmail.com +* **OSVVM** (``vunit/vhdl/osvvm``): these files are licensed under the terms of `Apache License, v 2.0`_, |copy| 2010 - 2020 by `SynthWorks Design Inc`_. All rights reserved. -OSVVM ------ +* **JSON-for-VHDL** (``vunit/vhdl/JSON-for-VHDL``): these files are licensed under the terms of `Apache License, v 2.0`_, |copy| 2015 - 2020 Patrick Lehmann. -OSVVM is redistributed as a submodule to VUnit for your convenience. OSVVM and derivative work -located under examples/vhdl/osvvm_integration/src are licensed under the terms of Artistic License 2.0. - -Copyright (c) 2006-2016, SynthWorks Design Inc http://www.synthworks.com +.. |copy| unicode:: U+000A9 .. COPYRIGHT SIGN +.. _redistributed: https://github.com/VUnit/vunit/blob/master/.gitmodules +.. _Mozilla Public License, v. 2.0: http://mozilla.org/MPL/2.0/ +.. _ARTISTIC License: http://www.perlfoundation.org/artistic_license_2_0 +.. _Apache License, v 2.0: http://www.apache.org/licenses/LICENSE-2.0 +.. _SynthWorks Design Inc: http://www.synthworks.com """ @@ -54,8 +54,6 @@ def doc(): welcome. Read our `contribution guide `__ to get started. -License -======= """ + license_text() ) From 55c36ac4265f6efed309fbe8c9be39c8da76a877 Mon Sep 17 00:00:00 2001 From: eine Date: Sat, 8 Aug 2020 07:06:11 +0200 Subject: [PATCH 55/79] replace README.rst with README.md --- README.md | 60 ++++++++++++++++++++++++++++++ README.rst | 35 ----------------- docs/{ => _static}/vunit_demo.gif | Bin docs/index.rst | 2 +- tests/lint/test_readme.py | 27 -------------- vunit/about.py | 12 ++---- 6 files changed, 64 insertions(+), 72 deletions(-) create mode 100644 README.md delete mode 100644 README.rst rename docs/{ => _static}/vunit_demo.gif (100%) delete mode 100644 tests/lint/test_readme.py diff --git a/README.md b/README.md new file mode 100644 index 000000000..1f6c3914f --- /dev/null +++ b/README.md @@ -0,0 +1,60 @@ +

+ + + +

+ +

+ + + 'docs' workflow Status + 'images' workflow Status + 'push' workflow Status + 'coverage' workflow Status +

+ +**VUnit** is an [open source](LICENSE.rst) unit testing framework for VHDL/SystemVerilog. It features the functionality +needed to realize continuous and automated testing of your HDL code. VUnit doesn't replace but rather complements +traditional testing methodologies by supporting a *test early and often* approach through automation. +**Read more** [about VUnit](http://vunit.github.io/about.html). + +Contributing in the form of code, docs, feedback, ideas or bug reports is welcome. +Read our [contribution guide](https://vunit.github.io/contributing.html) to get started. + +

+ +

diff --git a/README.rst b/README.rst deleted file mode 100644 index 439a58602..000000000 --- a/README.rst +++ /dev/null @@ -1,35 +0,0 @@ -What is VUnit? -============== - -VUnit is an open source unit testing framework for VHDL/SystemVerilog -released under the terms of Mozilla Public License, v. 2.0. It -features the functionality needed to realize continuous and automated -testing of your HDL code. VUnit doesn't replace but rather complements -traditional testing methodologies by supporting a "test early and -often" approach through automation. - -**Read more on our** `Website `__ - -Contributing -============ -Contributing in the form of code, feedback, ideas or bug reports are -welcome. Read our `contribution guide -`__ to get started. - -License -======= -VUnit ------ - -VUnit except for OSVVM (see below) is released under the terms of -Mozilla Public License, v. 2.0. - -Copyright (c) 2014-2020, Lars Asplund lars.anders.asplund@gmail.com - -OSVVM ------ - -OSVVM is redistributed as a submodule to VUnit for your convenience. OSVVM and derivative work -located under examples/vhdl/osvvm_integration/src are licensed under the terms of Artistic License 2.0. - -Copyright (c) 2006-2016, SynthWorks Design Inc http://www.synthworks.com diff --git a/docs/vunit_demo.gif b/docs/_static/vunit_demo.gif similarity index 100% rename from docs/vunit_demo.gif rename to docs/_static/vunit_demo.gif diff --git a/docs/index.rst b/docs/index.rst index 7aee1ae99..c090822e3 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -23,7 +23,7 @@ testing of your HDL code. VUnit doesn't replace but rather complements traditional testing methodologies by supporting a *"test early and often"* approach through automation. :ref:`Read more ` -.. image:: vunit_demo.gif +.. image:: _static/vunit_demo.gif .. toctree:: :hidden: diff --git a/tests/lint/test_readme.py b/tests/lint/test_readme.py deleted file mode 100644 index 16e962d34..000000000 --- a/tests/lint/test_readme.py +++ /dev/null @@ -1,27 +0,0 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this file, -# You can obtain one at http://mozilla.org/MPL/2.0/. -# -# Copyright (c) 2014-2020, Lars Asplund lars.anders.asplund@gmail.com - -""" -Check that README.rst matches VUnit docstring -""" - -import unittest -from warnings import simplefilter, catch_warnings -from pathlib import Path -from vunit import ROOT -from vunit.about import doc - - -class TestReadMe(unittest.TestCase): - """ - Check that README.rst matches VUnit docstring - """ - - def test_that_readme_file_matches_vunit_docstring(self): - with catch_warnings(): - simplefilter("ignore", category=DeprecationWarning) - with Path(ROOT, "README.rst").open("rU") as readme: - self.assertEqual(readme.read(), doc()) diff --git a/vunit/about.py b/vunit/about.py index b7d556351..f8eef38c2 100644 --- a/vunit/about.py +++ b/vunit/about.py @@ -36,20 +36,14 @@ def doc(): Returns short introduction to VUnit """ return ( - r"""What is VUnit? -============== - -VUnit is an open source unit testing framework for VHDL/SystemVerilog + r"""VUnit is an open source unit testing framework for VHDL/SystemVerilog released under the terms of Mozilla Public License, v. 2.0. It features the functionality needed to realize continuous and automated testing of your HDL code. VUnit doesn't replace but rather complements traditional testing methodologies by supporting a "test early and -often" approach through automation. - -**Read more on our** `Website `__ +often" approach through automation. **Read more on our** +`Website `__ -Contributing -============ Contributing in the form of code, feedback, ideas or bug reports are welcome. Read our `contribution guide `__ to get started. From e188bb733f14908ade2b54ca8f3ed699a35b111e Mon Sep 17 00:00:00 2001 From: eine Date: Sat, 8 Aug 2020 09:14:35 +0200 Subject: [PATCH 56/79] docs: move 'Requirements' from 'About' to 'Installing' --- docs/about.rst | 82 +++++---------------------------------------- docs/installing.rst | 54 +++++++++++++++++++++++++++-- 2 files changed, 59 insertions(+), 77 deletions(-) diff --git a/docs/about.rst b/docs/about.rst index 001be216f..75ebd960b 100644 --- a/docs/about.rst +++ b/docs/about.rst @@ -4,11 +4,11 @@ What is VUnit? ============== VUnit is an open source unit testing framework for VHDL/SystemVerilog -released under the terms of Mozilla Public License, v. 2.0. It +released under the terms of `Mozilla Public License, v. 2.0`_. It features the functionality needed to realize continuous and automated testing of your HDL code. VUnit doesn't replace but rather complements -traditional testing methodologies by supporting a "test early and -often" approach through automation. +traditional testing methodologies by supporting a *"test early and +often"* approach through automation. VUnit reduces the overhead of testing by supporting automatic discovery of test benches and compilation order as well as including @@ -54,77 +54,17 @@ Main Features - Outputs JUnit report files for better `Jenkins`_ :ref:`integration `. - Builds on the commonly used `xUnit`_ architecture. -Requirements ------------- - -VUnit depends on a number of components as listed below. Full VUnit -functionality requires Python and a simulator supported by the VUnit -Python test runner. However, VUnit can run with limited functionality -entirely within VHDL using the :doc:`VHDL test runner -<./run/user_guide>`. - - -Languages -********* - -- VHDL-93 -- VHDL-2002 -- VHDL-2008 -- VHDL-2019 -- Verilog -- SystemVerilog (Support is experimental) - -Operating systems -***************** - -- Windows -- Linux -- Mac OS X - -Python -****** - -- Python 3.6 or higher - -Simulators -********** - -- `Aldec Riviera-PRO`_ - - - Tested with Riviera-PRO 2015.06, 2015.10, 2016.02, 2016.10 (x64/x86). - - Only VHDL -- `Aldec Active-HDL`_ - - - Tested with Active-HDL 9.3, 10.1, 10.2, 10.3 (x64/x86) - - Only VHDL -- `Mentor Graphics ModelSim/Questa`_ - - - Tested with 10.1 - 10.5 -- `GHDL`_ - - - Only VHDL - - Works with versions >= 0.33 - - Tested with LLVM and mcode backends, gcc backend might work aswell. - - Integrated support for using `GTKWave`_ to view waveforms. -- `Cadence Incisive`_ (**Experimental**) - - - Community contribution by `Colin Marquardt - `_. VUnit maintainers does not have - access to this simulator to verify the functionality. - - Run ``incisive_vhdl_fixup.py`` to remove VHDL constructs that are - not compatible with Incisive - Getting Started --------------- There are a number of ways to get started. -- The :ref:`VUnit User Guide ` will guide users on how to use start using +- :ref:`VUnit User Guide ` will guide users on how to use start using the basic features of VUnit but also provides information about more specific and advanced usage. -- The :ref:`Run Library User Guide ` presents the run packages. -- The :ref:`Check Library User Guide ` presents the check packages. -- The :ref:`Logging Library User Guide ` presents the log packages. +- :ref:`Run Library User Guide ` presents the run packages. +- :ref:`Check Library User Guide ` presents the check packages. +- :ref:`Logging Library User Guide ` presents the log packages. - There are also various presentations of VUnit on `YouTube`_. For example `an introduction to unit testing (6 min)`_ and a `short introduction to VUnit (12 min)`_. @@ -135,7 +75,7 @@ Support Any bug reports, feature requests or questions about the usage of VUnit can be made by creating a `new issue`_. -Credits and license +Credits and License ------------------- - Founders: @@ -164,12 +104,6 @@ Credits and license .. _xUnit: http://en.wikipedia.org/wiki/XUnit .. _Jenkins: http://jenkins-ci.org/ -.. _Aldec Riviera-PRO: https://www.aldec.com/en/products/functional_verification/riviera-pro -.. _Aldec Active-HDL: https://www.aldec.com/en/products/fpga_simulation/active-hdl -.. _Mentor Graphics ModelSim/Questa: http://www.mentor.com/products/fv/modelsim/ -.. _Cadence Incisive: https://www.cadence.com/content/cadence-www/global/en_US/home/tools/system-design-and-verification/simulation-and-testbench-verification/incisive-enterprise-simulator.html -.. _GHDL: https://github.com/ghdl/ghdl -.. _GTKWave: http://gtkwave.sourceforge.net/ .. _YouTube: https://www.youtube.com/channel/UCCPVCaeWkz6C95aRUTbIwdg .. _an introduction to unit testing (6 min): https://www.youtube.com/watch?v=PZuBqcxS8t4 .. _short introduction to VUnit (12 min): https://www.youtube.com/watch?v=D8s_VLD91tw diff --git a/docs/installing.rst b/docs/installing.rst index 6c4750922..c18982b93 100644 --- a/docs/installing.rst +++ b/docs/installing.rst @@ -2,6 +2,50 @@ Installing ========== + +Requirements +------------ + +VUnit supports VHDL (93, 2002, 2008 and 2019), Verilog and (experimentally) SystemVerilog; and it is known to work on +GNU/Linux, Windows and Mac OS; on x86, x64, armv7 and aarch64. Full VUnit functionality requires Python (3.6 or higher) and +a simulator supported by the VUnit Python test runner (see list below). However, VUnit can run with limited functionality +entirely within VHDL using the :doc:`VHDL test runner <./run/user_guide>`. + +Simulators: + +.. admonition:: Only VHDL + + - `Aldec Riviera-PRO`_: Tested with Riviera-PRO 2015.06, 2015.10, 2016.02, 2016.10 (x64/x86). + - `Aldec Active-HDL`_: Tested with Active-HDL 9.3, 10.1, 10.2, 10.3 (x64/x86) + - `GHDL`_ + + - Works with versions >= 0.33 + - Tested with LLVM and mcode backends, gcc backend might work aswell. + - Integrated support for using `GTKWave`_ to view waveforms. + +.. admonition:: VHDL or SystemVerilog + + - `Mentor Graphics ModelSim/Questa`_: Tested with 10.1 - 10.5 + +.. CAUTION:: + + - `Cadence Incisive`_ (**Experimental**) + + - Community contribution by `Colin Marquardt `_. + VUnit maintainers do not have access to this simulator to verify the functionality. + + - Run ``incisive_vhdl_fixup.py`` to remove VHDL constructs that are + not compatible with Incisive + +.. _Aldec Riviera-PRO: https://www.aldec.com/en/products/functional_verification/riviera-pro +.. _Aldec Active-HDL: https://www.aldec.com/en/products/fpga_simulation/active-hdl +.. _Mentor Graphics ModelSim/Questa: http://www.mentor.com/products/fv/modelsim/ +.. _Cadence Incisive: https://www.cadence.com/content/cadence-www/global/en_US/home/tools/system-design-and-verification/simulation-and-testbench-verification/incisive-enterprise-simulator.html +.. _GHDL: https://github.com/ghdl/ghdl +.. _GTKWave: http://gtkwave.sourceforge.net/ + +.. _installing_pypi: + Using the Python Package Manager -------------------------------- The recommended way to get VUnit is to install the :ref:`latest stable release ` via `pip `__: @@ -16,6 +60,7 @@ Once installed, VUnit may be updated to new versions via a similar method: > pip install -U vunit_hdl +.. _installing_master: Using the Development Version ----------------------------- @@ -23,9 +68,9 @@ Start by cloning our `GIT repository on GitHub .. code-block:: console - git clone --recursive https://github.com/VUnit/vunit.git + git clone --recurse-submodules https://github.com/VUnit/vunit.git -The ``--recursive`` option initializes `OSVVM `__ which is included as a submodule in the VUnit repository. +The ``--recurse-submodules`` option initializes `OSVVM `__ which is included as a submodule in the VUnit repository. To be able to import :class:`VUnit ` in your ``run.py`` script you need to make it visible to Python or else the following error @@ -59,8 +104,11 @@ There are three methods to make VUnit importable in your ``run.py`` script.: sys.path.append("/path/to/vunit_repo_root/") import vunit +.. _installing_dev: + For VUnit Developers -~~~~~~~~~~~~~~~~~~~~ +-------------------- + For those interested in development of VUnit, it is best to install VUnit so that the sources from git are installed in-place instead of to the Python site-packages directory. This can be achieved by using From 97053b7c7e636f85389a4dd8497fd6cee7a59684 Mon Sep 17 00:00:00 2001 From: eine Date: Sat, 8 Aug 2020 09:29:18 +0200 Subject: [PATCH 57/79] docs: add captioned toctrees --- docs/documentation.rst | 12 ------------ docs/index.rst | 23 +++++++++++++++++++++-- 2 files changed, 21 insertions(+), 14 deletions(-) delete mode 100644 docs/documentation.rst diff --git a/docs/documentation.rst b/docs/documentation.rst deleted file mode 100644 index e4255abc2..000000000 --- a/docs/documentation.rst +++ /dev/null @@ -1,12 +0,0 @@ -Documentation -============= -.. toctree:: - :maxdepth: 2 - - user_guide - cli - py/ui - vhdl_libraries - examples - -* :ref:`genindex` diff --git a/docs/index.rst b/docs/index.rst index c090822e3..0914ab100 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -29,10 +29,29 @@ often"* approach through automation. :ref:`Read more ` :hidden: blog/index + +.. toctree:: + :caption: About + :hidden: + about installing - documentation testimonials/testimonials - contributing + +.. toctree:: + :caption: Documentation + :hidden: + + user_guide + cli + py/ui + vhdl_libraries + examples + +.. toctree:: + :caption: Appendix + :hidden: + release_notes + contributing genindex From aac9c64f2e94e6412de194d1bc15af83effe22bf Mon Sep 17 00:00:00 2001 From: eine Date: Sat, 8 Aug 2020 09:55:41 +0200 Subject: [PATCH 58/79] docs: use admonitions --- docs/index.rst | 4 +- docs/user_guide.rst | 53 +++++++------- docs/verification_components/user_guide.rst | 69 +++++++++---------- .../verification_components/{vci => }/vci.rst | 8 +-- 4 files changed, 67 insertions(+), 67 deletions(-) rename docs/verification_components/{vci => }/vci.rst (62%) diff --git a/docs/index.rst b/docs/index.rst index 0914ab100..ebfc158b9 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -13,8 +13,8 @@ .. |shieldTwitter| image:: https://img.shields.io/twitter/follow/VUnitFramework.svg?longCache=true&style=flat-square&color=1DA1F2&label=%40VUnitFramework&logo=twitter&logoColor=fff .. _shieldTwitter: https://www.twitter.com/VUnitFramework -VUnit -===== +VUnit: a test framework for HDL +=============================== VUnit is an open source unit testing framework for VHDL/SystemVerilog released under the terms of Mozilla Public License, v. 2.0. It diff --git a/docs/user_guide.rst b/docs/user_guide.rst index 3a2df0c3e..ffbc30884 100644 --- a/docs/user_guide.rst +++ b/docs/user_guide.rst @@ -5,6 +5,7 @@ User Guide Introduction ------------ + VUnit is invoked by a user-defined project specified in a Python script. At minimum, a VUnit project consists of a set of HDL source files mapped to libraries. The project serves as single point of entry @@ -66,6 +67,11 @@ how to choose which one to use :ref:`here `. VHDL Test Benches ----------------- + +.. HINT:: + + Example code available at :vunit_example:`vhdl/user_guide`. + In its simplest form a VUnit VHDL test bench looks like this: .. literalinclude:: ../examples/vhdl/user_guide/tb_example.vhd @@ -101,13 +107,15 @@ From ``tb_example_many.vhd``'s ``run()`` calls, two test cases are created: * ``lib.tb_example_many.test_pass`` * ``lib.tb_example_many.test_fail`` - -The above example code can be found in :vunit_example:`vhdl/user_guide`. - .. _sv_test_benches: SystemVerilog Test Benches -------------------------- + +.. HINT:: + + Example code available at :vunit_example:`verilog/user_guide`. + In its simplest form a VUnit SystemVerilog test bench looks like this: .. literalinclude:: ../examples/verilog/user_guide/tb_example.sv @@ -125,44 +133,39 @@ Each test is run in an individual simulation. Putting multiple tests in the same test bench is a good way to share a common test environment. -The above example code can be found in :vunit_example:`verilog/user_guide`. - .. _test_bench_scanning: Scanning for Test Benches ------------------------- + VUnit will recognize a module or entity as a test bench and run it if it has a ``runner_cfg`` generic or parameter. A SystemVerilog test bench using the ``TEST_SUITE`` macro will have a ``runner_cfg`` parameter created by the macro and thus match the criteria. -A warning will be given if the test bench entity or module name does -not match the pattern ``tb_*`` or ``*_tb``. +.. WARNING:: A warning will be given if: + + * The test bench entity or module name **does not match** the pattern + ``tb_*`` or ``*_tb``. -A warning will be given if the name *does* match the above pattern but -lacks a ``runner_cfg`` generic or parameter preventing it to be run -by VUnit. + * The name **does match** the above pattern **but lacks** a ``runner_cfg`` + generic or parameter preventing it to be run by VUnit. .. _special_generics: Special generics/parameters --------------------------- -A VUnit test bench can have several special generics/parameters. -Optional generics are filled in automatically by VUnit if detected on -the test bench. -- ``runner_cfg : string`` +- [**required**] ``runner_cfg : string``, used by VUnit to pass private information + between Python and the HDL test runner. - Required by VUnit to pass private information between Python and the HDL test runner +- [**optional**] ``output_path : string``, path to the output directory of the + current test; this is useful to create additional output files that can + be checked after simulation by a **post_check** Python function. -- ``output_path : string`` - - Optional path to the output directory of the current test. - This is useful to create additional output files that can be checked - after simulation by a **post_check** Python function. - -- ``tb_path : string`` - - Optional path to the directory containing the test bench. - This is useful to read input data with a known location relative to +- [**optional**] ``tb_path : string``, path to the directory containing the test + bench; this is useful to read input data with a known location relative to the test bench location. + +.. HINT:: Optional generics/parameters are filled in automatically by VUnit if detected + on the test bench. diff --git a/docs/verification_components/user_guide.rst b/docs/verification_components/user_guide.rst index 81a56c036..705e47b53 100644 --- a/docs/verification_components/user_guide.rst +++ b/docs/verification_components/user_guide.rst @@ -41,26 +41,15 @@ single VC typically implements several VCIs. For example an AXI-lite VC or RAM master VC can support the same generic bus master and synchronization VCI while also supporting their own bus specific VCIs. -The main benefit of generic VCIs is to reduce redundancy between VCs -and allow the user to write generic code that will work regardless of -the specific VC instance used. For example control registers might be -defined as a RAM-style bus in a submodule but be mapped to an AXI-lite -interface on the top level. The same testbench code for talking to the -submodule can be used in both the submodule test bench as well as the -top level test bench regardless of the fact that two different VCs -have been used. Without generic VCIs copy pasting the code and -changing the type of read/write procedure call would be required. - -Included verification component interfaces (VCIs): - -:ref:`Bus master ` - Generic read and write of bus with address and byte enable. - -:ref:`Stream ` - Push and pop of data stream without address. - -:ref:`Synchronization ` - Wait for time and events. +.. TIP:: The main benefit of generic VCIs is to reduce redundancy between + VCs and allow the user to write generic code that will work regardless + of the specific VC instance used. For example control registers might be + defined as a RAM-style bus in a submodule but be mapped to an AXI-lite + interface on the top level. The same testbench code for talking to the + submodule can be used in both the submodule test bench as well as the + top level test bench regardless of the fact that two different VCs + have been used. Without generic VCIs copy pasting the code and + changing the type of read/write procedure call would be required. Neither a VC or a VCI there is the :ref:`memory model ` which is a model of a memory space such as the DRAM address space in a @@ -75,11 +64,35 @@ reading and writing data. memory_model +.. _verification_component_interfaces: + +Verification Component Interfaces +--------------------------------- + +A verification component interface (VCI) is a procedural interface to +a VC. A VCI is defined as procedures in a package file. Several VCs can +support the same generic VCI to enable code re-use both for the users +and the VC-developers. + +List of VCIs included in the main repository: + +Included verification component interfaces (VCIs): + +* :ref:`Bus master `: generic read and write of bus with address and byte enable. +* :ref:`Stream `: push and pop of data stream without address. +* :ref:`Synchronization `: wait for time and events. + +.. toctree:: + :maxdepth: 1 + :hidden: + + vci .. _verification_components: Verification Components ----------------------- + A verification component (VC) is an entity that is normally connected to the DUT via a bus signal interface such as AXI-Lite. The main test sequence in the test bench sends messages to the VCs that will then @@ -92,19 +105,3 @@ sending to and receiving messages from the VC. Each VC instance is associated with a handle that is created in the test bench and set as a generic on the VC instantiation. The handle is given as and argument to the procedure calls to direct messages to the specfic VC instance. - -.. _verification_component_interfaces: - -Verification Component Interfaces ---------------------------------- -A verification component interface (VCI) is a procedural interface to -a VC. A VCI is defined as procedures in a package file. Several VC can -support the same generic VCI to enable code re-use both for the users -and the VC-developers. - - -.. toctree:: - :maxdepth: 1 - :hidden: - - vci/vci diff --git a/docs/verification_components/vci/vci.rst b/docs/verification_components/vci.rst similarity index 62% rename from docs/verification_components/vci/vci.rst rename to docs/verification_components/vci.rst index 0b14bd9ad..3fdf1de33 100644 --- a/docs/verification_components/vci/vci.rst +++ b/docs/verification_components/vci.rst @@ -3,7 +3,7 @@ Bus Master VCI ============== -.. literalinclude:: ../../../vunit/vhdl/verification_components/src/bus_master_pkg.vhd +.. literalinclude:: ../../vunit/vhdl/verification_components/src/bus_master_pkg.vhd :caption: Bus master verification component interface :language: vhdl :lines: 7- @@ -13,7 +13,7 @@ Bus Master VCI Stream Master VCI ================= -.. literalinclude:: ../../../vunit/vhdl/verification_components/src/stream_master_pkg.vhd +.. literalinclude:: ../../vunit/vhdl/verification_components/src/stream_master_pkg.vhd :caption: Stream master verification component interface :language: vhdl :lines: 7- @@ -21,7 +21,7 @@ Stream Master VCI Stream Slave VCI ================ -.. literalinclude:: ../../../vunit/vhdl/verification_components/src/stream_slave_pkg.vhd +.. literalinclude:: ../../vunit/vhdl/verification_components/src/stream_slave_pkg.vhd :caption: Stream slave verification component interface :language: vhdl :lines: 7- @@ -31,7 +31,7 @@ Stream Slave VCI Synchronization VCI =================== -.. literalinclude:: ../../../vunit/vhdl/verification_components/src/sync_pkg.vhd +.. literalinclude:: ../../vunit/vhdl/verification_components/src/sync_pkg.vhd :caption: Synchronization verification component interface :language: vhdl :lines: 7- From c418e8a18ba38313da013091495e841659ab6c05 Mon Sep 17 00:00:00 2001 From: eine Date: Sat, 8 Aug 2020 11:08:47 +0200 Subject: [PATCH 59/79] docs/blog: use admonitions --- ...11_15_vunit_the_best_value_for_initial_effort_part1.rst | 7 +++---- ...11_16_vunit_the_best_value_for_initial_effort_part2.rst | 7 +++---- ...11_22_vunit_the_best_value_for_initial_effort_part3.rst | 7 +++---- ..._28_sigasi_adds_support_for_vunit_testing_framework.rst | 7 +++---- docs/blog/2017_10_31_vunit_3_0_color_logging.rst | 7 +++---- .../2017_11_07_vunit_3_0_while_waiting_for_vhdl_2017.rst | 7 +++---- docs/blog/2017_11_23_vunit_matlab_integration.rst | 7 +++---- docs/blog/2017_12_14_vunit_bfms_as_simple_as_emailing.rst | 5 +++-- docs/blog/2018_03_22_vunit_community_developed_bfms.rst | 7 +++---- ...epens_its_commitment_to_the_vunit_testing_framework.rst | 7 +++---- docs/blog/2018_09_22_sigasi_adds_full_vunit_support.rst | 7 +++---- 11 files changed, 33 insertions(+), 42 deletions(-) diff --git a/docs/blog/2016_11_15_vunit_the_best_value_for_initial_effort_part1.rst b/docs/blog/2016_11_15_vunit_the_best_value_for_initial_effort_part1.rst index 7edb826be..75d7934eb 100644 --- a/docs/blog/2016_11_15_vunit_the_best_value_for_initial_effort_part1.rst +++ b/docs/blog/2016_11_15_vunit_the_best_value_for_initial_effort_part1.rst @@ -5,14 +5,13 @@ VUnit - The Best Value for Initial Effort - Part 1 ================================================== +.. NOTE:: This article was originally posted on `LinkedIn `__ + where you may find some comments on its contents. + .. figure:: img/bestvalue1.jpg :alt: Best Value Part 1 :align: center -This article was originally posted on `LinkedIn -`__ -where you may find some comments on its contents. - In the book Effective Coding with VHDL published this summer VUnit was presented as the most advanced testing framework of its kind. diff --git a/docs/blog/2016_11_16_vunit_the_best_value_for_initial_effort_part2.rst b/docs/blog/2016_11_16_vunit_the_best_value_for_initial_effort_part2.rst index 1f3dc7113..d3fcd5f7c 100644 --- a/docs/blog/2016_11_16_vunit_the_best_value_for_initial_effort_part2.rst +++ b/docs/blog/2016_11_16_vunit_the_best_value_for_initial_effort_part2.rst @@ -5,14 +5,13 @@ VUnit - The Best Value for Initial Effort - Part 2 ================================================== +.. NOTE:: This article was originally posted on `LinkedIn `__ + where you may find some comments on its contents. + .. figure:: img/bestvalue2.jpg :alt: Best Value Part 2 :align: center -This article was originally posted on `LinkedIn -`__ -where you may find some comments on its contents. - In the previous `blog `__ I showed how `VUnit `__ can be installed in less than diff --git a/docs/blog/2016_11_22_vunit_the_best_value_for_initial_effort_part3.rst b/docs/blog/2016_11_22_vunit_the_best_value_for_initial_effort_part3.rst index cb017221b..bbe40d168 100644 --- a/docs/blog/2016_11_22_vunit_the_best_value_for_initial_effort_part3.rst +++ b/docs/blog/2016_11_22_vunit_the_best_value_for_initial_effort_part3.rst @@ -5,14 +5,13 @@ VUnit - The Best Value for Initial Effort - Part 3 ================================================== +.. NOTE:: This article was originally posted on `LinkedIn `__ + where you may find some comments on its contents. + .. figure:: img/bestvalue3.jpg :alt: Best Value Part 3 :align: center -This article was originally posted on `LinkedIn -`__ -where you may find some comments on its contents. - After spending one minute on `installing VUnit `__ and one minute on `creating a run script diff --git a/docs/blog/2017_09_28_sigasi_adds_support_for_vunit_testing_framework.rst b/docs/blog/2017_09_28_sigasi_adds_support_for_vunit_testing_framework.rst index 8dbf49d6b..5c7d0ab97 100644 --- a/docs/blog/2017_09_28_sigasi_adds_support_for_vunit_testing_framework.rst +++ b/docs/blog/2017_09_28_sigasi_adds_support_for_vunit_testing_framework.rst @@ -5,14 +5,13 @@ Sigasi Adds Support for VUnit Testing Framework =============================================== +.. NOTE:: This article was originally posted on `LinkedIn `__ + where you may find some comments on its contents. + .. figure:: img/vunit_sigasistudio.jpg :alt: Sigasi Support :align: center -This article was originally posted on `LinkedIn -`__ -where you may find some comments on its contents. - `VUnit `__ was born out of frustration over the lack of an efficient test framework. The continuous and automated approach to testing I used for software diff --git a/docs/blog/2017_10_31_vunit_3_0_color_logging.rst b/docs/blog/2017_10_31_vunit_3_0_color_logging.rst index a07e8f432..99641f250 100644 --- a/docs/blog/2017_10_31_vunit_3_0_color_logging.rst +++ b/docs/blog/2017_10_31_vunit_3_0_color_logging.rst @@ -5,14 +5,13 @@ VUnit 3.0 Color Logging ======================= +.. NOTE:: This article was originally posted on `LinkedIn `__ + where you may find some comments on its contents. + .. figure:: img/color_logging.jpg :alt: VUnit 3.0 Color Logging :align: center -This article was originally posted on `LinkedIn -`__ -where you may find some comments on its contents. - VUnit 3.0, our next major release, is around the corner and with it comes a number of updates and additions. This first preview post will demonstrate color logging, one of the updates we made to our logging diff --git a/docs/blog/2017_11_07_vunit_3_0_while_waiting_for_vhdl_2017.rst b/docs/blog/2017_11_07_vunit_3_0_while_waiting_for_vhdl_2017.rst index 843c63432..3a1e8b43c 100644 --- a/docs/blog/2017_11_07_vunit_3_0_while_waiting_for_vhdl_2017.rst +++ b/docs/blog/2017_11_07_vunit_3_0_while_waiting_for_vhdl_2017.rst @@ -5,14 +5,13 @@ VUnit 3.0 - While Waiting for VHDL-2017 ======================================= +.. NOTE:: This article was originally posted on `LinkedIn `__ + where you may find some comments on its contents. + .. figure:: img/vunit_waiting.jpg :alt: VUnit 3.0 - While Waiting for VHDL-2017 :align: center -This article was originally posted on `LinkedIn -`__ -where you may find some comments on its contents. - Background ---------- diff --git a/docs/blog/2017_11_23_vunit_matlab_integration.rst b/docs/blog/2017_11_23_vunit_matlab_integration.rst index 872031e1a..7de043c2d 100644 --- a/docs/blog/2017_11_23_vunit_matlab_integration.rst +++ b/docs/blog/2017_11_23_vunit_matlab_integration.rst @@ -5,14 +5,13 @@ VUnit Matlab Integration ======================== +.. NOTE:: This article was originally posted on `LinkedIn `__ + where you may find some comments on its contents. + .. figure:: img/vunit_matlab.jpg :alt: VUnit Matlab Integration :align: center -This article was originally posted on `LinkedIn -`__ -where you may find some comments on its contents. - Recently I got a question from an ASIC team if it is possible to integrate their VUnit simulations with Matlab. I've been getting this question several times lately so this post will show you how it can be diff --git a/docs/blog/2017_12_14_vunit_bfms_as_simple_as_emailing.rst b/docs/blog/2017_12_14_vunit_bfms_as_simple_as_emailing.rst index ba82ff411..36e948541 100644 --- a/docs/blog/2017_12_14_vunit_bfms_as_simple_as_emailing.rst +++ b/docs/blog/2017_12_14_vunit_bfms_as_simple_as_emailing.rst @@ -5,12 +5,13 @@ VUnit BFMs - as Simple as Emailing ================================== +.. NOTE:: This article was originally posted on `LinkedIn `__ + where you may find some comments on its contents. + .. figure:: img/vunit_emailing.jpg :alt: VUnit BFMs - as Simple as Emailing :align: center -This article was originally posted on `LinkedIn `__ where you may find some comments on its contents. - VUnit 3.0, our next major release, is around the corner and with it comes a number of updates and additions. One area which we have improved is our support for creating advanced bus functional models diff --git a/docs/blog/2018_03_22_vunit_community_developed_bfms.rst b/docs/blog/2018_03_22_vunit_community_developed_bfms.rst index ea05789d8..98f7a3bc0 100644 --- a/docs/blog/2018_03_22_vunit_community_developed_bfms.rst +++ b/docs/blog/2018_03_22_vunit_community_developed_bfms.rst @@ -5,14 +5,13 @@ VUnit Community Developed BFMs ============================== +.. NOTE:: This article was originally posted on `LinkedIn `__ + where you may find some comments on its contents. + .. figure:: img/vunit_wishbone.png :alt: VUnit Community Developed BFMs :align: center -This article was originally posted on `LinkedIn -`__ -where you may find some comments on its contents. - One month ago we released VUnit 3.0 which was a release focused on our BFM support. Our previous BFM building blocks were extended and further simplified, we added new building blocks, and also a number of diff --git a/docs/blog/2018_07_22_sigasi_deepens_its_commitment_to_the_vunit_testing_framework.rst b/docs/blog/2018_07_22_sigasi_deepens_its_commitment_to_the_vunit_testing_framework.rst index 115639cd2..6f5db5ced 100644 --- a/docs/blog/2018_07_22_sigasi_deepens_its_commitment_to_the_vunit_testing_framework.rst +++ b/docs/blog/2018_07_22_sigasi_deepens_its_commitment_to_the_vunit_testing_framework.rst @@ -5,14 +5,13 @@ Sigasi Deepens Its Commitment to the VUnit Testing Framework ============================================================ +.. NOTE:: This article was originally posted on `LinkedIn `__ + where you may find some comments on its contents. + .. figure:: img/sigasi_deep.png :alt: Sigasi Deepens Its Commitment to the VUnit Testing Framework :align: center -This article was originally posted on `LinkedIn -`__ -where you may find some comments on its contents. - Sigasi started to support the VUnit testing framework when their Sigasi Studio IDE became VUnit-aware in the 3.6 release. That release introduced a feature to conveniently configure the VUnit library for diff --git a/docs/blog/2018_09_22_sigasi_adds_full_vunit_support.rst b/docs/blog/2018_09_22_sigasi_adds_full_vunit_support.rst index dfb46d855..3d9839194 100644 --- a/docs/blog/2018_09_22_sigasi_adds_full_vunit_support.rst +++ b/docs/blog/2018_09_22_sigasi_adds_full_vunit_support.rst @@ -5,14 +5,13 @@ Sigasi Adds Full VUnit Support ============================== +.. NOTE:: This article was originally posted on `LinkedIn `__ + where you may find some comments on its contents. + .. figure:: img/sigasi_full.png :alt: Sigasi Adds Full VUnit Support :align: center -This article was originally posted on `LinkedIn -`__ -where you may find some comments on its contents. - Some time ago I gave a `preview `__ of this update of Sigasi Studio and From 9ec4f36a7fcfa6589d64bdad1ce132cd39eb4821 Mon Sep 17 00:00:00 2001 From: eine Date: Mon, 10 Aug 2020 17:33:46 +0200 Subject: [PATCH 60/79] tox: add pyproject.toml, use isolated_build --- pyproject.toml | 6 ++++++ tox.ini | 1 + 2 files changed, 7 insertions(+) create mode 100644 pyproject.toml diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 000000000..bf54bd474 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,6 @@ +[build-system] +requires = [ + "setuptools >= 35.0.2", + "setuptools_scm >= 2.0.0, <3" +] +build-backend = "setuptools.build_meta" diff --git a/tox.ini b/tox.ini index 1777d94b0..6bb510830 100644 --- a/tox.ini +++ b/tox.ini @@ -1,6 +1,7 @@ [tox] envlist = py{36,37,38}-{fmt,unit,lint,docs}, py{36,37,38}-{acceptance,vcomponents}-{activehdl,ghdl,modelsim,rivierapro}, py{36,37,38}-coverage skip_missing_interpreters = True +isolated_build = True [testenv] recreate=True From c85003e81b1f0e38a9aff639675dc341d560ec64 Mon Sep 17 00:00:00 2001 From: eine Date: Mon, 10 Aug 2020 17:53:11 +0200 Subject: [PATCH 61/79] tox: merge tox.ini into pyproject.yml --- pyproject.toml | 41 +++++++++++++++++++++++++++++++++++++++++ tox.ini | 37 ------------------------------------- 2 files changed, 41 insertions(+), 37 deletions(-) delete mode 100644 tox.ini diff --git a/pyproject.toml b/pyproject.toml index bf54bd474..4ed847cb6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,3 +4,44 @@ requires = [ "setuptools_scm >= 2.0.0, <3" ] build-backend = "setuptools.build_meta" + +[tool.tox] +legacy_tox_ini = """ +[tox] +envlist = py{36,37,38}-{fmt,unit,lint,docs}, py{36,37,38}-{acceptance,vcomponents}-{activehdl,ghdl,modelsim,rivierapro}, py{36,37,38}-coverage +skip_missing_interpreters = True +isolated_build = True + +[testenv] +recreate=True +passenv=ALDEC_LICENSE_FILE + +deps= + fmt: black + pytest + lint: pycodestyle + lint: pylint + lint: mypy + coverage: coverage + coverage: pycodestyle + coverage: pylint + coverage: mypy + docs: docutils + docs: sphinx + docs: sphinx-argparse + +setenv= + acceptance-activehdl: VUNIT_SIMULATOR=activehdl + acceptance-ghdl: VUNIT_SIMULATOR=ghdl + acceptance-modelsim: VUNIT_SIMULATOR=modelsim + acceptance-rivierapro: VUNIT_SIMULATOR=rivierapro + +commands= + fmt: {envpython} -m black ./ --exclude 'vunit/vhdl/JSON-for-VHDL|.eggs|.git|.hg|.mypy_cache|.nox|.tox|.venv|_build|buck-out|build|dist' {posargs} + unit: {envpython} -m pytest -v -ra tests/unit {posargs} + lint: {envpython} -m pytest -v -ra tests/lint {posargs} + docs: {envpython} tools/build_docs.py {envtmpdir}/docsbuild {posargs} + acceptance: {envpython} -m pytest -v -ra tests/acceptance {posargs} + vcomponents: {envpython} vunit/vhdl/verification_components/run.py --clean + coverage: {envpython} -m coverage run --branch --source vunit/ -m unittest discover tests/ +""" diff --git a/tox.ini b/tox.ini deleted file mode 100644 index 6bb510830..000000000 --- a/tox.ini +++ /dev/null @@ -1,37 +0,0 @@ -[tox] -envlist = py{36,37,38}-{fmt,unit,lint,docs}, py{36,37,38}-{acceptance,vcomponents}-{activehdl,ghdl,modelsim,rivierapro}, py{36,37,38}-coverage -skip_missing_interpreters = True -isolated_build = True - -[testenv] -recreate=True -passenv=ALDEC_LICENSE_FILE - -deps= - fmt: black - pytest - lint: pycodestyle - lint: pylint - lint: mypy - coverage: coverage - coverage: pycodestyle - coverage: pylint - coverage: mypy - docs: docutils - docs: sphinx - docs: sphinx-argparse - -setenv= - acceptance-activehdl: VUNIT_SIMULATOR=activehdl - acceptance-ghdl: VUNIT_SIMULATOR=ghdl - acceptance-modelsim: VUNIT_SIMULATOR=modelsim - acceptance-rivierapro: VUNIT_SIMULATOR=rivierapro - -commands= - fmt: {envpython} -m black ./ --exclude 'vunit\/vhdl\/JSON-for-VHDL|\.eggs|\.git|\.hg|\.mypy_cache|\.nox|\.tox|\.venv|_build|buck-out|build|dist' {posargs} - unit: {envpython} -m pytest -v -ra tests/unit {posargs} - lint: {envpython} -m pytest -v -ra tests/lint {posargs} - docs: {envpython} tools/build_docs.py {envtmpdir}/docsbuild {posargs} - acceptance: {envpython} -m pytest -v -ra tests/acceptance {posargs} - vcomponents: {envpython} vunit/vhdl/verification_components/run.py --clean - coverage: {envpython} -m coverage run --branch --source vunit/ -m unittest discover tests/ From d6975b7488126816c42a70f976f86fb888919554 Mon Sep 17 00:00:00 2001 From: eine Date: Mon, 10 Aug 2020 18:39:13 +0200 Subject: [PATCH 62/79] setup: ensure that the source tree is on the sys path (tox-dev/tox#1650) --- setup.py | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/setup.py b/setup.py index 159e45b33..2011fe539 100644 --- a/setup.py +++ b/setup.py @@ -9,10 +9,16 @@ """ import os +import sys +from pathlib import Path from logging import warning from setuptools import setup -from vunit.about import version, doc -from vunit.builtins import osvvm_is_installed + +# Ensure that the source tree is on the sys path +sys.path.insert(0, str(Path(__file__).parent.resolve())) + +from vunit.about import version, doc # pylint: disable=wrong-import-position +from vunit.builtins import osvvm_is_installed # pylint: disable=wrong-import-position def find_all_files(directory, endings=None): @@ -24,15 +30,15 @@ def find_all_files(directory, endings=None): for filename in filenames: ending = os.path.splitext(filename)[-1] if endings is None or ending in endings: - result.append(os.path.join(root, filename)) + result.append(str(Path(root) / filename)) return result DATA_FILES = [] -DATA_FILES += find_all_files(os.path.join("vunit"), endings=[".tcl"]) -DATA_FILES += find_all_files(os.path.join("vunit", "vhdl")) +DATA_FILES += find_all_files("vunit", endings=[".tcl"]) +DATA_FILES += find_all_files(str(Path("vunit") / "vhdl")) DATA_FILES += find_all_files( - os.path.join("vunit", "verilog"), endings=[".v", ".sv", ".svh"] + str(Path("vunit") / "verilog"), endings=[".v", ".sv", ".svh"] ) DATA_FILES = [os.path.relpath(file_name, "vunit") for file_name in DATA_FILES] From 7c3f930be75bb34b3f32ebbaee97786801cbadb4 Mon Sep 17 00:00:00 2001 From: eine Date: Mon, 10 Aug 2020 19:19:43 +0200 Subject: [PATCH 63/79] ci: pin pytest version in tox, to work around actions/virtual-environments#1381 --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 4ed847cb6..dfe9d004e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -18,7 +18,7 @@ passenv=ALDEC_LICENSE_FILE deps= fmt: black - pytest + pytest==5.4.3 # work around actions/virtual-environments#1381 lint: pycodestyle lint: pylint lint: mypy From 1d2b07ae8f446f83354a2c2dbba39ff099738e71 Mon Sep 17 00:00:00 2001 From: umarcor Date: Sat, 8 Aug 2020 10:42:53 +0200 Subject: [PATCH 64/79] docs: move CI out from CLI and update content --- docs/_static/gha_flow.png | Bin 0 -> 161106 bytes docs/ci.rst | 74 ------------------------ docs/ci/container.rst | 115 +++++++++++++++++++++++++++++++++++++ docs/ci/intro.rst | 28 +++++++++ docs/ci/manual.rst | 29 ++++++++++ docs/ci/script.rst | 116 ++++++++++++++++++++++++++++++++++++++ docs/ci/usecases.rst | 66 ++++++++++++++++++++++ docs/cli.rst | 5 +- docs/index.rst | 10 ++++ 9 files changed, 365 insertions(+), 78 deletions(-) create mode 100644 docs/_static/gha_flow.png delete mode 100644 docs/ci.rst create mode 100644 docs/ci/container.rst create mode 100644 docs/ci/intro.rst create mode 100644 docs/ci/manual.rst create mode 100644 docs/ci/script.rst create mode 100644 docs/ci/usecases.rst diff --git a/docs/_static/gha_flow.png b/docs/_static/gha_flow.png new file mode 100644 index 0000000000000000000000000000000000000000..de5de856bea8d388f0c734a38d43cf9b212baf22 GIT binary patch literal 161106 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 z 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 literal 0 HcmV?d00001 diff --git a/docs/ci.rst b/docs/ci.rst deleted file mode 100644 index fb8a36f34..000000000 --- a/docs/ci.rst +++ /dev/null @@ -1,74 +0,0 @@ -Continuous Integration (CI) Environment ---------------------------------------- - -Because VUnit features the functionality needed to realize continuous and automated testing of HDL code, it is a very valuable resource in continuous integration environments. Once a project ``run.py`` has been setup, tests can be run in a headless environment with standardized `Xunit `_ style output to a file; which allows dynamic interpretation of results avoiding custom (and error-prone) parsing of the logs. - -.. code-block:: console - :caption: Execute VUnit tests on CI server with XML output - - python run.py --xunit-xml test_output.xml - -After tests have finished running, the ``test_output.xml`` file can be parsed -using standard xUnit test parsers such as `Jenkins xUnit Plugin `_. - -Furthermore, VUnit can be easily executed in many different platforms (either operating systems or architectures), because it is written in Python, which is an interpreted language. However, besides the sources and VUnit, a `HDL compiler/simulator `_ is required in order to run the tests. Due to performance, all the HDL simulators are written in compiled languages, which makes the releases platform specific. I.e., each simulator needs to be specifically compiled for a given architecture and operating system. This might represent a burden for the adoption of continuous integration in hardware development teams, as it falls into the category of dev ops. - -Nevertheless, thanks to the striking research about portable development environment solutions in the last decade, there are a bunch of alternatives to ease the path. The 'classic' approach is to use virtual machines with tools -such as `VirtualBox `_, `QEMU `_ or `VMware `_. This is still an option, but for most use cases sharing complete system images is overkill. Here, `containerization or operating-system-level virtualization `_ comes into the game. Without going into technical details, containers are a kind of lightweight virtual machines, and the most known product that uses such a technology is `Docker `_. Indeed, products such as `Vagrant `_ are meant to simplify the usage of virtual machines and/or containers by providing a common (black) box approach. In the end, there are enough open/non-open and free/non-free solutions for each user/company to choose the one that best fits their needs. From the hardware designer point-of-view, we 'just' need a box (no matter the exact underlying technology) that includes VUnit and a simulator. - -Fortunately, contributors of project `GHDL `_ provide ready-to-use docker images at `hub.docker.com/u/ghdl/dashboard `_. Some of these include not only GHDL but also VUnit. Precisely, ``ghdl/vunit:{mcode|llvm|gcc}`` are images based on Debian Buster image with GHDL built from the latest commit of the master branch, and the latest release of VUnit installed through ``pip``. ``ghdl/vunit:{mcode|llvm|gcc}-master`` images include the latest commit of VUnit from the master branch. - -As a result, the burden for the adoption of continuous integration for VUnit users is reduced to using docker; which is available in GNU/Linux, FreeBSD, Windows and macOS, and is supported in most cloud services (`GitHub Actions `_, `Travis CI `_, `AWS `_, `Codefresh `_, etc.) or CI frameworks (`Jenkins `_, `Drone `_, `GitLab Runner `_, etc.). - -For example, script :vunit_file:`examples/vhdl/docker_runall.sh ` shows how to run all the VHDL examples in any x86 platform: - -.. code-block:: bash - - docker run --rm -t \ - -v /$(pwd)://work \ - -w //work \ - ghdl/vunit:llvm-master sh -c ' \ - VUNIT_SIMULATOR=ghdl; \ - for f in $(find ./ -name 'run.py'); do python3 $f; done \ - ' - -where: - -* ``run``: create and start a container. -* ``--rm``: automatically remove the container when it exits. -* ``-t``: allocate a pseudo-TTY, to get the stdout of the container forwarded. -* ``-v``: bind mount a volume, to share a folder between the host and the container. In this example the current path in the host is used (``$(pwd)``), and it is bind to `/work` inside the container. Note that both paths must be absolute. -* ``-w``: sets the working directory inside the container, i.e. where the commands we provide as arguments are executed. -* ``ghdl/vunit:llvm-master``: the image we want to create a container from. -* ``sh -c``: the command that is executed as soon as the container is created. - -Note that: - -* The arguments to ``sh -c`` are the same commands that you would execute locally, shall all the dependencies be installed in the host: - - .. code-block:: bash - - VUNIT_SIMULATOR=ghdl - for f in $(find ./ -name 'run.py'); do python3 $f; done - -* The leading slashes in ``/$(pwd)`` and ``//work`` are only required for the paths to be properly handled in MINGW shells, and are ignored in other shells. See `docker/for-win#1509 `_. - -Final comments: - -* All the (automated) flow to generate ``ghdl`` docker images is open source and public, in order to let any user learn and extend it. You can easily replicate it to build you own images with other development dependencies you use. - * There are ready-to-use images available with additional tools on top of GHDL and VUnit. For example, ``ghdl/ext`` includes `GTKWave `_. -* Although the licenses of most commercial simulators do not allow to share ready-to-use docker images, it is straightforward to mimic the process. - * If the installation of a tool needs to be executed with a GUI, a slightly different approach is required. See `Propietary applications inside a docker container `_ -* Both GHDL and VUnit are free software. Docker is almost fully open source, but this depends on the host platform. See `Is Docker still free and open source? `_. - -Further info: - -* `What is a container `_ -* `What is docker `_ -* `docs.docker.com/engine/reference `_ - * `run `_ - * `commandline/run `_ -* Docker offers two variants Community Edition (CE) and Enterprise Edition (EE). Any of them can be used. Moreover, part of Docker is being split to `Moby project `_. - * `Announcing Docker Enterprise Edition `_ - * `Introducing Moby Project: a new open-source project to advance the software containerization movement `_ -* If you don't want or cannot install docker, you can still use it online. `Play with Docker `_ (PWD) *"is a Docker playground which allows users to run Docker commands in a matter of seconds. It gives the experience of having a free Alpine Linux Virtual Machine in browser, where you can build and run Docker containers and even create clusters"*. \ No newline at end of file diff --git a/docs/ci/container.rst b/docs/ci/container.rst new file mode 100644 index 000000000..2b8d0e716 --- /dev/null +++ b/docs/ci/container.rst @@ -0,0 +1,115 @@ +.. _continuous_integration:container: + +Containers and/or Virtual Machines +################################## + +The 'classic' approach to virtual machines is through tools such as `VirtualBox `_, +`QEMU `_ or `VMware `_. However, for most use cases sharing complete system +images is overkill. Here, `containerization or operating-system-level virtualization `_ +comes into the game. Without going into technical details, containers are a kind of lightweight virtual machines, and the +most known product that uses the technology is `Docker `_. + +.. HINT:: Products such as `Vagrant `_ are meant to simplify the usage of virtual machines and/or + containers by providing a common (black) box approach. In the end, there are enough open/non-open and free/non-free + solutions for each user/company to choose the one that best fits their needs. From the hardware designer point-of-view, + we 'just' need a box (no matter the exact underlying technology) that includes VUnit and a simulator. + +Contributors of project `GHDL `_ provide ready-to-use docker images at `hub.docker.com/u/ghdl/dashboard `_. +Some of these include not only GHDL but also VUnit. Precisely, ``ghdl/vunit:{mcode|llvm|gcc}`` are images based on Debian +Buster image, with GHDL built from the latest commit of the master branch, and the latest release of VUnit installed through +``pip``. ``ghdl/vunit:{mcode|llvm|gcc}-master`` images include the latest commit of VUnit from the master branch. There are +other ready-to-use images with additional tools. For example, ``ghdl/ext`` includes `GTKWave `_. + +As a result, the burden for the adoption of continuous integration for VUnit users is significantly reduced by using +containers; which are available in GNU/Linux, FreeBSD, Windows and macOS, and are supported on most cloud services +(`GitHub Actions `_, `Travis CI `_, +`AWS `_, `Codefresh `_, etc.) or CI frameworks +(`Jenkins `_, `Drone `_, `GitLab Runner `_, etc.). + +For example, script :vunit_file:`examples/vhdl/docker_runall.sh ` shows how to run all the +:ref:`VHDL examples ` on any x86 platform: + +.. code-block:: bash + + docker run --rm \ + -v /$(pwd)://work \ + -w //work \ + ghdl/vunit:llvm-master sh -c ' \ + VUNIT_SIMULATOR=ghdl; \ + for f in $(find ./ -name 'run.py'); do python3 $f; done \ + ' + +where: + +* ``run``: create and start a container. +* ``--rm``: automatically remove the container when it exits. +* ``-v``: bind mount a volume, to share a folder between the host and the container. In this example the current path in the + host is used (``$(pwd)``), and it is bind to `/work` inside the container. Note that both paths must be absolute. +* ``-w``: sets the working directory inside the container, i.e. where the commands we provide as arguments are executed. +* ``ghdl/vunit:llvm-master``: the image we want to create a container from. +* ``sh -c``: the command that is executed as soon as the container is created. + +Note that, the arguments to ``sh -c`` are the same commands that you would execute locally, shall all the dependencies be +installed on the host: + +.. code-block:: bash + + VUNIT_SIMULATOR=ghdl + for f in $(find ./ -name 'run.py'); do python3 $f; done + +.. HINT:: The leading slashes in ``/$(pwd)`` and ``//work`` are only required for the paths to be properly handled in MINGW64 + shells, and are ignored in other shells. See `docker/for-win#1509 `_. + +.. NOTE:: Docker offers two variants Community Edition (CE) and Enterprise Edition (EE). Any of them can be used. Moreover, + part of Docker is being split to `Moby project `_. + + * `Announcing Docker Enterprise Edition `_ + * `Introducing Moby Project: a new open-source project to advance the software containerization movement `_ + +.. HINT:: If you don't want or cannot install docker, you can still use it online. `Play with Docker `_ + (PWD) *"is a Docker playground which allows users to run Docker commands in a matter of seconds. It provides a free Alpine + Linux Virtual Machine in browser, where you can build and run Docker containers and even create clusters"*. + +.. NOTE:: Both GHDL and VUnit are free software. Docker is almost fully open source, but it depends on the host platform. + See `Is Docker still free and open source? `_. + +.. NOTE:: + + * `What is a container `_ + * `What is docker `_ + * `docs.docker.com/engine/reference `_ + * `run `_ + * `commandline/run `_ + +.. _continuous_integration:container:customizing: + +Customizing existing images +*************************** + +All the (automated) flow to generate images in `ghdl/docker `_ is open source and public. +Hence, any user can learn and extend it. However, many users will want to just add a few dependencies to an existing image, +without the hassle of handling credentials to access `hub.docker.com `_. That can be achieved with +a short ``Dockerfile``. For instance: + +.. code-block:: Dockerfile + + FROM ghdl/vunit:llvm-master + + RUN pip install pytest matplotlib + +Then, in the CI workflow: + +.. code-block:: bash + + docker build -t imageName - < path/to/Dockerfile + docker run ... imageName ... + +Packaging non-FLOSS simulators +****************************** + +Although the licenses of most commercial simulators do not allow to share ready-to-use docker images, it is straightforward +to mimic the process for in-house usage. Unlike GHDL, many commercial simulators provide a GUI and/or require a GUI for executing +the installer. In those contexts, `mviereck/x11docker `_ and +`mviereck/runx `_ can be useful. +See `mviereck/x11docker#201 `_. + diff --git a/docs/ci/intro.rst b/docs/ci/intro.rst new file mode 100644 index 000000000..7b4d68425 --- /dev/null +++ b/docs/ci/intro.rst @@ -0,0 +1,28 @@ +.. _continuous_integration: + +Introduction +############ + +Because VUnit features the functionality needed to realize continuous and automated testing of HDL code, it is a very valuable +resource in Continuous Integration (CI) environments. Once a project ``run.py`` has been setup, tests can be run in a headless +environment. Optionally, a standardized `Xunit `_ style output +can be saved to a file; which allows dynamic interpretation of results and avoids custom (and error-prone) parsing of the logs. +After tests have finished running, the ``test_output.xml`` file can be parsed using standard xUnit test parsers such as +`Jenkins xUnit Plugin `_. + +.. code-block:: console + :caption: Execute VUnit tests on CI server with XML output + + python run.py --xunit-xml test_output.xml + +Furthermore, VUnit can be easily executed on many different platforms (either operating systems or architectures), because it +is written in Python, which is an interpreted language. However, besides own HDL sources and VUnit, a +`HDL compiler/simulator `_ is required in order to run the tests. Since +most HDL simulators are written using compiled languages, releases are typically platform specific. Hence, installation +and setup might be non trivial. This is specially so with non-free tools that require license servers to be configured. This +might represent a burden for the adoption of continuous integration in hardware development teams, as it falls into the +category of dev ops. + +Nevertheless, thanks to free and public CI/CD services, along with the striking research about portable development +environment solutions, there are a bunch of alternatives to ease the path. In this section, solutions are grouped in three +categories: :ref:`continuous_integration:script`, :ref:`continuous_integration:container` and :ref:`continuous_integration:manual`. diff --git a/docs/ci/manual.rst b/docs/ci/manual.rst new file mode 100644 index 000000000..c164aaa3e --- /dev/null +++ b/docs/ci/manual.rst @@ -0,0 +1,29 @@ +.. _continuous_integration:manual: + +Manual setup +############ + +Since CI/CD services typically provide full-featured Ubuntu/Debian, Windows and/or macOS environments, regular installation +procedures can be used (see :ref:`installing`). That is, an HDL simulator and Python need to be installed by any means. + +.. IMPORTANT:: When installing the development version of VUnit, remember to install the dependencies (see :vunit_file:`requirements.txt`). + +Due to the single supported open source simulator being GHDL, most users on GitHub are likely to install it along with VUnit. +There are six possible procedures to setup GHDL: + +* `ghdl.rtfd.io: Releases and sources `_: + + * Use a package manager, such as ``apt`` or ``pacman``. + + * Get and extract a tarball/zipfile from the *latest stable* release: `github.com/ghdl/ghdl/releases/latest `_. + + * Get and extract a tarball/zipfile from the *nightly* pre-release: `github.com/ghdl/ghdl/releases/nightly `_. + + * (On GitHub Actions only) Use Action `ghdl/setup-ghdl-ci `_. + +* Use one of the Docker/OCI images provided in `ghdl/docker `_. + +* Build it from sources: `ghdl.rtfd.io: Building GHDL from Sources `_. + +.. HINT:: Since building GHDL each time is time-consuming, it is recommented to use pre-built tarballs/zipfiles or Docker/OCI + images. Images/containers usually provide the fastest startup time, because all the dependencies can be pre-installed already. diff --git a/docs/ci/script.rst b/docs/ci/script.rst new file mode 100644 index 000000000..f41e12949 --- /dev/null +++ b/docs/ci/script.rst @@ -0,0 +1,116 @@ +.. _continuous_integration:script: + +Setup/configuration scripts +########################### + +Keeping testing environments up to date with rapidly evolving tools can be time-consuming and can lead to frustration. In this +section, scripts and configuration tools to automate the setup and/or configuration of simulators and VUnit are presented. + +.. _continuous_integration:gha: + +GitHub Actions +************** + +GitHub's CI/CD service is named `GitHub Actions `_ (GHA). It allows to create automated +*workflows* for your repositories, which are defined through `YAML `_ files. Workflows +can be triggered by any event, such as push, issue creation or publication of releases. + +GHA provides virtual machines with GNU/Linux (Ubuntu), Windows or macOS. Hence, it is possible to write the steps/tasks using +the default shells/terminals (*bash*, *powershell*, etc.), as in any other CI/CD service. By the same token, any language can +be used (Python, JavaScript, Ruby, Go, Rust, etc.). However, there are also predefined tasks named *Actions*. Those are +written either in *JavaScript/TypeScript* (for any OS) or packaged in a *Container Action* (GNU/Linux only). Some Actions are +provided by GitHub (see `github.com/actions `_), and some are published in the +`GitHub marketplace `_. Nevertheless, any GitHub repository can contain *Actions*. + +Hence, the recommended procedure to create workflows is to pick and reuse existing Actions +(see `docs.github.com/actions `_). + +Anyway, when further customization is required, the procedures explained in sections *Containers and/or Virtual Machines* and/or +*Manual setup* can also be used in GHA workflows. + +.. NOTE:: Implementation differences between *JavaScript* and *Container* actions can be found at + `docs.github.com/actions/creating-actions `_. + +.. NOTE:: GitHub Actions is free (as in *free beer*) for public (open source) repositories. For private repositories, + 2000-3000 minutes are included per month. See section "*Simple, pay-as-you-go pricing*" at `GitHub Actions `_. + +.. _continuous_integration:gha:vunit: + +VUnit Action +============ + +`VUnit Action `_ is a reusable *Action*, which is published in the marketplace +(`github.com/marketplace/actions/vunit-action `_). It helps you build a +workflow for running your HDL testbenches, and then present the results. + +To use *VUnit Action* for your project, you need to create a `YAML `_ file (``some_name.yml``) +and place that in a directory named ``.github\workflows`` (located directly under your project root), in the default branch +of your repository. The YAML file should contain, at least, the following piece of code. + +.. code-block:: yaml + + name: VUnit Tests + + on: + push: + pull_request: + + jobs: + + test: + runs-on: ubuntu-latest + steps: + + - uses: actions/checkout@v2 + + - uses: VUnit/vunit_action@v0.1.0 + +.. IMPORTANT:: Currently, VUnit Action is implemented as a *Container Action*. As a result, tests are executed in a Docker/OCI + container, regardless of the CI/CD *host* being `ubuntu-latest`. + +Whenever someone pushes code to the project or makes a pull request, this workflow is triggered. First, the code is checked +out using the `checkout action `_. Then, the *VUnit Action* is triggered, +to run the ``run.py`` script located in the root of your repository. If the VUnit run script is located elsewhere, you specify +it in the YAML file: + +.. code-block:: yaml + + - uses: VUnit/vunit_action@v0.1.0 + with: + run_file: path/to/vunit_run_script.py + +To build trust with the user community by clearly showing that you have tests up and running, we recommend that you add a +badge/shield to the README of your project. It will show the latest status of you tests: + +.. code-block:: md + + [![](https://github.com///workflows/VUnit%20Tests/badge.svg)](https://github.com///actions) + +.. HINT:: `shields.io `_ is another badge/shield provider which allows customizing some characteristics, + such as shape, color, labels, icons, etc. When combining shields corresponding to different services, it is suggested to + use *shields.io* in order to get an homogeneous result. Moreover, *shields.io* provides ready-to-copy snippets for multiple + languages (Markdown, reStructuredText, HTML, etc.). + +Clicking the badge/shield will take you to a list of workflow runs, and then further to the results of those runs: + +.. figure:: ../_static/gha_flow.png + :align: center + :alt: Presenting GHA test results. + + Presenting GHA test results. + +Self-hosted runners +=================== + +By default, GitHub Actions workflows are executed on GitHub's servers. However, it is possible to setup so-called +*self-hosted runners*. Those are machines owned by users/developers/organizations/companies, where a client service is +executed. Then, users can assign specific workflows to be executed on self-hosted runners. See `docs.github.com/actions/hosting-your-own-runners `_. + +As explained in `docs.github.com/actions/hosting-your-own-runners: Self-hosted runner security with public repositories `_, +it is strongly discouraged to use self-hosted runners with public repositorites, in order to avoid PRs executing potentially +dangerous code. That is mainly because self-hosted runners have access to the tools available on the host. Yet, for that same +reason, using self-hosted runners is a suitable solution for having CI with non-FLOSS simulators. + +.. IMPORTANT:: VUnit is currently tested in CI with GHDL only. Specific companies provide a limited set of licenses for + non-FLOSS simulators, which some developers can use locally. Ideally, companies interested in supporting VUnit would + provide a machine to serve a self-hosted runner in a private fork. If you want to contribute, get in touch! diff --git a/docs/ci/usecases.rst b/docs/ci/usecases.rst new file mode 100644 index 000000000..78dc547ce --- /dev/null +++ b/docs/ci/usecases.rst @@ -0,0 +1,66 @@ +.. _continuous_integration:usecases: + +Practical use cases +################### + +Workflow `tests.yml `_ from repo `VUnit/tdd-intro `_ +showcases five procedures to setup continuous integration in GitHub Actions, using GHDL and VUnit as a regression framework. +The entrypoint to all the jobs is the same `pytest `_ script (`test.py `_), +thus, all jobs are equivalent solutions. Tests called through pytest can be defined in any language: VUnit run.py scripts, +bash/shell scripts, makefiles, etc. + +It is suggested for new users to clone/fork this template repository, and then remove the jobs they don't want to use. Since +all are equivalent, using a single job is enough to have HDL designs tested. However, it might be useful to have designs +tested on different platforms. + +lin-vunit +********* + +Uses *Docker Action* `VUnit/vunit_action `_, based on image ``ghdl/vunit:llvm`` (see +`ghdl/docker: VUnit `_). It takes a +single optional argument: the path to the ``run.py``. See `VUnit/vunit_action: README.md `_ +for further info. + +This is the most straightforward solution, and the one with fastest startup. + +lin-docker +********** + +Docker based job, which can be used in any CI system. An (optional) `Dockerfile `_ is used to add some packages on top of image ``ghdl/vunit:llvm`` (see :ref:`continuous_integration:container:customizing`). However, the same procedure can be used with any other image. + +This is equivalent to *lin-vunit*, but it is slightly more verbose. + +lin-setup +********* + +Uses *JavaScript Action* `ghdl/setup-ghdl-ci `_ to install GHDL on the Ubuntu host/VM. +Then, additional system packages and Python packages are installed explicitly. + +Compared to previous approaches, in this case runtime dependencies are not pre-installed. As a result, startup is slightly +slower. + +win-setup +********* + +Uses Actions `ghdl/setup-ghdl-ci `_ and `msys2/setup-msys2 `_ +to install latest *nightly* GHDL, other MSYS2 packages and Python packages in a *clean* MINGW64 environment. + +This is the recommended approach to run tests on Windows. Action setup-msys2 caches installed packages/dependencies +automatically. + +win-stable +********** + +The *traditional* procedure of downloading a tarball/zipfile from GHDL's latest *stable* release. Additional Python packages +are installed explicitly. + +This is more verbose than the previous approach, but it's currently the only solution to use latest *stable* GHDL without +building it from sources. + +Repositories using VUnit for CI +******************************* + +This is a non-exhaustive list of projects where VUnit is used for testing HDL designs: + +* `VUnit/vunit `_ +* `ghdl/ghdl-cosim `_ diff --git a/docs/cli.rst b/docs/cli.rst index 944caa3cc..c326a7739 100644 --- a/docs/cli.rst +++ b/docs/cli.rst @@ -2,6 +2,7 @@ Command Line Interface ====================== + A :class:`VUnit ` object can be created from command line arguments by using the :meth:`from_argv ` method effectively creating a custom @@ -194,10 +195,6 @@ Test Output Path Length Environment Variables output path margin on Windows. By default the test output path is shortened to allow a 100 character margin. -.. _continuous_integration: - -.. include:: ci.rst - .. _json_export: JSON Export diff --git a/docs/index.rst b/docs/index.rst index ebfc158b9..f5d25eb0d 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -48,6 +48,16 @@ often"* approach through automation. :ref:`Read more ` vhdl_libraries examples +.. toctree:: + :caption: Continuous Integration + :hidden: + + ci/intro + ci/script + ci/container + ci/manual + ci/usecases + .. toctree:: :caption: Appendix :hidden: From 5d80503d4b2510feb29fec0d7511f0a37f27d6fe Mon Sep 17 00:00:00 2001 From: Lars Asplund Date: Wed, 12 Aug 2020 23:04:25 +0200 Subject: [PATCH 65/79] Added blog post on continuous integration --- ..._with_vunit_action_in_10_lines_of_code.rst | 119 ++++++++++++++++++ docs/blog/img/gha_flow.png | Bin 0 -> 161106 bytes .../blog/img/repositories_providing_tests.png | Bin 0 -> 24852 bytes docs/blog/img/top_image.png | Bin 0 -> 17623 bytes 4 files changed, 119 insertions(+) create mode 100644 docs/blog/2020-08-12_continuous_integration_with_vunit_action_in_10_lines_of_code.rst create mode 100644 docs/blog/img/gha_flow.png create mode 100644 docs/blog/img/repositories_providing_tests.png create mode 100644 docs/blog/img/top_image.png diff --git a/docs/blog/2020-08-12_continuous_integration_with_vunit_action_in_10_lines_of_code.rst b/docs/blog/2020-08-12_continuous_integration_with_vunit_action_in_10_lines_of_code.rst new file mode 100644 index 000000000..1332eead9 --- /dev/null +++ b/docs/blog/2020-08-12_continuous_integration_with_vunit_action_in_10_lines_of_code.rst @@ -0,0 +1,119 @@ +:tags: VUnit +:author: lasplund +:excerpt: 1 + +Continuous Integration With VUnit Action in 10 Lines of Code +============================================================ + +.. figure:: img/top_image.png + :align: center + +The other week, `semiengineering.com `__ published an +`article `__ on open-source verification. +It had one, rather obvious, conclusion. + + *Verification is required to answer the question, 'Do you trust the piece of hardware you received?'* + + -- Neil Hand, director of marketing for design verification technology at Mentor, a Siemens Business + +Despite being obvious, IP providers often make it hard to gain that trust. + + *When you buy IP, you usually get a very simple verification environment. This enables you to run a + few demo tests or check configurations. You do not usually get the entire verification environment.* + + -- Olivera Stojanovic, senior verification manager for Vtool + +This is not unique to commercial IPs. Our `study `__ +of VHDL projects on GitHub shows that less than half of all projects provide tests at all, and the trend +is declining (see Figure 1). + +.. figure:: img/repositories_providing_tests.png + :align: center + + Figure 1. Repositories providing tests. + +So, what are the reasons for not providing tests with the IPs? + + *With complex IPs, they don’t want to provide you with the verification environment, which is too + complicated and potentially may provide insights that they might want to keep from you.* + + -- Olivera Stojanovic, senior verification manager for Vtool + +Keeping secrets is not a reason for not providing tests with public projects on GitHub, as everything is +open/public. However, it can be complex to create a user-friendly online verification environment that +clearly shows what has been tested and the status of those tests. Thanks to +`VUnit Action `__ this is now much simpler, as it +provides a continuous integration flow with just 10 lines of code. + +If you're not familiar with VUnit, the following reading will set you up for the VUnit Action described +in the next section. + +1. `Installing VUnit in 1 minute `__ +2. `Compiling your project in 1 minute `__ +3. `Fully automating your testbench with 5 lines of code `__ + +VUnit Action +------------ + +GitHub’s continuous integration/continuous deployment (CI/CD) service is named +`GitHub Actions `__ (GHA). It allows to create automated workflows +for your repositories, which are defined through `YAML `__ files. +Workflows can be triggered by any event, such as push, issue creation or publication of releases. + +GHA provides virtual machines with GNU/Linux (Ubuntu), Windows or macOS. Hence, it is possible to create +a custom CI/CD workflow using bash, powershell, Python etc. However, there are also predefined workflow +tasks named Actions. Some Actions are provided by GitHub +(see `github.com/actions `__), and some are published in the +`GitHub marketplace `__. Nevertheless, any GitHub repository +can contain Actions. + +`VUnit Action `__ is a reusable Action, built on the +`GHDL simulator `__, and available in the marketplace +(`github.com/marketplace/actions/vunit-action `__). +It helps you build a workflow for running your HDL testbenches, and then present the results. + +To use VUnit Action for your project, you need to create a `YAML `__ file +(`some_name.yml`) and place that in a directory named `.github\\workflows` (located directly under your project root), +in the default branch of your repository. The YAML file should contain, at least, the following piece of code. + +.. code-block:: yaml + + name: VUnit Tests + on: + push: + pull_request: + jobs: + test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: VUnit/vunit_action@v0.1.0 + +Whenever someone pushes code to the project or makes a pull request, this workflow is triggered. First, the code +is checked out using the `checkout action `__. Then, the VUnit +Action is triggered, to run the `run.py` script located in the root of your repository. If the VUnit run script is +located elsewhere, you specify it in the YAML file: + +.. code-block:: yaml + + - uses: VUnit/vunit_action@v0.1.0 + with: + run_file: path/to/vunit_run_script.py + +To build trust with the user community by clearly showing that you have tests up and running, we recommend that +you add a badge/shield to the `README.md` of your project. It will show the latest status of you tests: + +.. code-block:: + + [![](https://github.com///workflows/VUnit%20Tests/badge.svg)](https://github.com///actions) + +Clicking the badge/shield will take you to a list of workflow runs, and then further to the results of those runs: + +.. figure:: img/gha_flow.png + :align: center + + Figure 2. Presenting Test Results. + +The simple solution presented here will get you started and you can read more about the details in our +`documentation `__. Once you have that working there are a +number of extra steps you can take and that will be the topic of the next post on continuous integration. diff --git a/docs/blog/img/gha_flow.png b/docs/blog/img/gha_flow.png new file mode 100644 index 0000000000000000000000000000000000000000..de5de856bea8d388f0c734a38d43cf9b212baf22 GIT binary patch literal 161106 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 z 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 literal 0 HcmV?d00001 diff --git a/docs/blog/img/repositories_providing_tests.png b/docs/blog/img/repositories_providing_tests.png new file mode 100644 index 0000000000000000000000000000000000000000..919e95e585ad29971ef07e06acb1af45e9fc2b1e GIT binary patch literal 24852 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 literal 0 HcmV?d00001 From 744098a796db620ee3539d48460547d0a91142b5 Mon Sep 17 00:00:00 2001 From: eine Date: Wed, 12 Aug 2020 23:34:25 +0200 Subject: [PATCH 66/79] docs: update latest blog post --- docs/_static/gha_flow.png | Bin 161106 -> 0 bytes ..._with_vunit_action_in_10_lines_of_code.rst} | 2 +- docs/blog/img/gha_top_image.png | Bin 0 -> 23316 bytes docs/blog/img/top_image.png | Bin 17623 -> 0 bytes docs/ci/script.rst | 2 +- 5 files changed, 2 insertions(+), 2 deletions(-) delete mode 100644 docs/_static/gha_flow.png rename docs/blog/{2020-08-12_continuous_integration_with_vunit_action_in_10_lines_of_code.rst => 2020_08_12_continuous_integration_with_vunit_action_in_10_lines_of_code.rst} (99%) create mode 100644 docs/blog/img/gha_top_image.png delete mode 100644 docs/blog/img/top_image.png diff --git a/docs/_static/gha_flow.png b/docs/_static/gha_flow.png deleted file mode 100644 index de5de856bea8d388f0c734a38d43cf9b212baf22..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 161106 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 z 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 diff --git a/docs/blog/2020-08-12_continuous_integration_with_vunit_action_in_10_lines_of_code.rst b/docs/blog/2020_08_12_continuous_integration_with_vunit_action_in_10_lines_of_code.rst similarity index 99% rename from docs/blog/2020-08-12_continuous_integration_with_vunit_action_in_10_lines_of_code.rst rename to docs/blog/2020_08_12_continuous_integration_with_vunit_action_in_10_lines_of_code.rst index 1332eead9..89c2089cb 100644 --- a/docs/blog/2020-08-12_continuous_integration_with_vunit_action_in_10_lines_of_code.rst +++ b/docs/blog/2020_08_12_continuous_integration_with_vunit_action_in_10_lines_of_code.rst @@ -5,7 +5,7 @@ Continuous Integration With VUnit Action in 10 Lines of Code ============================================================ -.. figure:: img/top_image.png +.. figure:: img/gha_top_image.png :align: center The other week, `semiengineering.com `__ published an diff --git a/docs/blog/img/gha_top_image.png b/docs/blog/img/gha_top_image.png new file mode 100644 index 0000000000000000000000000000000000000000..d54dd8d35e7186bfe6bd49dab21c67cbc2249e64 GIT binary patch literal 23316 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 IM6N<$g4rVnr~m)} literal 0 HcmV?d00001 diff --git a/docs/blog/img/top_image.png b/docs/blog/img/top_image.png deleted file mode 100644 index d62b16a29a7967dc29d6d91c80d86bc45c391fa7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 17623 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 diff --git a/docs/ci/script.rst b/docs/ci/script.rst index f41e12949..db100838a 100644 --- a/docs/ci/script.rst +++ b/docs/ci/script.rst @@ -93,7 +93,7 @@ badge/shield to the README of your project. It will show the latest status of yo Clicking the badge/shield will take you to a list of workflow runs, and then further to the results of those runs: -.. figure:: ../_static/gha_flow.png +.. figure:: ../blog/img/gha_flow.png :align: center :alt: Presenting GHA test results. From f0d861e466eaff965357e9cbb45858e7e9a5c047 Mon Sep 17 00:00:00 2001 From: eine Date: Sat, 15 Aug 2020 05:30:51 +0200 Subject: [PATCH 67/79] test/lint: fix function name, is test_mypy --- tests/lint/test_mypy.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/lint/test_mypy.py b/tests/lint/test_mypy.py index a64b22f28..d98cf2f51 100644 --- a/tests/lint/test_mypy.py +++ b/tests/lint/test_mypy.py @@ -19,5 +19,5 @@ class TestMyPy(unittest.TestCase): """ @staticmethod - def test_pycodestyle(): + def test_mypy(): check_call([sys.executable, "-m", "mypy", "vunit"]) From 94faae79b549b622927cd41c6056c541ff0ec775 Mon Sep 17 00:00:00 2001 From: eine Date: Wed, 19 Aug 2020 20:48:30 +0200 Subject: [PATCH 68/79] ci: reduce 'many_keys' to avoid failure with latest GHDL --- vunit/vhdl/data_types/test/tb_dict.vhd | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/vunit/vhdl/data_types/test/tb_dict.vhd b/vunit/vhdl/data_types/test/tb_dict.vhd index cef91d6c2..a038c4885 100644 --- a/vunit/vhdl/data_types/test/tb_dict.vhd +++ b/vunit/vhdl/data_types/test/tb_dict.vhd @@ -20,7 +20,7 @@ begin main : process variable dict : dict_t; - constant many_keys : natural := 2**16; + constant many_keys : natural := 2**13; constant long_key : string := "long--------------------------------------------------------key"; begin test_runner_setup(runner, runner_cfg); @@ -94,6 +94,7 @@ begin remove(dict, long_key & integer'image(i)); check_equal(num_keys(dict), i-1); end loop; + end loop; deallocate(dict); From 245c4ea191fa4d0bce0433f23b249e33f0494f12 Mon Sep 17 00:00:00 2001 From: eine Date: Wed, 19 Aug 2020 20:49:40 +0200 Subject: [PATCH 69/79] ci: revert 7c3f930be75bb34b3f32ebbaee97786801cbadb4 --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index dfe9d004e..4ed847cb6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -18,7 +18,7 @@ passenv=ALDEC_LICENSE_FILE deps= fmt: black - pytest==5.4.3 # work around actions/virtual-environments#1381 + pytest lint: pycodestyle lint: pylint lint: mypy From 7e3502f0551ef3132edce12831498ae383f2dfa7 Mon Sep 17 00:00:00 2001 From: Guy Eschemann Date: Wed, 19 Aug 2020 21:55:02 +0200 Subject: [PATCH 70/79] fix riviera pro coverage merge error (#675) Co-authored-by: Guy Eschemann --- vunit/sim_if/rivierapro.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vunit/sim_if/rivierapro.py b/vunit/sim_if/rivierapro.py index fa6fe222b..8561c58bd 100644 --- a/vunit/sim_if/rivierapro.py +++ b/vunit/sim_if/rivierapro.py @@ -429,7 +429,7 @@ def merge_coverage(self, file_name, args=None): str(Path(self._prefix) / "vsim"), "-c", "-do", - "source %s; quit;" % merge_script_name.replace("\\", "/"), + "source {%s}; quit;" % merge_script_name.replace("\\", "/"), ] print("Merging coverage files into %s..." % file_name) From 42100ed98b06cffd5ae25cc4e828c57f031e5f69 Mon Sep 17 00:00:00 2001 From: Lars Asplund Date: Fri, 28 Aug 2020 12:59:28 +0200 Subject: [PATCH 71/79] Added null AXI stream master and slave constants. --- .../src/axi_stream_pkg.vhd | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/vunit/vhdl/verification_components/src/axi_stream_pkg.vhd b/vunit/vhdl/verification_components/src/axi_stream_pkg.vhd index e56d298f9..b1944d952 100644 --- a/vunit/vhdl/verification_components/src/axi_stream_pkg.vhd +++ b/vunit/vhdl/verification_components/src/axi_stream_pkg.vhd @@ -117,6 +117,18 @@ package axi_stream_pkg is p_protocol_checker : axi_stream_protocol_checker_t; end record; + constant null_axi_stream_master : axi_stream_master_t := ( + p_actor => null_actor, + p_data_length => 0, + p_id_length => 0, + p_dest_length => 0, + p_user_length => 0, + p_stall_config => null_stall_config, + p_logger => null_logger, + p_monitor => null_axi_stream_monitor, + p_protocol_checker => null_axi_stream_protocol_checker + ); + type axi_stream_slave_t is record p_actor : actor_t; p_data_length : natural; @@ -129,6 +141,18 @@ package axi_stream_pkg is p_protocol_checker : axi_stream_protocol_checker_t; end record; + constant null_axi_stream_slave : axi_stream_slave_t := ( + p_actor => null_actor, + p_data_length => 0, + p_id_length => 0, + p_dest_length => 0, + p_user_length => 0, + p_stall_config => null_stall_config, + p_logger => null_logger, + p_monitor => null_axi_stream_monitor, + p_protocol_checker => null_axi_stream_protocol_checker + ); + constant axi_stream_logger : logger_t := get_logger("vunit_lib:axi_stream_pkg"); constant axi_stream_checker : checker_t := new_checker(axi_stream_logger); From 710f68d51f1c2a82db2b6cf589b3e28a1f3194f0 Mon Sep 17 00:00:00 2001 From: Lars Asplund Date: Fri, 28 Aug 2020 13:40:30 +0200 Subject: [PATCH 72/79] Fixed lint errors --- vunit/vhdl/verification_components/src/axi_stream_pkg.vhd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vunit/vhdl/verification_components/src/axi_stream_pkg.vhd b/vunit/vhdl/verification_components/src/axi_stream_pkg.vhd index b1944d952..dfd8d30e2 100644 --- a/vunit/vhdl/verification_components/src/axi_stream_pkg.vhd +++ b/vunit/vhdl/verification_components/src/axi_stream_pkg.vhd @@ -152,7 +152,7 @@ package axi_stream_pkg is p_monitor => null_axi_stream_monitor, p_protocol_checker => null_axi_stream_protocol_checker ); - + constant axi_stream_logger : logger_t := get_logger("vunit_lib:axi_stream_pkg"); constant axi_stream_checker : checker_t := new_checker(axi_stream_logger); From 00cfd5ec9b1f0a4628a780907f1e02f7f2eba290 Mon Sep 17 00:00:00 2001 From: eine Date: Fri, 28 Aug 2020 14:08:05 +0200 Subject: [PATCH 73/79] ci: run black 20.8b1 --- tests/lint/test_license.py | 3 +- tests/unit/test_project.py | 4 +-- tests/unit/test_ui.py | 3 +- vunit/check_preprocessor.py | 15 ++++------ vunit/com/codec_vhdl_array_type.py | 28 +++++++++++------- vunit/com/codec_vhdl_package.py | 44 +++++++++++++++++----------- vunit/parsing/verilog/preprocess.py | 8 ++--- vunit/sim_if/vsim_simulator_mixin.py | 2 +- 8 files changed, 62 insertions(+), 45 deletions(-) diff --git a/tests/lint/test_license.py b/tests/lint/test_license.py index 47656010d..548d965f0 100644 --- a/tests/lint/test_license.py +++ b/tests/lint/test_license.py @@ -144,7 +144,8 @@ def find_licensed_files(): if str(ROOT / ".tox") in root: continue if is_prefix_of( - (VHDL_PATH / "osvvm").resolve(), (Path(root) / file_name).resolve(), + (VHDL_PATH / "osvvm").resolve(), + (Path(root) / file_name).resolve(), ): continue if is_prefix_of( diff --git a/tests/unit/test_project.py b/tests/unit/test_project.py index 3f4691bb8..58fdf878a 100644 --- a/tests/unit/test_project.py +++ b/tests/unit/test_project.py @@ -1732,8 +1732,8 @@ def test_dependencies_on_separated_architecture(self): def test_dependencies_on_verilog_component(self): """ - Create a projected containing an verilog file separated. - Dependency should involve it. + Create a projected containing an verilog file separated. + Dependency should involve it. """ self.project = Project() self.project.add_library("lib", "work_path") diff --git a/tests/unit/test_ui.py b/tests/unit/test_ui.py index 56c657e21..f7644a934 100644 --- a/tests/unit/test_ui.py +++ b/tests/unit/test_ui.py @@ -77,7 +77,8 @@ def test_global_custom_preprocessors_should_be_applied_in_the_order_they_are_add fname = Path(file_name).name with (Path(self._preprocessed_path) / "lib" / fname).open() as fread: self.assertEqual( - fread.read(), pp_source.substitute(entity="ent0", file=fname), + fread.read(), + pp_source.substitute(entity="ent0", file=fname), ) def test_global_check_and_location_preprocessors_should_be_applied_after_global_custom_preprocessors( diff --git a/vunit/check_preprocessor.py b/vunit/check_preprocessor.py index dec146e66..108e2ce31 100644 --- a/vunit/check_preprocessor.py +++ b/vunit/check_preprocessor.py @@ -249,13 +249,10 @@ def __init__(self, left, operand, right): self._right = right def make_context_msg(self): - return ( - '"Expected %s %s %s. Left is " & to_string(%s) & ". Right is " & to_string(%s) & "."' - % ( - self._left.replace('"', '""'), - self._operand, - self._right.replace('"', '""'), - self._left, - self._right, - ) + return '"Expected %s %s %s. Left is " & to_string(%s) & ". Right is " & to_string(%s) & "."' % ( + self._left.replace('"', '""'), + self._operand, + self._right.replace('"', '""'), + self._left, + self._right, ) diff --git a/vunit/com/codec_vhdl_array_type.py b/vunit/com/codec_vhdl_array_type.py index 62a32ce95..5bdc13b40 100644 --- a/vunit/com/codec_vhdl_array_type.py +++ b/vunit/com/codec_vhdl_array_type.py @@ -39,15 +39,19 @@ def generate_codecs_and_support_functions(self): definitions += template.constrained_1d_array_definition.substitute( type=self.identifier ) - definitions += template.constrained_1d_array_to_string_definition.substitute( - type=self.identifier + definitions += ( + template.constrained_1d_array_to_string_definition.substitute( + type=self.identifier + ) ) else: definitions += template.constrained_2d_array_definition.substitute( type=self.identifier ) - definitions += template.constrained_2d_array_to_string_definition.substitute( - type=self.identifier + definitions += ( + template.constrained_2d_array_to_string_definition.substitute( + type=self.identifier + ) ) else: if has_one_dimension: @@ -57,8 +61,10 @@ def generate_codecs_and_support_functions(self): init_value=init_value, range_type=self.range1.range_type, ) - definitions += template.unconstrained_1d_array_to_string_definition.substitute( - array_type=self.identifier, range_type=self.range1.range_type + definitions += ( + template.unconstrained_1d_array_to_string_definition.substitute( + array_type=self.identifier, range_type=self.range1.range_type + ) ) else: definitions += template.unconstrained_2d_array_definition.substitute( @@ -66,10 +72,12 @@ def generate_codecs_and_support_functions(self): range_type1=self.range1.range_type, range_type2=self.range2.range_type, ) - definitions += template.unconstrained_2d_array_to_string_definition.substitute( - array_type=self.identifier, - range_type1=self.range1.range_type, - range_type2=self.range2.range_type, + definitions += ( + template.unconstrained_2d_array_to_string_definition.substitute( + array_type=self.identifier, + range_type1=self.range1.range_type, + range_type2=self.range2.range_type, + ) ) return declarations, definitions diff --git a/vunit/com/codec_vhdl_package.py b/vunit/com/codec_vhdl_package.py index 536b5dcac..fa308f7d5 100644 --- a/vunit/com/codec_vhdl_package.py +++ b/vunit/com/codec_vhdl_package.py @@ -78,9 +78,11 @@ def generate_codecs_and_support_functions(self): msg_type_enumeration_types, ) = self._create_enumeration_of_all_msg_types() if all_msg_types_enumeration_type is not None: - declarations += self._template.all_msg_types_enumeration_type_declaration.substitute( - identifier=all_msg_types_enumeration_type.identifier, - literals=", ".join(all_msg_types_enumeration_type.literals), + declarations += ( + self._template.all_msg_types_enumeration_type_declaration.substitute( + identifier=all_msg_types_enumeration_type.identifier, + literals=", ".join(all_msg_types_enumeration_type.literals), + ) ) if all_msg_types_enumeration_type is not None: @@ -263,17 +265,21 @@ def _generate_msg_type_encoders(self): # pylint: disable=too-many-locals encodings = " & ".join(encoding_list) - declarations += self._template.msg_type_record_codec_declaration.substitute( - name=value, - parameter_part=parameter_part, - alias_signature=alias_signature, - alias_name=value + "_msg", + declarations += ( + self._template.msg_type_record_codec_declaration.substitute( + name=value, + parameter_part=parameter_part, + alias_signature=alias_signature, + alias_name=value + "_msg", + ) ) - definitions += self._template.msg_type_record_codec_definition.substitute( - name=value, - parameter_part=parameter_part, - num_of_encodings=len(encoding_list), - encodings=encodings, + definitions += ( + self._template.msg_type_record_codec_definition.substitute( + name=value, + parameter_part=parameter_part, + num_of_encodings=len(encoding_list), + encodings=encodings, + ) ) return declarations, definitions @@ -290,11 +296,15 @@ def _generate_get_functions(self): msg_type_type = record.elements[0].subtype_indication.code if msg_type_type not in msg_type_types: msg_type_types.append(msg_type_type) - declarations += self._template.get_specific_msg_type_declaration.substitute( - type=msg_type_type + declarations += ( + self._template.get_specific_msg_type_declaration.substitute( + type=msg_type_type + ) ) - definitions += self._template.get_specific_msg_type_definition.substitute( - type=msg_type_type + definitions += ( + self._template.get_specific_msg_type_definition.substitute( + type=msg_type_type + ) ) return declarations, definitions diff --git a/vunit/parsing/verilog/preprocess.py b/vunit/parsing/verilog/preprocess.py index c93713c9a..c7dc5b506 100644 --- a/vunit/parsing/verilog/preprocess.py +++ b/vunit/parsing/verilog/preprocess.py @@ -150,10 +150,10 @@ def preprocessor( # pylint: disable=too-many-arguments,too-many-branches @staticmethod def _skip_protected_region(stream): """ - Skip a protected region -`pragma protect begin_protected -Skipped -`pragma protect end_protected + Skip a protected region + `pragma protect begin_protected + Skipped + `pragma protect end_protected """ while not stream.eof: stream.skip_while(WHITESPACE) diff --git a/vunit/sim_if/vsim_simulator_mixin.py b/vunit/sim_if/vsim_simulator_mixin.py index 81d0cad3a..1bbb42337 100644 --- a/vunit/sim_if/vsim_simulator_mixin.py +++ b/vunit/sim_if/vsim_simulator_mixin.py @@ -54,7 +54,7 @@ def create_process(ident): @staticmethod def _create_restart_function(): - """" + """ " Create the vunit_restart function to recompile and restart the simulation This function is quite complicated to work around limitations From 8bd4064e757926b77c91de2aea68d9e26f9918ee Mon Sep 17 00:00:00 2001 From: eine Date: Fri, 28 Aug 2020 14:18:24 +0200 Subject: [PATCH 74/79] lint: re-raise explicitly using 'from' --- vunit/parsing/verilog/preprocess.py | 24 ++++++++++++------------ vunit/project.py | 4 ++-- vunit/source_file.py | 8 ++++---- vunit/test/runner.py | 4 ++-- vunit/ui/__init__.py | 8 ++++---- vunit/vunit_cli.py | 6 ++++-- 6 files changed, 28 insertions(+), 26 deletions(-) diff --git a/vunit/parsing/verilog/preprocess.py b/vunit/parsing/verilog/preprocess.py index c7dc5b506..9fff6bbee 100644 --- a/vunit/parsing/verilog/preprocess.py +++ b/vunit/parsing/verilog/preprocess.py @@ -114,10 +114,10 @@ def preprocessor( # pylint: disable=too-many-arguments,too-many-branches include_paths=include_paths, included_files=included_files, ) - except EOFException: + except EOFException as exe: raise LocationException.warning( "EOF reached when parsing `%s" % token.value, token.location - ) + ) from exe elif token.value in ("celldefine", "endcelldefine", "nounconnected_drive"): # Ignored @@ -275,10 +275,10 @@ def include( # pylint: disable=too-many-arguments stream.skip_while(WHITESPACE) try: tok = stream.pop() - except EOFException: + except EOFException as exe: raise LocationException.warning( "EOF reached when parsing `include argument", token.location - ) + ) from exe if tok.kind == PREPROCESSOR: if tok.value in defines: @@ -363,10 +363,10 @@ def undef(undef_token, stream, defines): stream.skip_while(WHITESPACE, NEWLINE) try: name_token = stream.pop() - except EOFException: + except EOFException as exe: raise LocationException.warning( "EOF reached when parsing `undef", undef_token.location - ) + ) from exe if name_token.kind != IDENTIFIER: raise LocationException.warning("Bad argument to `undef", name_token.location) @@ -386,10 +386,10 @@ def define(define_token, stream): stream.skip_while(WHITESPACE, NEWLINE) try: name_token = stream.pop() - except EOFException: + except EOFException as exe: raise LocationException.warning( "Verilog `define without argument", define_token.location - ) + ) from exe if name_token.kind != IDENTIFIER: raise LocationException.warning( @@ -429,10 +429,10 @@ def define(define_token, stream): token = stream.pop() else: token = stream.pop() - except EOFException: + except EOFException as exe: raise LocationException.warning( "EOF reached when parsing `define argument list", lpar_token.location - ) + ) from exe stream.skip_while(WHITESPACE) start = stream.idx @@ -500,10 +500,10 @@ def expand_from_stream(self, token, stream, previous=None): else: try: values = self._parse_macro_actuals(token, stream) - except EOFException: + except EOFException as exe: raise LocationException.warning( "EOF reached when parsing `define actuals", location=token.location - ) + ) from exe # Bind defaults if len(values) < len(self.args): diff --git a/vunit/project.py b/vunit/project.py index ea7ac3f9b..e4ad6ac9c 100644 --- a/vunit/project.py +++ b/vunit/project.py @@ -526,7 +526,7 @@ def _get_affected_files(self, target_files, get_depend_func): return get_depend_func(target_files) except CircularDependencyException as exc: self._handle_circular_dependency(exc) - raise CompileError + raise CompileError from exc def _get_compile_order(self, files, dependency_graph): """ @@ -537,7 +537,7 @@ def _get_compile_order(self, files, dependency_graph): compile_order = dependency_graph.toposort() except CircularDependencyException as exc: self._handle_circular_dependency(exc) - raise CompileError + raise CompileError from exc def comparison_key(source_file): return compile_order.index(source_file) diff --git a/vunit/source_file.py b/vunit/source_file.py index 81731bba2..df5bac350 100644 --- a/vunit/source_file.py +++ b/vunit/source_file.py @@ -185,8 +185,8 @@ def parse(self, parser, database, include_dirs): for instance_name in design_file.instances: self.module_dependencies.append(instance_name) - except KeyboardInterrupt: - raise KeyboardInterrupt + except KeyboardInterrupt as exk: + raise KeyboardInterrupt from exk except: # pylint: disable=bare-except traceback.print_exc() LOGGER.error("Failed to parse %s", self.name) @@ -222,8 +222,8 @@ def __init__( # pylint: disable=too-many-arguments try: design_file = vhdl_parser.parse(self.name) - except KeyboardInterrupt: - raise KeyboardInterrupt + except KeyboardInterrupt as exk: + raise KeyboardInterrupt from exk except: # pylint: disable=bare-except traceback.print_exc() LOGGER.error("Failed to parse %s", self.name) diff --git a/vunit/test/runner.py b/vunit/test/runner.py index 5edae0529..1fb87267a 100644 --- a/vunit/test/runner.py +++ b/vunit/test/runner.py @@ -219,11 +219,11 @@ def read_output(): return contents results = test_suite.run(output_path=output_path, read_output=read_output) - except KeyboardInterrupt: + except KeyboardInterrupt as exk: self._add_skipped_tests( test_suite, results, start_time, num_tests, output_file_name ) - raise KeyboardInterrupt + raise KeyboardInterrupt from exk except: # pylint: disable=bare-except if self._dont_catch_exceptions: raise diff --git a/vunit/ui/__init__.py b/vunit/ui/__init__.py index 21ffd256d..61f2d1ac9 100644 --- a/vunit/ui/__init__.py +++ b/vunit/ui/__init__.py @@ -180,8 +180,8 @@ def _create_database(self): try: database = DataBase(project_database_file_name) create_new = (key not in database) or (database[key] != version) - except KeyboardInterrupt: - raise KeyboardInterrupt + except KeyboardInterrupt as exk: + raise KeyboardInterrupt from exk except: # pylint: disable=bare-except traceback.print_exc() create_new = True @@ -649,8 +649,8 @@ def _preprocess( code = ostools.read_file(file_name, encoding=HDL_FILE_ENCODING) for preprocessor in preprocessors: code = preprocessor.run(code, fname) - except KeyboardInterrupt: - raise KeyboardInterrupt + except KeyboardInterrupt as exk: + raise KeyboardInterrupt from exk except: # pylint: disable=bare-except traceback.print_exc() LOGGER.error("Failed to preprocess %s", fstr) diff --git a/vunit/vunit_cli.py b/vunit/vunit_cli.py index 1feea021c..6bb69bc09 100644 --- a/vunit/vunit_cli.py +++ b/vunit/vunit_cli.py @@ -263,8 +263,10 @@ def positive_int(val): ival = int(val) assert ival > 0 return ival - except (ValueError, AssertionError): - raise argparse.ArgumentTypeError("'%s' is not a valid positive int" % val) + except (ValueError, AssertionError) as exv: + raise argparse.ArgumentTypeError( + "'%s' is not a valid positive int" % val + ) from exv def _parser_for_documentation(): From e96eed6985e1f15b9afb5534ee6ba26e31efdfca Mon Sep 17 00:00:00 2001 From: Lars Asplund Date: Fri, 28 Aug 2020 14:43:37 +0200 Subject: [PATCH 75/79] lint: use Python 3 style super() without arguments --- vunit/com/codec_vhdl_package.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/vunit/com/codec_vhdl_package.py b/vunit/com/codec_vhdl_package.py index fa308f7d5..4304b2b62 100644 --- a/vunit/com/codec_vhdl_package.py +++ b/vunit/com/codec_vhdl_package.py @@ -20,9 +20,7 @@ class CodecVHDLPackage(VHDLPackage): in the package.""" def __init__(self, identifier, enumeration_types, record_types, array_types): - super(CodecVHDLPackage, self).__init__( - identifier, enumeration_types, record_types, array_types - ) + super().__init__(identifier, enumeration_types, record_types, array_types) self._template = None @classmethod From 2054f26a031c58d4ffa5f12a23129ac71b7ec3c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=98yvind=20Hagen?= Date: Fri, 7 Feb 2020 09:56:15 +0100 Subject: [PATCH 76/79] Fixed RUNTIME_0232 and RUNTIME_022 messages from being printed in Riviera-PRO GUI despite being disabled in startup.tcl. --- vunit/sim_if/rivierapro.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/vunit/sim_if/rivierapro.py b/vunit/sim_if/rivierapro.py index 8561c58bd..d29bfb38c 100644 --- a/vunit/sim_if/rivierapro.py +++ b/vunit/sim_if/rivierapro.py @@ -327,6 +327,10 @@ def _create_load_function( # Make the variable 'LICENSE_QUEUE' visible (if set); otherwise vsim # will not wait for simulation licenses. global LICENSE_QUEUE + # Make the variable 'sv_container_non_existent_entry_verbose' visible + # (if set); otherwise RUNTIME_0232 and RUNTIME_0222 messages will be + # printed even if they have been disabled in startup.tcl. + global sv_container_non_existent_entry_verbose set vsim_failed [catch {{ eval vsim {{{vsim_flags}}} From 3dfc272410dfa5aaf64e4ce6b453db71c88da456 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=98yvind=20Hagen?= Date: Sun, 9 Feb 2020 18:43:46 +0100 Subject: [PATCH 77/79] Run the Riviera-PRO 'vsim' command in 'vunit_load' in the global variable context to make sure all the global variables set by the tool is available. --- vunit/sim_if/rivierapro.py | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/vunit/sim_if/rivierapro.py b/vunit/sim_if/rivierapro.py index d29bfb38c..0a4f36f18 100644 --- a/vunit/sim_if/rivierapro.py +++ b/vunit/sim_if/rivierapro.py @@ -321,19 +321,13 @@ def _create_load_function( tcl = """ proc vunit_load {{}} {{ - # Make the variable 'aldec' visible; otherwise, the Matlab interface - # is broken because vsim does not find the library aldec_matlab_cosim. - global aldec - # Make the variable 'LICENSE_QUEUE' visible (if set); otherwise vsim - # will not wait for simulation licenses. - global LICENSE_QUEUE - # Make the variable 'sv_container_non_existent_entry_verbose' visible - # (if set); otherwise RUNTIME_0232 and RUNTIME_0222 messages will be - # printed even if they have been disabled in startup.tcl. - global sv_container_non_existent_entry_verbose - + # Run the 'vsim' command in the global variable context. This will make + # variables such as 'aldec' visible, otherwise the Matlab interface + # is broken because vsim does not find the library aldec_matlab_cosim, + # and 'LICENSE_QUEUE' visible (if set); otherwise vsim will not wait + # for simulation licenses. set vsim_failed [catch {{ - eval vsim {{{vsim_flags}}} + uplevel #0 vsim {{{vsim_flags}}} }}] if {{${{vsim_failed}}}} {{ From 2ac30bfe52aafe6721ea6460c94cd95181133944 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=98yvind=20Hagen?= Date: Mon, 10 Feb 2020 09:09:04 +0100 Subject: [PATCH 78/79] Fixed comment for uplevel vsim in vunit_load for Riviera-PRO simulator. --- vunit/sim_if/rivierapro.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/vunit/sim_if/rivierapro.py b/vunit/sim_if/rivierapro.py index 0a4f36f18..4bc74d7c5 100644 --- a/vunit/sim_if/rivierapro.py +++ b/vunit/sim_if/rivierapro.py @@ -322,10 +322,10 @@ def _create_load_function( tcl = """ proc vunit_load {{}} {{ # Run the 'vsim' command in the global variable context. This will make - # variables such as 'aldec' visible, otherwise the Matlab interface - # is broken because vsim does not find the library aldec_matlab_cosim, - # and 'LICENSE_QUEUE' visible (if set); otherwise vsim will not wait - # for simulation licenses. + # variables such as 'aldec' and 'LICENSE_QUEUE' visible, if set. Otherwise, + # respectively, the Matlab interface is broken because vsim does not find + # the library aldec_matlab_cosim and vsim will not wait for simulation + # licenses. set vsim_failed [catch {{ uplevel #0 vsim {{{vsim_flags}}} }}] From b5032143186270366bf81e6cb4a02faa023e666b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=98yvind=20Hagen?= Date: Mon, 10 Feb 2020 16:23:40 +0100 Subject: [PATCH 79/79] Made comment for uplevel vsim in vunit_load for Riviera-PRO simulator more legible. --- vunit/sim_if/rivierapro.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/vunit/sim_if/rivierapro.py b/vunit/sim_if/rivierapro.py index 4bc74d7c5..db5561905 100644 --- a/vunit/sim_if/rivierapro.py +++ b/vunit/sim_if/rivierapro.py @@ -322,10 +322,11 @@ def _create_load_function( tcl = """ proc vunit_load {{}} {{ # Run the 'vsim' command in the global variable context. This will make - # variables such as 'aldec' and 'LICENSE_QUEUE' visible, if set. Otherwise, - # respectively, the Matlab interface is broken because vsim does not find - # the library aldec_matlab_cosim and vsim will not wait for simulation - # licenses. + # variables such as 'aldec' and 'LICENSE_QUEUE' visible, if set. + # Otherwise: + # - The Matlab interface is broken because vsim does not find the + # library aldec_matlab_cosim + # - vsim will not wait for simulation licenses set vsim_failed [catch {{ uplevel #0 vsim {{{vsim_flags}}} }}]