@@ -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 = """
304342proc 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+
679802def parse_modelsimini (file_name ):
680803 """
681804 Parse a modelsim.ini file
0 commit comments