trellis: Switch to using LPF for constraints
authorDavid Shah <dave@ds0.me>
Wed, 31 Oct 2018 11:43:39 +0000 (11:43 +0000)
committerDavid Shah <dave@ds0.me>
Wed, 31 Oct 2018 11:43:39 +0000 (11:43 +0000)
Signed-off-by: David Shah <dave@ds0.me>
litex/build/lattice/prjtrellis.py

index d019e14795ccd6369a22fdf08c2d17126c1d9e42..e27ae1c6a1c3cce4232a05719415ac43e2a50a72 100644 (file)
@@ -13,8 +13,6 @@ from litex.build.lattice import common
 # TODO:
 # - add timing constraint support.
 # - check/document attr_translate.
-# - use constraint file when prjtrellis will support it.
-
 
 nextpnr_ecp5_architectures = {
     "lfe5u-25f": "25k",
@@ -29,6 +27,37 @@ nextpnr_ecp5_architectures = {
 }
 
 
+def _format_constraint(c):
+    if isinstance(c, Pins):
+        return ("LOCATE COMP ", " SITE " + "\"" + c.identifiers[0] + "\"")
+    elif isinstance(c, IOStandard):
+        return ("IOBUF PORT ", " IO_TYPE=" + c.name)
+    elif isinstance(c, Misc):
+        return c.misc
+
+
+def _format_lpf(signame, pin, others, resname):
+    fmt_c = [_format_constraint(c) for c in ([Pins(pin)] + others)]
+    r = ""
+    for pre, suf in fmt_c:
+        r += pre + "\"" + signame + "\"" + suf + ";\n"
+    return r
+
+
+def _build_lpf(named_sc, named_pc):
+    r = "BLOCK RESETPATHS;\n"
+    r += "BLOCK ASYNCPATHS;\n"
+    for sig, pins, others, resname in named_sc:
+        if len(pins) > 1:
+            for i, p in enumerate(pins):
+                r += _format_lpf(sig + "[" + str(i) + "]", p, others, resname)
+        else:
+            r += _format_lpf(sig, pins[0], others, resname)
+    if named_pc:
+        r += "\n" + "\n\n".join(named_pc)
+    return r
+
+
 def yosys_import_sources(platform):
     includes = ""
     reads = []
@@ -40,93 +69,6 @@ def yosys_import_sources(platform):
     return "\n".join(reads)
 
 
-def generate_prjtrellis_top(top_file, platform, vns):
-    # resolve ios directions / types
-    ios, _ = platform.resolve_signals(vns)
-    ios_direction = {}
-    ios_type = {}
-    cm = platform.constraint_manager
-    for io_name, io_pins, _, _ in ios:
-        for cm_sig, cm_pins, _, _ in cm.get_sig_constraints():
-            if io_pins == cm_pins:
-                ios_direction[io_name] = cm_sig.direction
-                ios_type[io_name] = cm_sig.type
-        last_io_name = io_name
-
-    # prjtrellis module / ios declaration
-    top_contents = []
-    top_contents.append("module prjtrellis_{build_name}(")
-    ios_declaration = ""
-    for io_name, io_pins, io_others, _ in ios:
-        for io_other in io_others:
-            if isinstance(io_other, IOStandard):
-                io_standard = io_other.name
-        for i, io_pin in enumerate(io_pins):
-            ios_declaration += "(* LOC=\"{}\" *) (* IO_TYPE=\"{}\" *)\n".format(io_pin, io_standard)
-            ios_declaration += "\t" + ios_direction[io_name] + " " + io_name + "_io" + (str(i) if len(io_pins) > 1 else "")
-            ios_declaration += ",\n" if io_name != last_io_name or (i != len(io_pins) - 1) else ""
-    top_contents.append(ios_declaration)
-    top_contents.append(");\n")
-
-    # top signals declaration
-    signals_declaration = ""
-    for io_name, io_pins, _, _ in ios:
-        signals_declaration += ios_type[io_name] + " "
-        if len(io_pins) > 1:
-            signals_declaration += "[{}:0] ".format(len(io_pins) - 1)
-        signals_declaration += io_name
-        signals_declaration += ";\n"
-    top_contents.append(signals_declaration)
-
-    # trellis_ios declaration
-    trellis_io_declaration = ""
-    for io_name, io_pins, io_others, _ in ios:
-        for i, io_pin in enumerate(io_pins):
-            io_suffix = "io" + str(i) if len(io_pins) > 1 else "io"
-            if ios_direction[io_name] == "input":
-                trellis_io_declaration += \
-                "TRELLIS_IO #(.DIR(\"INPUT\")) {} (.B({}), .O({}));\n".format(
-                    io_name + "_buf" + str(i), io_name + "_" + io_suffix, io_name + "[" + str(i) + "]")
-            elif ios_direction[io_name] == "output":
-                trellis_io_declaration += \
-                "TRELLIS_IO #(.DIR(\"OUTPUT\")) {} (.B({}), .I({}));\n".format(
-                    io_name + "_buf" + str(i), io_name + "_" + io_suffix, io_name + "[" + str(i) + "]")
-            else:
-                pass # handled by Migen's Tristate
-    top_contents.append(trellis_io_declaration)
-
-    # top_recopy:
-    # - skip module definition.
-    # - use ios names for inouts.
-    def replace_inouts(l):
-        r = l
-        for io_name, io_pins, _, _ in ios:
-            if ios_direction[io_name] == "inout":
-                if len(io_pins) > 1:
-                    for i in range(len(io_pins)):
-                        r = r.replace(io_name + "[" + str(i) + "]", io_name + "_io" + str(i))
-                else:
-                    r = r.replace(io_name, io_name + "_io")
-        return r
-
-    skip = True
-    f = open(top_file, "r")
-    for l in f:
-        if not skip:
-            l = l.replace("\n", "")
-            l = l.replace("{", "{{")
-            l = l.replace("}", "}}")
-            l = replace_inouts(l)
-            top_contents.append(l)
-        if ");" in l:
-            skip = False
-    f.close()
-
-    top_contents = "\n".join(top_contents)
-
-    return top_contents
-
-
 class LatticePrjTrellisToolchain:
     attr_translate = {
         # FIXME: document
@@ -157,21 +99,18 @@ class LatticePrjTrellisToolchain:
         platform.finalize(fragment)
 
         top_output = platform.get_verilog(fragment)
+        named_sc, named_pc = platform.resolve_signals(top_output.ns)
         top_file = build_name + ".v"
         top_output.write(top_file)
 
-        # insert constraints and trellis_io to generated verilog
-        prjtrellis_top_file = build_name + "_prjtrellis.v"
-        prjtrellis_top_contents = generate_prjtrellis_top(top_file, platform, top_output.ns)
-        prjtrellis_top_contents = prjtrellis_top_contents.format(build_name=build_name)
-        tools.write_to_file(prjtrellis_top_file, prjtrellis_top_contents)
-        platform.add_source(prjtrellis_top_file)
+        # generate constraints
+        tools.write_to_file(build_name + ".lpf", _build_lpf(named_sc, named_pc))
 
         # generate yosys script
         yosys_script_file = build_name + ".ys"
         yosys_script_contents = [
             yosys_import_sources(platform),
-            "synth_ecp5 -nomux -json {build_name}.json -top prjtrellis_{build_name}"
+            "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)
@@ -187,7 +126,7 @@ class LatticePrjTrellisToolchain:
         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}",
+            "nextpnr-ecp5 --json {build_name}.json --lpf {build_name}.lpf --textcfg {build_name}.config --basecfg {basecfg} --{architecture}",
             "ecppack {build_name}.config {build_name}.bit"
 
         ]