Skip to content

Commit d484ecd

Browse files
committed
WIP: Added support Questa Visualizer.
1 parent bc01d74 commit d484ecd

File tree

2 files changed

+176
-49
lines changed

2 files changed

+176
-49
lines changed

vunit/sim_if/modelsim.py

Lines changed: 134 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,19 @@ class ModelSimInterface(VsimSimulatorMixin, SimulatorInterface): # pylint: disa
5151
BooleanOption("modelsim.three_step_flow"),
5252
]
5353

54+
@staticmethod
55+
def add_arguments(parser):
56+
"""
57+
Add command line arguments
58+
"""
59+
group = parser.add_argument_group("modelsim/questa", description="ModelSim/Questa specific flags")
60+
group.add_argument(
61+
"--debugger",
62+
choices=["original", "visualizer"],
63+
default="original",
64+
help="Debugger to use.",
65+
)
66+
5467
@classmethod
5568
def from_args(cls, args, output_path, **kwargs):
5669
"""
@@ -63,6 +76,7 @@ def from_args(cls, args, output_path, **kwargs):
6376
output_path=output_path,
6477
persistent=persistent,
6578
gui=args.gui,
79+
debugger=args.debugger,
6680
)
6781

6882
@classmethod
@@ -90,7 +104,7 @@ def supports_coverage():
90104
"""
91105
return True
92106

93-
def __init__(self, prefix, output_path, persistent=False, gui=False):
107+
def __init__(self, prefix, output_path, *, persistent=False, gui=False, debugger="original"):
94108
SimulatorInterface.__init__(self, output_path, gui)
95109
VsimSimulatorMixin.__init__(
96110
self,
@@ -102,6 +116,7 @@ def __init__(self, prefix, output_path, persistent=False, gui=False):
102116
self._coverage_files = set()
103117
assert not (persistent and gui)
104118
self._create_modelsim_ini()
119+
self._debugger = debugger
105120
# Contains design already optimized, i.e. the optimized design can be reused
106121
self._optimized_designs = {}
107122
# Contains locks for each library. If locked, a design belonging to the library
@@ -248,6 +263,15 @@ def _optimize_design(self, config):
248263

249264
return config.sim_options.get("modelsim.three_step_flow", False)
250265

266+
def _early_load_in_gui_mode(self): # pylint: disable=unused-argument
267+
"""
268+
Return True if design is to be loaded on the first vsim call rather than
269+
in the second vsim call embedded in the script file.
270+
271+
This is required for Questa Visualizer.
272+
"""
273+
return self._debugger == "visualizer"
274+
251275
@staticmethod
252276
def _design_to_optimize(config):
253277
"""
@@ -282,13 +306,28 @@ def _create_optimize_function(self, config):
282306
design_to_optimize = self._design_to_optimize(config)
283307
optimized_design = self._to_optimized_design(design_to_optimize)
284308

309+
vopt_library_flags = []
310+
design_file_directory = None
311+
for library in self._libraries:
312+
vopt_library_flags += ["-L", library.name]
313+
if library.name == config.library_name:
314+
design_file_directory = library.directory
315+
316+
if not design_file_directory:
317+
raise RuntimeError(f"Failed to find library directory for {config.library_name}")
318+
319+
design_file = str(Path(design_file_directory) / f"{optimized_design}.bin")
320+
285321
vopt_flags = [
286322
self._vopt_extra_args(config),
287323
f"{design_to_optimize}",
288-
f"-work {{{config.library_name}}}",
324+
"-work",
325+
f"{{{config.library_name}}}",
289326
"-quiet",
290327
f"-floatgenerics+{config.entity_name}.",
291328
f"-o {{{optimized_design}}}",
329+
"-design_file",
330+
f"{{{fix_path(design_file)}}}",
292331
]
293332

294333
# There is a known bug in modelsim that prevents the -modelsimini flag from accepting
@@ -297,8 +336,7 @@ def _create_optimize_function(self, config):
297336
modelsimini_option = f"-modelsimini {fix_path(self._sim_cfg_file_name)!s}"
298337
vopt_flags.insert(0, modelsimini_option)
299338

300-
for library in self._libraries:
301-
vopt_flags += ["-L", library.name]
339+
vopt_flags += vopt_library_flags
302340

303341
tcl = """
304342
proc vunit_optimize {{vopt_extra_args ""}} {"""
@@ -478,18 +516,7 @@ def _create_load_function(self, test_suite_name, config, output_path, optimize_d
478516
Create the vunit_load TCL function that runs the vsim command and loads the design
479517
"""
480518

481-
if optimize_design:
482-
simulation_target = self._to_optimized_design(self._design_to_optimize(config))
483-
else:
484-
simulation_target = self._design_to_optimize(config)
485-
486-
set_generic_str = " ".join(
487-
(
488-
f"-g/{config.entity_name!s}/{name!s}={encode_generic_value(value)!s}"
489-
for name, value in config.generics.items()
490-
)
491-
)
492-
pli_str = " ".join(f"-pli {{{fix_path(name)!s}}}" for name in config.sim_options.get("pli", []))
519+
vsim_flags = self._get_vsim_flags(config, output_path, optimize_design)
493520

494521
if config.sim_options.get("enable_coverage", False):
495522
coverage_file = str(Path(output_path) / "coverage.ucdb")
@@ -498,32 +525,8 @@ def _create_load_function(self, test_suite_name, config, output_path, optimize_d
498525
f"coverage save -onexit -testname {{{test_suite_name!s}}} -assert -directive "
499526
f"-cvg -codeAll {{{fix_path(coverage_file)!s}}}"
500527
)
501-
coverage_args = "-coverage"
502528
else:
503529
coverage_save_cmd = ""
504-
coverage_args = ""
505-
506-
vsim_flags = [
507-
f"-wlf {{{fix_path(str(Path(output_path) / 'vsim.wlf'))!s}}}",
508-
f"-work {{{config.library_name}}}",
509-
"-quiet",
510-
"-t ps",
511-
# for correct handling of verilog fatal/finish
512-
"-onfinish stop",
513-
pli_str,
514-
set_generic_str,
515-
simulation_target,
516-
coverage_args,
517-
self._vsim_extra_args(config),
518-
]
519-
520-
# There is a known bug in modelsim that prevents the -modelsimini flag from accepting
521-
# a space in the path even with escaping, see issue #36
522-
if " " not in self._sim_cfg_file_name:
523-
vsim_flags.insert(0, f"-modelsimini {fix_path(self._sim_cfg_file_name)!s}")
524-
525-
for library in self._libraries:
526-
vsim_flags += ["-L", library.name]
527530

528531
vhdl_assert_stop_level_mapping = {"warning": 1, "error": 2, "failure": 3}
529532

@@ -566,6 +569,84 @@ def _create_load_function(self, test_suite_name, config, output_path, optimize_d
566569

567570
return tcl
568571

572+
def _common_vsim_flags(self, config, output_path, optimize_design):
573+
"""Return vsim flags to normal and early load mode."""
574+
if optimize_design:
575+
simulation_target = self._to_optimized_design(self._design_to_optimize(config))
576+
else:
577+
simulation_target = self._design_to_optimize(config)
578+
579+
pli_str = " ".join(f"-pli {{{fix_path(name)!s}}}" for name in config.sim_options.get("pli", []))
580+
581+
if config.sim_options.get("enable_coverage", False):
582+
coverage_args = "-coverage"
583+
else:
584+
coverage_args = ""
585+
586+
vsim_flags = [
587+
simulation_target,
588+
"-wlf",
589+
f"{fix_path(str(Path(output_path) / 'vsim.wlf'))}",
590+
"-work",
591+
f"{config.library_name}",
592+
"-quiet",
593+
pli_str,
594+
coverage_args,
595+
self._vsim_extra_args(config),
596+
]
597+
598+
# There is a known bug in modelsim that prevents the -modelsimini flag from accepting
599+
# a space in the path even with escaping, see issue #36
600+
if " " not in self._sim_cfg_file_name:
601+
vsim_flags.insert(1, "-modelsimini")
602+
vsim_flags.insert(2, f"{fix_path(self._sim_cfg_file_name)}")
603+
604+
for library in self._libraries:
605+
vsim_flags += ["-L", library.name]
606+
607+
return vsim_flags
608+
609+
def _get_vsim_flags(self, config, output_path, optimize_design):
610+
"""Return vsim flags for load function."""
611+
vsim_flags = self._common_vsim_flags(config, output_path, optimize_design)
612+
613+
set_generic_str = " ".join(
614+
(
615+
f"-g/{config.entity_name!s}/{name!s}={encode_generic_value(value)!s}"
616+
for name, value in config.generics.items()
617+
)
618+
)
619+
620+
vsim_flags += [
621+
set_generic_str,
622+
"-t ps",
623+
# for correct handling of Verilog fatal/finish
624+
"-onfinish stop",
625+
]
626+
627+
return vsim_flags
628+
629+
def _get_load_flags(self, config, output_path, optimize_design):
630+
"""
631+
Return extra flags needed for the first vsim call in GUI mode when early load is enabled.
632+
633+
This is required for Questa Visualizer.
634+
"""
635+
vsim_flags = self._common_vsim_flags(config, output_path, optimize_design)
636+
637+
set_generic_str = " ".join(
638+
(
639+
f"-g/{config.entity_name!s}/{name!s}={encode_generic_value_for_file(value)!s}"
640+
for name, value in config.generics.items()
641+
)
642+
)
643+
generics_file_name = Path(output_path) / "generics.flags"
644+
write_file(str(generics_file_name), set_generic_str)
645+
646+
vsim_flags += ["-visualizer", "-f", f"{fix_path(str(generics_file_name))}"]
647+
648+
return vsim_flags
649+
569650
@staticmethod
570651
def _create_run_function():
571652
"""
@@ -666,7 +747,7 @@ def get_env():
666747

667748
def encode_generic_value(value):
668749
"""
669-
Ensure values with space in them are quoted
750+
Ensure values with space in them are quoted properly for the command line
670751
"""
671752
s_value = str(value)
672753
if " " in s_value:
@@ -676,6 +757,18 @@ def encode_generic_value(value):
676757
return s_value
677758

678759

760+
def encode_generic_value_for_file(value):
761+
"""
762+
Ensure values with space in them are quoted properly for argument files
763+
"""
764+
s_value = str(value)
765+
if " " in s_value:
766+
return f"'\"{s_value}\"'"
767+
if "," in s_value:
768+
return f"'\"{s_value}\"'"
769+
return s_value
770+
771+
679772
def parse_modelsimini(file_name):
680773
"""
681774
Parse a modelsim.ini file

vunit/sim_if/vsim_simulator_mixin.py

Lines changed: 42 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -290,19 +290,24 @@ def _source_tcl_file(file_name, config, message):
290290
)
291291
return tcl
292292

293-
def _create_gui_script(self, common_file_name, config):
293+
def _create_gui_script(self, common_file_name, config, include_vunit_load=True):
294294
"""
295295
Create the user facing script which loads common functions and prints a help message
296296
"""
297297
tcl = f'source "{fix_path(common_file_name)!s}"\n'
298298
tcl += self._create_user_init_function(config)
299-
tcl += "if {![vunit_load]} {\n"
300-
tcl += " vunit_user_init\n"
301-
tcl += " vunit_help\n"
302-
tcl += "}\n"
299+
if include_vunit_load:
300+
tcl += "if {![vunit_load]} {\n"
301+
tcl += " vunit_user_init\n"
302+
tcl += " vunit_help\n"
303+
tcl += "}\n"
304+
else:
305+
tcl += "vunit_user_init\n"
306+
tcl += "vunit_help\n"
307+
303308
return tcl
304309

305-
def _run_batch_file(self, batch_file_name, gui=False):
310+
def _run_batch_file(self, batch_file_name, gui=False, extra_args=None):
306311
"""
307312
Run a test bench in batch by invoking a new vsim process from the command line
308313
"""
@@ -317,6 +322,9 @@ def _run_batch_file(self, batch_file_name, gui=False):
317322
f'source "{fix_path(batch_file_name)!s}"',
318323
]
319324

325+
if extra_args:
326+
args += extra_args
327+
320328
proc = Process(args, cwd=str(Path(self._sim_cfg_file_name).parent))
321329
proc.consume_output()
322330
except Process.NonZeroExitCode:
@@ -354,6 +362,23 @@ def _optimize(self, config, script_path): # pylint: disable=unused-argument
354362
"""
355363
return False
356364

365+
def _early_load_in_gui_mode(self): # pylint: disable=unused-argument
366+
"""
367+
Return True if design is to be loaded on the first vsim call rather than
368+
in the second vsim call embedded in the script file.
369+
370+
This is required for Questa Visualizer.
371+
"""
372+
return False
373+
374+
def _get_load_flags(self, config, output_path, optimize_design): # pylint: disable=unused-argument
375+
"""
376+
Return extra flags needed for the first vsim call in GUI mode when early load is enabled.
377+
378+
This is required for Questa Visualizer.
379+
"""
380+
return []
381+
357382
def simulate(self, output_path, test_suite_name, config, elaborate_only):
358383
"""
359384
Run a test bench
@@ -375,14 +400,23 @@ def simulate(self, output_path, test_suite_name, config, elaborate_only):
375400
test_suite_name, config, script_path, output_path, optimize_design=optimize_design
376401
),
377402
)
378-
write_file(str(gui_file_name), self._create_gui_script(str(common_file_name), config))
403+
404+
early_load = self._gui and self._early_load_in_gui_mode()
405+
write_file(
406+
str(gui_file_name),
407+
self._create_gui_script(str(common_file_name), config, include_vunit_load=not early_load),
408+
)
379409
write_file(
380410
str(batch_file_name),
381411
self._create_batch_script(str(common_file_name), elaborate_only),
382412
)
383413

384414
if self._gui:
385-
return self._run_batch_file(str(gui_file_name), gui=True)
415+
return self._run_batch_file(
416+
str(gui_file_name),
417+
gui=True,
418+
extra_args=self._get_load_flags(config, output_path, optimize_design) if early_load else None,
419+
)
386420

387421
if self._persistent_shell is not None:
388422
return self._run_persistent(str(common_file_name), load_only=elaborate_only)

0 commit comments

Comments
 (0)