Skip to content

Commit 2a8514c

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

File tree

2 files changed

+215
-62
lines changed

2 files changed

+215
-62
lines changed

vunit/sim_if/modelsim.py

Lines changed: 182 additions & 59 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+
"-designfile",
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 ""}} {"""
@@ -473,23 +511,31 @@ def _optimize(self, config, script_path):
473511

474512
return True
475513

476-
def _create_load_function(self, test_suite_name, config, output_path, optimize_design):
514+
def _load_setup(self, config, output_path, optimize_design):
477515
"""
478-
Create the vunit_load TCL function that runs the vsim command and loads the design
516+
Return setup part of load function that 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)
519+
vsim_flags = " ".join(self._get_vsim_flags(config, output_path, optimize_design))
485520

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", []))
521+
tcl = f"""
522+
set vsim_failed [catch {{
523+
eval vsim ${{vsim_extra_args}} {{{vsim_flags}}}
524+
}}]
525+
526+
if {{${{vsim_failed}}}} {{
527+
echo Command 'vsim ${{vsim_extra_args}} {vsim_flags}' failed
528+
echo Bad flag from vsim_extra_args?
529+
return true
530+
}}
531+
"""
532+
533+
return tcl
534+
535+
def _load_init(self, test_suite_name, config, output_path):
536+
"""
537+
Return initialiation part ofter loading design.
538+
"""
493539

494540
if config.sim_options.get("enable_coverage", False):
495541
coverage_file = str(Path(output_path) / "coverage.ucdb")
@@ -498,73 +544,138 @@ def _create_load_function(self, test_suite_name, config, output_path, optimize_d
498544
f"coverage save -onexit -testname {{{test_suite_name!s}}} -assert -directive "
499545
f"-cvg -codeAll {{{fix_path(coverage_file)!s}}}"
500546
)
501-
coverage_args = "-coverage"
502547
else:
503548
coverage_save_cmd = ""
549+
550+
vhdl_assert_stop_level_mapping = {"warning": 1, "error": 2, "failure": 3}
551+
break_on_assert = vhdl_assert_stop_level_mapping[config.vhdl_assert_stop_level]
552+
no_warnings = 1 if config.sim_options.get("disable_ieee_warnings", False) else 0
553+
554+
tcl = f"""
555+
if {{[_vunit_source_init_files_after_load]}} {{
556+
return true
557+
}}
558+
559+
global BreakOnAssertion
560+
set BreakOnAssertion {break_on_assert}
561+
562+
global NumericStdNoWarnings
563+
set NumericStdNoWarnings {no_warnings}
564+
565+
global StdArithNoWarnings
566+
set StdArithNoWarnings {no_warnings}
567+
568+
{coverage_save_cmd}
569+
"""
570+
571+
return tcl
572+
573+
def _create_load_function(self, test_suite_name, config, output_path, optimize_design):
574+
"""
575+
Create the vunit_load TCL function that runs the vsim command and loads the design
576+
"""
577+
578+
tcl = """
579+
proc vunit_load {{vsim_extra_args ""}} {"""
580+
581+
early_load = self._gui and self._early_load_in_gui_mode()
582+
if not early_load:
583+
tcl += self._load_setup(config, output_path, optimize_design)
584+
585+
tcl += self._load_init(test_suite_name, config, output_path)
586+
tcl += """
587+
return false
588+
}
589+
"""
590+
591+
return tcl
592+
593+
def _common_vsim_flags(self, config, optimize_design):
594+
"""Return vsim flags to normal and early load mode."""
595+
if optimize_design:
596+
simulation_target = self._to_optimized_design(self._design_to_optimize(config))
597+
else:
598+
simulation_target = self._design_to_optimize(config)
599+
600+
if config.sim_options.get("enable_coverage", False):
601+
coverage_args = "-coverage"
602+
else:
504603
coverage_args = ""
505604

506605
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,
515606
simulation_target,
607+
"-work",
608+
f"{config.library_name}",
609+
"-quiet",
516610
coverage_args,
517611
self._vsim_extra_args(config),
518612
]
519613

520614
# There is a known bug in modelsim that prevents the -modelsimini flag from accepting
521615
# a space in the path even with escaping, see issue #36
522616
if " " not in self._sim_cfg_file_name:
523-
vsim_flags.insert(0, f"-modelsimini {fix_path(self._sim_cfg_file_name)!s}")
617+
vsim_flags.insert(1, "-modelsimini")
618+
vsim_flags.insert(2, f"{fix_path(self._sim_cfg_file_name)}")
524619

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

528-
vhdl_assert_stop_level_mapping = {"warning": 1, "error": 2, "failure": 3}
623+
return vsim_flags
529624

530-
tcl = """
531-
proc vunit_load {{vsim_extra_args ""}} {"""
625+
def _get_vsim_flags(self, config, output_path, optimize_design):
626+
"""Return vsim flags for load function."""
627+
vsim_flags = self._common_vsim_flags(config, optimize_design)
532628

533-
tcl += """
534-
set vsim_failed [catch {{
535-
eval vsim ${{vsim_extra_args}} {{{vsim_flags}}}
536-
}}]
629+
pli_str = " ".join(f"-pli {{{fix_path(name)!s}}}" for name in config.sim_options.get("pli", []))
537630

538-
if {{${{vsim_failed}}}} {{
539-
echo Command 'vsim ${{vsim_extra_args}} {vsim_flags}' failed
540-
echo Bad flag from vsim_extra_args?
541-
return true
542-
}}
631+
set_generic_str = " ".join(
632+
(
633+
f"-g/{config.entity_name!s}/{name!s}={encode_generic_value_for_tcl(value)!s}"
634+
for name, value in config.generics.items()
635+
)
636+
)
543637

544-
if {{[_vunit_source_init_files_after_load]}} {{
545-
return true
546-
}}
638+
vsim_flags += [
639+
"-wlf",
640+
f"{{{fix_path(str(Path(output_path) / 'vsim.wlf'))}}}",
641+
pli_str,
642+
set_generic_str,
643+
"-t ps",
644+
# for correct handling of Verilog fatal/finish
645+
"-onfinish stop",
646+
]
547647

548-
global BreakOnAssertion
549-
set BreakOnAssertion {break_on_assert}
648+
return vsim_flags
550649

551-
global NumericStdNoWarnings
552-
set NumericStdNoWarnings {no_warnings}
650+
def _get_load_flags(self, config, output_path, optimize_design):
651+
"""
652+
Return extra flags needed for the first vsim call in GUI mode when early load is enabled.
553653
554-
global StdArithNoWarnings
555-
set StdArithNoWarnings {no_warnings}
654+
This is required for Questa Visualizer.
655+
"""
656+
vsim_flags = self._common_vsim_flags(config, optimize_design)
556657

557-
{coverage_save_cmd}
558-
return false
559-
}}
560-
""".format(
561-
coverage_save_cmd=coverage_save_cmd,
562-
vsim_flags=" ".join(vsim_flags),
563-
break_on_assert=vhdl_assert_stop_level_mapping[config.vhdl_assert_stop_level],
564-
no_warnings=1 if config.sim_options.get("disable_ieee_warnings", False) else 0,
658+
pli_str = " ".join(f"-pli {fix_path(name)}" for name in config.sim_options.get("pli", []))
659+
660+
set_generic_str = " ".join(
661+
(
662+
f"-g/{config.entity_name!s}/{name!s}={encode_generic_value_for_args(value)!s}"
663+
for name, value in config.generics.items()
664+
)
565665
)
666+
generics_file_name = Path(output_path) / "generics.flags"
667+
write_file(str(generics_file_name), set_generic_str)
566668

567-
return tcl
669+
vsim_flags += [
670+
"-wlf",
671+
f"{fix_path(str(Path(output_path) / 'vsim.wlf'))}",
672+
pli_str,
673+
"-visualizer",
674+
"-f",
675+
f"{fix_path(str(generics_file_name))}",
676+
]
677+
678+
return vsim_flags
568679

569680
@staticmethod
570681
def _create_run_function():
@@ -664,9 +775,9 @@ def get_env():
664775
return env
665776

666777

667-
def encode_generic_value(value):
778+
def encode_generic_value_for_tcl(value):
668779
"""
669-
Ensure values with space in them are quoted
780+
Ensure values with space and commas in them are quoted properly for TCL files.
670781
"""
671782
s_value = str(value)
672783
if " " in s_value:
@@ -676,6 +787,18 @@ def encode_generic_value(value):
676787
return s_value
677788

678789

790+
def encode_generic_value_for_args(value):
791+
"""
792+
Ensure values with space and commas in them are quoted properly for argument files.
793+
"""
794+
s_value = str(value)
795+
if " " in s_value:
796+
return f"'\"{s_value}\"'"
797+
if "," in s_value:
798+
return f"'\"{s_value}\"'"
799+
return s_value
800+
801+
679802
def parse_modelsimini(file_name):
680803
"""
681804
Parse a modelsim.ini file

0 commit comments

Comments
 (0)