build/lattice/prjtrellis: simplify code, remove some workarounds
authorFlorent Kermarrec <florent@enjoy-digital.fr>
Mon, 29 Oct 2018 07:49:32 +0000 (08:49 +0100)
committerFlorent Kermarrec <florent@enjoy-digital.fr>
Mon, 29 Oct 2018 08:40:35 +0000 (09:40 +0100)
litex/boards/targets/versaecp55g_prjtrellis.py
litex/build/lattice/prjtrellis.py

index 40cd52ab79b402bc566df132b83e13d51fb8c1f4..fd972ed19ee3e516756a3342697564d23d6abcec 100755 (executable)
@@ -16,7 +16,7 @@ class BaseSoC(Module):
         led0_pads = platform.request("user_led", 0)
         led1_pads = platform.request("user_led", 1)
 
-        # no constraint file for now with prjtrellis
+        # FIXME: no constraint file for now with prjtrellis
         platform.lookup_request("clk100").attr.add(("LOC", "P3"))
         platform.lookup_request("clk100").attr.add(("IO_TYPE", "LVDS"))
         platform.lookup_request("user_dip_btn").attr.add(("LOC", "H2"))
@@ -26,7 +26,7 @@ class BaseSoC(Module):
         platform.lookup_request("user_led", 1).attr.add(("LOC", "D17"))
         platform.lookup_request("user_led", 1).attr.add(("IO_TYPE", "LVCMOS25"))
 
-        # add TRELLIS_IO instance on all inputs/outputs
+        # FIXME: add TRELLIS_IO instance on all inputs/outputs
         sys_clk_pads_i = Signal()
         btn_pads_i = Signal()
         led0_pads_i = Signal()
@@ -53,7 +53,7 @@ class BaseSoC(Module):
 def main():
     platform = versaecp55g.Platform(toolchain="prjtrellis")
     soc = BaseSoC(platform)
-    platform.build(soc)
+    platform.build(soc, toolchain_path="/home/florent/dev/symbiflow/prjtrellis") # FIXME
 
 
 if __name__ == "__main__":
index 3801e9a1ac8147137d563f2f86ab003547bfea59..642c9be6b7cf7fddbbe1a431a6aacadafb47d79d 100644 (file)
@@ -2,7 +2,6 @@
 # License: BSD
 
 import os
-import sys
 import subprocess
 
 from migen.fhdl.structure import _Fragment
@@ -12,19 +11,17 @@ 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")
+nextpnr_ecp5_architectures = {
+    "lfe5u-25f": "25k",
+    "lfe5u-45f": "45k",
+    "lfe5u-85f": "85k",
+    "lfe5um-25f": "um-25k",
+    "lfe5um-45f": "um-45k",
+    "lfe5um-85f": "um-85k",
+    "lfe5um5g-25f": "um5g-25k",
+    "lfe5um5g-45f": "um5g-45k",
+    "lfe5um5g-85f": "um5g-85k",
+}
 
 
 class LatticePrjTrellisToolchain:
@@ -43,25 +40,13 @@ class LatticePrjTrellisToolchain:
 
     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)
 
+        # generate verilog
         if not isinstance(fragment, _Fragment):
             fragment = fragment.get_fragment()
         platform.finalize(fragment)
@@ -70,36 +55,57 @@ class LatticePrjTrellisToolchain:
         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 yosys script
+        def yosys_import_sources(platform):
+            includes = ""
+            reads = []
+            for path in platform.verilog_include_paths:
+                includes += " -I" + path
+            for filename, language, library in platform.sources:
+                reads.append("read_{}{} {}".format(
+                    language, includes, filename))
+            return "\n".join(reads)
+
+        yosys_script_file = build_name + ".ys"
+        yosys_script_contents = [
+            yosys_import_sources(platform),
+            "synth_ecp5 -nomux -json {build_name}.json -top {build_name}"
+        ]
+        yosys_script_contents = "\n".join(yosys_script_contents)
+        yosys_script_contents = yosys_script_contents.format(build_name=build_name)
+        tools.write_to_file(yosys_script_file, yosys_script_contents)
+
+        # transform platform.device to nextpnr's architecture / basecfg
+        (family, size, package) = platform.device.split("-")
+        architecture = nextpnr_ecp5_architectures[(family + "-" + size).lower()]
+        basecfg = "empty_" + (family + "-" + size).lower() + ".config"
+        basecfg = os.path.join(toolchain_path, "misc", "basecfgs", basecfg)
+
+        # generate build script
+        build_script_file = "build_" + build_name + ".sh"
+        build_script_contents = [
+            "yosys -q -l {build_name}.rpt {build_name}.ys",
+            "nextpnr-ecp5 --json {build_name}.json --textcfg {build_name}.config --basecfg {basecfg} --{architecture}",
+            "ecppack {build_name}.config {build_name}.bit"
 
-        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)
+        ]
+        build_script_contents = "\n".join(build_script_contents)
+        build_script_contents = build_script_contents.format(
+            build_name=build_name,
+            architecture=architecture,
+            basecfg=basecfg)
+        tools.write_to_file(build_script_file, build_script_contents)
+
+        # run scripts
+        if run:
+            if subprocess.call(["bash", build_script_file]) != 0:
+                raise OSError("Subprocess failed")
 
         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")