build/lattice: add initial prjtrellis support
authorFlorent Kermarrec <florent@enjoy-digital.fr>
Sun, 28 Oct 2018 16:51:16 +0000 (17:51 +0100)
committerFlorent Kermarrec <florent@enjoy-digital.fr>
Sun, 28 Oct 2018 16:51:16 +0000 (17:51 +0100)
litex/build/lattice/platform.py
litex/build/lattice/prjtrellis.py [new file with mode: 0644]

index 7fbf9c57e92be3595d10059ba6485487b5e30e6a..385fba5bbe9d9ca8526bb523e160a9941d43c985 100644 (file)
@@ -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 (file)
index 0000000..3801e9a
--- /dev/null
@@ -0,0 +1,105 @@
+# This file is Copyright (c) 2018 Florent Kermarrec <florent@enjoy-digital.fr>
+# 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")