From a69197d2dbaedebc7caedd0e525ecd4af2b00a47 Mon Sep 17 00:00:00 2001 From: Florent Kermarrec Date: Sun, 28 Oct 2018 17:51:16 +0100 Subject: [PATCH] build/lattice: add initial prjtrellis support --- litex/build/lattice/platform.py | 4 +- litex/build/lattice/prjtrellis.py | 105 ++++++++++++++++++++++++++++++ 2 files changed, 108 insertions(+), 1 deletion(-) create mode 100644 litex/build/lattice/prjtrellis.py diff --git a/litex/build/lattice/platform.py b/litex/build/lattice/platform.py index 7fbf9c57e..385fba5bb 100644 --- a/litex/build/lattice/platform.py +++ b/litex/build/lattice/platform.py @@ -1,5 +1,5 @@ from litex.build.generic_platform import GenericPlatform -from litex.build.lattice import common, diamond, icestorm +from litex.build.lattice import common, diamond, icestorm, prjtrellis class LatticePlatform(GenericPlatform): @@ -9,6 +9,8 @@ class LatticePlatform(GenericPlatform): GenericPlatform.__init__(self, *args, **kwargs) if toolchain == "diamond": self.toolchain = diamond.LatticeDiamondToolchain() + elif toolchain == "prjtrellis": + self.toolchain = prjtrellis.LatticePrjTrellisToolchain() elif toolchain == "icestorm": self.bitstream_ext = ".bin" self.toolchain = icestorm.LatticeIceStormToolchain() diff --git a/litex/build/lattice/prjtrellis.py b/litex/build/lattice/prjtrellis.py new file mode 100644 index 000000000..3801e9a1a --- /dev/null +++ b/litex/build/lattice/prjtrellis.py @@ -0,0 +1,105 @@ +# This file is Copyright (c) 2018 Florent Kermarrec +# License: BSD + +import os +import sys +import subprocess + +from migen.fhdl.structure import _Fragment + +from litex.build.generic_platform import * +from litex.build import tools +from litex.build.lattice import common + + +def _build_script(source, build_template, build_name, device, basecfg): + build_script_contents = "# Autogenerated by LiteX\nset -e\n" + for s in build_template: + build_script_contents += s.format(build_name=build_name, device=device, basecfg=basecfg) + '\n' + build_script_file = "build_" + build_name + ".sh" + tools.write_to_file(build_script_file, build_script_contents) + return build_script_file + + +def _run_script(script): + r = subprocess.call(["bash", script]) + if r != 0: + raise OSError("Subprocess failed") + + +class LatticePrjTrellisToolchain: + attr_translate = { + # FIXME: document + "keep": ("keep", "true"), + "no_retiming": None, + "async_reg": None, + "mr_ff": None, + "mr_false_path": None, + "ars_ff1": None, + "ars_ff2": None, + "ars_false_path": None, + "no_shreg_extract": None + } + + special_overrides = common.lattice_ecpx_special_overrides + + def __init__(self): + self.nextpnr_yosys_template = [ + "{read_files}", + "attrmap -tocase keep -imap keep=\"true\" keep=1 -imap keep=\"false\" keep=0 -remove keep=0", + "synth_ecp5 -nomux -json {build_name}.json -top {build_name}", + ] + + self.nextpnr_build_template = [ + "yosys -q -l {build_name}.rpt {build_name}.ys", + "nextpnr-ecp5 --json {build_name}.json --textcfg {build_name}.config --basecfg {basecfg} --{device}", + "ecppack {build_name}.config {build_name}.bit" + ] + + def build(self, platform, fragment, build_dir="build", build_name="top", + toolchain_path=None, run=True): + os.makedirs(build_dir, exist_ok=True) + cwd = os.getcwd() + os.chdir(build_dir) + + if not isinstance(fragment, _Fragment): + fragment = fragment.get_fragment() + platform.finalize(fragment) + + v_output = platform.get_verilog(fragment) + named_sc, named_pc = platform.resolve_signals(v_output.ns) + v_file = build_name + ".v" + v_output.write(v_file) + + yosys_template = self.nextpnr_yosys_template + ys_contents = "\n".join(_.format(build_name=build_name, + read_files=self.gen_read_files(platform, v_file)) + for _ in yosys_template) + + ys_name = build_name + ".ys" + tools.write_to_file(ys_name, ys_contents) + + build_template = self.nextpnr_build_template + script = _build_script(False, build_template, build_name, + "um5g-45k", # FIXME + "../../../../../../../symbiflow/prjtrellis/misc/basecfgs/empty_lfe5um5g-45f.config") # FIXME + _run_script(script) + + os.chdir(cwd) + + return v_output.ns + + def gen_read_files(self, platform, main): + sources = platform.sources + [(main, "verilog", "work")] + incflags = "" + read_files = list() + for path in platform.verilog_include_paths: + incflags += " -I" + path + for filename, language, library in sources: + read_files.append("read_{}{} {}".format(language, + incflags, + filename)) + return "\n".join(read_files) + + def add_period_constraint(self, platform, clk, period): + print("TODO: add_period_constraint")