Skip to content
Open
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
5 changes: 5 additions & 0 deletions vunit/sim_if/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -329,6 +329,11 @@ def get_env():
Allows inheriting classes to overload this to modify environment variables. Return None for default environment
"""

def clean_up(self):
"""
Can overload to add simulator specific clean up options
"""


def isfile(file_name):
"""
Expand Down
118 changes: 82 additions & 36 deletions vunit/sim_if/modelsim.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ def add_arguments(parser):
group = parser.add_argument_group("modelsim/questa", description="ModelSim/Questa specific flags")
group.add_argument(
"--debugger",
choices=["original", "visualizer"],
choices=["original", "visualizer", "qone"],
default="original",
help="Debugger to use.",
)
Expand Down Expand Up @@ -111,14 +111,14 @@ def _find_any_ini_file(root: Path) -> Path | None:
def _find_ini_file(self, prefix: str, support_ini_flag: bool) -> tuple[Path, str] | None:
"""
Find the INI file to use for the simulation and the name of the copy to be used for simulation.
"""

# The standard simulation INI file name is based on the name of the INI flag option. If such a file
# doesn't exist in the installation but there is another similar INI file, the simulation name
# is based on that file instead. We only revert to finding any INI file if the standard names
# can't be found. The reason is that the non-standard approaches are a somewhat unknown territory
# and this way we avoid discarding a standard name if there are several INI files in the installation
# directory.
The standard simulation INI file name is based on the name of the INI flag option. If such a file
doesn't exist in the installation but there is another similar INI file, the simulation name
is based on that file instead. We only revert to finding any INI file if the standard names
can't be found. The reason is that the non-standard approaches are a somewhat unknown territory
and this way we avoid discarding a standard name if there are several INI files in the installation
directory.
"""
parent_dir = Path(prefix).parent
installation_ini_name = "questa.ini" if support_ini_flag else "modelsim.ini"
standard_installation_ini_file = parent_dir / installation_ini_name
Expand Down Expand Up @@ -146,7 +146,6 @@ def find_prefix_from_path(cls):
"""
Find first valid Modelsim/Questa toolchain prefix
"""

def has_ini(path):
return cls._find_any_ini_file(Path(path).parent) is not None

Expand Down Expand Up @@ -188,6 +187,7 @@ def __init__(self, prefix, output_path, *, persistent=False, gui=False, debugger
support_ini_flag = self._find_in_help(prefix, "vcom", "-ini")
self._ini_flag = "-ini" if support_ini_flag else "-modelsimini"
self._ini_file_path, simulation_ini_file_name = self._find_ini_file(prefix, support_ini_flag)
self._debugger = debugger

SimulatorInterface.__init__(self, output_path, gui)
VsimSimulatorMixin.__init__(
Expand All @@ -201,7 +201,6 @@ def __init__(self, prefix, output_path, *, persistent=False, gui=False, debugger
self._coverage_files = set()
assert not (persistent and gui)
self._create_ini()
self._debugger = debugger
self._vopt_retries = 3
# Contains design already optimized, i.e. the optimized design can be reused
self._optimized_designs = {}
Expand Down Expand Up @@ -345,17 +344,31 @@ def _optimize_design(self, config):
"""
Return True if design shall be optimized.
"""

if config.sim_options.get("modelsim.three_step_flow", False) and self._debugger == "qone":
print ("VUNIT: three step flow is broken for questa one - two step flow will be attempted instead")
return False
return config.sim_options.get("modelsim.three_step_flow", False)

def _early_load_in_gui_mode(self): # pylint: disable=unused-argument
def _early_load_in_gui_mode(self):
"""
Return True if design is to be loaded on the first vsim call rather than
in the second vsim call embedded in the script file.

This is required for Questa Visualizer.
"""
return self._debugger == "visualizer"
return self._debugger in ["visualizer", "qone"]

def _vsim_command(self):
"""
Returns 'qsim' if using questa one gui else returns 'vsim'
"""
return "qsim" if self._debugger == "qone" else "vsim"

def _vopt_command(self):
"""
Returns 'qopt' if using questa one gui else returns 'vopt'
"""
return "qopt" if self._debugger == "qone" else "vopt"

@staticmethod
def _design_to_optimize(config):
Expand Down Expand Up @@ -407,12 +420,12 @@ def _create_optimize_function(self, config):
self._vopt_extra_args(config),
f"{design_to_optimize}",
"-work",
f"{{{config.library_name}}}",
f"{design_file_directory}",
"-quiet",
f"-floatgenerics+{config.entity_name}.",
f"-o {{{optimized_design}}}",
f"-o {optimized_design}",
"-designfile",
f"{{{fix_path(design_file)}}}",
f"{fix_path(design_file)}",
]

# There is a known bug in Modelsim/Questa that prevents the -(modelsim)ini flag from accepting
Expand All @@ -423,24 +436,28 @@ def _create_optimize_function(self, config):

vopt_flags += vopt_library_flags

if self._debugger == "qone":
vopt_flags.append("-debug,livesim")

tcl = """
proc vunit_optimize {{vopt_extra_args ""}} {"""
tcl += """
echo Optimizing using command 'vopt ${{vopt_extra_args}} {vopt_flags}'
echo Optimizing using command '{vopt} ${{vopt_extra_args}} {vopt_flags}'
set vopt_failed [catch {{
eval vopt ${{vopt_extra_args}} {{{vopt_flags}}}
eval {vopt} ${{vopt_extra_args}} {{{vopt_flags}}}
}}]

if {{${{vopt_failed}}}} {{
echo Command 'vopt ${{vopt_extra_args}} {vopt_flags}' failed
echo Command '{vopt} ${{vopt_extra_args}} {vopt_flags}' failed
echo Bad flag from vopt_extra_args?
return true
}}

return false
}}
""".format(
vopt_flags=" ".join(vopt_flags)
vopt_flags=" ".join(vopt_flags),
vopt=self._vopt_command()
)

return tcl
Expand All @@ -466,16 +483,15 @@ def _run_optimize_batch_file(self, batch_file_name, script_path):
"""
Run a test bench in batch by invoking a new vsim process from the command line
"""
try:
args = [
str(Path(self._prefix) / "vsim"),
"-c",
"-l",
str(script_path / "transcript"),
"-do",
f'source "{fix_path(str(batch_file_name))!s}"',
]
args = [str(Path(self._prefix) / self._vsim_command()),
"-c",
"-l",
str(script_path / "transcript"),
"-do",
f'source "{fix_path(str(batch_file_name))!s}"',
]

try:
proc = Process(args, cwd=str(Path(self._sim_cfg_file_name).parent))
proc.consume_output()
status = True
Expand Down Expand Up @@ -637,14 +653,15 @@ def _load_setup(self, config, output_path, optimize_design):
"""

vsim_flags = " ".join(self._get_vsim_flags(config, output_path, optimize_design))
vsim_command = self._vsim_command()

tcl = f"""
set vsim_failed [catch {{
eval vsim ${{vsim_extra_args}} {{{vsim_flags}}}
eval {vsim_command} ${{vsim_extra_args}} {{{vsim_flags}}}
}}]

if {{${{vsim_failed}}}} {{
echo Command 'vsim ${{vsim_extra_args}} {vsim_flags}' failed
echo Command '{vsim_command} ${{vsim_extra_args}} {vsim_flags}' failed
echo Bad flag from vsim_extra_args?
return true
}}
Expand Down Expand Up @@ -760,9 +777,17 @@ def _get_vsim_flags(self, config, output_path, optimize_design):
)
)

if self._debugger == "original":
vsim_flags += [
"-wlf",
f"{{{fix_path(str(Path(output_path) / 'vsim.wlf'))}}}",
]
else:
vsim_flags += [
str("-" + self._vopt_command() + "args=-debug,livesim"),
]

vsim_flags += [
"-wlf",
f"{{{fix_path(str(Path(output_path) / 'vsim.wlf'))}}}",
pli_str,
set_generic_str,
]
Expand All @@ -775,7 +800,12 @@ def _get_gui_option(self):

This is required to support Questa Visualizer.
"""
return "-visualizer" if self._debugger == "visualizer" else "-gui"
arg = "-gui"
if self._debugger == "visualizer":
arg = "-visualizer"
elif self._debugger == "qone":
arg = ""
return arg

def _get_load_flags(self, config, output_path, optimize_design):
"""
Expand All @@ -800,9 +830,17 @@ def _get_load_flags(self, config, output_path, optimize_design):
generics_file_name = Path(output_path) / "generics.flags"
write_file(str(generics_file_name), set_generic_str)

if self._debugger == "original":
vsim_flags += [
"-wlf",
f"{{{fix_path(str(Path(output_path) / 'vsim.wlf'))}}}",
]
else:
vsim_flags += [
str("-" + self._vopt_command() + "args=-debug,livesim"),
]

vsim_flags += [
"-wlf",
f"{fix_path(str(Path(output_path) / 'vsim.wlf'))}",
pli_str,
"-f",
f"{fix_path(str(generics_file_name))}",
Expand Down Expand Up @@ -907,6 +945,14 @@ def get_env():
del env[key]
return env

def clean_up(self):
"""
Explicitly clean up the persistent tcl shell instance
"""
SimulatorInterface.clean_up(self)
if self._persistent_shell is not None:
self._persistent_shell.teardown()


def encode_generic_value_for_tcl(value):
"""
Expand Down
10 changes: 8 additions & 2 deletions vunit/sim_if/vsim_simulator_mixin.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ def __init__(self, prefix, persistent, sim_cfg_file_name):
def create_process(ident):
return Process(
[
str(Path(prefix) / "vsim"),
str(Path(prefix) / self._vsim_command()),
"-c",
"-l",
str(Path(sim_cfg_file_name).parent / f"transcript{ident}"),
Expand Down Expand Up @@ -314,14 +314,20 @@ def _create_gui_script(self, common_file_name, config):

return tcl

def _vsim_command(self):
"""
Returns 'qsim' if using questa one gui else returns 'vsim'
"""
return "vsim"

def _run_batch_file(self, batch_file_name, gui=False, gui_option="-gui", extra_args=None):
"""
Run a test bench in batch by invoking a new vsim process from the command line
"""

try:
args = [
str(Path(self._prefix) / "vsim"),
str(Path(self._prefix) / self._vsim_command()),
gui_option if gui else "-c",
"-l",
str(Path(batch_file_name).parent / "transcript"),
Expand Down
1 change: 1 addition & 0 deletions vunit/ui/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -1062,6 +1062,7 @@ def _main_run(self, post_run):
if post_run is not None:
post_run(results=Results(self._output_path, simulator_if, report))

simulator_if.clean_up()
del simulator_if

if self._args.xunit_xml is not None:
Expand Down