From fdf1cc122c4aa99e81a5fb50aac5ced4ef95e1f1 Mon Sep 17 00:00:00 2001 From: Olof Kindgren Date: Thu, 22 Aug 2024 15:23:03 +0200 Subject: [PATCH] Port Modelsim/Questasim backend to flow API --- edalize/tools/modelsim.py | 208 ++++++++++++++++++ tests/test_tool_modelsim.py | 38 ++++ tests/tools/modelsim/Makefile | 12 + tests/tools/modelsim/edalize_build_rtl.tcl | 9 + tests/tools/modelsim/edalize_main.tcl | 3 + tests/tools/modelsim/mfcu/Makefile | 12 + .../tools/modelsim/mfcu/edalize_build_rtl.tcl | 6 + tests/tools/modelsim/mfcu/edalize_main.tcl | 3 + 8 files changed, 291 insertions(+) create mode 100644 edalize/tools/modelsim.py create mode 100644 tests/test_tool_modelsim.py create mode 100644 tests/tools/modelsim/Makefile create mode 100644 tests/tools/modelsim/edalize_build_rtl.tcl create mode 100644 tests/tools/modelsim/edalize_main.tcl create mode 100644 tests/tools/modelsim/mfcu/Makefile create mode 100644 tests/tools/modelsim/mfcu/edalize_build_rtl.tcl create mode 100644 tests/tools/modelsim/mfcu/edalize_main.tcl diff --git a/edalize/tools/modelsim.py b/edalize/tools/modelsim.py new file mode 100644 index 000000000..c718d9d71 --- /dev/null +++ b/edalize/tools/modelsim.py @@ -0,0 +1,208 @@ +# Copyright edalize contributors +# Licensed under the 2-Clause BSD License, see LICENSE for details. +# SPDX-License-Identifier: BSD-2-Clause + +from pathlib import Path + +from edalize.tools.edatool import Edatool +from edalize.utils import EdaCommands + + +class Modelsim(Edatool): + + description = "ModelSim/QuestaSim simulator from Siemens EDA" + + TOOL_OPTIONS = { + "compilation_mode": { + "type": "str", + "desc": "Common or separate compilation, sep - for separate compilation, common - for common compilation", + }, + "vcom_options": { + "type": "str", + "desc": "Additional options for compilation with vcom", + list: True, + }, + "vlog_options": { + "type": "str", + "desc": "Additional options for compilation with vlog", + list: True, + }, + "vsim_options": { + "type": "str", + "desc": "Additional run options for vsim", + list: True, + }, + } + + def setup(self, edam): + super().setup(edam) + + incdirs = [] + vlog_files = [] + depfiles = [] + unused_files = [] + libs = [] + + vlog_defines = [] + for k, v in self.vlogdefine.items(): + vlog_defines.append("+define+{}={}".format(k, self._param_value_str(v))) + + common_compilation = self.tool_options.get("compilation_mode") == "common" + vlog_options = self.tool_options.get("vlog_options", []) + vcom_options = self.tool_options.get("vcom_options", []) + vsim_options = self.tool_options.get("vsim_options", []) + + # Get all include dirs first + for f in self.files: + file_type = f.get("file_type", "") + if file_type.startswith("verilogSource") or file_type.startswith( + "systemVerilogSource" + ): + self._add_include_dir(f, incdirs, force_slash=True) + + vlog_include_dirs = ["+incdir+" + d.replace("\\", "/") for d in incdirs] + + self.tcl_files = [] + self.tcl_main = [] + self.tcl_build_rtl = [] + for f in self.files: + if not f.get("logical_name"): + f["logical_name"] = "work" + if not f["logical_name"] in libs: + self.tcl_build_rtl.append(f"vlib {f['logical_name']}") + libs.append(f["logical_name"]) + file_type = f.get("file_type", "") + if file_type.startswith("verilogSource") or file_type.startswith( + "systemVerilogSource" + ): + depfiles.append(f["name"]) + if self._add_include_dir(f, incdirs, force_slash=True): + cmd = None + else: + vlog_files.append(f) + cmd = "vlog" + args = [] + + args += vlog_include_dirs + args += vlog_options + args += vlog_defines + + if file_type.startswith("systemVerilogSource"): + args += ["-sv"] + + elif file_type.startswith("vhdlSource"): + depfiles.append(f["name"]) + cmd = "vcom" + if file_type.endswith("-87"): + args = ["-87"] + if file_type.endswith("-93"): + args = ["-93"] + if file_type.endswith("-2008"): + args = ["-2008"] + else: + args = [] + + args += vcom_options + + elif file_type == "tclSource": + depfiles.append(f["name"]) + cmd = None + self.tcl_files.append(f["name"]) + elif file_type == "user": + depfiles.append(f["name"]) + cmd = None + else: + unused_files.append(f) + cmd = None + if cmd and ((cmd != "vlog") or not common_compilation): + args += ["-quiet"] + args += ["-work", f["logical_name"]] + args += [f["name"].replace("\\", "/")] + self.tcl_build_rtl.append(f"{cmd} {' '.join(args)}") + + if common_compilation: + args = vlog_include_dirs + vlog_options + vlog_defines + _vlog_files = [] + has_sv = False + for f in vlog_files: + _vlog_files.append(f["name"].replace("\\", "/")) + if f.get("file_type", "").startswith("systemVerilogSource"): + has_sv = True + + if has_sv: + args += ["-sv"] + args += vlog_include_dirs + args += ["-quiet"] + args += ["-work", "work"] + args += ["-mfcu"] + self.tcl_build_rtl.append(f"vlog {' '.join(args)} {' '.join(_vlog_files)}") + + self.edam = edam.copy() + self.edam["files"] = unused_files + + self.commands = EdaCommands() + + _parameters = [] + + db_file = str(Path("work") / "_lib.qdb") + self.commands.add( + ["vsim"] + ["-c", "-do", '"do edalize_main.tcl; exit"'], + [db_file], + depfiles + ["edalize_main.tcl", "edalize_build_rtl.tcl"], + ) + + for key, value in self.vlogparam.items(): + _parameters += ["-g", "{}={}".format(key, self._param_value_str(value))] + for key, value in self.generic.items(): + _parameters += [ + "-g", + "{}={}".format(key, self._param_value_str(value, bool_is_str=True)), + ] + + self.commands.add( + ["vsim", "-c"] + + vsim_options + + _parameters + + [ + "$(EXTRA_OPTIONS)", + "-do", + '"run -all; quit -code [expr [coverage attribute -name TESTSTATUS -concise] >= 2 ? [coverage attribute -name TESTSTATUS -concise] : 0]; exit"', + self.toplevel, + ], + ["run"], + [db_file], + ) + + self.commands.add( + ["vsim", "-gui"] + + vsim_options + + _parameters + + ["$(EXTRA_OPTIONS)", self.toplevel], + ["run-gui"], + [db_file], + ) + self.commands.set_default_target(db_file) + + def write_config_files(self): + tcl_main = "onerror { quit -code 1; }\ndo edalize_build_rtl.tcl\n" + + for f in self.tcl_files: + tcl_main += f"do {f}\n" + + self.update_config_file("edalize_main.tcl", tcl_main) + + tcl_build_rtl = "\n".join(self.tcl_build_rtl) + + self.update_config_file("edalize_build_rtl.tcl", tcl_build_rtl) + + def run(self): + args = ["run"] + + # Set plusargs + if self.plusarg: + plusargs = [] + for key, value in self.plusarg.items(): + plusargs += ["+{}={}".format(key, self._param_value_str(value))] + args.append("EXTRA_OPTIONS=" + " ".join(plusargs)) + + return ("make", args, self.work_root) diff --git a/tests/test_tool_modelsim.py b/tests/test_tool_modelsim.py new file mode 100644 index 000000000..9a5ae4b4a --- /dev/null +++ b/tests/test_tool_modelsim.py @@ -0,0 +1,38 @@ +from .edalize_tool_common import tool_fixture + + +def test_tool_modelsim(tool_fixture): + tool_name = "modelsim" + + # "compilation_mode": { + # "desc": "Common or separate compilation, sep - for separate compilation, common - for common compilation", + tool_options = { + "vcom_options": ["several", "vcom", "options"], + "vlog_options": ["a", "few", "vlog", "options"], + "vsim_options": ["some", "vsim", "options"], + } + tf = tool_fixture(tool_name, tool_options=tool_options) + + tf.tool.configure() + tf.compare_config_files(["edalize_build_rtl.tcl", "edalize_main.tcl"]) + + tf.tool.run() + + +def test_tool_modelsim_mfcu(tool_fixture): + tool_name = "modelsim" + + # "compilation_mode": { + # "desc": "Common or separate compilation, sep - for separate compilation, common - for common compilation", + tool_options = { + "compilation_mode": "common", + "vcom_options": ["several", "vcom", "options"], + "vlog_options": ["a", "few", "vlog", "options"], + "vsim_options": ["some", "vsim", "options"], + } + tf = tool_fixture(tool_name, tool_options=tool_options, ref_subdir="mfcu") + + tf.tool.configure() + tf.compare_config_files(["edalize_build_rtl.tcl", "edalize_main.tcl"]) + + tf.tool.run() diff --git a/tests/tools/modelsim/Makefile b/tests/tools/modelsim/Makefile new file mode 100644 index 000000000..930b5662c --- /dev/null +++ b/tests/tools/modelsim/Makefile @@ -0,0 +1,12 @@ +#Auto generated by Edalize + +all: work/_lib.qdb + +work/_lib.qdb: sv_file.sv user_file tcl_file.tcl vlog_file.v vlog05_file.v vlog_incfile vhdl_file.vhd vhdl_lfile vhdl2008_file another_sv_file.sv edalize_main.tcl edalize_build_rtl.tcl + $(EDALIZE_LAUNCHER) vsim -c -do "do edalize_main.tcl; exit" + +run: work/_lib.qdb + $(EDALIZE_LAUNCHER) vsim -c some vsim options -g vlogparam_bool=1 -g vlogparam_int=42 -g vlogparam_str=hello $(EXTRA_OPTIONS) -do "run -all; quit -code [expr [coverage attribute -name TESTSTATUS -concise] >= 2 ? [coverage attribute -name TESTSTATUS -concise] : 0]; exit" top_module + +run-gui: work/_lib.qdb + $(EDALIZE_LAUNCHER) vsim -gui some vsim options -g vlogparam_bool=1 -g vlogparam_int=42 -g vlogparam_str=hello $(EXTRA_OPTIONS) top_module diff --git a/tests/tools/modelsim/edalize_build_rtl.tcl b/tests/tools/modelsim/edalize_build_rtl.tcl new file mode 100644 index 000000000..4f2860c5e --- /dev/null +++ b/tests/tools/modelsim/edalize_build_rtl.tcl @@ -0,0 +1,9 @@ +vlib work +vlog +incdir+. a few vlog options +define+vlogdefine_bool=1 +define+vlogdefine_int=42 +define+vlogdefine_str=hello -sv -quiet -work work sv_file.sv +vlog +incdir+. a few vlog options +define+vlogdefine_bool=1 +define+vlogdefine_int=42 +define+vlogdefine_str=hello -quiet -work work vlog_file.v +vlog +incdir+. a few vlog options +define+vlogdefine_bool=1 +define+vlogdefine_int=42 +define+vlogdefine_str=hello -quiet -work work vlog05_file.v +vcom several vcom options -quiet -work work vhdl_file.vhd +vlib libx +vcom several vcom options -quiet -work libx vhdl_lfile +vcom -2008 several vcom options -quiet -work work vhdl2008_file +vlog +incdir+. a few vlog options +define+vlogdefine_bool=1 +define+vlogdefine_int=42 +define+vlogdefine_str=hello -sv -quiet -work work another_sv_file.sv \ No newline at end of file diff --git a/tests/tools/modelsim/edalize_main.tcl b/tests/tools/modelsim/edalize_main.tcl new file mode 100644 index 000000000..ec4861eb3 --- /dev/null +++ b/tests/tools/modelsim/edalize_main.tcl @@ -0,0 +1,3 @@ +onerror { quit -code 1; } +do edalize_build_rtl.tcl +do tcl_file.tcl diff --git a/tests/tools/modelsim/mfcu/Makefile b/tests/tools/modelsim/mfcu/Makefile new file mode 100644 index 000000000..930b5662c --- /dev/null +++ b/tests/tools/modelsim/mfcu/Makefile @@ -0,0 +1,12 @@ +#Auto generated by Edalize + +all: work/_lib.qdb + +work/_lib.qdb: sv_file.sv user_file tcl_file.tcl vlog_file.v vlog05_file.v vlog_incfile vhdl_file.vhd vhdl_lfile vhdl2008_file another_sv_file.sv edalize_main.tcl edalize_build_rtl.tcl + $(EDALIZE_LAUNCHER) vsim -c -do "do edalize_main.tcl; exit" + +run: work/_lib.qdb + $(EDALIZE_LAUNCHER) vsim -c some vsim options -g vlogparam_bool=1 -g vlogparam_int=42 -g vlogparam_str=hello $(EXTRA_OPTIONS) -do "run -all; quit -code [expr [coverage attribute -name TESTSTATUS -concise] >= 2 ? [coverage attribute -name TESTSTATUS -concise] : 0]; exit" top_module + +run-gui: work/_lib.qdb + $(EDALIZE_LAUNCHER) vsim -gui some vsim options -g vlogparam_bool=1 -g vlogparam_int=42 -g vlogparam_str=hello $(EXTRA_OPTIONS) top_module diff --git a/tests/tools/modelsim/mfcu/edalize_build_rtl.tcl b/tests/tools/modelsim/mfcu/edalize_build_rtl.tcl new file mode 100644 index 000000000..6d36220b6 --- /dev/null +++ b/tests/tools/modelsim/mfcu/edalize_build_rtl.tcl @@ -0,0 +1,6 @@ +vlib work +vcom several vcom options -quiet -work work vhdl_file.vhd +vlib libx +vcom several vcom options -quiet -work libx vhdl_lfile +vcom -2008 several vcom options -quiet -work work vhdl2008_file +vlog +incdir+. a few vlog options +define+vlogdefine_bool=1 +define+vlogdefine_int=42 +define+vlogdefine_str=hello -sv +incdir+. -quiet -work work -mfcu sv_file.sv vlog_file.v vlog05_file.v another_sv_file.sv \ No newline at end of file diff --git a/tests/tools/modelsim/mfcu/edalize_main.tcl b/tests/tools/modelsim/mfcu/edalize_main.tcl new file mode 100644 index 000000000..ec4861eb3 --- /dev/null +++ b/tests/tools/modelsim/mfcu/edalize_main.tcl @@ -0,0 +1,3 @@ +onerror { quit -code 1; } +do edalize_build_rtl.tcl +do tcl_file.tcl