From 9f981c1e2ccbcebaa0b79afdbc0d3d41e3f56ce1 Mon Sep 17 00:00:00 2001 From: Olof Kindgren Date: Mon, 9 Oct 2023 21:58:02 +0200 Subject: [PATCH] Port Efinity backend to flow API --- edalize/tools/efinity.py | 156 ++++++++++++++++++ edalize/tools/templates/efinity/isf_to_xml.py | 18 ++ .../templates/efinity/newproj_tmpl.xml.j2 | 89 ++++++++++ 3 files changed, 263 insertions(+) create mode 100644 edalize/tools/efinity.py create mode 100644 edalize/tools/templates/efinity/isf_to_xml.py create mode 100644 edalize/tools/templates/efinity/newproj_tmpl.xml.j2 diff --git a/edalize/tools/efinity.py b/edalize/tools/efinity.py new file mode 100644 index 000000000..281f06481 --- /dev/null +++ b/edalize/tools/efinity.py @@ -0,0 +1,156 @@ +# Copyright edalize contributors +# Licensed under the 2-Clause BSD License, see LICENSE for details. +# SPDX-License-Identifier: BSD-2-Clause + +import os +import sys + +from edalize.tools.edatool import Edatool +from edalize.utils import EdaCommands + + +class Efinity(Edatool): + """ + Efinity Backend. + + The Efinity backend executes Efinity to build systems and program the FPGA + """ + + argtypes = ["vlogdefine", "vlogparam", "generic"] + + TOOL_OPTIONS = { + "family": { + "type": "str", + "desc": "FPGA family. Accepted is Trion and Titanium (default)", + }, + "part": { + "type": "str", + "desc": "FPGA part number (e.g. Ti180M484)", + }, + "timing": { + "type": "str", + "desc": "Speed grade (e.g. C4)", + }, + } + + def setup(self, edam): + """ + Create required files to make an Efinix build. Two files required: + - XML project file + - XML file with IO and hard blocks definitions (only done if an ISF file is included) + """ + super().setup(edam) + + self.efinity_home = os.getenv("EFINITY_HOME") + if self.efinity_home == None: + raise RuntimeError("The environment variable EFINITY_HOME is not set.") + + if sys.platform == "win32" or sys.platform == "cygwin": + self.efinity_python = self.efinity_home + "/python38/bin/python" + else: + self.efinity_python = self.efinity_home + "/bin/python3" + + for i in ["family", "part", "timing"]: + if not i in self.tool_options: + raise RuntimeError("Missing required option '{}'".format(i)) + + design_files = [] + constr_files = [] + self.isf_file = None + dep_files = [] + unused_files = [] + + for f in self.files: + _fn = f["name"] + _ft = f.get("file_type", "") + version = None + if _ft.startswith("vhdlSource") or _ft.startswith("verilogSource"): + version = "" + elif _ft.startswith("systemVerilogSource"): + version = "sv_09" + + if version is not None: + f["version"] = version + design_files.append(f) + dep_files.append(_fn) + elif _ft == "SDC": + constr_files.append(_fn) + dep_files.append(_fn) + elif _ft == "ISF": + if self.isf_file: + raise RuntimeError( + "The Efinity backend only supports a single ISF file." + ) + self.isf_file = _fn + else: + unused_files.append(f) + + bit_file = os.path.join("outflow", self.name + ".bit") + self.edam = edam.copy() + + self.edam["files"] = unused_files + self.edam["files"].append( + { + "name": bit_file, + } + ) + + self.template_vars = { + "name": self.name, + "design_files": design_files, + "constr_files": constr_files, + "tool_options": self.tool_options, + "toplevel": self.toplevel, + "vlogparam": self.vlogparam, + "vlogdefine": self.vlogdefine, + "generic": self.generic, + } + + commands = EdaCommands() + + if self.isf_file: + # Run script to generate .peri.xml from isf + commands.add( + [ + self.efinity_python, + "isf_to_xml.py", + self.name, + self.tool_options.get("part"), + self.isf_file, + ], + [self.name + ".peri.xml"], + [self.isf_file], + variables={"EFXPT_HOME": self.efinity_home + "/pt"}, + ) + dep_files.append(self.name + ".peri.xml") + + commands.add( + [ + self.efinity_python, + os.path.join(self.efinity_home, "scripts", "efx_run.py"), + "--prj", + self.name + ".xml", + "--work_dir", + self.work_root, + "--flow", + "compile", + ], + [bit_file], + dep_files, + variables={ + "EFXPT_HOME": self.efinity_home + "/pt", + "EFXPGM_HOME": self.efinity_home + "/pgm", + }, + ) + commands.set_default_target(bit_file) + self.commands = commands + + def write_config_files(self): + # Render XML project file + self.render_template( + "newproj_tmpl.xml.j2", self.name + ".xml", self.template_vars + ) + + if self.isf_file: + # Create XML file with IO and hard blocks definitions + self.render_template("isf_to_xml.py", "isf_to_xml.py") diff --git a/edalize/tools/templates/efinity/isf_to_xml.py b/edalize/tools/templates/efinity/isf_to_xml.py new file mode 100644 index 000000000..cbd0572b5 --- /dev/null +++ b/edalize/tools/templates/efinity/isf_to_xml.py @@ -0,0 +1,18 @@ +import os +import sys + +sys.path.append(os.getenv("EFXPT_HOME") + "/bin") + +from api_service.design import DesignAPI +import api_service.excp.design_excp as APIExcp + +design = DesignAPI(False) + +name = sys.argv[1] +part = sys.argv[2] +workdir = "." +isf_file = sys.argv[3] +design.create(name, part, workdir) +design.import_design(isf_file) +design.generate(enable_bitstream=False) +design.save_as(name + ".peri.xml") diff --git a/edalize/tools/templates/efinity/newproj_tmpl.xml.j2 b/edalize/tools/templates/efinity/newproj_tmpl.xml.j2 new file mode 100644 index 000000000..a4e8125ca --- /dev/null +++ b/edalize/tools/templates/efinity/newproj_tmpl.xml.j2 @@ -0,0 +1,89 @@ + + + + {% if tool_options.family -%} + + {%- endif %} + {% if tool_options.part -%} + + {%- endif %} + {% if tool_options.timing -%} + + {%- endif %} + + + {% if tool_options.timing -%} + + {%- endif %} + {%- for f in design_files %} + + {% endfor -%} + + + + + + + {% if constr_files -%} + {%- for f in constr_files %} + + {% endfor -%} + {%- endif %} + + + + + + + + + {% if vlogparam -%} + {%- for k, v in vlogparam.items() %} + + {% endfor -%} + {%- endif %} + {% if generic -%} + {%- for k, v in generic.items() %} + + {% endfor -%} + {%- endif %} + {% if vlogdefine -%} + {%- for k, v in vlogdefine.items() %} + + {% endfor -%} + {%- endif %} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +