Skip to content

Commit

Permalink
Port Efinity backend to flow API
Browse files Browse the repository at this point in the history
  • Loading branch information
olofk committed Oct 9, 2023
1 parent 7a0ff97 commit 9f981c1
Show file tree
Hide file tree
Showing 3 changed files with 263 additions and 0 deletions.
156 changes: 156 additions & 0 deletions edalize/tools/efinity.py
Original file line number Diff line number Diff line change
@@ -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")
18 changes: 18 additions & 0 deletions edalize/tools/templates/efinity/isf_to_xml.py
Original file line number Diff line number Diff line change
@@ -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")
89 changes: 89 additions & 0 deletions edalize/tools/templates/efinity/newproj_tmpl.xml.j2
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
<?xml version="1.0" encoding="UTF-8"?>
<efx:project name="{{name}}" description="" last_change_date="" location="" sw_version="" xmlns:efx="http://www.efinixinc.com/enf_proj" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.efinixinc.com/enf_proj enf_proj.xsd">
<efx:device_info>
{% if tool_options.family -%}
<efx:family name="{{tool_options.family}}"/>
{%- endif %}
{% if tool_options.part -%}
<efx:device name="{{tool_options.part}}"/>
{%- endif %}
{% if tool_options.timing -%}
<efx:timing_model name="{{tool_options.timing}}"/>
{%- endif %}
</efx:device_info>
<efx:design_info def_veri_version="verilog_2k" def_vhdl_version="vhdl_2008">
{% if tool_options.timing -%}
<efx:top_module name="{{toplevel}}"/>
{%- endif %}
{%- for f in design_files %}
<efx:design_file name="{{f.name}}" version="{{f.version}}"/>
{% endfor -%}
<efx:top_vhdl_arch name=""/>
</efx:design_info>
<efx:package_info>
<efx:package name=""/>
</efx:package_info>
<efx:constraint_info>
{% if constr_files -%}
{%- for f in constr_files %}
<efx:sdc_file name="{{f}}" />
{% endfor -%}
{%- endif %}
</efx:constraint_info>
<efx:sim_info/>
<efx:misc_info/>
<efx:ip_info/>
<efx:synthesis tool_name="efx_map">
<efx:param name="work_dir" value="work_syn" value_type="e_string"/>
<efx:param name="write_efx_verilog" value="on" value_type="e_bool"/>
<efx:param name="seq_opt" value="0" value_type="e_option"/>
{% if vlogparam -%}
{%- for k, v in vlogparam.items() %}
<efx:dynparam name="{{k}}" value="{{v}}"/>
{% endfor -%}
{%- endif %}
{% if generic -%}
{%- for k, v in generic.items() %}
<efx:dynparam name="{{k}}" value="{{v}}"/>
{% endfor -%}
{%- endif %}
{% if vlogdefine -%}
{%- for k, v in vlogdefine.items() %}
<efx:defmacro name="{{k}}" value="{{v}}"/>
{% endfor -%}
{%- endif %}
</efx:synthesis>
<efx:place_and_route tool_name="efx_pnr">
<efx:param name="work_dir" value="work_pnr" value_type="e_string" />
<efx:param name="verbose" value="off" value_type="e_bool" />
<efx:param name="load_delaym" value="on" value_type="e_bool"/>
</efx:place_and_route>
<efx:bitstream_generation tool_name="efx_pgm">
<efx:param name="mode" value="active" value_type="e_string"/>
<efx:param name="width" value="1" value_type="e_string"/>
<!-- on, off, smart -->
<efx:param name="enable_roms" value="smart" value_type="e_option"/>
<efx:param name="spi_low_power_mode" value="on" value_type="e_bool"/>
<efx:param name="io_weak_pullup" value="on" value_type="e_bool"/>
<efx:param name="oscillator_clock_divider" value="DIV8" value_type="e_option"/>
<!-- on, off -->
<efx:param name="bitstream_compression" value="on" value_type="e_bool"/>
<!-- on, off -->
<efx:param name="enable_external_master_clock" value="off" value_type="e_bool"/>
<!-- posedge, negedge -->
<efx:param name="active_capture_clk_edge" value="posedge" value_type="e_option"/>
<efx:param name="jtag_usercode" value="0xFFFFFFFF" value_type="e_string"/>
<!-- on, off -->
<efx:param name="release_tri_then_reset" value="on" value_type="e_bool"/>
</efx:bitstream_generation>
<efx:debugger>
<efx:param name="work_dir" value="work_dbg" value_type="e_string" />
<efx:param name="auto_instantiation" value="off" value_type="e_bool" />
<efx:param name="profile" value="NONE" value_type="e_string" />
</efx:debugger>
<efx:security>
<!-- on, off -->
<efx:param name="randomize_iv_value" value="on" value_type="e_bool"/>
<efx:param name="iv_value" value="" value_type="e_string"/>
</efx:security>
</efx:project>

0 comments on commit 9f981c1

Please sign in to comment.