Пример #1
0
 def test_string_option(self):
     option = StringOption("optname")
     self._test_ok(option, "hello")
     self._test_ok(option, u"hello")
     self._test_not_ok(option, False,
                       "Option 'optname' must be a string. Got False")
     self._test_not_ok(option, ["foo"],
                       "Option 'optname' must be a string. Got ['foo']")
Пример #2
0
class ActiveHDLInterface(SimulatorInterface):
    """
    Active HDL interface
    """

    name = "activehdl"
    supports_gui_flag = True
    package_users_depend_on_bodies = True
    compile_options = [
        ListOfStringOption("activehdl.vcom_flags"),
        ListOfStringOption("activehdl.vlog_flags"),
    ]

    sim_options = [
        ListOfStringOption("activehdl.vsim_flags"),
        ListOfStringOption("activehdl.vsim_flags.gui"),
        StringOption("activehdl.init_file.gui"),
    ]

    @classmethod
    def from_args(cls, output_path, args):
        """
        Create new instance from command line arguments object
        """
        return cls(prefix=cls.find_prefix(),
                   library_cfg=join(output_path, "library.cfg"),
                   gui=args.gui)

    @classmethod
    def find_prefix_from_path(cls):
        return cls.find_toolchain(["vsim", "avhdl"])

    @classmethod
    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())
        consumer = VersionConsumer()
        proc.consume_output(consumer)
        if consumer.major is not None:
            return consumer.minor >= 1 if consumer.major == 10 else consumer.major > 10

        return False

    def __init__(self, prefix, library_cfg="library.cfg", gui=False):
        SimulatorInterface.__init__(self)
        self._library_cfg = abspath(library_cfg)
        self._prefix = prefix
        self._gui = gui
        self._create_library_cfg()
        self._libraries = []

    def setup_library_mapping(self, project):
        """
        Setup library mapping
        """
        mapped_libraries = self._get_mapped_libraries()

        for library in project.get_libraries():
            self._libraries.append(library)
            self.create_library(library.name, library.directory,
                                mapped_libraries)

    def compile_source_file_command(self, source_file):
        """
        Returns the command to compile a single source_file
        """
        if source_file.is_vhdl:
            return self.compile_vhdl_file_command(source_file)
        elif source_file.is_any_verilog:
            return self.compile_verilog_file_command(source_file)

        LOGGER.error("Unknown file type: %s", source_file.file_type)
        raise CompileError

    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)
        ] + source_file.compile_options.get("activehdl.vcom_flags", []) + [
            '-' + source_file.get_vhdl_standard(), '-work',
            source_file.library.name, source_file.name
        ])

    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 += source_file.compile_options.get("activehdl.vlog_flags", [])
        args += ['-work', source_file.library.name, source_file.name]
        for library in self._libraries:
            args += ["-l", library.name]
        for include_dir in source_file.include_dirs:
            args += ["+incdir+%s" % include_dir]
        for key, value in source_file.defines.items():
            args += ["+define+%s=%s" % (key, value)]
        return args

    def create_library(self, library_name, path, mapped_libraries=None):
        """
        Create and map a library_name to path
        """
        mapped_libraries = mapped_libraries if mapped_libraries is not None else {}

        if not file_exists(dirname(abspath(path))):
            os.makedirs(dirname(abspath(path)))

        if not file_exists(path):
            proc = Process([join(self._prefix, 'vlib'), library_name, path],
                           cwd=dirname(self._library_cfg),
                           env=self.get_env())
            proc.consume_output(callback=None)

        if library_name in mapped_libraries and mapped_libraries[
                library_name] == path:
            return

        proc = Process([join(self._prefix, 'vmap'), library_name, path],
                       cwd=dirname(self._library_cfg),
                       env=self.get_env())
        proc.consume_output(callback=None)

    def _create_library_cfg(self):
        """
        Create the library.cfg file if it does not exist
        """
        if file_exists(self._library_cfg):
            return

        with open(self._library_cfg, "w") as ofile:
            ofile.write('$INCLUDE = "%s"\n' %
                        join(self._prefix, "..", "vlib", "library.cfg"))

    _library_re = re.compile(r'([a-zA-Z_]+)\s=\s"(.*)"')

    def _get_mapped_libraries(self):
        """
        Get mapped libraries from library.cfg file
        """
        with open(self._library_cfg, "r") as fptr:
            text = fptr.read()

        libraries = {}
        for line in text.splitlines():
            match = self._library_re.match(line)
            if match is None:
                continue
            key = match.group(1)
            value = match.group(2)
            libraries[key] = abspath(
                join(dirname(self._library_cfg), dirname(value)))
        return libraries

    def _vsim_extra_args(self, config):
        """
        Determine vsim_extra_args
        """
        vsim_extra_args = []
        vsim_extra_args = config.sim_options.get("activehdl.vsim_flags",
                                                 vsim_extra_args)

        if self._gui:
            vsim_extra_args = config.sim_options.get(
                "activehdl.vsim_flags.gui", vsim_extra_args)

        return " ".join(vsim_extra_args)

    def _create_load_function(self, config):
        """
        Create the vunit_load TCL function that runs the vsim command and loads the design
        """
        set_generic_str = "\n    ".join(
            ('set vunit_generic_%s {%s}' % (name, value)
             for name, value in config.generics.items()))
        set_generic_name_str = " ".join(
            ('-g/%s/%s=${vunit_generic_%s}' % (config.entity_name, name, name)
             for name in config.generics))
        pli_str = " ".join("-pli \"%s\"" % fix_path(name)
                           for name in config.sim_options.get('pli', []))

        vsim_flags = [
            pli_str, set_generic_name_str, "-lib", config.library_name,
            config.entity_name,
            self._vsim_extra_args(config)
        ]

        if config.architecture_name is not None:
            vsim_flags.append(config.architecture_name)

        if config.sim_options.get("disable_ieee_warnings", False):
            vsim_flags.append("-ieee_nowarn")

        vhdl_assert_stop_level_mapping = dict(warning=1, error=2, failure=3)

        tcl = """
proc vunit_load {{}} {{
    {set_generic_str}
    set vsim_failed [catch {{
        vsim {vsim_flags}
    }}]
    if {{${{vsim_failed}}}} {{
        return 1
    }}

    global breakassertlevel
    set breakassertlevel {breaklevel}

    global builtinbreakassertlevel
    set builtinbreakassertlevel $breakassertlevel

    set no_vhdl_test_runner_exit [catch {{examine /run_base_pkg/runner.exit_simulation}}]
    if {{${{no_vhdl_test_runner_exit}}}}  {{
        echo {{Error: No vunit test runner package used}}
        return 1
    }}
    return 0
}}
""".format(set_generic_str=set_generic_str,
           vsim_flags=" ".join(vsim_flags),
           breaklevel=vhdl_assert_stop_level_mapping[
               config.vhdl_assert_stop_level])

        return tcl

    @staticmethod
    def _create_run_function():
        """
        Create the vunit_run function to run the test bench
        """
        return """
proc vunit_run {} {
    set has_vhdl_runner [expr ![catch {examine /run_base_pkg/runner}]]

    if {${has_vhdl_runner}} {
        set status_boolean "/run_base_pkg/runner.exit_without_errors"
        set true_value true
    } else {
        echo "No finish mechanism detected"
        return 1;
    }

    run -all
    set failed [expr [examine ${status_boolean}]!=${true_value}]
    if {$failed} {
        catch {
            # tb command can fail when error comes from pli
            echo ""
            echo "Stack trace result from 'bt' command"
            bt
        }
    }
    return $failed
}
"""

    def _create_common_script(self, config):
        """
        Create tcl script with functions common to interactive and batch modes
        """
        tcl = ""
        tcl += self._create_load_function(config)
        tcl += self._create_run_function()
        return tcl

    @staticmethod
    def _create_batch_script(common_file_name, load_only=False):
        """
        Create tcl script to run in batch mode
        """
        batch_do = ""
        batch_do += "source \"%s\"\n" % fix_path(common_file_name)
        batch_do += "set failed [vunit_load]\n"
        batch_do += "if {$failed} {quit -code 1}\n"
        if not load_only:
            batch_do += "set failed [vunit_run]\n"
            batch_do += "if {$failed} {quit -code 1}\n"
        batch_do += "quit -code 0\n"
        return batch_do

    def _create_gui_script(self, common_file_name, config):
        """
        Create the user facing script which loads common functions and prints a help message
        """

        tcl = ""
        tcl += 'source "%s"\n' % fix_path(common_file_name)
        tcl += 'workspace create workspace\n'
        tcl += 'design create -a design .\n'

        for library in self._libraries:
            tcl += "vmap %s %s\n" % (library.name, fix_path(library.directory))

        tcl += 'vunit_load\n'

        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 += 'puts "VUnit help: Design already loaded. Use run -all to run the test."\n'

        return tcl

    def _run_batch_file(self, batch_file_name, gui, cwd):
        """
        Run a test bench in batch by invoking a new vsim process from the command line
        """

        todo = "@do -tcl \"\"%s\"\"" % fix_path(batch_file_name)
        if not gui:
            todo = "@onerror {quit -code 1};" + todo

        try:
            args = [
                join(self._prefix, "vsim"), "-gui" if gui else "-c", "-l",
                join(dirname(batch_file_name), "transcript"), '-do', todo
            ]

            proc = Process(args, cwd=cwd, env=self.get_env())
            proc.consume_output()
        except Process.NonZeroExitCode:
            return False
        return True

    def simulate(self, output_path, test_suite_name, config, elaborate_only):
        """
        Run a test bench
        """
        common_file_name = join(output_path, "common.tcl")
        batch_file_name = join(output_path, "batch.tcl")
        gui_file_name = join(output_path, "gui.tcl")

        write_file(common_file_name, self._create_common_script(config))
        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))

        if self._gui:
            gui_path = join(output_path, "gui")
            renew_path(gui_path)
            return self._run_batch_file(gui_file_name, gui=True, cwd=gui_path)

        return self._run_batch_file(batch_file_name,
                                    gui=False,
                                    cwd=dirname(self._library_cfg))
Пример #3
0
class RivieraProInterface(VsimSimulatorMixin, SimulatorInterface):
    """
    Riviera Pro interface
    """

    name = "rivierapro"
    supports_gui_flag = True
    package_users_depend_on_bodies = True

    compile_options = [
        ListOfStringOption("rivierapro.vcom_flags"),
        ListOfStringOption("rivierapro.vlog_flags"),
    ]

    sim_options = [
        ListOfStringOption("rivierapro.vsim_flags"),
        ListOfStringOption("rivierapro.vsim_flags.gui"),
        ListOfStringOption("rivierapro.init_files.after_load"),
        StringOption("rivierapro.init_file.gui"),
    ]

    @classmethod
    def from_args(cls, args, output_path, **kwargs):
        """
        Create new instance from command line arguments object
        """
        persistent = not (args.unique_sim or args.gui)

        return cls(prefix=cls.find_prefix(),
                   output_path=output_path,
                   persistent=persistent,
                   gui=args.gui)

    @classmethod
    def find_prefix_from_path(cls):
        """
        Find RivieraPro toolchain.

        Must have vsim and vsimsa binaries but no avhdl.exe
        """
        def no_avhdl(path):
            return not file_exists(join(path, "avhdl.exe"))
        return cls.find_toolchain(["vsim",
                                   "vsimsa"],
                                  constraints=[no_avhdl])

    @classmethod
    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())
        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 cls.name

        return None

    @classmethod
    def supports_vhdl_package_generics(cls):
        """
        Returns True when this simulator supports VHDL package generics
        """
        return True

    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._create_library_cfg()
        self._libraries = []
        self._coverage_files = set()

    def add_simulator_specific(self, project):
        """
        Add builtin (global) libraries
        """
        built_in_libraries = self._get_mapped_libraries(self._builtin_library_cfg)

        for library_name in built_in_libraries:
            # A user might shadow a built in library with their own version
            if not project.has_library(library_name):
                project.add_builtin_library(library_name)

    def setup_library_mapping(self, project):
        """
        Setup library mapping
        """
        mapped_libraries = self._get_mapped_libraries(self._sim_cfg_file_name)
        for library in project.get_libraries():
            self._libraries.append(library)
            self.create_library(library.name, library.directory, mapped_libraries)

    def compile_source_file_command(self, source_file):
        """
        Returns the command to compile a single source_file
        """
        if source_file.is_vhdl:
            return self.compile_vhdl_file_command(source_file)

        if source_file.is_any_verilog:
            return self.compile_verilog_file_command(source_file)

        LOGGER.error("Unknown file type: %s", source_file.file_type)
        raise CompileError

    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._sim_cfg_file_name)]
                + source_file.compile_options.get("rivierapro.vcom_flags", [])
                + ['-' + source_file.get_vhdl_standard(), '-work', source_file.library.name, source_file.name])

    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]
        if source_file.is_system_verilog:
            args += ['-sv2k12']
        args += source_file.compile_options.get("rivierapro.vlog_flags", [])
        args += ['-work', source_file.library.name, source_file.name]
        for library in self._libraries:
            args += ["-l", library.name]
        for include_dir in source_file.include_dirs:
            args += ["+incdir+%s" % include_dir]
        for key, value in source_file.defines.items():
            args += ["+define+%s=%s" % (key, value)]
        return args

    def create_library(self, library_name, path, mapped_libraries=None):
        """
        Create and map a library_name to path
        """
        mapped_libraries = mapped_libraries if mapped_libraries is not None else {}

        if not file_exists(dirname(abspath(path))):
            os.makedirs(dirname(abspath(path)))

        if not file_exists(path):
            proc = Process([join(self._prefix, 'vlib'), library_name, path],
                           cwd=dirname(self._sim_cfg_file_name),
                           env=self.get_env())
            proc.consume_output(callback=None)

        if library_name in mapped_libraries and mapped_libraries[library_name] == path:
            return

        proc = Process([join(self._prefix, 'vmap'), library_name, path],
                       cwd=dirname(self._sim_cfg_file_name),
                       env=self.get_env())
        proc.consume_output(callback=None)

    def _create_library_cfg(self):
        """
        Create the library.cfg file if it does not exist
        """
        if file_exists(self._sim_cfg_file_name):
            return

        with open(self._sim_cfg_file_name, "w") as ofile:
            ofile.write('$INCLUDE = "%s"\n' % self._builtin_library_cfg)

    @property
    def _builtin_library_cfg(self):
        return join(self._prefix, "..", "vlib", "library.cfg")

    _library_re = re.compile(r'([a-zA-Z_0-9]+)\s=\s(.*)')

    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.consume_output(callback=lines.append)

        libraries = {}
        for line in lines:
            match = self._library_re.match(line)
            if match is None:
                continue
            key = match.group(1)
            value = match.group(2)
            libraries[key] = abspath(join(dirname(library_cfg_file), dirname(value)))
        return libraries

    def _create_load_function(self,
                              test_suite_name,  # pylint: disable=unused-argument
                              config, output_path):
        """
        Create the vunit_load TCL function that runs the vsim command and loads the design
        """
        set_generic_str = " ".join(('-g/%s/%s=%s' % (config.entity_name,
                                                     name,
                                                     format_generic(value))
                                    for name, value in config.generics.items()))
        pli_str = " ".join("-pli \"%s\"" % fix_path(name) for name in config.sim_options.get('pli', []))

        vsim_flags = ["-dataset {%s}" % fix_path(join(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")
            self._coverage_files.add(coverage_file_path)
            vsim_flags += ["-acdb_file {%s}" % coverage_file_path]

        vsim_flags += [self._vsim_extra_args(config)]

        if config.sim_options.get("disable_ieee_warnings", False):
            vsim_flags.append("-ieee_nowarn")

        # Add the the testbench top-level unit last as coverage is
        # only collected for the top-level unit specified last
        vsim_flags += ["-lib",
                       config.library_name,
                       config.entity_name]

        if config.architecture_name is not None:
            vsim_flags.append(config.architecture_name)

        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

    set vsim_failed [catch {{
        eval vsim {{{vsim_flags}}}
    }}]

    if {{${{vsim_failed}}}} {{
        return true
    }}

    if {{[_vunit_source_init_files_after_load]}} {{
        return true
    }}

    vhdlassert.break {break_level}
    vhdlassert.break -builtin {break_level}

    return false
}}
""".format(vsim_flags=" ".join(vsim_flags),
           break_level=config.vhdl_assert_stop_level)

        return tcl

    def _vsim_extra_args(self, config):
        """
        Determine vsim_extra_args
        """
        vsim_extra_args = []
        vsim_extra_args = config.sim_options.get("rivierapro.vsim_flags",
                                                 vsim_extra_args)

        if self._gui:
            vsim_extra_args = config.sim_options.get("rivierapro.vsim_flags.gui",
                                                     vsim_extra_args)

        return " ".join(vsim_extra_args)

    @staticmethod
    def _create_run_function():
        """
        Create the vunit_run function to run the test bench
        """
        return """
proc _vunit_run_failure {} {
    catch {
        # tb command can fail when error comes from pli
        echo "Stack trace result from 'bt' command"
        bt
    }
}

proc _vunit_run {} {
    proc on_break {} {
        resume
    }
    onbreak {on_break}

    run -all
}

proc _vunit_sim_restart {} {
    restart
}
"""

    def merge_coverage(self, file_name, args=None):
        """
        Merge coverage from all test cases,
        """

        # Teardown to ensure acdb file was written.
        self._persistent_shell.teardown()

        merge_command = "acdb merge"

        for coverage_file in self._coverage_files:
            if file_exists(coverage_file):
                merge_command += " -i {%s}" % coverage_file.replace('\\', '/')
            else:
                LOGGER.warning("Missing coverage file: %s", coverage_file)

        if args is not None:
            merge_command += " " + " ".join("{%s}" % arg for arg in args)

        merge_command += " -o {%s}" % file_name.replace('\\', '/')

        merge_script_name = join(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'), '-c', '-do',
                      'source %s; quit;' % merge_script_name.replace('\\', '/')]

        print("Merging coverage files into %s..." % file_name)
        vcover_merge_process = Process(vcover_cmd,
                                       env=self.get_env())
        vcover_merge_process.consume_output()
        print("Done merging coverage files")
Пример #4
0
class ModelSimInterface(VsimSimulatorMixin, SimulatorInterface):  # pylint: disable=too-many-instance-attributes
    """
    Mentor Graphics ModelSim interface

    The interface supports both running each simulation in separate vsim processes or
    re-using the same vsim process to avoid startup-overhead (persistent=True)
    """
    name = "modelsim"
    supports_gui_flag = True
    package_users_depend_on_bodies = False

    compile_options = [
        ListOfStringOption("modelsim.vcom_flags"),
        ListOfStringOption("modelsim.vlog_flags"),
    ]

    sim_options = [
        ListOfStringOption("modelsim.vsim_flags"),
        ListOfStringOption("modelsim.vsim_flags.gui"),
        ListOfStringOption("modelsim.init_files.after_load"),
        StringOption("modelsim.init_file.gui"),
    ]

    @classmethod
    def from_args(cls, output_path, args):
        """
        Create new instance from command line arguments object
        """
        persistent = not (args.unique_sim or args.gui)

        return cls(prefix=cls.find_prefix(),
                   output_path=output_path,
                   persistent=persistent,
                   coverage=args.coverage,
                   gui=args.gui)

    @classmethod
    def find_prefix_from_path(cls):
        """
        Find first valid modelsim toolchain prefix
        """
        def has_modelsim_ini(path):
            return os.path.isfile(join(path, "..", "modelsim.ini"))

        return cls.find_toolchain(["vsim"], constraints=[has_modelsim_ini])

    @classmethod
    def supports_vhdl_package_generics(cls):
        """
        Returns True when this simulator supports VHDL package generics
        """
        return True

    def __init__(self,
                 prefix,
                 output_path,
                 persistent=False,
                 gui=False,
                 coverage=None):
        SimulatorInterface.__init__(self, output_path, gui)
        VsimSimulatorMixin.__init__(self,
                                    prefix,
                                    persistent,
                                    sim_cfg_file_name=join(
                                        output_path, "modelsim.ini"))
        self._libraries = []
        self._coverage = coverage
        self._coverage_files = set()
        assert not (persistent and gui)
        self._create_modelsim_ini()

    def _create_modelsim_ini(self):
        """
        Create the modelsim.ini file if it does not exist
        """
        if file_exists(self._sim_cfg_file_name):
            return

        parent = dirname(self._sim_cfg_file_name)
        if not file_exists(parent):
            os.makedirs(parent)

        original_modelsim_ini = os.environ.get(
            "VUNIT_MODELSIM_INI", join(self._prefix, "..", "modelsim.ini"))
        with open(original_modelsim_ini, 'rb') as fread:
            with open(self._sim_cfg_file_name, 'wb') as fwrite:
                fwrite.write(fread.read())

    def add_simulator_specific(self, project):
        """
        Add libraries from modelsim.ini file and add coverage flags
        """
        mapped_libraries = self._get_mapped_libraries()
        for library_name in mapped_libraries:
            if not project.has_library(library_name):
                project.add_builtin_library(library_name)

        if self._coverage is None:
            return

        # Add coverage options
        for source_file in project.get_source_files_in_order():
            if not source_file.compile_options.get("disable_coverage", False):
                source_file.add_compile_option("modelsim.vcom_flags",
                                               ["+cover=" + self._coverage])
                source_file.add_compile_option("modelsim.vlog_flags",
                                               ["+cover=" + self._coverage])

    def setup_library_mapping(self, project):
        """
        Setup library mapping
        """
        mapped_libraries = self._get_mapped_libraries()

        for library in project.get_libraries():
            self._libraries.append(library)
            self.create_library(library.name, library.directory,
                                mapped_libraries)

    def compile_source_file_command(self, source_file):
        """
        Returns the command to compile a single source file
        """
        if source_file.is_vhdl:
            return self.compile_vhdl_file_command(source_file)
        elif source_file.is_any_verilog:
            return self.compile_verilog_file_command(source_file)

        LOGGER.error("Unknown file type: %s", source_file.file_type)
        raise CompileError

    def compile_vhdl_file_command(self, source_file):
        """
        Returns the command to compile a vhdl file
        """
        return ([
            join(self._prefix, 'vcom'), '-quiet', '-modelsimini',
            self._sim_cfg_file_name
        ] + source_file.compile_options.get("modelsim.vcom_flags", []) + [
            '-' + source_file.get_vhdl_standard(), '-work',
            source_file.library.name, source_file.name
        ])

    def compile_verilog_file_command(self, source_file):
        """
        Returns the command to compile a verilog file
        """
        args = [
            join(self._prefix, 'vlog'), '-quiet', '-modelsimini',
            self._sim_cfg_file_name
        ]
        if source_file.is_system_verilog:
            args += ["-sv"]
        args += source_file.compile_options.get("modelsim.vlog_flags", [])
        args += ['-work', source_file.library.name, source_file.name]

        for library in self._libraries:
            args += ["-L", library.name]
        for include_dir in source_file.include_dirs:
            args += ["+incdir+%s" % include_dir]
        for key, value in source_file.defines.items():
            args += ["+define+%s=%s" % (key, value)]
        return args

    def create_library(self, library_name, path, mapped_libraries=None):
        """
        Create and map a library_name to path
        """
        mapped_libraries = mapped_libraries if mapped_libraries is not None else {}

        if not file_exists(dirname(abspath(path))):
            os.makedirs(dirname(abspath(path)))

        if not file_exists(path):
            proc = Process([join(self._prefix, 'vlib'), '-unix', path],
                           env=self.get_env())
            proc.consume_output(callback=None)

        if library_name in mapped_libraries and mapped_libraries[
                library_name] == path:
            return

        cfg = parse_modelsimini(self._sim_cfg_file_name)
        cfg.set("Library", library_name, path)
        write_modelsimini(cfg, self._sim_cfg_file_name)

    def _get_mapped_libraries(self):
        """
        Get mapped libraries from modelsim.ini file
        """
        cfg = parse_modelsimini(self._sim_cfg_file_name)
        libraries = dict(cfg.items("Library"))
        if "others" in libraries:
            del libraries["others"]
        return libraries

    def _create_load_function(self, test_suite_name, config, output_path):
        """
        Create the vunit_load TCL function that runs the vsim command and loads the design
        """

        set_generic_str = " ".join(
            ('-g/%s/%s=%s' %
             (config.entity_name, name, encode_generic_value(value))
             for name, value in config.generics.items()))
        pli_str = " ".join("-pli {%s}" % fix_path(name)
                           for name in config.sim_options.get('pli', []))

        if config.architecture_name is None:
            architecture_suffix = ""
        else:
            architecture_suffix = "(%s)" % config.architecture_name

        if self._coverage is None:
            coverage_save_cmd = ""
            coverage_args = ""
        else:
            coverage_file = join(output_path, "coverage.ucdb")
            self._coverage_files.add(coverage_file)
            coverage_save_cmd = (
                "coverage save -onexit -testname {%s} -assert -directive -cvg -codeAll {%s}"
                % (test_suite_name, fix_path(coverage_file)))

            coverage_args = "-coverage"

        vsim_flags = [
            "-wlf {%s}" % fix_path(join(output_path, "vsim.wlf")),
            "-quiet",
            "-t ps",
            # for correct handling of verilog fatal/finish
            "-onfinish stop",
            pli_str,
            set_generic_str,
            config.library_name + "." + config.entity_name +
            architecture_suffix,
            coverage_args,
            self._vsim_extra_args(config)
        ]

        # There is a known bug in modelsim that prevents the -modelsimini flag from accepting
        # a space in the path even with escaping, see issue #36
        if " " not in self._sim_cfg_file_name:
            vsim_flags.insert(
                0, "-modelsimini %s" % fix_path(self._sim_cfg_file_name))

        for library in self._libraries:
            vsim_flags += ["-L", library.name]

        vhdl_assert_stop_level_mapping = dict(warning=1, error=2, failure=3)

        tcl = """
proc vunit_load {{{{vsim_extra_args ""}}}} {{
    set vsim_failed [catch {{
        eval vsim ${{vsim_extra_args}} {{{vsim_flags}}}
    }}]

    if {{${{vsim_failed}}}} {{
       echo Command 'vsim ${{vsim_extra_args}} {vsim_flags}' failed
       echo Bad flag from vsim_extra_args?
       return true
    }}

    if {{[_vunit_source_init_files_after_load]}} {{
        return true
    }}

    global BreakOnAssertion
    set BreakOnAssertion {break_on_assert}

    global NumericStdNoWarnings
    set NumericStdNoWarnings {no_warnings}

    global StdArithNoWarnings
    set StdArithNoWarnings {no_warnings}

    {coverage_save_cmd}
    return false
}}
""".format(coverage_save_cmd=coverage_save_cmd,
           vsim_flags=" ".join(vsim_flags),
           break_on_assert=vhdl_assert_stop_level_mapping[
               config.vhdl_assert_stop_level],
           no_warnings=1
           if config.sim_options.get("disable_ieee_warnings", False) else 0)

        return tcl

    @staticmethod
    def _create_run_function():
        """
        Create the vunit_run function to run the test bench
        """
        return """

proc _vunit_run_failure {} {
    catch {
        # tb command can fail when error comes from pli
        echo "Stack trace result from 'tb' command"
        echo [tb]
        echo
        echo "Surrounding code from 'see' command"
        echo [see]
    }
}

proc _vunit_run {} {
    proc on_break {} {
        resume
    }
    onbreak {on_break}

    run -all
}

proc _vunit_sim_restart {} {
    restart -f
}
"""

    def _vsim_extra_args(self, config):
        """
        Determine vsim_extra_args
        """
        vsim_extra_args = []
        vsim_extra_args = config.sim_options.get("modelsim.vsim_flags",
                                                 vsim_extra_args)

        if self._gui:
            vsim_extra_args = config.sim_options.get("modelsim.vsim_flags.gui",
                                                     vsim_extra_args)

        return " ".join(vsim_extra_args)

    def post_process(self, output_path):
        """
        Merge coverage from all test cases,
        top hierarchy level is removed since it has different name in each test case
        """
        if self._coverage is None:
            return

        # Teardown to ensure ucdb file was written.
        del self._persistent_shell

        merged_coverage_file = join(output_path, "merged_coverage.ucdb")
        vcover_cmd = [
            join(self._prefix, 'vcover'), 'merge', '-strip', '1',
            merged_coverage_file
        ]

        for coverage_file in self._coverage_files:
            if file_exists(coverage_file):
                vcover_cmd.append(coverage_file)
            else:
                LOGGER.warning("Missing coverage file: %s", coverage_file)

        print("Merging coverage files into %s..." % merged_coverage_file)
        vcover_merge_process = Process(vcover_cmd, env=self.get_env())
        vcover_merge_process.consume_output()
        print("Done merging coverage files")

    @staticmethod
    def get_env():
        """
        Remove MODELSIM environment variable
        """
        remove = ("MODELSIM", )
        env = os.environ.copy()
        for key in remove:
            if key in env.keys():
                del env[key]
        return env
Пример #5
0
class ActiveHDLInterface(SimulatorInterface):
    """
    Active HDL interface
    """

    name = "activehdl"
    supports_gui_flag = True
    package_users_depend_on_bodies = True
    compile_options = [
        ListOfStringOption("activehdl.vcom_flags"),
        ListOfStringOption("activehdl.vlog_flags"),
    ]

    sim_options = [
        ListOfStringOption("activehdl.vsim_flags"),
        ListOfStringOption("activehdl.vsim_flags.gui"),
        StringOption("activehdl.init_file.gui"),
    ]

    @classmethod
    def from_args(cls, args, output_path, **kwargs):
        """
        Create new instance from command line arguments object
        """
        return cls(prefix=cls.find_prefix(),
                   output_path=output_path,
                   gui=args.gui)

    @classmethod
    def find_prefix_from_path(cls):
        return cls.find_toolchain(["vsim", "avhdl"])

    @classmethod
    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())
        consumer = VersionConsumer()
        proc.consume_output(consumer)
        if consumer.major is not None:
            return consumer.minor >= 1 if consumer.major == 10 else consumer.major > 10

        return False

    def __init__(self, prefix, output_path, gui=False):
        SimulatorInterface.__init__(self, output_path, gui)
        self._library_cfg = join(output_path, "library.cfg")
        self._prefix = prefix
        self._create_library_cfg()
        self._libraries = []
        self._coverage_files = set()

    def setup_library_mapping(self, project):
        """
        Setup library mapping
        """
        mapped_libraries = self._get_mapped_libraries()

        for library in project.get_libraries():
            self._libraries.append(library)
            self.create_library(library.name, library.directory,
                                mapped_libraries)

    def compile_source_file_command(self, source_file):
        """
        Returns the command to compile a single source_file
        """
        if source_file.is_vhdl:
            return self.compile_vhdl_file_command(source_file)

        if source_file.is_any_verilog:
            return self.compile_verilog_file_command(source_file)

        LOGGER.error("Unknown file type: %s", source_file.file_type)
        raise CompileError

    @staticmethod
    def _std_str(vhdl_standard):
        """
        Convert standard to format of Active-HDL command line flag
        """
        if vhdl_standard <= VHDL.STD_2008:
            return "-%s" % vhdl_standard

        raise ValueError("Invalid VHDL standard %s" % vhdl_standard)

    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)
        ] + source_file.compile_options.get("activehdl.vcom_flags", []) + [
            self._std_str(source_file.get_vhdl_standard()),
            "-work",
            source_file.library.name,
            source_file.name,
        ])

    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 += source_file.compile_options.get("activehdl.vlog_flags", [])
        args += ["-work", source_file.library.name, source_file.name]
        for library in self._libraries:
            args += ["-l", library.name]
        for include_dir in source_file.include_dirs:
            args += ["+incdir+%s" % include_dir]
        for key, value in source_file.defines.items():
            args += ["+define+%s=%s" % (key, value)]
        return args

    def create_library(self, library_name, path, mapped_libraries=None):
        """
        Create and map a library_name to path
        """
        mapped_libraries = mapped_libraries if mapped_libraries is not None else {}

        if not file_exists(dirname(abspath(path))):
            os.makedirs(dirname(abspath(path)))

        if not file_exists(path):
            proc = Process(
                [join(self._prefix, "vlib"), library_name, path],
                cwd=dirname(self._library_cfg),
                env=self.get_env(),
            )
            proc.consume_output(callback=None)

        if library_name in mapped_libraries and mapped_libraries[
                library_name] == path:
            return

        proc = Process(
            [join(self._prefix, "vmap"), library_name, path],
            cwd=dirname(self._library_cfg),
            env=self.get_env(),
        )
        proc.consume_output(callback=None)

    def _create_library_cfg(self):
        """
        Create the library.cfg file if it does not exist
        """
        if file_exists(self._library_cfg):
            return

        with open(self._library_cfg, "w") as ofile:
            ofile.write('$INCLUDE = "%s"\n' %
                        join(self._prefix, "..", "vlib", "library.cfg"))

    _library_re = re.compile(r'([a-zA-Z_]+)\s=\s"(.*)"')

    def _get_mapped_libraries(self):
        """
        Get mapped libraries from library.cfg file
        """
        with open(self._library_cfg, "r") as fptr:
            text = fptr.read()

        libraries = {}
        for line in text.splitlines():
            match = self._library_re.match(line)
            if match is None:
                continue
            key = match.group(1)
            value = match.group(2)
            libraries[key] = abspath(
                join(dirname(self._library_cfg), dirname(value)))
        return libraries

    def _vsim_extra_args(self, config):
        """
        Determine vsim_extra_args
        """
        vsim_extra_args = []
        vsim_extra_args = config.sim_options.get("activehdl.vsim_flags",
                                                 vsim_extra_args)

        if self._gui:
            vsim_extra_args = config.sim_options.get(
                "activehdl.vsim_flags.gui", vsim_extra_args)

        return " ".join(vsim_extra_args)

    def _create_load_function(self, config, output_path):
        """
        Create the vunit_load TCL function that runs the vsim command and loads the design
        """
        set_generic_str = "\n    ".join(
            ("set vunit_generic_%s {%s}" % (name, value)
             for name, value in config.generics.items()))
        set_generic_name_str = " ".join(
            ("-g/%s/%s=${vunit_generic_%s}" % (config.entity_name, name, name)
             for name in config.generics))
        pli_str = " ".join('-pli "%s"' % fix_path(name)
                           for name in config.sim_options.get("pli", []))

        vsim_flags = [
            pli_str,
            set_generic_name_str,
            "-lib",
            config.library_name,
            config.entity_name,
        ]

        if config.architecture_name is not None:
            vsim_flags.append(config.architecture_name)

        if config.sim_options.get("enable_coverage", False):
            coverage_file_path = join(output_path, "coverage.acdb")
            self._coverage_files.add(coverage_file_path)
            vsim_flags += ["-acdb_file {%s}" % fix_path(coverage_file_path)]

        vsim_flags += [self._vsim_extra_args(config)]

        if config.sim_options.get("disable_ieee_warnings", False):
            vsim_flags.append("-ieee_nowarn")

        # Add the the testbench top-level unit last as coverage is
        # only collected for the top-level unit specified last

        vhdl_assert_stop_level_mapping = dict(warning=1, error=2, failure=3)

        tcl = """
proc vunit_load {{}} {{
    {set_generic_str}
    set vsim_failed [catch {{
        vsim {vsim_flags}
    }}]
    if {{${{vsim_failed}}}} {{
        return true
    }}

    global breakassertlevel
    set breakassertlevel {breaklevel}

    global builtinbreakassertlevel
    set builtinbreakassertlevel $breakassertlevel

    return false
}}
""".format(
            set_generic_str=set_generic_str,
            vsim_flags=" ".join(vsim_flags),
            breaklevel=vhdl_assert_stop_level_mapping[
                config.vhdl_assert_stop_level],
        )

        return tcl

    @staticmethod
    def _create_run_function():
        """
        Create the vunit_run function to run the test bench
        """
        return """
proc vunit_run {} {
    run -all
    if {![is_test_suite_done]} {
        catch {
            # tb command can fail when error comes from pli
            echo ""
            echo "Stack trace result from 'bt' command"
            bt
        }
        return true;
    }
    return false;
}
"""

    def merge_coverage(self, file_name, args=None):
        """
        Merge coverage from all test cases,
        """

        merge_command = "onerror {quit -code 1}\n"
        merge_command += "acdb merge"

        for coverage_file in self._coverage_files:
            if file_exists(coverage_file):
                merge_command += " -i {%s}" % fix_path(coverage_file)
            else:
                LOGGER.warning("Missing coverage file: %s", coverage_file)

        if args is not None:
            merge_command += " " + " ".join("{%s}" % arg for arg in args)

        merge_command += " -o {%s}" % fix_path(file_name) + "\n"

        merge_script_name = join(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"),
            "-tcl",
            "%s" % fix_path(merge_script_name),
        ]

        print("Merging coverage files into %s..." % file_name)
        vcover_merge_process = Process(vcover_cmd, env=self.get_env())
        vcover_merge_process.consume_output()
        print("Done merging coverage files")

    def _create_common_script(self, config, output_path):
        """
        Create tcl script with functions common to interactive and batch modes
        """
        tcl = ""
        tcl += get_is_test_suite_done_tcl(get_result_file_name(output_path))
        tcl += self._create_load_function(config, output_path)
        tcl += self._create_run_function()
        return tcl

    @staticmethod
    def _create_batch_script(common_file_name, load_only=False):
        """
        Create tcl script to run in batch mode
        """
        batch_do = ""
        batch_do += 'source "%s"\n' % fix_path(common_file_name)
        batch_do += "set failed [vunit_load]\n"
        batch_do += "if {$failed} {quit -code 1}\n"
        if not load_only:
            batch_do += "set failed [vunit_run]\n"
            batch_do += "if {$failed} {quit -code 1}\n"
        batch_do += "quit -code 0\n"
        return batch_do

    def _create_gui_script(self, common_file_name, config):
        """
        Create the user facing script which loads common functions and prints a help message
        """

        tcl = ""
        tcl += 'source "%s"\n' % fix_path(common_file_name)
        tcl += "workspace create workspace\n"
        tcl += "design create -a design .\n"

        for library in self._libraries:
            tcl += "vmap %s %s\n" % (library.name, fix_path(library.directory))

        tcl += "vunit_load\n"

        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 += (
            'puts "VUnit help: Design already loaded. Use run -all to run the test."\n'
        )

        return tcl

    def _run_batch_file(self, batch_file_name, gui, cwd):
        """
        Run a test bench in batch by invoking a new vsim process from the command line
        """

        todo = '@do -tcl ""%s""' % fix_path(batch_file_name)
        if not gui:
            todo = "@onerror {quit -code 1};" + todo

        try:
            args = [
                join(self._prefix, "vsim"),
                "-gui" if gui else "-c",
                "-l",
                join(dirname(batch_file_name), "transcript"),
                "-do",
                todo,
            ]

            proc = Process(args, cwd=cwd, env=self.get_env())
            proc.consume_output()
        except Process.NonZeroExitCode:
            return False
        return True

    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")

        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))

        if self._gui:
            gui_path = join(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(batch_file_name,
                                    gui=False,
                                    cwd=dirname(self._library_cfg))
Пример #6
0
class GHDLInterface(SimulatorInterface):
    """
    Interface for GHDL simulator
    """

    name = "ghdl"
    executable = os.environ.get("GHDL", "ghdl")
    supports_gui_flag = True
    supports_colors_in_gui = True

    compile_options = [ListOfStringOption("ghdl.flags")]

    sim_options = [
        ListOfStringOption("ghdl.sim_flags"),
        ListOfStringOption("ghdl.elab_flags"),
        StringOption("ghdl.gtkwave_script.gui"),
        BooleanOption("ghdl.elab_e"),
    ]

    @staticmethod
    def add_arguments(parser):
        """
        Add command line arguments
        """
        group = parser.add_argument_group("ghdl", description="GHDL specific flags")
        group.add_argument(
            "--gtkwave-fmt",
            choices=["vcd", "ghw"],
            default=None,
            help="Save .vcd or .ghw to open in gtkwave",
        )
        group.add_argument(
            "--gtkwave-args", default="", help="Arguments to pass to gtkwave"
        )

    @classmethod
    def from_args(cls, args, output_path, **kwargs):
        """
        Create instance from args namespace
        """
        prefix = cls.find_prefix()
        return cls(
            output_path=output_path,
            prefix=prefix,
            gui=args.gui,
            gtkwave_fmt=args.gtkwave_fmt,
            gtkwave_args=args.gtkwave_args,
            backend=cls.determine_backend(prefix),
        )

    @classmethod
    def find_prefix_from_path(cls):
        """
        Find first valid ghdl toolchain prefix
        """
        return cls.find_toolchain([cls.executable])

    def __init__(  # pylint: disable=too-many-arguments
        self,
        output_path,
        prefix,
        gui=False,
        gtkwave_fmt=None,
        gtkwave_args="",
        backend="llvm",
    ):
        SimulatorInterface.__init__(self, output_path, gui)
        self._prefix = prefix
        self._project = None

        if gui and (not self.find_executable("gtkwave")):
            raise RuntimeError(
                "Cannot find the gtkwave executable in the PATH environment variable. GUI not possible"
            )

        self._gui = gui
        self._gtkwave_fmt = "ghw" if gui and gtkwave_fmt is None else gtkwave_fmt
        self._gtkwave_args = gtkwave_args
        self._backend = backend
        self._vhdl_standard = None

    def has_valid_exit_code(self):
        """
        Return if the simulation should fail with nonzero exit codes
        """
        return self._vhdl_standard >= VHDL.STD_2008

    @classmethod
    def determine_backend(cls, prefix):
        """
        Determine the GHDL backend
        """
        mapping = {
            "mcode code generator": "mcode",
            "llvm code generator": "llvm",
            "GCC back-end code generator": "gcc",
        }
        output = subprocess.check_output(
            [join(prefix, cls.executable), "--version"]
        ).decode()
        for name, backend in mapping.items():
            if name in output:
                LOGGER.debug("Detected GHDL %s", name)
                return backend

        LOGGER.error("Could not detect known LLVM backend by parsing 'ghdl --version'")
        print("Expected to find one of %r" % mapping.keys())
        print("== Output of 'ghdl --version'" + ("=" * 60))
        print(output)
        print("=============================" + ("=" * 60))
        raise AssertionError(
            "No known GHDL back-end could be detected from running 'ghdl --version'"
        )

    @classmethod
    def supports_vhpi(cls):
        """
        Return if the simulator supports VHPI
        """
        return cls.determine_backend(cls.find_prefix_from_path()) != "mcode"

    def _has_output_flag(self):
        """
        Returns if backend supports output flag
        """
        return self._backend in ("llvm", "gcc")

    def setup_library_mapping(self, project):
        """
        Setup library mapping
        """
        self._project = project
        for library in project.get_libraries():
            if not exists(library.directory):
                os.makedirs(library.directory)

        vhdl_standards = set(
            source_file.get_vhdl_standard()
            for source_file in project.get_source_files_in_order()
            if source_file.is_vhdl
        )

        if not vhdl_standards:
            self._vhdl_standard = VHDL.STD_2008
        elif len(vhdl_standards) != 1:
            raise RuntimeError(
                "GHDL cannot handle mixed VHDL standards, found %r"
                % list(vhdl_standards)
            )
        else:
            self._vhdl_standard = list(vhdl_standards)[0]

    def compile_source_file_command(self, source_file):
        """
        Returns the command to compile a single source_file
        """
        if source_file.is_vhdl:
            return self.compile_vhdl_file_command(source_file)

        LOGGER.error("Unknown file type: %s", source_file.file_type)
        raise CompileError

    @staticmethod
    def _std_str(vhdl_standard):
        """
        Convert standard to format of GHDL command line flag
        """
        if vhdl_standard == VHDL.STD_2002:
            return "02"

        if vhdl_standard == VHDL.STD_2008:
            return "08"

        if vhdl_standard == VHDL.STD_1993:
            return "93"

        raise ValueError("Invalid VHDL standard %s" % vhdl_standard)

    def compile_vhdl_file_command(self, source_file):
        """
        Returns the command to compile a vhdl file
        """
        cmd = [
            join(self._prefix, self.executable),
            "-a",
            "--workdir=%s" % source_file.library.directory,
            "--work=%s" % source_file.library.name,
            "--std=%s" % self._std_str(source_file.get_vhdl_standard()),
        ]
        for library in self._project.get_libraries():
            cmd += ["-P%s" % library.directory]
        cmd += source_file.compile_options.get("ghdl.flags", [])
        cmd += [source_file.name]
        return cmd

    def _get_command(self, config, output_path, ghdl_e):
        """
        Return GHDL simulation command
        """
        cmd = [join(self._prefix, self.executable)]

        if ghdl_e:
            cmd += ["-e"]
        else:
            cmd += ["--elab-run"]

        cmd += ["--std=%s" % self._std_str(self._vhdl_standard)]
        cmd += ["--work=%s" % config.library_name]
        cmd += [
            "--workdir=%s" % self._project.get_library(config.library_name).directory
        ]
        cmd += ["-P%s" % lib.directory for lib in self._project.get_libraries()]
        if self._has_output_flag():
            cmd += [
                "-o",
                join(
                    output_path,
                    "%s-%s" % (config.entity_name, config.architecture_name),
                ),
            ]
        cmd += config.sim_options.get("ghdl.elab_flags", [])
        cmd += [config.entity_name, config.architecture_name]

        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"]

        return cmd

    def simulate(  # pylint: disable=too-many-locals
        self, output_path, test_suite_name, config, elaborate_only
    ):
        """
        Simulate with entity as top level using generics
        """

        script_path = join(output_path, self.name)

        if not exists(script_path):
            os.makedirs(script_path)

        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:
            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

        status = True
        try:
            proc = Process(cmd)
            proc.consume_output()
        except Process.NonZeroExitCode:
            status = False

        if self._gui and not elaborate_only:
            cmd = ["gtkwave"] + shlex.split(self._gtkwave_args) + [data_file_name]

            init_file = config.sim_options.get(self.name + ".gtkwave_script.gui", None)
            if init_file is not None:
                cmd += ["--script", "{}".format(abspath(init_file))]

            stdout.write("%s\n" % " ".join(cmd))
            subprocess.call(cmd)

        return status
Пример #7
0
class RivieraProInterface(VsimSimulatorMixin, SimulatorInterface):
    """
    Riviera Pro interface
    """

    name = "rivierapro"
    supports_gui_flag = True
    package_users_depend_on_bodies = True

    compile_options = [
        ListOfStringOption("rivierapro.vcom_flags"),
        ListOfStringOption("rivierapro.vlog_flags"),
    ]

    sim_options = [
        ListOfStringOption("rivierapro.vsim_flags"),
        ListOfStringOption("rivierapro.vsim_flags.gui"),
        ListOfStringOption("rivierapro.init_files.after_load"),
        StringOption("rivierapro.init_file.gui"),
    ]

    @classmethod
    def from_args(cls, output_path, args):
        """
        Create new instance from command line arguments object
        """
        persistent = not (args.unique_sim or args.gui)

        return cls(prefix=cls.find_prefix(),
                   library_cfg=join(output_path, "library.cfg"),
                   persistent=persistent,
                   coverage=args.coverage,
                   gui=args.gui)

    @classmethod
    def find_prefix_from_path(cls):
        """
        Find RivieraPro toolchain.

        Must have vsim and vsimsa binaries but no avhdl.exe
        """
        def no_avhdl(path):
            return not file_exists(join(path, "avhdl.exe"))
        return cls.find_toolchain(["vsim",
                                   "vsimsa"],
                                  constraints=[no_avhdl])

    @classmethod
    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())
        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 cls.name

        return None

    @classmethod
    def supports_vhdl_package_generics(cls):
        """
        Returns True when this simulator supports VHDL package generics
        """
        return True

    def __init__(self, prefix, library_cfg="library.cfg", persistent=False, gui=False, coverage=None):
        SimulatorInterface.__init__(self)
        VsimSimulatorMixin.__init__(self, prefix, persistent, gui, library_cfg)
        self._create_library_cfg()
        self._libraries = []
        self._coverage = coverage
        self._coverage_files = set()

    def add_simulator_specific(self, project):
        """
        Add coverage flags
        """
        if self._coverage is None:
            return

        # Add coverage options
        for source_file in project.get_source_files_in_order():
            source_file.add_compile_option("rivierapro.vcom_flags", ['-coverage', self._coverage])
            source_file.add_compile_option("rivierapro.vlog_flags", ['-coverage', self._coverage])

    def setup_library_mapping(self, project):
        """
        Setup library mapping
        """
        mapped_libraries = self._get_mapped_libraries()
        for library in project.get_libraries():
            self._libraries.append(library)
            self.create_library(library.name, library.directory, mapped_libraries)

    def compile_source_file_command(self, source_file):
        """
        Returns the command to compile a single source_file
        """
        if source_file.file_type == 'vhdl':
            return self.compile_vhdl_file_command(source_file)
        elif source_file.file_type == 'verilog':
            return self.compile_verilog_file_command(source_file)

        LOGGER.error("Unknown file type: %s", source_file.file_type)
        raise CompileError

    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._sim_cfg_file_name)] +
                source_file.compile_options.get("rivierapro.vcom_flags", []) +
                ['-' + source_file.get_vhdl_standard(), '-work', source_file.library.name, source_file.name])

    def compile_verilog_file_command(self, source_file):
        """
        Returns the command to compile a Verilog file
        """
        args = [join(self._prefix, 'vlog'), '-quiet', '-sv2k12', '-lc', self._sim_cfg_file_name]
        args += source_file.compile_options.get("rivierapro.vlog_flags", [])
        args += ['-work', source_file.library.name, source_file.name]
        for library in self._libraries:
            args += ["-l", library.name]
        for include_dir in source_file.include_dirs:
            args += ["+incdir+%s" % include_dir]
        for key, value in source_file.defines.items():
            args += ["+define+%s=%s" % (key, value)]
        return args

    def create_library(self, library_name, path, mapped_libraries=None):
        """
        Create and map a library_name to path
        """
        mapped_libraries = mapped_libraries if mapped_libraries is not None else {}

        if not file_exists(dirname(abspath(path))):
            os.makedirs(dirname(abspath(path)))

        if not file_exists(path):
            proc = Process([join(self._prefix, 'vlib'), library_name, path],
                           cwd=dirname(self._sim_cfg_file_name),
                           env=self.get_env())
            proc.consume_output(callback=None)

        if library_name in mapped_libraries and mapped_libraries[library_name] == path:
            return

        proc = Process([join(self._prefix, 'vmap'), library_name, path],
                       cwd=dirname(self._sim_cfg_file_name),
                       env=self.get_env())
        proc.consume_output(callback=None)

    def _create_library_cfg(self):
        """
        Create the library.cfg file if it does not exist
        """
        if file_exists(self._sim_cfg_file_name):
            return

        with open(self._sim_cfg_file_name, "w") as ofile:
            ofile.write('$INCLUDE = "%s"\n' % join(self._prefix, "..", "vlib", "library.cfg"))

    _library_re = re.compile(r'([a-zA-Z_0-9]+)\s=\s"(.*)"')

    def _get_mapped_libraries(self):
        """
        Get mapped libraries from library.cfg file
        """
        with open(self._sim_cfg_file_name, "r") as fptr:
            text = fptr.read()

        libraries = {}
        for line in text.splitlines():
            match = self._library_re.match(line)
            if match is None:
                continue
            key = match.group(1)
            value = match.group(2)
            libraries[key] = abspath(join(dirname(self._sim_cfg_file_name), dirname(value)))
        return libraries

    def _create_load_function(self,
                              test_suite_name,  # pylint: disable=unused-argument
                              config, output_path):
        """
        Create the vunit_load TCL function that runs the vsim command and loads the design
        """
        set_generic_str = " ".join(('-g/%s/%s=%s' % (config.entity_name,
                                                     name,
                                                     format_generic(value))
                                    for name, value in config.generics.items()))
        pli_str = " ".join("-pli \"%s\"" % fix_path(name) for name in config.sim_options.get('pli', []))

        if self._coverage is None:
            coverage_args = ""
            coverage_file = ""
        else:
            coverage_args = "-acdb_cov " + self._coverage
            coverage_file_path = join(output_path, "coverage.acdb")
            self._coverage_files.add(coverage_file_path)
            coverage_file = "-acdb_file {%s}" % coverage_file_path

        vsim_flags = ["-dataset {%s}" % fix_path(join(output_path, "dataset.asdb")),
                      pli_str,
                      set_generic_str,
                      "-lib",
                      config.library_name,
                      config.entity_name,
                      coverage_args,
                      coverage_file,
                      self._vsim_extra_args(config)]

        if config.architecture_name is not None:
            vsim_flags.append(config.architecture_name)

        if config.sim_options.get("disable_ieee_warnings", False):
            vsim_flags.append("-ieee_nowarn")

        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

    set vsim_failed [catch {{
        eval vsim {{{vsim_flags}}}
    }}]

    if {{${{vsim_failed}}}} {{
        return 1
    }}

    if {{[_vunit_source_init_files_after_load]}} {{
        return 1
    }}

    set no_vhdl_test_runner_exit [catch {{examine /vunit_lib.run_base_pkg/runner.exit_simulation}}]
    set no_verilog_test_runner_exit [catch {{examine /\\\\package vunit_lib.vunit_pkg\\\\/__runner__}}]
    if {{${{no_vhdl_test_runner_exit}} && ${{no_verilog_test_runner_exit}}}}  {{
        echo {{Error: No vunit test runner package used}}
        return 1
    }}

    vhdlassert.break {break_level}
    vhdlassert.break -builtin {break_level}

    return 0
}}
""".format(vsim_flags=" ".join(vsim_flags),
           break_level=config.vhdl_assert_stop_level)

        return tcl

    def _vsim_extra_args(self, config):
        """
        Determine vsim_extra_args
        """
        vsim_extra_args = []
        vsim_extra_args = config.sim_options.get("rivierapro.vsim_flags",
                                                 vsim_extra_args)

        if self._gui:
            vsim_extra_args = config.sim_options.get("rivierapro.vsim_flags.gui",
                                                     vsim_extra_args)

        return " ".join(vsim_extra_args)

    @staticmethod
    def _create_run_function():
        """
        Create the vunit_run function to run the test bench
        """
        return """
proc vunit_run {} {
    proc on_break {} {
        resume
    }
    onbreak {on_break}

    set has_vhdl_runner [expr ![catch {examine /vunit_lib.run_base_pkg/runner}]]
    set has_verilog_runner [expr ![catch {examine /\\\\package vunit_lib.vunit_pkg\\\\/__runner__}]]

    if {${has_vhdl_runner}} {
        set status_boolean "/vunit_lib.run_base_pkg/runner.exit_without_errors"
        set true_value true
    } elseif {${has_verilog_runner}} {
        set status_boolean "/\\\\package vunit_lib.vunit_pkg\\\\/__runner__.exit_without_errors"
        set true_value 1
    } else {
        echo "No finish mechanism detected"
        return 1;
    }

    run -all
    set failed [expr [examine ${status_boolean}]!=${true_value}]
    if {$failed} {
        catch {
            # tb command can fail when error comes from pli
            echo ""
            echo "Stack trace result from 'bt' command"
            bt
        }
    }
    return $failed
}

proc _vunit_sim_restart {} {
    restart
}
"""

    def post_process(self, output_path):
        """
        Merge coverage from all test cases,
        """

        if self._coverage is None:
            return

        # Teardown to ensure acdb file was written.
        del self._persistent_shell

        merged_coverage_file = join(output_path, "merged_coverage.acdb")
        merge_command = "acdb merge"

        for coverage_file in self._coverage_files:
            if file_exists(coverage_file):
                merge_command += " -i {%s}" % coverage_file.replace('\\', '/')
            else:
                LOGGER.warning("Missing coverage file: %s", coverage_file)

        merge_command += " -o {%s}" % merged_coverage_file.replace('\\', '/')

        vcover_cmd = [join(self._prefix, 'vsim'), '-c', '-do', '%s; quit;' % merge_command]

        print("Merging coverage files into %s..." % merged_coverage_file)
        vcover_merge_process = Process(vcover_cmd,
                                       env=self.get_env())
        vcover_merge_process.consume_output()
        print("Done merging coverage files")