diff --git a/edalize/tools/vcs.py b/edalize/tools/vcs.py new file mode 100644 index 00000000..c382e50f --- /dev/null +++ b/edalize/tools/vcs.py @@ -0,0 +1,193 @@ +# 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 Vcs(Edatool): + + description = "VCS simulator from Synopsys" + + TOOL_OPTIONS = { + "32bit": { + "type": "bool", + "desc": "Disable 64-bit mode", + }, + "vlogan_options": { + "type": "str", + "desc": "Additional options for analysis with vlogan", + "list": True, + }, + "vhdlan_options": { + "type": "str", + "desc": "Additional options for analysis with vhdlan", + "list": True, + }, + "vcs_options": { + "type": "str", + "desc": "Additional options for elaboration with vcs", + "list": True, + }, + "run_options": { + "type": "str", + "desc": "Additional run-time options for the simulation", + "list": True, + }, + } + + def setup(self, edam): + super().setup(edam) + + incdirs = [] + include_files = [] + unused_files = self.files.copy() + # Get all include dirs. Move include files to a separate list + for f in self.files: + if not "simulation" in f.get("tags", ["simulation"]): + continue + file_type = f.get("file_type", "") + if file_type.startswith("verilogSource") or file_type.startswith( + "systemVerilogSource" + ): + if self._add_include_dir(f, incdirs, force_slash=True): + include_files.append(f["name"]) + unused_files.remove(f) + + user_files = [] + commands = {} + libs = {} + has_sv = False + for f in unused_files.copy(): + lib = f.get("logical_name", "work") + + file_type = f.get("file_type", "") + if file_type.startswith("verilogSource") or file_type.startswith( + "systemVerilogSource" + ): + + if file_type.startswith("systemVerilogSource"): + has_sv = True + + vlog_defines = self.vlogdefine.copy() + vlog_defines.update(f.get("define", {})) + + _args = [] + for k, v in vlog_defines.items(): + _args.append( + "+define+{}={}".format( + k, self._param_value_str(v, str_quote_style='""') + ) + ) + defines = " ".join(_args) + cmd = "vlogan" + elif file_type.startswith("vhdlSource"): + cmd = "vhdlan" + elif file_type == "user": + user_files.append(f["name"]) + cmd = None + else: + cmd = None + + if not "simulation" in f.get("tags", ["simulation"]): + cmd = None + + if cmd: + if not lib in libs: + libs[lib] = [] + libs[lib].append((cmd, f["name"], defines)) + if not commands.get((cmd, lib, defines)): + commands[(cmd, lib, defines)] = [] + commands[(cmd, lib, defines)].append(f["name"]) + unused_files.remove(f) + + full64 = [] if self.tool_options.get("32bit") else ["-full64"] + self.commands = EdaCommands() + self.f_files = {} + for lib, files in libs.items(): + cmds = {} + depfiles = [] + has_vlog = False + # Group into individual commands + for (cmd, fname, defines) in files: + if not (cmd, defines) in cmds: + cmds[(cmd, defines)] = [] + cmds[(cmd, defines)].append(fname) + depfiles.append(fname) + if cmd == "vlogan": + has_vlog = True + commands = [["mkdir", "-p", lib]] + i = 1 + f_files = {} + for (cmd, defines), fnames in cmds.items(): + options = [] + if cmd == "vlogan": + if has_sv: + options.append("-sverilog") + options += self.tool_options.get("vlogan_options", []) + options += [defines] + options += ["+incdir+" + d for d in incdirs] + elif cmd == "vhdlan": + options += self.tool_options.get("vhdlan_options", []) + f_file = f"{lib}.{i}.f" + f_files[f_file] = options + i += 1 + commands.append([cmd] + full64 + ["-f", f_file, "-work", lib] + fnames) + if has_vlog: + depfiles += include_files + self.commands.add( + commands, [lib + "/AN.DB"], depfiles + list(f_files.keys()) + ) + self.f_files.update(f_files) + + self.edam = edam.copy() + self.edam["files"] = unused_files + + _parameters = {**self.vlogparam, **self.generic} + parameters = [] + for key, value in _parameters.items(): + parameters += [f"-pvalue+{key}=" + self._param_value_str(value)] + + self.f_files["vcs.f"] = ( + ["-top", self.toplevel] + + self.tool_options.get("vcs_options", []) + + parameters + ) + self.commands.add( + ["vcs"] + full64 + ["-o", self.name, "-file", "vcs.f"], + [self.name], + [x + "/AN.DB" for x in libs.keys()] + user_files + ["vcs.f"], + ) + + self.commands.add( + ["./" + self.name, "$(EXTRA_OPTIONS)"] + + self.tool_options.get("run_options", []), + ["run"], + [self.name], + ) + self.commands.set_default_target(self.name) + self.libs = libs.keys() + + def write_config_files(self): + s = "WORK > DEFAULT\nDEFAULT : ./work\n" + for lib in self.libs: + if lib != "work": + s += f"{lib} : ./{lib}\n" + self.update_config_file("synopsys_sim.setup", s) + for k, v in self.f_files.items(): + self.update_config_file(k, " ".join(v) + "\n") + + 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_vcs.py b/tests/test_tool_vcs.py new file mode 100644 index 00000000..22a6a8d4 --- /dev/null +++ b/tests/test_tool_vcs.py @@ -0,0 +1,32 @@ +from .edalize_tool_common import tool_fixture + +# TODO: File-specific defines +# Run / run-options +def test_tool_vcs(tool_fixture): + + tool_options = { + "vlogan_options": ["a", "few", "vlogan", "options"], + "vhdlan_options": ["also", "vhdlan", "options"], + "vcs_options": ["some", "vcs", "options"], + "run_options": ["and", "some", "run", "options"], + } + + tf = tool_fixture("vcs", tool_options=tool_options, ref_subdir="basic") + + name = "design" + + tf.tool.configure() + tf.compare_config_files( + ["synopsys_sim.setup", "libx.1.f", "vcs.f", "work.1.f", "work.2.f", "work.3.f"] + ) + + +def test_tool_vcs_minimal(tool_fixture): + tf = tool_fixture("vcs", ref_subdir="minimal") + + name = "design" + + tf.tool.configure() + tf.compare_config_files( + ["synopsys_sim.setup", "libx.1.f", "vcs.f", "work.1.f", "work.2.f", "work.3.f"] + ) diff --git a/tests/tools/vcs/basic/Makefile b/tests/tools/vcs/basic/Makefile new file mode 100644 index 00000000..39211958 --- /dev/null +++ b/tests/tools/vcs/basic/Makefile @@ -0,0 +1,19 @@ +#Auto generated by Edalize + +all: design + +work/AN.DB: sv_file.sv vlog_file.v vlog_with_define.v vlog05_file.v vhdl_file.vhd vhdl2008_file another_sv_file.sv vlog_incfile work.1.f work.2.f work.3.f + $(EDALIZE_LAUNCHER) mkdir -p work + $(EDALIZE_LAUNCHER) vlogan -full64 -f work.1.f -work work sv_file.sv vlog_file.v vlog05_file.v another_sv_file.sv + $(EDALIZE_LAUNCHER) vlogan -full64 -f work.2.f -work work vlog_with_define.v + $(EDALIZE_LAUNCHER) vhdlan -full64 -f work.3.f -work work vhdl_file.vhd vhdl2008_file + +libx/AN.DB: vhdl_lfile libx.1.f + $(EDALIZE_LAUNCHER) mkdir -p libx + $(EDALIZE_LAUNCHER) vhdlan -full64 -f libx.1.f -work libx vhdl_lfile + +design: work/AN.DB libx/AN.DB user_file vcs.f + $(EDALIZE_LAUNCHER) vcs -full64 -o design -f vcs.f + +run: design + $(EDALIZE_LAUNCHER) ./design $(EXTRA_OPTIONS) and some run options diff --git a/tests/tools/vcs/basic/libx.1.f b/tests/tools/vcs/basic/libx.1.f new file mode 100644 index 00000000..4fd7ff33 --- /dev/null +++ b/tests/tools/vcs/basic/libx.1.f @@ -0,0 +1 @@ +also vhdlan options diff --git a/tests/tools/vcs/basic/synopsys_sim.setup b/tests/tools/vcs/basic/synopsys_sim.setup new file mode 100644 index 00000000..07124e38 --- /dev/null +++ b/tests/tools/vcs/basic/synopsys_sim.setup @@ -0,0 +1,3 @@ +WORK > DEFAULT +DEFAULT : ./work +libx : ./libx diff --git a/tests/tools/vcs/basic/vcs.f b/tests/tools/vcs/basic/vcs.f new file mode 100644 index 00000000..f1224df0 --- /dev/null +++ b/tests/tools/vcs/basic/vcs.f @@ -0,0 +1 @@ +-top top_module some vcs options -pvalue+vlogparam_bool=1 -pvalue+vlogparam_int=42 -pvalue+vlogparam_str=hello diff --git a/tests/tools/vcs/basic/work.1.f b/tests/tools/vcs/basic/work.1.f new file mode 100644 index 00000000..7d4badd6 --- /dev/null +++ b/tests/tools/vcs/basic/work.1.f @@ -0,0 +1 @@ +-sverilog a few vlogan options +define+vlogdefine_bool=1 +define+vlogdefine_int=42 +define+vlogdefine_str=""hello"" +incdir+. diff --git a/tests/tools/vcs/basic/work.2.f b/tests/tools/vcs/basic/work.2.f new file mode 100644 index 00000000..1bdc1d02 --- /dev/null +++ b/tests/tools/vcs/basic/work.2.f @@ -0,0 +1 @@ +-sverilog a few vlogan options +define+vlogdefine_bool=1 +define+vlogdefine_int=42 +define+vlogdefine_str=""hello"" +define+FD_KEY=""FD_VAL"" +incdir+. diff --git a/tests/tools/vcs/basic/work.3.f b/tests/tools/vcs/basic/work.3.f new file mode 100644 index 00000000..4fd7ff33 --- /dev/null +++ b/tests/tools/vcs/basic/work.3.f @@ -0,0 +1 @@ +also vhdlan options diff --git a/tests/tools/vcs/minimal/Makefile b/tests/tools/vcs/minimal/Makefile new file mode 100644 index 00000000..84d9ba70 --- /dev/null +++ b/tests/tools/vcs/minimal/Makefile @@ -0,0 +1,19 @@ +#Auto generated by Edalize + +all: design + +work/AN.DB: sv_file.sv vlog_file.v vlog_with_define.v vlog05_file.v vhdl_file.vhd vhdl2008_file another_sv_file.sv vlog_incfile work.1.f work.2.f work.3.f + $(EDALIZE_LAUNCHER) mkdir -p work + $(EDALIZE_LAUNCHER) vlogan -full64 -f work.1.f -work work sv_file.sv vlog_file.v vlog05_file.v another_sv_file.sv + $(EDALIZE_LAUNCHER) vlogan -full64 -f work.2.f -work work vlog_with_define.v + $(EDALIZE_LAUNCHER) vhdlan -full64 -f work.3.f -work work vhdl_file.vhd vhdl2008_file + +libx/AN.DB: vhdl_lfile libx.1.f + $(EDALIZE_LAUNCHER) mkdir -p libx + $(EDALIZE_LAUNCHER) vhdlan -full64 -f libx.1.f -work libx vhdl_lfile + +design: work/AN.DB libx/AN.DB user_file vcs.f + $(EDALIZE_LAUNCHER) vcs -full64 -o design -f vcs.f + +run: design + $(EDALIZE_LAUNCHER) ./design $(EXTRA_OPTIONS) diff --git a/tests/tools/vcs/minimal/libx.1.f b/tests/tools/vcs/minimal/libx.1.f new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/tests/tools/vcs/minimal/libx.1.f @@ -0,0 +1 @@ + diff --git a/tests/tools/vcs/minimal/synopsys_sim.setup b/tests/tools/vcs/minimal/synopsys_sim.setup new file mode 100644 index 00000000..07124e38 --- /dev/null +++ b/tests/tools/vcs/minimal/synopsys_sim.setup @@ -0,0 +1,3 @@ +WORK > DEFAULT +DEFAULT : ./work +libx : ./libx diff --git a/tests/tools/vcs/minimal/vcs.f b/tests/tools/vcs/minimal/vcs.f new file mode 100644 index 00000000..cd057dbb --- /dev/null +++ b/tests/tools/vcs/minimal/vcs.f @@ -0,0 +1 @@ +-top top_module -pvalue+vlogparam_bool=1 -pvalue+vlogparam_int=42 -pvalue+vlogparam_str=hello diff --git a/tests/tools/vcs/minimal/work.1.f b/tests/tools/vcs/minimal/work.1.f new file mode 100644 index 00000000..f1e48733 --- /dev/null +++ b/tests/tools/vcs/minimal/work.1.f @@ -0,0 +1 @@ +-sverilog +define+vlogdefine_bool=1 +define+vlogdefine_int=42 +define+vlogdefine_str=""hello"" +incdir+. diff --git a/tests/tools/vcs/minimal/work.2.f b/tests/tools/vcs/minimal/work.2.f new file mode 100644 index 00000000..8b1da4fe --- /dev/null +++ b/tests/tools/vcs/minimal/work.2.f @@ -0,0 +1 @@ +-sverilog +define+vlogdefine_bool=1 +define+vlogdefine_int=42 +define+vlogdefine_str=""hello"" +define+FD_KEY=""FD_VAL"" +incdir+. diff --git a/tests/tools/vcs/minimal/work.3.f b/tests/tools/vcs/minimal/work.3.f new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/tests/tools/vcs/minimal/work.3.f @@ -0,0 +1 @@ +