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; diff --git a/vunit/ui.py b/vunit/ui.py index 73520f2f8..c146f5501 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 @@ -288,6 +289,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 +333,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) @@ -346,6 +357,9 @@ 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 @@ -374,6 +388,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 @@ -490,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) @@ -1141,6 +1159,27 @@ 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, 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. + """ + + 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) + 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) class Library(object): """ @@ -1350,6 +1389,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_latch: + try: + duplicate = self.get_source_file('*/' + os.path.basename(file_name)) + return duplicate + except ValueError: + pass + source_file = self._project.add_source_file(new_file_name, self._library_name, file_type=file_type, @@ -1360,7 +1406,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,