diff --git a/docs/news.d/1032.feature.rst b/docs/news.d/1032.feature.rst new file mode 100644 index 000000000..5bd16141f --- /dev/null +++ b/docs/news.d/1032.feature.rst @@ -0,0 +1 @@ +[Active-HDL] Added vunit_help, vunit_load, vunit_run, vunit_compile, vunit_restart, and vunit_user_init TCL commands to Active-HDL. diff --git a/vunit/sim_if/activehdl.py b/vunit/sim_if/activehdl.py index 18e16738d..eebc5cc3f 100644 --- a/vunit/sim_if/activehdl.py +++ b/vunit/sim_if/activehdl.py @@ -13,6 +13,7 @@ import os import re import logging +import sys from ..exceptions import CompileError from ..ostools import Process, write_file, file_exists, renew_path from ..test.suites import get_result_file_name @@ -340,14 +341,105 @@ def merge_coverage(self, file_name, args=None): vcover_merge_process.consume_output() print("Done merging coverage files") + @staticmethod + def _create_restart_function(): + """ " + Create the vunit_restart function to recompile and restart the simulation + + This function is quite complicated to work around limitations + of modelsim not being able to change working directory. + + Thus python is called with an explicit command string that in + turn call the python command we actually wanted but in the + correct working directory using subprocess.call + + -u flag is needed for continuous output + """ + recompile_command = [sys.executable, "-u", sys.argv[0], "--compile"] + + # Strip --clean from re-compile command + # Leave other arguments intact since users can add custom CLI options + recompile_command += [arg for arg in sys.argv[1:] if arg != "--clean"] + + recompile_command_visual = " ".join(recompile_command) + + # stderr is intentionally re-directed to stdout so that the tcl's catch + # relies on the return code from the python process rather than being + # tricked by output going to stderr. See issue #228. + recompile_command_eval = [ + str(sys.executable), + "-u", + "-c", + ( + "import sys;" + "import subprocess;" + f"exit(subprocess.call({recompile_command!r}, " + f"cwd={str(Path(os.getcwd()).resolve())!r}, " + "bufsize=0, " + "universal_newlines=True, " + "stdout=sys.stdout, " + "stderr=sys.stdout))" + ), + ] + recompile_command_eval_tcl = " ".join([f"{{{part}}}" for part in recompile_command_eval]) + + return f""" +proc vunit_compile {{}} {{ + set cmd_show {{{recompile_command_visual!s}}} + puts "Re-compiling using command ${{cmd_show}}" + + set chan [open |[list {recompile_command_eval_tcl!s}] r] + echo $chan + while {{[gets $chan line] >= 0}} {{ + puts $line + }} + + if {{[catch {{close $chan}} error_msg]}} {{ + puts "Re-compile failed" + puts ${{error_msg}} + return true + }} else {{ + puts "Re-compile finished" + return false + }} +}} + +proc vunit_restart {{}} {{ + if {{![vunit_compile]}} {{ + restart + vunit_run + }} +}} +""" + def _create_common_script(self, config, output_path): """ Create tcl script with functions common to interactive and batch modes """ - tcl = "" + tcl = """ +proc vunit_help {} { + puts {List of VUnit commands:} + puts {vunit_help} + puts { - Prints this help} + puts {vunit_load} + puts { - Load design with correct generics for the test} + puts {vunit_user_init} + puts { - Re-runs the user defined init file} + puts {vunit_run} + puts { - Run test, must do vunit_load first} + puts {vunit_compile} + puts { - Recompiles the source files} + puts {vunit_restart} + puts { - Recompiles the source files} + puts { - and re-runs the simulation if the compile was successful} +} +""" + tcl += get_is_test_suite_done_tcl(get_result_file_name(output_path)) tcl += self._create_load_function(config, output_path) tcl += self._create_run_function() + tcl += self._create_restart_function() + tcl += "scripterconf -tcl\n" return tcl @staticmethod @@ -365,6 +457,20 @@ def _create_batch_script(common_file_name, load_only=False): batch_do += "quit -code 0\n" return batch_do + def _create_user_init_function(self, config): + """ + Create the vunit_user_init function which sources the user defined TCL file in + simulator_name.init_file.gui + """ + opt_name = self.name + ".init_file.gui" + init_file = config.sim_options.get(opt_name, None) + tcl = "proc vunit_user_init {} {\n" + if init_file is not None: + tcl += f'source "{fix_path(str(Path(init_file).resolve()))!s}"\n' + tcl += " return 0\n" + tcl += "}\n" + return tcl + def _create_gui_script(self, common_file_name, config): """ Create the user facing script which loads common functions and prints a help message @@ -378,13 +484,11 @@ def _create_gui_script(self, common_file_name, config): for library in self._libraries: tcl += f"vmap {library.name!s} {fix_path(library.directory)!s}\n" - tcl += "vunit_load\n" - - init_file = config.sim_options.get(self.name + ".init_file.gui", None) - if init_file is not None: - tcl += f'source "{fix_path(str(Path(init_file).resolve()))!s}"\n' - - tcl += 'puts "VUnit help: Design already loaded. Use run -all to run the test."\n' + tcl += self._create_user_init_function(config) + tcl += "if {![vunit_load]} {\n" + tcl += " vunit_user_init\n" + tcl += " vunit_help\n" + tcl += "}\n" return tcl