From 356e0f7e76d97461c029e8d908d515c89a6840f8 Mon Sep 17 00:00:00 2001 From: Franz Forstmayr Date: Fri, 19 Jul 2019 14:22:30 +0200 Subject: [PATCH 1/8] Modified __new__ to act as singleton --- vunit/ui.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/vunit/ui.py b/vunit/ui.py index 73520f2f8..43cd2f5db 100644 --- a/vunit/ui.py +++ b/vunit/ui.py @@ -288,6 +288,13 @@ class VUnit(object): # pylint: disable=too-many-instance-attributes, too-many-p from vunit import VUnit """ + _instance = None + _initialized = False + def __new__(cls, *args, **kwargs): + if not cls._instance: + cls._instance = super(VUnit, cls).__new__(cls) + + return cls._instance @classmethod def from_argv(cls, argv=None, compile_builtins=True, vhdl_standard=None): @@ -325,6 +332,9 @@ def from_args(cls, args, compile_builtins=True, vhdl_standard=None): return cls(args, compile_builtins=compile_builtins, vhdl_standard=vhdl_standard) def __init__(self, args, compile_builtins=True, vhdl_standard=None): + if self._initialized != False: + return + self._args = args self._configure_logging(args.log_level) self._output_path = abspath(args.output_path) @@ -374,6 +384,8 @@ def test_filter(name, attribute_names): if compile_builtins: self.add_builtins() + self._initialized = True + def _create_database(self): """ Create a persistent database to store expensive parse results From 3236f4596826aa736800f7b25d82cd784a7b735d Mon Sep 17 00:00:00 2001 From: Franz Forstmayr Date: Fri, 27 Sep 2019 15:13:27 +0200 Subject: [PATCH 2/8] Added methods to remove source files from project --- vunit/project.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/vunit/project.py b/vunit/project.py index 88e5a49cf..0fca3f665 100644 --- a/vunit/project.py +++ b/vunit/project.py @@ -139,6 +139,11 @@ def add_source_file(self, # pylint: disable=too-many-arguments return old_source_file + def remove_source_file(self, file_name, library_name): + library = self._libraries[library_name] + sourcefile = library.remove_source_file(file_name) + self._source_files_in_order.remove(sourcefile) + def add_manual_dependency(self, source_file, depends_on): """ Add manual dependency where 'source_file' depends_on 'depends_on' @@ -550,6 +555,14 @@ def get_source_file(self, file_name): """ return self._source_files[file_name] + def remove_source_file(self, file_name): + """ + Remove source file with file name from library or raise KeyError + """ + print("Try to remove ", file_name, "from ", self.name) + print("Removing ", self._source_files[file_name]) + return self._source_files.pop(file_name) + @property def is_external(self): """ From a829e35681c8c325754c4a7f2794211fdbef0ecf Mon Sep 17 00:00:00 2001 From: Franz Forstmayr Date: Fri, 27 Sep 2019 15:15:04 +0200 Subject: [PATCH 3/8] Add method to add complete vunit python files at once --- vunit/ui.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/vunit/ui.py b/vunit/ui.py index 43cd2f5db..0bad551b5 100644 --- a/vunit/ui.py +++ b/vunit/ui.py @@ -251,6 +251,7 @@ from os.path import exists, abspath, join, basename, splitext, normpath, dirname from glob import glob from fnmatch import fnmatch +import importlib from vunit.database import PickledDataBase, DataBase from vunit import ostools from vunit.vunit_cli import VUnitCLI @@ -356,6 +357,7 @@ def test_filter(name, attribute_names): self._test_filter = test_filter self._vhdl_standard = select_vhdl_standard(vhdl_standard) + self._import_submodule = False self._external_preprocessors = [] self._location_preprocessor = None @@ -1153,6 +1155,17 @@ def get_implementation_subset(self, source_files): return SourceFileList([SourceFile(source_file, self._project, self) for source_file in source_files]) + def add_submodule(self, module): + + rootpath = os.getcwd() + path = os.path.abspath(os.path.dirname(module)) + sys.path.insert(0, path) + os.chdir(path) + self._import_submodule = True + importlib.import_module(os.path.basename(module).split('.')[0]) + self._import_submodule = False + os.chdir(rootpath) + sys.path.remove(path) class Library(object): """ @@ -1362,6 +1375,13 @@ def add_source_file(self, # pylint: disable=too-many-arguments new_file_name = self._parent._preprocess( # pylint: disable=protected-access self._library_name, file_name, preprocessors) + if self._parent._import_submodule: + try: + duplicate = self.get_source_file('*/' + os.path.basename(file_name)) + self._project.remove_source_file(os.path.abspath(duplicate.name), self._library_name) + except ValueError: + pass + source_file = self._project.add_source_file(new_file_name, self._library_name, file_type=file_type, From 013d6436a4aa7adbc8c75a0f41f8a15a4a4d8372 Mon Sep 17 00:00:00 2001 From: Franz Forstmayr Date: Fri, 27 Sep 2019 16:47:10 +0200 Subject: [PATCH 4/8] Keep files which are added first instead of remove duplicates --- vunit/ui.py | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/vunit/ui.py b/vunit/ui.py index 0bad551b5..c3fb4e1bd 100644 --- a/vunit/ui.py +++ b/vunit/ui.py @@ -358,6 +358,8 @@ def test_filter(name, attribute_names): self._test_filter = test_filter self._vhdl_standard = select_vhdl_standard(vhdl_standard) self._import_submodule = False + self._import_submodule_latch = False + self._import_testbenches = True self._external_preprocessors = [] self._location_preprocessor = None @@ -504,6 +506,8 @@ def add_library(self, library_name, vhdl_standard=None, allow_duplicate=False): path = join(self._simulator_output_path, "libraries", library_name) if not self._project.has_library(library_name): self._project.add_library(library_name, abspath(path), vhdl_standard) + elif self._import_submodule: + pass elif not allow_duplicate: raise ValueError("Library %s already added. Use allow_duplicate to ignore this error." % library_name) return self.library(library_name) @@ -1155,14 +1159,17 @@ def get_implementation_subset(self, source_files): return SourceFileList([SourceFile(source_file, self._project, self) for source_file in source_files]) - def add_submodule(self, module): + def add_submodule(self, module, import_testbenches = False): rootpath = os.getcwd() path = os.path.abspath(os.path.dirname(module)) sys.path.insert(0, path) os.chdir(path) self._import_submodule = True + self._import_submodule_latch = True + self._import_testbenches = import_testbenches importlib.import_module(os.path.basename(module).split('.')[0]) + self._import_testbenches = True self._import_submodule = False os.chdir(rootpath) sys.path.remove(path) @@ -1375,10 +1382,10 @@ def add_source_file(self, # pylint: disable=too-many-arguments new_file_name = self._parent._preprocess( # pylint: disable=protected-access self._library_name, file_name, preprocessors) - if self._parent._import_submodule: + if self._parent._import_submodule_latch: try: duplicate = self.get_source_file('*/' + os.path.basename(file_name)) - self._project.remove_source_file(os.path.abspath(duplicate.name), self._library_name) + return duplicate except ValueError: pass @@ -1392,7 +1399,8 @@ def add_source_file(self, # pylint: disable=too-many-arguments # To get correct tb_path generic source_file.original_name = file_name - self._test_bench_list.add_from_source_file(source_file) + if self._parent._import_testbenches: + self._test_bench_list.add_from_source_file(source_file) return SourceFile(source_file, self._project, From d3b36920f20362108a0d84ea86567344bea8dad7 Mon Sep 17 00:00:00 2001 From: Franz Forstmayr Date: Fri, 27 Sep 2019 16:47:34 +0200 Subject: [PATCH 5/8] Revert "Added methods to remove source files from project" This reverts commit 3236f4596826aa736800f7b25d82cd784a7b735d. --- vunit/project.py | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/vunit/project.py b/vunit/project.py index 0fca3f665..88e5a49cf 100644 --- a/vunit/project.py +++ b/vunit/project.py @@ -139,11 +139,6 @@ def add_source_file(self, # pylint: disable=too-many-arguments return old_source_file - def remove_source_file(self, file_name, library_name): - library = self._libraries[library_name] - sourcefile = library.remove_source_file(file_name) - self._source_files_in_order.remove(sourcefile) - def add_manual_dependency(self, source_file, depends_on): """ Add manual dependency where 'source_file' depends_on 'depends_on' @@ -555,14 +550,6 @@ def get_source_file(self, file_name): """ return self._source_files[file_name] - def remove_source_file(self, file_name): - """ - Remove source file with file name from library or raise KeyError - """ - print("Try to remove ", file_name, "from ", self.name) - print("Removing ", self._source_files[file_name]) - return self._source_files.pop(file_name) - @property def is_external(self): """ From 574d682120dc5292b75231eea847547d094b67b1 Mon Sep 17 00:00:00 2001 From: Franz Forstmayr Date: Sun, 6 Oct 2019 17:37:18 +0200 Subject: [PATCH 6/8] Docstring for add_submodule --- vunit/ui.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/vunit/ui.py b/vunit/ui.py index c3fb4e1bd..04ac3413e 100644 --- a/vunit/ui.py +++ b/vunit/ui.py @@ -1160,6 +1160,12 @@ def get_implementation_subset(self, source_files): for source_file in source_files]) def add_submodule(self, module, import_testbenches = False): + """ + Add a complete vunit build script to the design at once. + + :param module: Path to the submodule's run.py file + :param import_testbenches : If set, the submodules testbenches are imported, otherwise not. + """ rootpath = os.getcwd() path = os.path.abspath(os.path.dirname(module)) From a73a842364271c5b11a657c31aa3ff75fb3b82a0 Mon Sep 17 00:00:00 2001 From: Franz Forstmayr Date: Sun, 6 Oct 2019 17:37:41 +0200 Subject: [PATCH 7/8] Added example for add_submodule feature --- examples/vhdl/submodule/run.py | 34 ++++++++++++ examples/vhdl/submodule/src/adder/run.py | 29 ++++++++++ .../vhdl/submodule/src/adder/src/adder.vhd | 30 ++++++++++ examples/vhdl/submodule/src/top.vhd | 38 +++++++++++++ examples/vhdl/submodule/tb/top_tb.vhd | 55 +++++++++++++++++++ 5 files changed, 186 insertions(+) create mode 100644 examples/vhdl/submodule/run.py create mode 100644 examples/vhdl/submodule/src/adder/run.py create mode 100644 examples/vhdl/submodule/src/adder/src/adder.vhd create mode 100644 examples/vhdl/submodule/src/top.vhd create mode 100644 examples/vhdl/submodule/tb/top_tb.vhd diff --git a/examples/vhdl/submodule/run.py b/examples/vhdl/submodule/run.py new file mode 100644 index 000000000..3c14f7a4d --- /dev/null +++ b/examples/vhdl/submodule/run.py @@ -0,0 +1,34 @@ +# 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-2019, Lars Asplund lars.anders.asplund@gmail.com + +""" +Submodule support +------------- +This script shows how to import smaller submodules in a bigger design. + +The submodules contains a own run.py script in which the sources are defined. +With 'add_submodule' you can add the complete project into your top design. +Usecase could be a CPU design which uses some periphery which is managed in +seperate projects. +""" + +from os.path import join, dirname +from vunit import VUnit + +root = dirname(__file__) + +vu = VUnit.from_argv() + +vu.add_json4vhdl() + +lib = vu.add_library("lib") +lib.add_source_files(join(root, "src/*.vhd")) +lib.add_source_files(join(root, "tb/*.vhd")) + +vu.add_submodule('src/adder/run.py') + +if __name__ == '__main__': + vu.main() diff --git a/examples/vhdl/submodule/src/adder/run.py b/examples/vhdl/submodule/src/adder/run.py new file mode 100644 index 000000000..774132adf --- /dev/null +++ b/examples/vhdl/submodule/src/adder/run.py @@ -0,0 +1,29 @@ +# 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-2019, Lars Asplund lars.anders.asplund@gmail.com + +""" +Submodule support +------------- + +This is only a small example and shows how a complete vunit design can be +imported at once. Intended use case could be a seperately managed periphery +which gets imported in a bigger top design like a CPU or complete vhdl librarys. +""" + +from os.path import join, dirname +from vunit import VUnit + +root = dirname(__file__) + +vu = VUnit.from_argv() + +vu.add_json4vhdl() + +lib = vu.add_library("adder") +lib.add_source_files(join(root, "src/*.vhd")) + +if __name__ == '__main__': + vu.main() diff --git a/examples/vhdl/submodule/src/adder/src/adder.vhd b/examples/vhdl/submodule/src/adder/src/adder.vhd new file mode 100644 index 000000000..9df1b5f23 --- /dev/null +++ b/examples/vhdl/submodule/src/adder/src/adder.vhd @@ -0,0 +1,30 @@ +-- 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-2019, Lars Asplund lars.anders.asplund@gmail.com + +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + +entity adder is + generic ( + BITS : positive); + port( + clk : in std_logic; + op_a : in unsigned(BITS - 1 downto 0); + op_b : in unsigned(BITS - 1 downto 0); + sum : out unsigned(BITS - 1 downto 0) + ); +end entity; + +architecture arch of adder is +begin + main : process(clk) + begin + if rising_edge(clk) then + sum <= op_a + op_b; + end if; + end process; +end architecture; diff --git a/examples/vhdl/submodule/src/top.vhd b/examples/vhdl/submodule/src/top.vhd new file mode 100644 index 000000000..43f87628c --- /dev/null +++ b/examples/vhdl/submodule/src/top.vhd @@ -0,0 +1,38 @@ +-- 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-2019, Lars Asplund lars.anders.asplund@gmail.com + +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + +library adder; + +entity top is + generic ( + BITS : positive); + port( + clk : in std_logic; + op_a : in unsigned(BITS - 1 downto 0); + op_b : in unsigned(BITS - 1 downto 0); + sum : out unsigned(BITS - 1 downto 0) + ); +end entity; + +architecture arch of top is +begin + + adder_inst: entity adder.adder + generic map( + BITS => BITS + ) + port map( + clk => clk, + op_a => op_a, + op_b => op_b, + sum => sum + ); + +end architecture; diff --git a/examples/vhdl/submodule/tb/top_tb.vhd b/examples/vhdl/submodule/tb/top_tb.vhd new file mode 100644 index 000000000..b4f6d2396 --- /dev/null +++ b/examples/vhdl/submodule/tb/top_tb.vhd @@ -0,0 +1,55 @@ +-- 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-2019, Lars Asplund lars.anders.asplund@gmail.com + +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + +library vunit_lib; +context vunit_lib.vunit_context; + +entity top_tb is + generic ( + RUNNER_CFG : string); +end entity; + +architecture arch of top_tb is + signal clk : std_logic := '0'; + + constant BITS : natural := 8; + signal in_a, in_b, add_out : unsigned(BITS - 1 downto 0); + +begin + clk <= not clk after 1 ns; + + + main: process + begin + test_runner_setup(runner, runner_cfg); + while test_suite loop + if run("test") then + in_a <= to_unsigned(10, in_a); + in_b <= to_unsigned(15, in_b); + wait for 3 ns; + check(add_out = in_a + in_b, "Addition failed"); + end if; + end loop; + test_runner_cleanup(runner); + wait; + end process; + + top_module: entity work.top + generic map( + BITS => BITS + ) + port map( + clk => clk, + op_a => in_a, + op_b => in_b, + sum => add_out + ); + +end architecture; From 7ce4b51149c2c21c0c2b85bd1a4ca886b4ff26f7 Mon Sep 17 00:00:00 2001 From: Franz Forstmayr Date: Sun, 6 Oct 2019 17:57:15 +0200 Subject: [PATCH 8/8] Raise FileNotFoundError if submodule's run.py not found --- vunit/ui.py | 1 + 1 file changed, 1 insertion(+) diff --git a/vunit/ui.py b/vunit/ui.py index 04ac3413e..c146f5501 100644 --- a/vunit/ui.py +++ b/vunit/ui.py @@ -1167,6 +1167,7 @@ def add_submodule(self, module, import_testbenches = False): :param import_testbenches : If set, the submodules testbenches are imported, otherwise not. """ + f = open(module) # raises FileNotFoundError if the module is not available rootpath = os.getcwd() path = os.path.abspath(os.path.dirname(module)) sys.path.insert(0, path)