From dc6568752b324a85d47ba6ad71df9803aa5af689 Mon Sep 17 00:00:00 2001 From: Theo Hussey Date: Mon, 4 Nov 2024 22:46:14 +0000 Subject: [PATCH] Add support for official gowin tool --- edalize/flows/gowin.py | 25 +++ edalize/tools/gowin.py | 150 ++++++++++++++++++ .../templates/gowin/gowin-project.tcl.j2 | 18 +++ tests/edalize_tool_common.py | 11 +- tests/test_tool_gowin.py | 30 ++++ tests/tools/gowin/edalize_gowin_template.tcl | 24 +++ .../gowin/minimal/edalize_gowin_template.tcl | 21 +++ 7 files changed, 277 insertions(+), 2 deletions(-) create mode 100644 edalize/flows/gowin.py create mode 100644 edalize/tools/gowin.py create mode 100644 edalize/tools/templates/gowin/gowin-project.tcl.j2 create mode 100644 tests/test_tool_gowin.py create mode 100644 tests/tools/gowin/edalize_gowin_template.tcl create mode 100644 tests/tools/gowin/minimal/edalize_gowin_template.tcl diff --git a/edalize/flows/gowin.py b/edalize/flows/gowin.py new file mode 100644 index 000000000..40a6f7596 --- /dev/null +++ b/edalize/flows/gowin.py @@ -0,0 +1,25 @@ +# Copyright edalize contributors +# Licensed under the 2-Clause BSD License, see LICENSE for details. +# SPDX-License-Identifier: BSD-2-Clause + +from edalize.flows.generic import Generic + + +class Gowin(Generic): + """Official Gowin FPGA toolchain""" + + argtypes = [] + + @classmethod + def get_flow_options(cls): + return {k: v for k, v in cls.FLOW_OPTIONS.items() if k != "tool"} + + @classmethod + def get_tool_options(cls, flow_options): + flow = flow_options.get("frontends", []).copy() + ["gowin"] + + return cls.get_filtered_tool_options(flow, cls.FLOW_DEFINED_TOOL_OPTIONS) + + def configure_flow(self, flow_options): + self.flow_options["tool"] = "gowin" + return super().configure_flow(flow_options) diff --git a/edalize/tools/gowin.py b/edalize/tools/gowin.py new file mode 100644 index 000000000..714cf1ef3 --- /dev/null +++ b/edalize/tools/gowin.py @@ -0,0 +1,150 @@ +# Copyright edalize contributors +# Licensed under the 2-Clause BSD License, see LICENSE for details. +# SPDX-License-Identifier: BSD-2-Clause + +import os.path + +from edalize.tools.edatool import Edatool +from edalize.utils import EdaCommands +from functools import partial + + +class Gowin(Edatool): + + description = "Official development tool for Gowin FPGAs" + + TOOL_OPTIONS = { + "part": { + "type": "str", + "desc": "FPGA part number (e.g. GW2AR-LV18QN88C8/I7)", + }, + "part_version": { + "type": "str", + "desc": "Part version. e.g 'C'", + }, + "synth": { + "type": "str", + "desc": "Synthesis tool. Allowed values are gowin (default) or none.", + }, + "pnr": { + "type": "str", + "desc": "P&R tool. Allowed values are gowin (default) and none (to just run synthesis)", + }, + "gowin_options": { + "type": "str", + "desc": "Additional options for Gowin. See Gowin Software User Guide SUG-100 > set_option", + "list": True, + }, + } + + def src_file_filter(self, f): + def _append_library(f): + s = "" + if f.get("logical_name"): + s += ( + "\nset_file_prop -lib " + f["logical_name"] + ' "' + f["name"] + '"' + ) + return s + + def _handle_src(t, f): + s = "add_file -type " + t + s += ' "' + f["name"] + '"' + s += _append_library(f) + return s + + def _handle_tcl(f): + return "source " + f["name"] + + file_mapping = { + "verilogSource": partial(_handle_src, "verilog"), + "systemVerilogSource": partial(_handle_src, "verilog"), + "vhdlSource": partial(_handle_src, "VHDL_FILE"), + "CST": partial(_handle_src, "cst"), + "SDC": partial(_handle_src, "sdc"), + "tclSource": partial(_handle_tcl), + } + + _file_type = f.get("file_type") + if _file_type in file_mapping: + return file_mapping[_file_type](f) + elif _file_type == "user": + return "" + + return "" + + def setup(self, edam): + super().setup(edam) + + file_table = [] + unused_files = [] + depfiles = [] + + has_vhdl2008 = "vhdlSource-2008" in [x["file_type"] for x in self.files] + has_systemVerilog = "systemVerilogSource" in [ + x["file_type"] for x in self.files + ] + + escaped_name = self.name.replace(".", "_") + + if not self.tool_options.get("synth"): + self.tool_options["synth"] = "gowin" + + if not self.tool_options.get("pnr"): + self.tool_options["pnr"] = "gowin" + + if not self.tool_options.get("part"): + raise RuntimeError("FPGA part number must be specified") + + if self.generic: + raise RuntimeError("Gowin does not support top level generics") + + if self.vlogparam: + raise RuntimeError("Gowin does not support top level verilog parameters") + + if self.vlogdefine: + raise RuntimeError("Gowin does not support top level verilog defines") + + commands = EdaCommands() + + for f in self.files: + cmd = self.src_file_filter(f) + + if cmd: + depfiles.append(f["name"]) + file_table.append(cmd) + else: + unused_files.append(f) + + self.edam = edam.copy() + self.edam["files"] = unused_files + + fs_file = os.path.join("pnr", escaped_name + ".fs") + + self.edam["files"].append( + { + "name": fs_file, + } + ) + + self.template_vars = { + "name": escaped_name, + "file_table": file_table, + "tool_options": self.tool_options, + "toplevel": self.toplevel, + "has_vhdl2008": has_vhdl2008, + "has_systemVerilog": has_systemVerilog, + } + + commands.add( + ["gw_sh", "edalize_gowin_template.tcl"], + [fs_file], + depfiles, + ) + + commands.set_default_target(fs_file) + self.commands = commands + + def write_config_files(self): + self.render_template( + "gowin-project.tcl.j2", "edalize_gowin_template.tcl", self.template_vars + ) diff --git a/edalize/tools/templates/gowin/gowin-project.tcl.j2 b/edalize/tools/templates/gowin/gowin-project.tcl.j2 new file mode 100644 index 000000000..e27358c23 --- /dev/null +++ b/edalize/tools/templates/gowin/gowin-project.tcl.j2 @@ -0,0 +1,18 @@ +set_device {{ tool_options.part }} {{ "--device_version " + tool_options.part_version if tool_options.part_version else "" }} + + +{% for src_file in file_table %} +{{ src_file }} +{% endfor %} + +set_option -top_module {{ toplevel }} +{{ "set_option -vhdl_std vhd2008" if has_vhdl2008 else "" }} +{{ "set_option -verilog_std sysv2017" if has_systemVerilog else "" }} + +{% for option in tool_options.gowin_options %} +set_option {{ option }} +{% endfor %} + + +{{ "run syn" if tool_options.synth == "gowin" else "" }} +{{ "run pnr" if tool_options.pnr == "gowin" else "" }} \ No newline at end of file diff --git a/tests/edalize_tool_common.py b/tests/edalize_tool_common.py index 05e0d0fec..d66990817 100644 --- a/tests/edalize_tool_common.py +++ b/tests/edalize_tool_common.py @@ -50,18 +50,25 @@ def _tool_fixture( toplevel="top_module", ref_subdir="", config_files=[], + paramtypes=["plusarg", "vlogdefine", "vlogparam"], + has_makefile=True, ): tf = ToolFixture(tool_name, ref_subdir) edam = get_edam( - tool_name, tool_options=tool_options, files=files, toplevel=toplevel + tool_name, + tool_options=tool_options, + files=files, + toplevel=toplevel, + paramtypes=paramtypes, ) tf.tool.work_root = tmp_path tf.tool.setup(edam) tf.tool.commands.write(tmp_path / "Makefile") - tf.compare_makefile() + if has_makefile: + tf.compare_makefile() return tf diff --git a/tests/test_tool_gowin.py b/tests/test_tool_gowin.py new file mode 100644 index 000000000..039f3d201 --- /dev/null +++ b/tests/test_tool_gowin.py @@ -0,0 +1,30 @@ +from .edalize_tool_common import tool_fixture + + +def test_tool_gowin(tool_fixture): + + tool_options = { + "part": "dummy_part", + "gowin_options": ["some", "gowin", "options"], + } + + tf = tool_fixture( + "gowin", tool_options=tool_options, paramtypes=[], has_makefile=False + ) + + tf.tool.configure() + tf.compare_config_files(["edalize_gowin_template.tcl"]) + + +def test_tool_gowin_minimal(tool_fixture): + tool_options = {"part": "dummy_part"} + tf = tool_fixture( + "gowin", + tool_options=tool_options, + ref_subdir="minimal", + paramtypes=[], + has_makefile=False, + ) + + tf.tool.configure() + tf.compare_config_files(["edalize_gowin_template.tcl"]) diff --git a/tests/tools/gowin/edalize_gowin_template.tcl b/tests/tools/gowin/edalize_gowin_template.tcl new file mode 100644 index 000000000..3820e35d9 --- /dev/null +++ b/tests/tools/gowin/edalize_gowin_template.tcl @@ -0,0 +1,24 @@ +set_device dummy_part + + +add_file -type sdc "sdc_file" +add_file -type verilog "sv_file.sv" +source tcl_file.tcl +add_file -type verilog "vlog_file.v" +add_file -type verilog "vlog_incfile" +add_file -type VHDL_FILE "vhdl_file.vhd" +add_file -type VHDL_FILE "vhdl_lfile" +set_file_prop -lib libx "vhdl_lfile" +add_file -type verilog "another_sv_file.sv" + +set_option -top_module top_module +set_option -vhdl_std vhd2008 +set_option -verilog_std sysv2017 + +set_option some +set_option gowin +set_option options + + +run syn +run pnr \ No newline at end of file diff --git a/tests/tools/gowin/minimal/edalize_gowin_template.tcl b/tests/tools/gowin/minimal/edalize_gowin_template.tcl new file mode 100644 index 000000000..1e6e898fa --- /dev/null +++ b/tests/tools/gowin/minimal/edalize_gowin_template.tcl @@ -0,0 +1,21 @@ +set_device dummy_part + + +add_file -type sdc "sdc_file" +add_file -type verilog "sv_file.sv" +source tcl_file.tcl +add_file -type verilog "vlog_file.v" +add_file -type verilog "vlog_incfile" +add_file -type VHDL_FILE "vhdl_file.vhd" +add_file -type VHDL_FILE "vhdl_lfile" +set_file_prop -lib libx "vhdl_lfile" +add_file -type verilog "another_sv_file.sv" + +set_option -top_module top_module +set_option -vhdl_std vhd2008 +set_option -verilog_std sysv2017 + + + +run syn +run pnr \ No newline at end of file