Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Visualizer support #1079

Merged
merged 4 commits into from
Dec 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions docs/news.d/899.feature.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
[Questa/Modelsim] Added support for 3-step flow and the Visualizer debugger.

The 3-step flow is enabled by setting the simulation option ``modelsim.three_step_flow`` to ``True``. Extra
flags to the ``vopt`` step can be provided with the simulation flags ``modelsim.vopt_flags`` and
``modelsim.vopt_flags.gui`` in normal and GUI mode, respectively.

The Visualizer debugger is enabled from the command line using the ``--debugger=visualizer`` option (in
addition to the ``--gui`` option for GUI mode). The 3-step flow must be enabled for this feature while
the default ``original`` debugger works with or without the 3-step flow.

Note: There is a bug in Visualizer preventing the normal ``restart`` command from being used. Please use the
``vunit_restart`` command instead.
13 changes: 13 additions & 0 deletions docs/py/opts.rst
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,19 @@ The following simulation options are known.
Additionally, the ``vunit_tb_name`` variable is defined as the name of the test bench.
Must be a string.

``modelsim.three_step_flow``
Enable 3-step flow where a separate ``vopt`` step is executed before ``vsim`` is called.
Must be a boolean value. Default is False.

``modelsim.vopt_flags``
Extra arguments passed to ``vopt`` when ``modelsim.three_step_flow`` is ``True``.
Must be a list of strings.

``modelsim.vsim_flags.gui``
Extra arguments passed to ``vopt`` when ``modelsim.three_step_flow`` is ``True`` and
GUI mode is enabled. Takes precedence over ``modelsim.vopt_flags``. Must be a list of
strings.

``rivierapro.vsim_flags``
Extra arguments passed to ``vsim`` when loading the design.
Must be a list of strings.
Expand Down
69 changes: 69 additions & 0 deletions tests/unit/test_modelsim_interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
from vunit.sim_if.modelsim import ModelSimInterface
from vunit.project import Project
from vunit.ostools import renew_path, write_file
from vunit.test.bench import Configuration
from vunit.vhdl_standard import VHDL


Expand Down Expand Up @@ -316,14 +317,61 @@ def test_overwrites_modelsim_ini_file_from_user(self):
with open(modelsim_ini, "r") as fptr:
self.assertEqual(fptr.read(), "user")

@mock.patch("vunit.sim_if.modelsim.LOGGER", autospec=True)
@mock.patch("vunit.sim_if.check_output", autospec=True, return_value="")
@mock.patch("vunit.sim_if.modelsim.Process", autospec=True)
@mock.patch("vunit.sim_if.vsim_simulator_mixin.Process", autospec=True)
def test_optimize(self, vsim_simulator_mixin_process, modelsim_process, check_output, LOGGER):
simif = ModelSimInterface(prefix=self.prefix_path, output_path=self.output_path, persistent=False)
project = Project()
project.add_library("lib", str(Path(self.libraries_path) / "lib"))
write_file("file.vhd", "")
project.add_source_file("file.vhd", "lib", file_type="vhdl", vhdl_standard=VHDL.standard("2008"))
simif.compile_project(project)
config = make_config(sim_options={"modelsim.three_step_flow": True})

# First call should optimize design
simif.simulate(self.simulation_output_path, "test_suite_name", config, False)
design_to_optimize = "lib.tb(test)"
expected_calls = [
mock.call("%s scheduled for optimization.", design_to_optimize),
mock.call("Acquired library lock for %s to optimize %s.", "lib", design_to_optimize),
mock.call("Optimizing %s.", design_to_optimize),
mock.call("%s optimization completed.", design_to_optimize),
]
self.assertEqual(LOGGER.debug.call_count, len(expected_calls))
LOGGER.debug.assert_has_calls(expected_calls)

# Second call should reuse the already optimized design
LOGGER.reset_mock()
simif.simulate(self.simulation_output_path, "test_suite_name", config, False)
LOGGER.debug.assert_called_once_with("Reusing optimized %s.", "lib.tb(test)")

# Fake that design is being optimized and that it is being waited for
LOGGER.reset_mock()
simif._optimized_designs[design_to_optimize]["optimized_design"] = None
simif.simulate(self.simulation_output_path, "test_suite_name", config, False)
expected_debug_calls = [mock.call("Waiting for %s to be optimized.", design_to_optimize)]
self.assertEqual(LOGGER.debug.call_count, len(expected_debug_calls))
LOGGER.debug.assert_has_calls(expected_debug_calls)
expected_error_calls = [
mock.call("Failed waiting for %s to be optimized (optimization failed).", design_to_optimize)
]
self.assertEqual(LOGGER.error.call_count, len(expected_error_calls))
LOGGER.error.assert_has_calls(expected_error_calls)

def setUp(self):
self.test_path = str(Path(__file__).parent / "test_modelsim_out")

self.output_path = str(Path(self.test_path) / "modelsim")
self.prefix_path = str(Path(self.test_path) / "prefix" / "bin")
self.libraries_path = str(Path(self.output_path) / "libraries")
self.simulation_output_path = str(Path(self.test_path) / "test_output" / "lib.tb")
renew_path(self.test_path)
renew_path(self.output_path)
renew_path(self.prefix_path)
renew_path(self.libraries_path)
renew_path(self.simulation_output_path)
installed_modelsim_ini = str(Path(self.prefix_path) / ".." / "modelsim.ini")
write_file(installed_modelsim_ini, "[Library]")
self.project = Project()
Expand All @@ -334,3 +382,24 @@ def tearDown(self):
os.chdir(self.cwd)
if Path(self.test_path).exists():
rmtree(self.test_path)


def make_config(sim_options=None, generics=None, verilog=False):
"""
Utility to reduce boiler plate in tests
"""
cfg = mock.Mock(spec=Configuration)
cfg.library_name = "lib"

if verilog:
cfg.entity_name = "tb"
cfg.architecture_name = None
else:
cfg.entity_name = "tb"
cfg.architecture_name = "test"

cfg.sim_options = {} if sim_options is None else sim_options
cfg.generics = {} if generics is None else generics
cfg.vhdl_configuration_name = None
cfg.vhdl_assert_stop_level = "error"
return cfg
2 changes: 1 addition & 1 deletion vunit/persistent_tcl_shell.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ def read_var(self, varname):
return consumer.var

def read_bool(self, varname):
result = self.read_var(varname)
result = self.read_var(varname).lower()
assert result in ("true", "false")
return result == "true"

Expand Down
Loading
Loading