build/lattice/prjtrellis: generate iowrapper to set constraints and TRELLIS_IO
authorFlorent Kermarrec <florent@enjoy-digital.fr>
Mon, 29 Oct 2018 10:44:31 +0000 (11:44 +0100)
committerFlorent Kermarrec <florent@enjoy-digital.fr>
Mon, 29 Oct 2018 10:44:31 +0000 (11:44 +0100)
PrjTrellis does not yet have constraint files support, constraints are set
with signal attributes and specific TRELLIS_IO instances are requested. This
iowrapper does this work for us automatically.

Remove this code and replace with a constraint file generation code when
PrjTrellis will have constraint file support.

litex/build/lattice/prjtrellis.py

index 642c9be6b7cf7fddbbe1a431a6aacadafb47d79d..4a2605b8d7f54ccfb0531c313a874a18a16b6111 100644 (file)
@@ -10,6 +10,11 @@ from litex.build.generic_platform import *
 from litex.build import tools
 from litex.build.lattice import common
 
+# TODO:
+# - add inout support to iowrapper
+# - verify iowrapper handle correctly multi-bit signals
+# - check/document attr_translate
+
 
 nextpnr_ecp5_architectures = {
     "lfe5u-25f": "25k",
@@ -24,6 +29,87 @@ nextpnr_ecp5_architectures = {
 }
 
 
+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)
+
+
+def generate_prjtrellis_iowrapper(platform, vns):
+    ios, _ = platform.resolve_signals(vns)
+    ios_direction = {}
+    # resolve ios directions
+    cm = platform.constraint_manager
+    for io_name, io_pin, _, _ in ios:
+        for cm_sig, cm_pin, _, _ in cm.get_sig_constraints():
+            if io_pin == cm_pin:
+                ios_direction[io_name] = cm_sig.direction
+        last_io_name = io_name
+
+    iowrapper_contents = []
+    iowrapper_contents.append("module {build_name}_iowrapper(")
+
+    # ios declaration
+    ios_declaration = ""
+    for io_name, io_pin, _, _ in ios:
+        ios_declaration += "\t" + ios_direction[io_name] + " "
+        if len(io_pin) > 1:
+            ios_declaration += "[{}:0] ".format(len(io_pin - 1))
+        ios_declaration += io_name + "_io"
+        ios_declaration += ",\n" if io_name != last_io_name else ""
+    iowrapper_contents.append(ios_declaration)
+    iowrapper_contents.append(");\n")
+
+    # wires declaration
+    wires_declaration = ""
+    for io_name, io_pin, _, _ in ios:
+        wires_declaration += "wire "
+        if len(io_pin) > 1:
+            wires_declaration += "[{}:0] ".format(len(io_pin - 1))
+        wires_declaration += io_name
+        wires_declaration += ";\n"
+    iowrapper_contents.append(wires_declaration)
+
+    # trellis_io declaration
+    trellis_io_declaration = ""
+    for io_name, io_pin, io_others, _ in ios:
+        for io_other in io_others:
+            if isinstance(io_other, IOStandard):
+                io_standard = io_other.name
+        trellis_io_declaration += \
+        "(* LOC=\"{}\" *) (* IO_TYPE=\"{}\" *)\n".format(io_pin[0], io_standard)
+        if ios_direction[io_name] == "input":
+            trellis_io_declaration += \
+            "TRELLIS_IO #(.DIR(\"INPUT\")) {} (.B({}), .O({}));\n".format(
+                io_name + "_buf", io_name + "_io", io_name)
+        elif ios_direction[io_name] == "output":
+            trellis_io_declaration += \
+            "TRELLIS_IO #(.DIR(\"OUTPUT\")) {} (.B({}), .I({}));\n".format(
+                io_name + "_buf", io_name + "_io", io_name)
+        else:
+            raise NotImplementedError
+    iowrapper_contents.append(trellis_io_declaration)
+
+    # top declaration
+    top_declaration = "{build_name} _{build_name}(\n"
+    for io_name, io_pin, _, _ in ios:
+        top_declaration += "\t." + io_name + "(" + io_name + ")"
+        top_declaration += ",\n" if io_name != last_io_name else "\n"
+    top_declaration += ");\n"
+    iowrapper_contents.append(top_declaration)
+
+    iowrapper_contents.append("endmodule")
+
+    iowrapper_contents = "\n".join(iowrapper_contents)
+
+    return iowrapper_contents
+
+
 class LatticePrjTrellisToolchain:
     attr_translate = {
         # FIXME: document
@@ -52,26 +138,23 @@ class LatticePrjTrellisToolchain:
         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)
         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)
+        # generate iowrapper (with constraints and trellis_ios)
+        # FIXME: remove when prjtrellis will support constraint files
+        iowrapper_file = build_name + "_iowrapper.v"
+        iowrapper_contents = generate_prjtrellis_iowrapper(platform, v_output.ns)
+        iowrapper_contents = iowrapper_contents.format(build_name=build_name)
+        tools.write_to_file(iowrapper_file, iowrapper_contents)
+        platform.add_source(iowrapper_file)
 
+        # generate yosys script
         yosys_script_file = build_name + ".ys"
         yosys_script_contents = [
             yosys_import_sources(platform),
-            "synth_ecp5 -nomux -json {build_name}.json -top {build_name}"
+            "synth_ecp5 -nomux -json {build_name}.json -top {build_name}_iowrapper"
         ]
         yosys_script_contents = "\n".join(yosys_script_contents)
         yosys_script_contents = yosys_script_contents.format(build_name=build_name)