build: gowin lattice/diamond lattice/oxide lattice/radiant microsemi quicklogic move to GenericToolchain

This commit is contained in:
Gwenhael Goavec-Merou 2022-06-26 21:40:56 +02:00
parent 1f9bf1bd06
commit 6541a6c93b
6 changed files with 849 additions and 1047 deletions

View File

@ -13,85 +13,25 @@ from shutil import which, copyfile
from migen.fhdl.structure import _Fragment
from litex.build.generic_toolchain import GenericToolchain
from litex.build.generic_platform import *
from litex.build import tools
# Constraints (.cst and .tcl) ----------------------------------------------------------------------
def _build_cst(named_sc, named_pc):
cst = []
flat_sc = []
for name, pins, other, resource in named_sc:
if len(pins) > 1:
for i, p in enumerate(pins):
flat_sc.append((f"{name}[{i}]", p, other))
else:
flat_sc.append((name, pins[0], other))
for name, pin, other in flat_sc:
if pin != "X":
cst.append(f"IO_LOC \"{name}\" {pin};")
for c in other:
if isinstance(c, IOStandard):
cst.append(f"IO_PORT \"{name}\" IO_TYPE={c.name};")
elif isinstance(c, Misc):
cst.append(f"IO_PORT \"{name}\" {c.misc};")
if named_pc:
cst.extend(named_pc)
with open("top.cst", "w") as f:
f.write("\n".join(cst))
def _build_sdc(clocks, vns):
sdc = []
for clk, period in sorted(clocks.items(), key=lambda x: x[0].duid):
sdc.append(f"create_clock -name {vns.get_name(clk)} -period {str(period)} [get_ports {{{vns.get_name(clk)}}}]")
with open("top.sdc", "w") as f:
f.write("\n".join(sdc))
# Script -------------------------------------------------------------------------------------------
def _build_tcl(name, partnumber, files, options):
tcl = []
# Set Device.
tcl.append(f"set_device -name {name} {partnumber}")
# Add IOs Constraints.
tcl.append("add_file top.cst")
# Add Timings Constraints.
tcl.append("add_file top.sdc")
# Add Sources.
for f, typ, lib in files:
# Support windows/powershell
if sys.platform == "win32":
f = f.replace("\\", "\\\\")
tcl.append(f"add_file {f}")
# Set Options.
for opt, val in options.items():
tcl.append(f"set_option -{opt} {val}")
# Run.
tcl.append("run all")
# Generate .tcl.
with open("run.tcl", "w") as f:
f.write("\n".join(tcl))
# GowinToolchain -----------------------------------------------------------------------------------
class GowinToolchain:
class GowinToolchain(GenericToolchain):
attr_translate = {}
def __init__(self):
super().__init__()
self.options = {}
self.clocks = dict()
def finalize(self):
if self.platform.verilog_include_paths:
self.options["include_path"] = "{" + ";".join(self.platform.verilog_include_paths) + "}"
self.apply_hyperram_integration_hack(self._build_name + ".v")
def apply_hyperram_integration_hack(self, v_file):
# FIXME: Gowin EDA expects a very specific HypeRAM integration pattern, modify generated verilog to match it.
@ -116,55 +56,84 @@ class GowinToolchain:
tools.replace_in_file(v_file, "[1:0] IO_psram_rwds,", "[1:0] IO_psram_rwds, /* synthesis syn_tristate = 1 */")
tools.replace_in_file(v_file, "[15:0] IO_psram_dq,", "[15:0] IO_psram_dq, /* synthesis syn_tristate = 1 */")
def build(self, platform, fragment,
build_dir = "build",
build_name = "top",
run = True,
**kwargs):
def build(self, platform, fragment, **kwargs):
return self._build(platform, fragment, **kwargs)
# Create build directory.
cwd = os.getcwd()
os.makedirs(build_dir, exist_ok=True)
os.chdir(build_dir)
# Constraints (.cst ) --------------------------------------------------------------------------
# Finalize design
if not isinstance(fragment, _Fragment):
fragment = fragment.get_fragment()
platform.finalize(fragment)
def build_io_constraints(self):
cst = []
# Generate verilog
v_output = platform.get_verilog(fragment, name=build_name, **kwargs)
named_sc, named_pc = platform.resolve_signals(v_output.ns)
v_file = build_name + ".v"
v_output.write(v_file)
platform.add_source(v_file)
self.apply_hyperram_integration_hack(v_file)
flat_sc = []
for name, pins, other, resource in self.named_sc:
if len(pins) > 1:
for i, p in enumerate(pins):
flat_sc.append((f"{name}[{i}]", p, other))
else:
flat_sc.append((name, pins[0], other))
if platform.verilog_include_paths:
self.options["include_path"] = "{" + ";".join(platform.verilog_include_paths) + "}"
for name, pin, other in flat_sc:
if pin != "X":
cst.append(f"IO_LOC \"{name}\" {pin};")
# Generate constraints file.
# IOs (.cst).
_build_cst(
named_sc = named_sc,
named_pc = named_pc
)
for c in other:
if isinstance(c, IOStandard):
cst.append(f"IO_PORT \"{name}\" IO_TYPE={c.name};")
elif isinstance(c, Misc):
cst.append(f"IO_PORT \"{name}\" {c.misc};")
# Timings (.sdc)
_build_sdc(
clocks = self.clocks,
vns = v_output.ns
)
if self.named_pc:
cst.extend(self.named_pc)
# Generate build script (.tcl)
script = _build_tcl(
name = platform.devicename,
partnumber = platform.device,
files = platform.sources,
options = self.options)
tools.write_to_file("top.cst", "\n".join(cst))
return ("top.cst", "CST")
# Run
if run:
# Timing Constraints (.sdc ) -------------------------------------------------------------------
def build_timing_constraints(self, vns):
sdc = []
for clk, period in sorted(self.clocks.items(), key=lambda x: x[0].duid):
sdc.append(f"create_clock -name {vns.get_name(clk)} -period {str(period)} [get_ports {{{vns.get_name(clk)}}}]")
tools.write_to_file("top.sdc", "\n".join(sdc))
return ("top.sdc", "SDC")
# Project (tcl) --------------------------------------------------------------------------------
def build_project(self):
tcl = []
# Set Device.
tcl.append(f"set_device -name {self.platform.devicename} {self.platform.device}")
# Add IOs Constraints.
tcl.append("add_file top.cst")
# Add Timings Constraints.
tcl.append("add_file top.sdc")
# Add Sources.
for f, typ, lib in self.platform.sources:
# Support windows/powershell
if sys.platform == "win32":
f = f.replace("\\", "\\\\")
tcl.append(f"add_file {f}")
# Set Options.
for opt, val in self.options.items():
tcl.append(f"set_option -{opt} {val}")
# Run.
tcl.append("run all")
# Generate .tcl.
tools.write_to_file("run.tcl", "\n".join(tcl))
# Script ---------------------------------------------------------------------------------------
def build_script(self):
return "" # gw_sh use
def run_script(self, script):
# Support Powershell/WSL platform
# Some python distros for windows (e.g, oss-cad-suite)
# which does not have 'os.uname' support, we should check 'sys.platform' firstly.
@ -185,16 +154,3 @@ class GowinToolchain:
os.path.join("impl", "pnr", "project.fs"),
os.path.join(build_name + ".fs")
)
os.chdir(cwd)
return v_output.ns
def add_period_constraint(self, platform, clk, period):
clk.attr.add("keep")
period = math.floor(period*1e3)/1e3 # round to lowest picosecond
if clk in self.clocks:
if period != self.clocks[clk]:
raise ValueError("Clock already constrained to {:.2f}ns, new constraint to {:.2f}ns"
.format(self.clocks[clk], period))
self.clocks[clk] = period

View File

@ -19,17 +19,78 @@ from migen.fhdl.structure import _Fragment
from litex.gen.fhdl.verilog import DummyAttrTranslate
from litex.build.generic_platform import *
from litex.build.generic_toolchain import GenericToolchain
from litex.build import tools
from litex.build.lattice import common
# Helpers ------------------------------------------------------------------------------------------
def _produces_jedec(device):
# LatticeDiamondToolchain --------------------------------------------------------------------------
class LatticeDiamondToolchain(GenericToolchain):
attr_translate = {
"keep": ("syn_keep", "true"),
"no_retiming": ("syn_no_retiming", "true"),
}
special_overrides = common.lattice_ecp5_special_overrides
def __init__(self):
super().__init__()
def build(self, platform, fragment,
timingstrict = False,
**kwargs):
self._timinstrict = timingstrict
return self._build(platform, fragment, **kwargs)
## Create build directory
#os.makedirs(build_dir, exist_ok=True)
#cwd = os.getcwd()
#os.chdir(build_dir)
## Finalize design
#if not isinstance(fragment, _Fragment):
# fragment = fragment.get_fragment()
#platform.finalize(fragment)
## Generate verilog
#v_output = platform.get_verilog(fragment, name=build_name, **kwargs)
#named_sc, named_pc = platform.resolve_signals(v_output.ns)
#v_file = build_name + ".v"
#v_output.write(v_file)
#platform.add_source(v_file)
## Generate design constraints file (.lpf)
#_build_lpf(named_sc, named_pc, self.clocks, v_output.ns, build_name)
## Generate design script file (.tcl)
#_build_tcl(platform.device, platform.sources, platform.verilog_include_paths, build_name)
## Generate build script
#script = _build_script(build_name, platform.device)
## Run
#if run:
# _run_script(script)
# if timingstrict:
# _check_timing(build_name)
#os.chdir(cwd)
#return v_output.ns
# Helpers --------------------------------------------------------------------------------------
@classmethod
def _produces_jedec(cls, device):
return device.startswith("LCMX")
# Constraints (.lpf) -------------------------------------------------------------------------------
# Constraints (.lpf) ---------------------------------------------------------------------------
def _format_constraint(c):
@classmethod
def _format_constraint(cls, c):
if isinstance(c, Pins):
return ("LOCATE COMP ", " SITE " + "\"" + c.identifiers[0] + "\"")
elif isinstance(c, IOStandard):
@ -37,63 +98,62 @@ def _format_constraint(c):
elif isinstance(c, Misc):
return ("IOBUF PORT ", " " + c.misc)
def _format_lpf(signame, pin, others, resname):
fmt_c = [_format_constraint(c) for c in ([Pins(pin)] + others)]
@classmethod
def _format_lpf(cls, signame, pin, others, resname):
fmt_c = [cls._format_constraint(c) for c in ([Pins(pin)] + others)]
lpf = []
for pre, suf in fmt_c:
lpf.append(pre + "\"" + signame + "\"" + suf + ";")
return "\n".join(lpf)
def _build_lpf(named_sc, named_pc, clocks, vns, build_name):
def build_io_constraints(self):
lpf = []
lpf.append("BLOCK RESETPATHS;")
lpf.append("BLOCK ASYNCPATHS;")
for sig, pins, others, resname in named_sc:
for sig, pins, others, resname in self.named_sc:
if len(pins) > 1:
for i, p in enumerate(pins):
lpf.append(_format_lpf(sig + "[" + str(i) + "]", p, others, resname))
lpf.append(self._format_lpf(sig + "[" + str(i) + "]", p, others, resname))
else:
lpf.append(_format_lpf(sig, pins[0], others, resname))
if named_pc:
lpf.append("\n".join(named_pc))
lpf.append(self._format_lpf(sig, pins[0], others, resname))
if self.named_pc:
lpf.append("\n".join(self.named_pc))
# Note: .lpf is only used post-synthesis, Synplify constraints clocks by default to 200MHz.
for clk, period in clocks.items():
clk_name = vns.get_name(clk)
for clk, period in self.clocks.items():
clk_name = self._vns.get_name(clk)
lpf.append("FREQUENCY {} \"{}\" {} MHz;".format(
"PORT" if clk_name in [name for name, _, _, _ in named_sc] else "NET",
"PORT" if clk_name in [name for name, _, _, _ in self.named_sc] else "NET",
clk_name,
str(1e3/period)))
tools.write_to_file(build_name + ".lpf", "\n".join(lpf))
tools.write_to_file(self._build_name + ".lpf", "\n".join(lpf))
# Project (.tcl) -----------------------------------------------------------------------------------
# Project (.tcl) -------------------------------------------------------------------------------
def _build_tcl(device, sources, vincpaths, build_name):
def build_project(self):
tcl = []
# Create project
tcl.append(" ".join([
"prj_project",
"new -name \"{}\"".format(build_name),
"new -name \"{}\"".format(self._build_name),
"-impl \"impl\"",
"-dev {}".format(device),
"-dev {}".format(self.platform.device),
"-synthesis \"synplify\""
]))
def tcl_path(path): return path.replace("\\", "/")
# Add include paths
vincpath = ";".join(map(lambda x: tcl_path(x), vincpaths))
vincpath = ";".join(map(lambda x: tcl_path(x), self.platform.verilog_include_paths))
tcl.append("prj_impl option {include path} {\"" + vincpath + "\"}")
# Add sources
for filename, language, library, *copy in sources:
for filename, language, library, *copy in self.platform.sources:
tcl.append("prj_src add \"{}\" -work {}".format(tcl_path(filename), library))
# Set top level
tcl.append("prj_impl option top \"{}\"".format(build_name))
tcl.append("prj_impl option top \"{}\"".format(self._build_name))
# Save project
tcl.append("prj_project save")
@ -104,17 +164,17 @@ def _build_tcl(device, sources, vincpaths, build_name):
tcl.append("prj_run Map -impl impl")
tcl.append("prj_run PAR -impl impl")
tcl.append("prj_run Export -impl impl -task Bitgen")
if _produces_jedec(device):
if self._produces_jedec(self.platform.device):
tcl.append("prj_run Export -impl impl -task Jedecgen")
# Close project
tcl.append("prj_project close")
tools.write_to_file(build_name + ".tcl", "\n".join(tcl))
tools.write_to_file(self._build_name + ".tcl", "\n".join(tcl))
# Script -------------------------------------------------------------------------------------------
# Script ---------------------------------------------------------------------------------------
def _build_script(build_name, device):
def build_script(self):
on_windows = sys.platform in ("win32", "cygwin")
if on_windows:
script_ext = ".bat"
@ -129,22 +189,22 @@ def _build_script(build_name, device):
script_contents += "{tool} {tcl_script}{fail_stmt}\n".format(
tool = "pnmainc" if on_windows else "diamondc",
tcl_script = build_name + ".tcl",
tcl_script = self._build_name + ".tcl",
fail_stmt = fail_stmt)
for ext in (".bit", ".jed"):
if ext == ".jed" and not _produces_jedec(device):
if ext == ".jed" and not self._produces_jedec(self.platform.device):
continue
script_contents += "{copy_stmt} {diamond_product} {migen_product} {fail_stmt}\n".format(
copy_stmt = copy_stmt,
fail_stmt = fail_stmt,
diamond_product = os.path.join("impl", build_name + "_impl" + ext),
migen_product = build_name + ext)
diamond_product = os.path.join("impl", self._build_name + "_impl" + ext),
migen_product = self._build_name + ext)
build_script_file = "build_" + build_name + script_ext
build_script_file = "build_" + self._build_name + script_ext
tools.write_to_file(build_script_file, script_contents, force_unix=False)
return build_script_file
def _run_script(script):
def run_script(self, script):
on_windows = sys.platform in ("win32", "cygwin")
if on_windows:
shell = ["cmd", "/c"]
@ -159,8 +219,11 @@ def _run_script(script):
if subprocess.call(shell + [script]) != 0:
raise OSError("Error occured during Diamond's script execution.")
def _check_timing(build_name):
lines = open("impl/{}_impl.par".format(build_name), "r").readlines()
if self.timingstrict:
self._check_timing()
def _check_timing(self):
lines = open("impl/{}_impl.par".format(self._build_name), "r").readlines()
runs = [None, None]
for i in range(len(lines)-1):
if lines[i].startswith("Level/") and lines[i+1].startswith("Cost "):
@ -186,75 +249,3 @@ def _check_timing(build_name):
# XXX is this necessarily the run from which outputs will be used?
return
raise Exception("Failed to meet timing")
# LatticeDiamondToolchain --------------------------------------------------------------------------
class LatticeDiamondToolchain:
attr_translate = {
"keep": ("syn_keep", "true"),
"no_retiming": ("syn_no_retiming", "true"),
}
special_overrides = common.lattice_ecp5_special_overrides
def __init__(self):
self.clocks = {}
self.false_paths = set() # FIXME: use it
def build(self, platform, fragment,
build_dir = "build",
build_name = "top",
run = True,
timingstrict = False,
**kwargs):
# Create build directory
os.makedirs(build_dir, exist_ok=True)
cwd = os.getcwd()
os.chdir(build_dir)
# Finalize design
if not isinstance(fragment, _Fragment):
fragment = fragment.get_fragment()
platform.finalize(fragment)
# Generate verilog
v_output = platform.get_verilog(fragment, name=build_name, **kwargs)
named_sc, named_pc = platform.resolve_signals(v_output.ns)
v_file = build_name + ".v"
v_output.write(v_file)
platform.add_source(v_file)
# Generate design constraints file (.lpf)
_build_lpf(named_sc, named_pc, self.clocks, v_output.ns, build_name)
# Generate design script file (.tcl)
_build_tcl(platform.device, platform.sources, platform.verilog_include_paths, build_name)
# Generate build script
script = _build_script(build_name, platform.device)
# Run
if run:
_run_script(script)
if timingstrict:
_check_timing(build_name)
os.chdir(cwd)
return v_output.ns
def add_period_constraint(self, platform, clk, period):
clk.attr.add("keep")
period = math.floor(period*1e3)/1e3 # round to lowest picosecond
if clk in self.clocks:
if period != self.clocks[clk]:
raise ValueError("Clock already constrained to {:.2f}ns, new constraint to {:.2f}ns"
.format(self.clocks[clk], period))
self.clocks[clk] = period
def add_false_path_constraint(self, platform, from_, to):
from_.attr.add("keep")
to.attr.add("keep")
if (to, from_) not in self.false_paths:
self.false_paths.add((from_, to))

View File

@ -14,29 +14,70 @@ from shutil import which
from migen.fhdl.structure import _Fragment
from litex.build.generic_platform import *
from litex.build.generic_toolchain import GenericToolchain
from litex.build import tools
from litex.build.lattice import common
from litex.build.lattice.radiant import _format_constraint, _format_ldc, _build_pdc
import math
# Yosys/Nextpnr Helpers/Templates ------------------------------------------------------------------
_yosys_template = [
# LatticeOxideToolchain --------------------------------------------------------------------------
class LatticeOxideToolchain(GenericToolchain):
attr_translate = {
"keep": ("keep", "true"),
"syn_useioff": ("syn_useioff", 1),
}
special_overrides = common.lattice_NX_special_overrides_for_oxide
def __init__(self):
super().__init__()
self.yosys_template = self._yosys_template
self.build_template = self._build_template
def build(self, platform, fragment,
nowidelut = False,
abc9 = False,
timingstrict = False,
ignoreloops = False,
seed = 1,
es_device = False,
**kwargs):
self._nowidelut = nowidelut
self._abc9 = abc9
self._timingstrict = timingstrict
self._ignoreloops = ignoreloops
self._seed = seed
self._es_device = es_device
return self._build(platform, fragment, **kwargs)
# Constraints (.ldc) ---------------------------------------------------------------------------
def build_io_constraints(self):
_build_pdc(self.named_sc, self.named_pc, self.clocks, self._vns, self._build_name)
return (self._build_name + ".pdc", "PDC")
# Yosys/Nextpnr Helpers/Templates --------------------------------------------------------------
_yosys_template = [
"verilog_defaults -push",
"verilog_defaults -add -defer",
"{read_files}",
"verilog_defaults -pop",
"attrmap -tocase keep -imap keep=\"true\" keep=1 -imap keep=\"false\" keep=0 -remove keep=0",
"synth_nexus -flatten {nwl} {abc} -json {build_name}.json -top {build_name}",
]
]
def _yosys_import_sources(platform):
def _yosys_import_sources(self):
includes = ""
reads = []
for path in platform.verilog_include_paths:
for path in self.platform.verilog_include_paths:
includes += " -I" + path
for filename, language, library, *copy in platform.sources:
for filename, language, library, *copy in self.platform.sources:
# yosys has no such function read_systemverilog
if language == "systemverilog":
language = "verilog -sv"
@ -44,27 +85,27 @@ def _yosys_import_sources(platform):
language, includes, filename))
return "\n".join(reads)
def _build_yosys(template, platform, nowidelut, abc9, build_name):
def build_project(self):
ys = []
for l in template:
for l in self.yosys_template:
ys.append(l.format(
build_name = build_name,
nwl = "-nowidelut" if nowidelut else "",
abc = "-abc9" if abc9 else "",
read_files = _yosys_import_sources(platform)
build_name = self._build_name,
nwl = "-nowidelut" if self._nowidelut else "",
abc = "-abc9" if self._abc9 else "",
read_files = self._yosys_import_sources()
))
tools.write_to_file(build_name + ".ys", "\n".join(ys))
tools.write_to_file(self._build_name + ".ys", "\n".join(ys))
# Script -------------------------------------------------------------------------------------------
# Script ---------------------------------------------------------------------------------------
_build_template = [
_build_template = [
"yosys -l {build_name}.rpt {build_name}.ys",
"nextpnr-nexus --json {build_name}.json --pdc {build_name}.pdc --fasm {build_name}.fasm \
--device {device} {timefailarg} {ignoreloops} --seed {seed}",
"prjoxide pack {build_name}.fasm {build_name}.bit"
]
]
def _build_script(source, build_template, build_name, device, timingstrict, ignoreloops, seed):
def build_script(self):
if sys.platform in ("win32", "cygwin"):
script_ext = ".bat"
script_contents = "@echo off\nrem Autogenerated by LiteX / git: " + tools.get_litex_git_revision() + "\n\n"
@ -74,22 +115,22 @@ def _build_script(source, build_template, build_name, device, timingstrict, igno
script_contents = "# Autogenerated by LiteX / git: " + tools.get_litex_git_revision() + "\nset -e\n"
fail_stmt = ""
for s in build_template:
for s in self.build_template:
s_fail = s + "{fail_stmt}\n" # Required so Windows scripts fail early.
script_contents += s_fail.format(
build_name = build_name,
device = device,
timefailarg = "--timing-allow-fail" if not timingstrict else "",
ignoreloops = "--ignore-loops" if ignoreloops else "",
build_name = self._build_name,
device = "ES" if self._es_device else self.platform.device,
timefailarg = "--timing-allow-fail" if not self._timingstrict else "",
ignoreloops = "--ignore-loops" if self._ignoreloops else "",
fail_stmt = fail_stmt,
seed = seed,
seed = self._seed,
)
script_file = "build_" + build_name + script_ext
script_file = "build_" + self._build_name + script_ext
tools.write_to_file(script_file, script_contents, force_unix=False)
return script_file
def _run_script(script):
def run_script(self, script):
if sys.platform in ("win32", "cygwin"):
shell = ["cmd", "/c"]
else:
@ -103,90 +144,6 @@ def _run_script(script):
if subprocess.call(shell + [script]) != 0:
raise OSError("Error occured during Yosys/Nextpnr's script execution.")
# LatticeOxideToolchain --------------------------------------------------------------------------
class LatticeOxideToolchain:
attr_translate = {
"keep": ("keep", "true"),
"syn_useioff": ("syn_useioff", 1),
}
special_overrides = common.lattice_NX_special_overrides_for_oxide
def __init__(self):
self.yosys_template = _yosys_template
self.build_template = _build_template
self.clocks = {}
self.false_paths = set() # FIXME: use it
def build(self, platform, fragment,
build_dir = "build",
build_name = "top",
run = True,
nowidelut = False,
abc9 = False,
timingstrict = False,
ignoreloops = False,
seed = 1,
es_device = False,
**kwargs):
# Create build directory
os.makedirs(build_dir, exist_ok=True)
cwd = os.getcwd()
os.chdir(build_dir)
# Finalize design
if not isinstance(fragment, _Fragment):
fragment = fragment.get_fragment()
platform.finalize(fragment)
# Generate verilog
v_output = platform.get_verilog(fragment, name=build_name, **kwargs)
named_sc, named_pc = platform.resolve_signals(v_output.ns)
top_file = build_name + ".v"
v_output.write(top_file)
platform.add_source(top_file)
# Generate design constraints file (.pdc)
_build_pdc(named_sc, named_pc, self.clocks, v_output.ns, build_name)
# Generate Yosys script
_build_yosys(self.yosys_template, platform, nowidelut, abc9, build_name)
# N.B. Radiant does not allow a choice between ES1/production, this is determined
# solely by the installed Radiant version. nextpnr/oxide supports both, so we
# must choose what we are dealing with
device = platform.device
if es_device:
device += "ES"
# Generate build script
script = _build_script(False, self.build_template, build_name, device,
timingstrict, ignoreloops, seed)
# Run
if run:
_run_script(script)
os.chdir(cwd)
return v_output.ns
# N.B. these are currently ignored, but will be supported very soon
def add_period_constraint(self, platform, clk, period):
clk.attr.add("keep")
period = math.floor(period*1e3)/1e3 # round to lowest picosecond
if clk in self.clocks:
if period != self.clocks[clk]:
raise ValueError("Clock already constrained to {:.2f}ns, new constraint to {:.2f}ns"
.format(self.clocks[clk], period))
self.clocks[clk] = period
def add_false_path_constraint(self, platform, from_, to):
from_.attr.add("keep")
to.attr.add("keep")
if (to, from_) not in self.false_paths:
self.false_paths.add((from_, to))
def oxide_args(parser):
toolchain_group = parser.add_argument_group(title="Toolchain options")

View File

@ -22,59 +22,9 @@ from litex.gen.fhdl.verilog import DummyAttrTranslate
from litex.build.generic_platform import *
from litex.build import tools
from litex.build.lattice import common
from litex.build.generic_toolchain import GenericToolchain
# Mixed Radiant+Yosys support
def _run_yosys(device, sources, vincpaths, build_name):
ys_contents = ""
incflags = ""
for path in vincpaths:
incflags += " -I" + path
for filename, language, library, *copy in sources:
assert language != "vhdl"
ys_contents += "read_{}{} {}\n".format(language, incflags, filename)
ys_contents += """\
hierarchy -top {build_name}
# Map keep to keep=1 for yosys
log
log XX. Converting (* keep = "xxxx" *) attribute for Yosys
log
attrmap -tocase keep -imap keep="true" keep=1 -imap keep="false" keep=0 -remove keep=0
select -list a:keep=1
# Add keep=1 for yosys to objects which have dont_touch="true" attribute.
log
log XX. Converting (* dont_touch = "true" *) attribute for Yosys
log
select -list a:dont_touch=true
setattr -set keep 1 a:dont_touch=true
# Convert (* async_reg = "true" *) to async registers for Yosys.
# (* async_reg = "true", dont_touch = "true" *) reg xilinxmultiregimpl0_regs1 = 1'd0;
log
log XX. Converting (* async_reg = "true" *) attribute to async registers for Yosys
log
select -list a:async_reg=true
setattr -set keep 1 a:async_reg=true
synth_nexus -top {build_name} -vm {build_name}_yosys.vm
""".format(build_name=build_name)
ys_name = build_name + ".ys"
tools.write_to_file(ys_name, ys_contents)
if which("yosys") is None:
msg = "Unable to find Yosys toolchain, please:\n"
msg += "- Add Yosys toolchain to your $PATH."
raise OSError(msg)
if subprocess.call(["yosys", ys_name]) != 0:
raise OSError("Subprocess failed")
# Constraints (.ldc) -------------------------------------------------------------------------------
# Required by oxide too (FIXME)
def _format_constraint(c):
if isinstance(c, Pins):
return ("ldc_set_location -site {" + c.identifiers[0] + "} [get_ports ","]")
@ -116,43 +66,127 @@ def _build_pdc(named_sc, named_pc, clocks, vns, build_name):
tools.write_to_file(build_name + ".pdc", "\n".join(pdc))
# Project (.tcl) -----------------------------------------------------------------------------------
def _build_tcl(device, sources, vincpaths, build_name, pdc_file, synth_mode):
# LatticeRadiantToolchain --------------------------------------------------------------------------
class LatticeRadiantToolchain(GenericToolchain):
attr_translate = {
"keep": ("syn_keep", "true"),
"no_retiming": ("syn_no_retiming", "true"),
}
special_overrides = common.lattice_NX_special_overrides
def __init__(self):
super().__init__()
self._timingstrict = True
self._synth_mode = "radiant"
def build(self, platform, fragment,
timingstrict = True,
synth_mode = "radiant",
**kwargs):
self._timingstrict = timingstrict
self._synth_mode = synth_mode
return self._build(platform, fragment, **kwargs)
# Mixed Radiant+Yosys support ------------------------------------------------------------------
def _run_yosys(self):
ys_contents = ""
incflags = ""
for path in self.platform.verilog_include_paths:
incflags += " -I" + path
for filename, language, library, *copy in self.platform.sources:
assert language != "vhdl"
ys_contents += "read_{}{} {}\n".format(language, incflags, filename)
ys_contents += """\
hierarchy -top {build_name}
# Map keep to keep=1 for yosys
log
log XX. Converting (* keep = "xxxx" *) attribute for Yosys
log
attrmap -tocase keep -imap keep="true" keep=1 -imap keep="false" keep=0 -remove keep=0
select -list a:keep=1
# Add keep=1 for yosys to objects which have dont_touch="true" attribute.
log
log XX. Converting (* dont_touch = "true" *) attribute for Yosys
log
select -list a:dont_touch=true
setattr -set keep 1 a:dont_touch=true
# Convert (* async_reg = "true" *) to async registers for Yosys.
# (* async_reg = "true", dont_touch = "true" *) reg xilinxmultiregimpl0_regs1 = 1'd0;
log
log XX. Converting (* async_reg = "true" *) attribute to async registers for Yosys
log
select -list a:async_reg=true
setattr -set keep 1 a:async_reg=true
synth_nexus -top {build_name} -vm {build_name}_yosys.vm
""".format(build_name=self._build_name)
ys_name = self._build_name + ".ys"
tools.write_to_file(ys_name, ys_contents)
if which("yosys") is None:
msg = "Unable to find Yosys toolchain, please:\n"
msg += "- Add Yosys toolchain to your $PATH."
raise OSError(msg)
if subprocess.call(["yosys", ys_name]) != 0:
raise OSError("Subprocess failed")
# Constraints (.ldc) ---------------------------------------------------------------------------
def build_io_constraints(self):
_build_pdc(self.named_sc, self.named_pc, self.clocks, self._vns, self._build_name)
return (self._build_name + ".pdc", "PDC")
# Project (.tcl) -------------------------------------------------------------------------------
def build_project(self):
pdc_file = os.path.join(self._build_dir, self._build_name + ".pdc")
tcl = []
# Create project
syn = "lse" if synth_mode == "lse" else "synplify"
syn = "lse" if self._synth_mode == "lse" else "synplify"
tcl.append(" ".join([
"prj_create",
"-name \"{}\"".format(build_name),
"-name \"{}\"".format(self._build_name),
"-impl \"impl\"",
"-dev {}".format(device),
"-dev {}".format(self.platform.device),
"-synthesis \"" + syn + "\""
]))
def tcl_path(path): return path.replace("\\", "/")
# Add include paths
vincpath = ";".join(map(lambda x: tcl_path(x), vincpaths))
vincpath = ";".join(map(lambda x: tcl_path(x), self.platform.verilog_include_paths))
tcl.append("prj_set_impl_opt {include path} {\"" + vincpath + "\"}")
# Add sources
if synth_mode == "yosys":
if self._synth_mode == "yosys":
# NOTE: it is seemingly impossible to skip synthesis using the Tcl flow
# so we give Synplify the structural netlist from Yosys which it won't actually touch
# The other option is to call the low level Radiant commands starting from 'map'
# with the structural netlist from Yosys, but this would be harder to do in a cross
# platform way.
tcl.append("prj_add_source \"{}_yosys.vm\" -work work".format(build_name))
tcl.append("prj_add_source \"{}_yosys.vm\" -work work".format(self._build_name))
library = "work"
else:
for filename, language, library, *copy in sources:
for filename, language, library, *copy in self.platform.sources:
tcl.append("prj_add_source \"{}\" -work {}".format(tcl_path(filename), library))
tcl.append("prj_add_source \"{}\" -work {}".format(tcl_path(pdc_file), library))
# Set top level
tcl.append("prj_set_impl_opt top \"{}\"".format(build_name))
tcl.append("prj_set_impl_opt top \"{}\"".format(self._build_name))
# Save project
tcl.append("prj_save")
@ -166,11 +200,11 @@ def _build_tcl(device, sources, vincpaths, build_name, pdc_file, synth_mode):
# Close project
tcl.append("prj_close")
tools.write_to_file(build_name + ".tcl", "\n".join(tcl))
tools.write_to_file(self._build_name + ".tcl", "\n".join(tcl))
# Script -------------------------------------------------------------------------------------------
# Script ---------------------------------------------------------------------------------------
def _build_script(build_name, device):
def build_script(self):
if sys.platform in ("win32", "cygwin"):
tool = "pnmainc"
script_ext = ".bat"
@ -186,20 +220,23 @@ def _build_script(build_name, device):
script_contents += "{tool} {tcl_script}{fail_stmt}\n".format(
tool = tool,
tcl_script = build_name + ".tcl",
tcl_script = self._build_name + ".tcl",
fail_stmt = fail_stmt)
script_contents += "{copy_stmt} {radiant_product} {migen_product} {fail_stmt}\n".format(
copy_stmt = copy_stmt,
fail_stmt = fail_stmt,
radiant_product = os.path.join("impl", build_name + "_impl.bit"),
migen_product = build_name + ".bit")
radiant_product = os.path.join("impl", self._build_name + "_impl.bit"),
migen_product = self._build_name + ".bit")
build_script_file = "build_" + build_name + script_ext
build_script_file = "build_" + self._build_name + script_ext
tools.write_to_file(build_script_file, script_contents, force_unix=False)
return build_script_file
def _run_script(script):
def run_script(self, script):
if synth_mode == "yosys":
self._run_yosys()
if sys.platform in ("win32", "cygwin"):
shell = ["cmd", "/c"]
tool = "pnmainc"
@ -214,9 +251,11 @@ def _run_script(script):
if subprocess.call(shell + [script]) != 0:
raise OSError("Error occured during Radiant's script execution.")
if timingstrict:
self._check_timing()
def _check_timing(build_name):
lines = open("impl/{}_impl.par".format(build_name), "r").readlines()
def _check_timing(self):
lines = open("impl/{}_impl.par".format(self._build_name), "r").readlines()
runs = [None, None]
for i in range(len(lines)-1):
if lines[i].startswith("Level/") and lines[i+1].startswith("Cost "):
@ -243,81 +282,6 @@ def _check_timing(build_name):
return
raise Exception("Failed to meet timing")
# LatticeRadiantToolchain --------------------------------------------------------------------------
class LatticeRadiantToolchain:
attr_translate = {
"keep": ("syn_keep", "true"),
"no_retiming": ("syn_no_retiming", "true"),
}
special_overrides = common.lattice_NX_special_overrides
def __init__(self):
self.clocks = {}
self.false_paths = set() # FIXME: use it
def build(self, platform, fragment,
build_dir = "build",
build_name = "top",
run = True,
timingstrict = True,
synth_mode = "radiant",
**kwargs):
# Create build directory
os.makedirs(build_dir, exist_ok=True)
cwd = os.getcwd()
os.chdir(build_dir)
# Finalize design
if not isinstance(fragment, _Fragment):
fragment = fragment.get_fragment()
platform.finalize(fragment)
# Generate verilog
v_output = platform.get_verilog(fragment, name=build_name, **kwargs)
named_sc, named_pc = platform.resolve_signals(v_output.ns)
v_file = build_name + ".v"
v_output.write(v_file)
platform.add_source(v_file)
# Generate design constraints file (.pdc)
_build_pdc(named_sc, named_pc, self.clocks, v_output.ns, build_name)
pdc_file = build_dir + "\\" + build_name + ".pdc"
# Generate design script file (.tcl)
_build_tcl(platform.device, platform.sources, platform.verilog_include_paths, build_name, pdc_file, synth_mode)
# Generate build script
script = _build_script(build_name, platform.device)
# Run
if run:
if synth_mode == "yosys":
_run_yosys(platform.device, platform.sources, platform.verilog_include_paths, build_name)
_run_script(script)
if timingstrict:
_check_timing(build_name)
os.chdir(cwd)
return v_output.ns
def add_period_constraint(self, platform, clk, period):
clk.attr.add("keep")
period = math.floor(period*1e3)/1e3 # round to lowest picosecond
if clk in self.clocks:
if period != self.clocks[clk]:
raise ValueError("Clock already constrained to {:.2f}ns, new constraint to {:.2f}ns"
.format(self.clocks[clk], period))
self.clocks[clk] = period
def add_false_path_constraint(self, platform, from_, to):
from_.attr.add("keep")
to.attr.add("keep")
if (to, from_) not in self.false_paths:
self.false_paths.add((from_, to))
def radiant_build_args(parser):
toolchain_group = parser.add_argument_group(title="Toolchain options")

View File

@ -13,17 +13,37 @@ from migen.fhdl.structure import _Fragment
from litex.build.generic_platform import *
from litex.build import tools
from litex.build.generic_toolchain import GenericToolchain
from litex.build.microsemi import common
# Helpers ------------------------------------------------------------------------------------------
# MicrosemiLiberoSoCPolarfireToolchain -------------------------------------------------------------
def tcl_name(name):
class MicrosemiLiberoSoCPolarfireToolchain(GenericToolchain):
attr_translate = {}
special_overrides = common.microsemi_polarfire_special_overrides
def __init__(self):
super().__init__()
self.additional_io_constraints = []
self.additional_fp_constraints = []
self.additional_timing_constraints = []
def build(self, platform, fragment, **kwargs):
return self._build(platform, fragment, **kwargs)
# Helpers --------------------------------------------------------------------------------------
@classmethod
def tcl_name(cls, name):
return "{" + name + "}"
# IO Constraints (.pdc) ----------------------------------------------------------------------------
# IO Constraints (.pdc) ------------------------------------------------------------------------
def _format_io_constraint(c):
@classmethod
def _format_io_constraint(cls, c):
if isinstance(c, Pins):
return "-pin_name {} ".format(c.identifiers[0])
elif isinstance(c, IOStandard):
@ -33,8 +53,8 @@ def _format_io_constraint(c):
else:
raise NotImplementedError
def _format_io_pdc(signame, pin, others):
@classmethod
def _format_io_pdc(cls, signame, pin, others):
fmt_c = [_format_io_constraint(c) for c in ([Pins(pin)] + others)]
r = "set_io "
r += "-port_name {} ".format(tcl_name(signame))
@ -44,33 +64,35 @@ def _format_io_pdc(signame, pin, others):
r += "\n"
return r
def _build_io_pdc(named_sc, named_pc, build_name, additional_io_constraints):
def build_io_constraints(self):
pdc = ""
for sig, pins, others, resname in named_sc:
for sig, pins, others, resname in self.named_sc:
if len(pins) > 1:
for i, p in enumerate(pins):
pdc += _format_io_pdc(sig + "[" + str(i) + "]", p, others)
pdc += self._format_io_pdc(sig + "[" + str(i) + "]", p, others)
else:
pdc += _format_io_pdc(sig, pins[0], others)
pdc += "\n".join(additional_io_constraints)
tools.write_to_file(build_name + "_io.pdc", pdc)
pdc += self._format_io_pdc(sig, pins[0], others)
pdc += "\n".join(self.additional_io_constraints)
tools.write_to_file(self._build_name + "_io.pdc", pdc)
return (self._build_name + "_io.pdc", "PDC")
# Placement Constraints (.pdc) ---------------------------------------------------------------------
# Placement Constraints (.pdc) -----------------------------------------------------------------
def _build_fp_pdc(build_name, additional_fp_constraints):
pdc = "\n".join(additional_fp_constraints)
tools.write_to_file(build_name + "_fp.pdc", pdc)
def build_placement_constraints(self):
pdc = "\n".join(self.additional_fp_constraints)
tools.write_to_file(self._build_name + "_fp.pdc", pdc)
return (self._build_name + "_fp.pdc", "PDC")
# Project (.tcl) -----------------------------------------------------------------------------------
# Project (.tcl) -------------------------------------------------------------------------------
def _build_tcl(platform, sources, build_dir, build_name):
def build_project(self):
tcl = []
# Create project
tcl.append(" ".join([
"new_project",
"-location {./impl}",
"-name {}".format(tcl_name(build_name)),
"-name {}".format(tcl_name(self._build_name)),
"-project_description {}",
"-block_mode 0",
"-standalone_peripheral_initialization 0",
@ -87,7 +109,7 @@ def _build_tcl(platform, sources, build_dir, build_name):
"-adv_options {}"
]))
die, package, speed = platform.device.split("-")
die, package, speed = self.platform.device.split("-")
tcl.append(" ".join([
"set_device",
"-family {PolarFire}",
@ -111,46 +133,46 @@ def _build_tcl(platform, sources, build_dir, build_name):
]))
# Add sources
for filename, language, library, *copy in sources:
for filename, language, library, *copy in self.platform.sources:
filename_tcl = "{" + filename + "}"
tcl.append("import_files -hdl_source " + filename_tcl)
# Set top level
tcl.append("set_root -module {}".format(tcl_name(build_name)))
tcl.append("set_root -module {}".format(tcl_name(self._build_name)))
# Copy init files FIXME: support for include path on LiberoSoC?
for file in os.listdir(build_dir):
for file in os.listdir(self._build_dir):
if file.endswith(".init"):
tcl.append("file copy -- {} impl/synthesis".format(file))
# Import io constraints
tcl.append("import_files -io_pdc {}".format(tcl_name(build_name + "_io.pdc")))
tcl.append("import_files -io_pdc {}".format(tcl_name(self._build_name + "_io.pdc")))
# Import floorplanner constraints
tcl.append("import_files -fp_pdc {}".format(tcl_name(build_name + "_fp.pdc")))
tcl.append("import_files -fp_pdc {}".format(tcl_name(self._build_name + "_fp.pdc")))
# Import timing constraints
tcl.append("import_files -convert_EDN_to_HDL 0 -sdc {}".format(tcl_name(build_name + ".sdc")))
tcl.append("import_files -convert_EDN_to_HDL 0 -sdc {}".format(tcl_name(self._build_name + ".sdc")))
# Associate constraints with tools
tcl.append(" ".join(["organize_tool_files",
"-tool {SYNTHESIZE}",
"-file impl/constraint/{}.sdc".format(build_name),
"-module {}".format(build_name),
"-file impl/constraint/{}.sdc".format(self._build_name),
"-module {}".format(self._build_name),
"-input_type {constraint}"
]))
tcl.append(" ".join(["organize_tool_files",
"-tool {PLACEROUTE}",
"-file impl/constraint/io/{}_io.pdc".format(build_name),
"-file impl/constraint/fp/{}_fp.pdc".format(build_name),
"-file impl/constraint/{}.sdc".format(build_name),
"-module {}".format(build_name),
"-file impl/constraint/io/{}_io.pdc".format(self._build_name),
"-file impl/constraint/fp/{}_fp.pdc".format(self._build_name),
"-file impl/constraint/{}.sdc".format(self._build_name),
"-module {}".format(self._build_name),
"-input_type {constraint}"
]))
tcl.append(" ".join(["organize_tool_files",
"-tool {VERIFYTIMING}",
"-file impl/constraint/{}.sdc".format(build_name),
"-module {}".format(build_name),
"-file impl/constraint/{}.sdc".format(self._build_name),
"-module {}".format(self._build_name),
"-input_type {constraint}"
]))
@ -162,18 +184,19 @@ def _build_tcl(platform, sources, build_dir, build_name):
tcl.append("run_tool -name {GENERATEPROGRAMMINGFILE}")
# Generate tcl
tools.write_to_file(build_name + ".tcl", "\n".join(tcl))
tools.write_to_file(self._build_name + ".tcl", "\n".join(tcl))
return self._build_name + ".tcl"
# Timing Constraints (.sdc) ------------------------------------------------------------------------
# Timing Constraints (.sdc) --------------------------------------------------------------------
def _build_timing_sdc(vns, clocks, false_paths, build_name, additional_timing_constraints):
def build_timing_constraints(self, vns):
sdc = []
for clk, period in sorted(clocks.items(), key=lambda x: x[0].duid):
for clk, period in sorted(self.clocks.items(), key=lambda x: x[0].duid):
sdc.append(
"create_clock -name {clk} -period " + str(period) +
" [get_nets {clk}]".format(clk=vns.get_name(clk)))
for from_, to in sorted(false_paths,
for from_, to in sorted(self.false_paths,
key=lambda x: (x[0].duid, x[1].duid)):
sdc.append(
"set_clock_groups "
@ -182,12 +205,13 @@ def _build_timing_sdc(vns, clocks, false_paths, build_name, additional_timing_co
"-asynchronous".format(from_=from_, to=to))
# generate sdc
sdc += additional_timing_constraints
tools.write_to_file(build_name + ".sdc", "\n".join(sdc))
sdc += self.additional_timing_constraints
tools.write_to_file(self._build_name + ".sdc", "\n".join(sdc))
return (self._build_name + ".sdc", "SDC")
# Script -------------------------------------------------------------------------------------------
# Script ---------------------------------------------------------------------------------------
def _build_script(build_name, device):
def build_script(self):
if sys.platform in ("win32", "cygwin"):
script_ext = ".bat"
script_contents = "@echo off\nREM Autogenerated by LiteX / git: " + tools.get_litex_git_revision() + "\n\n"
@ -199,12 +223,16 @@ def _build_script(build_name, device):
copy_stmt = "cp"
fail_stmt = " || exit 1"
script_file = "build_" + build_name + script_ext
script_file = "build_" + self._build_name + script_ext
tools.write_to_file(script_file, script_contents,
force_unix=False)
return script_file
def _run_script(script):
def run_script(self.script):
# Delete previous impl
if os.path.exists("impl"):
shutil.rmtree("impl")
if sys.platform in ["win32", "cygwin"]:
shell = ["cmd", "/c"]
else:
@ -213,70 +241,6 @@ def _run_script(script):
if subprocess.call(shell + [script]) != 0:
raise OSError("Subprocess failed")
# MicrosemiLiberoSoCPolarfireToolchain -------------------------------------------------------------
class MicrosemiLiberoSoCPolarfireToolchain:
attr_translate = {}
special_overrides = common.microsemi_polarfire_special_overrides
def __init__(self):
self.clocks = dict()
self.false_paths = set()
self.additional_io_constraints = []
self.additional_fp_constraints = []
self.additional_timing_constraints = []
def build(self, platform, fragment,
build_dir = "build",
build_name = "top",
run = False,
**kwargs):
# Create build directory
os.makedirs(build_dir, exist_ok=True)
cwd = os.getcwd()
os.chdir(build_dir)
# Finalize design
if not isinstance(fragment, _Fragment):
fragment = fragment.get_fragment()
platform.finalize(fragment)
# Generate verilog
v_output = platform.get_verilog(fragment, name=build_name, **kwargs)
named_sc, named_pc = platform.resolve_signals(v_output.ns)
top_file = build_name + ".v"
v_output.write(top_file)
platform.add_source(top_file)
# Generate design script file (.tcl)
_build_tcl(platform, platform.sources, build_dir, build_name)
# Generate design io constraints file (.pdc)
_build_io_pdc(named_sc, named_pc, build_name, self.additional_io_constraints)
# Generate design placement constraints file (.pdc)
_build_fp_pdc(build_name, self.additional_fp_constraints)
# Generate design timing constraints file (.sdc)
_build_timing_sdc(v_output.ns, self.clocks, self.false_paths, build_name,
self.additional_timing_constraints)
# Generate build script
script = _build_script(build_name, platform.device)
# Run
if run:
# Delete previous impl
if os.path.exists("impl"):
shutil.rmtree("impl")
_run_script(script)
os.chdir(cwd)
return v_output.ns
def add_period_constraint(self, platform, clk, period):
if clk in self.clocks:
if period != self.clocks[clk]:

View File

@ -13,59 +13,79 @@ from shutil import which
from migen.fhdl.structure import _Fragment
from litex.build.generic_platform import *
from litex.build.generic_toolchain import GenericToolchain
from litex.build import tools
from litex.build.quicklogic import common
# IO Constraints (.pcf) ----------------------------------------------------------------------------
def _format_io_pcf(signame, pin, others):
# F4PGAToolchain -------------------------------------------------------------------------------
# Formerly SymbiflowToolchain, Symbiflow has been renamed to F4PGA -----------------------------
class F4PGAToolchain(GenericToolchain):
attr_translate = {}
special_overrides = common.quicklogic_special_overrides
def __init__(self):
super().__init__()
def build(self, platform, fragment, **kwargs):
return self._build(platform, fragment, **kwargs)
# IO Constraints (.pcf) ------------------------------------------------------------------------
@classmethod
def _format_io_pcf(cls, signame, pin, others):
r = f"set_io {signame} {Pins(pin).identifiers[0]}\n"
return r
def _build_io_pcf(named_sc, named_pc, build_name):
def build_io_constraints(self):
pcf = ""
for sig, pins, others, resname in named_sc:
for sig, pins, others, resname in self.named_sc:
if len(pins) > 1:
for i, p in enumerate(pins):
pcf += _format_io_pcf(sig + "(" + str(i) + ")", p, others)
pcf += self._format_io_pcf(sig + "(" + str(i) + ")", p, others)
else:
pcf += _format_io_pcf(sig, pins[0], others)
tools.write_to_file(build_name + ".pcf", pcf)
pcf += self._format_io_pcf(sig, pins[0], others)
tools.write_to_file(self._build_name + ".pcf", pcf)
return (self._build_name + ".pcf", "PCF")
# Build Makefile -----------------------------------------------------------------------------------
# Build Makefile -------------------------------------------------------------------------------
def _build_makefile(platform, sources, build_dir, build_name):
def build_script(self):
makefile = []
# Define Paths.
makefile.append("mkfile_path := $(abspath $(lastword $(MAKEFILE_LIST)))")
makefile.append("current_dir := $(patsubst %/,%,$(dir $(mkfile_path)))")
# bit -> h and bit -> bin requires TOP_F
makefile.append(f"TOP_F={build_name}")
makefile.append(f"TOP_F={self._build_name}")
# Create Project.
# FIXME: Only use top file for now and ignore .init files.
makefile.append("all: {top}_bit.h {top}.bin build/{top}.bit".format(top=build_name))
makefile.append("all: {top}_bit.h {top}.bin build/{top}.bit".format(top=self._build_name))
# build bit file (default)
makefile.append(f"build/{build_name}.bit:")
makefile.append(f"build/{self._build_name}.bit:")
makefile.append("\tql_symbiflow -compile -d {device} -P {part} -v {verilog} -t {top} -p {pcf}".format(
device = platform.device,
part = {"ql-eos-s3": "PU64"}.get(platform.device),
verilog = f"{build_name}.v",
top = build_name,
pcf = f"{build_name}.pcf"
device = self.platform.device,
part = {"ql-eos-s3": "PU64"}.get(self.platform.device),
verilog = f"{self._build_name}.v",
top = self._build_name,
pcf = f"{self._build_name}.pcf"
))
# build header to include in CPU firmware
makefile.append("{top}_bit.h: build/{top}.bit".format(top=build_name))
makefile.append("{top}_bit.h: build/{top}.bit".format(top=self._build_name))
makefile.append(f"\t(cd build; TOP_F=$(TOP_F) symbiflow_write_bitheader)")
# build binary to write in dedicated FLASH area
makefile.append("{top}.bin: build/{top}.bit".format(top=build_name))
makefile.append("{top}.bin: build/{top}.bit".format(top=self._build_name))
makefile.append(f"\t(cd build; TOP_F=$(TOP_F) symbiflow_write_binary)")
# Generate Makefile.
tools.write_to_file("Makefile", "\n".join(makefile))
def _run_make():
return "Makefile"
def run_script(self, script):
make_cmd = ["make", "-j1"]
if which("ql_symbiflow") is None:
@ -75,53 +95,3 @@ def _run_make():
if subprocess.call(make_cmd) != 0:
raise OSError("Error occured during QuickLogic Symbiflow's script execution.")
# F4PGAToolchain -------------------------------------------------------------------------------
# Formerly SymbiflowToolchain, Symbiflow has been renamed to F4PGA -----------------------------
class F4PGAToolchain:
attr_translate = {}
special_overrides = common.quicklogic_special_overrides
def __init__(self):
self.clocks = dict()
self.false_paths = set()
def build(self, platform, fragment,
build_dir = "build",
build_name = "top",
run = False,
**kwargs):
# Create build directory.
os.makedirs(build_dir, exist_ok=True)
cwd = os.getcwd()
os.chdir(build_dir)
# Finalize design.
if not isinstance(fragment, _Fragment):
fragment = fragment.get_fragment()
platform.finalize(fragment)
# Generate verilog.
v_output = platform.get_verilog(fragment, name=build_name, **kwargs)
named_sc, named_pc = platform.resolve_signals(v_output.ns)
top_file = build_name + ".v"
v_output.write(top_file)
platform.add_source(top_file)
# Generate .pcf IO constraints file.
_build_io_pcf(named_sc, named_pc, build_name)
# Generate Makefie.
_build_makefile(platform, platform.sources, build_dir, build_name)
# Run.
if run:
_run_make()
os.chdir(cwd)
return v_output.ns