build/lattice: cleanup/simplify
authorFlorent Kermarrec <florent@enjoy-digital.fr>
Fri, 6 Dec 2019 11:13:20 +0000 (12:13 +0100)
committerFlorent Kermarrec <florent@enjoy-digital.fr>
Fri, 6 Dec 2019 11:13:20 +0000 (12:13 +0100)
litex/build/lattice/common.py
litex/build/lattice/diamond.py
litex/build/lattice/platform.py

index 005d9ec07b71464297e66d110246c4f5ac0b0e9d..2a99f848420351556b3acd215c6896f9375123b0 100644 (file)
@@ -9,15 +9,22 @@ from migen.fhdl.bitcontainer import value_bits_sign
 from migen.genlib.io import *
 from migen.genlib.resetsync import AsyncResetSynchronizer
 
+# ECPX AsyncResetSynchronizer ----------------------------------------------------------------------
 
 class LatticeECPXAsyncResetSynchronizerImpl(Module):
     def __init__(self, cd, async_reset):
         rst1 = Signal()
         self.specials += [
-            Instance("FD1S3BX", i_D=0, i_PD=async_reset,
-                     i_CK=cd.clk, o_Q=rst1),
-            Instance("FD1S3BX", i_D=rst1, i_PD=async_reset,
-                     i_CK=cd.clk, o_Q=cd.rst)
+            Instance("FD1S3BX",
+                i_D  = 0,
+                i_PD = async_reset,
+                i_CK = cd.clk,
+                o_Q  = rst1),
+            Instance("FD1S3BX",
+                i_D  = rst1,
+                i_PD = async_reset,
+                i_CK = cd.clk,
+                o_Q  = cd.rst)
         ]
 
 
@@ -26,13 +33,18 @@ class LatticeECPXAsyncResetSynchronizer:
     def lower(dr):
         return LatticeECPXAsyncResetSynchronizerImpl(dr.cd, dr.async_reset)
 
+# ECPX Differential Output -------------------------------------------------------------------------
 
 class LatticeECPXDDROutputImpl(Module):
     def __init__(self, i1, i2, o, clk):
-        self.specials += Instance("ODDRXD1",
-                                  synthesis_directive="ODDRAPPS=\"SCLK_ALIGNED\"",
-                                  i_SCLK=clk,
-                                  i_DA=i1, i_DB=i2, o_Q=o)
+        self.specials += [
+            Instance("ODDRXD1",
+                synthesis_directive="ODDRAPPS=\"SCLK_ALIGNED\"",
+                i_SCLK = clk,
+                i_DA   = i1,
+                i_DB   = i2,
+                o_Q    = o)
+        ]
 
 
 class LatticeECPXDDROutput:
@@ -40,36 +52,39 @@ class LatticeECPXDDROutput:
     def lower(dr):
         return LatticeECPXDDROutputImpl(dr.i1, dr.i2, dr.o, dr.clk)
 
+# ECPX Special Overrides ---------------------------------------------------------------------------
+
 lattice_ecpx_special_overrides = {
     AsyncResetSynchronizer: LatticeECPXAsyncResetSynchronizer,
-    DDROutput: LatticeECPXDDROutput
+    DDROutput:              LatticeECPXDDROutput
 }
 
+# ECPX Trellis Tristate ----------------------------------------------------------------------------
 
 class LatticeECPXTrellisTristateImpl(Module):
     def __init__(self, io, o, oe, i):
         nbits, sign = value_bits_sign(io)
         if nbits == 1:
-            # If `io` is an expression like `port[x]`, it is not legal to index further
-            # into it if it is only 1 bit wide.
-            self.specials += \
+            self.specials += [
               Instance("TRELLIS_IO",
-                    p_DIR="BIDIR",
-                    i_B=io,
-                    i_I=o,
-                    o_O=i,
-                    i_T=~oe,
+                    p_DIR = "BIDIR",
+                    i_B   = io,
+                    i_I   = o,
+                    o_O   = i,
+                    i_T   = ~oe
                 )
+            ]
         else:
             for bit in range(nbits):
-                self.specials += \
+                self.specials += [
                     Instance("TRELLIS_IO",
                         p_DIR="BIDIR",
-                        i_B=io[bit],
-                        i_I=o[bit],
-                        o_O=i[bit],
-                        i_T=~oe,
+                        i_B = io[bit],
+                        i_I = o[bit],
+                        o_O = i[bit],
+                        i_T = ~oe
                     )
+                ]
 
 
 class LatticeECPXTrellisTristate(Module):
@@ -77,12 +92,15 @@ class LatticeECPXTrellisTristate(Module):
     def lower(dr):
         return LatticeECPXTrellisTristateImpl(dr.target, dr.o, dr.oe, dr.i)
 
+# ECPX Trellis Special Overrides -------------------------------------------------------------------
+
 lattice_ecpx_trellis_special_overrides = {
     AsyncResetSynchronizer: LatticeECPXAsyncResetSynchronizer,
     Tristate:               LatticeECPXTrellisTristate,
     DDROutput:              LatticeECPXDDROutput
 }
 
+# iCE40 AsyncResetSynchronizer ----------------------------------------------------------------------
 
 class LatticeiCE40AsyncResetSynchronizerImpl(Module):
     def __init__(self, cd, async_reset):
@@ -100,31 +118,32 @@ class LatticeiCE40AsyncResetSynchronizer:
     def lower(dr):
         return LatticeiCE40AsyncResetSynchronizerImpl(dr.cd, dr.async_reset)
 
+# iCE40 Trellis Tristate ---------------------------------------------------------------------------
 
 class LatticeiCE40TristateImpl(Module):
     def __init__(self, io, o, oe, i):
         nbits, sign = value_bits_sign(io)
         if nbits == 1:
-            # If `io` is an expression like `port[x]`, it is not legal to index further
-            # into it if it is only 1 bit wide.
-            self.specials += \
+            self.specials +=  [
                 Instance("SB_IO",
-                    p_PIN_TYPE=C(0b101001, 6),
-                    io_PACKAGE_PIN=io,
-                    i_OUTPUT_ENABLE=oe,
-                    i_D_OUT_0=o,
-                    o_D_IN_0=i,
+                    p_PIN_TYPE      = C(0b101001, 6),
+                    io_PACKAGE_PIN  = io,
+                    i_OUTPUT_ENABLE = oe,
+                    i_D_OUT_0       = o,
+                    o_D_IN_0        = i
                 )
+            ]
         else:
             for bit in range(nbits):
-                self.specials += \
+                self.specials += [
                     Instance("SB_IO",
-                        p_PIN_TYPE=C(0b101001, 6),
-                        io_PACKAGE_PIN=io[bit],
-                        i_OUTPUT_ENABLE=oe,
-                        i_D_OUT_0=o[bit],
-                        o_D_IN_0=i[bit],
+                        p_PIN_TYPE      = C(0b101001, 6),
+                        io_PACKAGE_PIN  = io[bit],
+                        i_OUTPUT_ENABLE = oe,
+                        i_D_OUT_0       = o[bit],
+                        o_D_IN_0        = i[bit]
                     )
+                ]
 
 
 class LatticeiCE40Tristate(Module):
@@ -132,20 +151,27 @@ class LatticeiCE40Tristate(Module):
     def lower(dr):
         return LatticeiCE40TristateImpl(dr.target, dr.o, dr.oe, dr.i)
 
+# iCE40 Differential Output ------------------------------------------------------------------------
 
 class LatticeiCE40DifferentialOutputImpl(Module):
     def __init__(self, i, o_p, o_n):
-        self.specials += Instance("SB_IO",
-                                  p_PIN_TYPE=C(0b011000, 6),
-                                  p_IO_STANDARD="SB_LVCMOS",
-                                  io_PACKAGE_PIN=o_p,
-                                  i_D_OUT_0=i)
+        self.specials += [
+            Instance("SB_IO",
+                p_PIN_TYPE     = C(0b011000, 6),
+                p_IO_STANDARD  = "SB_LVCMOS",
+                io_PACKAGE_PIN = o_p,
+                i_D_OUT_0      = i
+            )
+        ]
 
-        self.specials += Instance("SB_IO",
-                                  p_PIN_TYPE=C(0b011000, 6),
-                                  p_IO_STANDARD="SB_LVCMOS",
-                                  io_PACKAGE_PIN=o_n,
-                                  i_D_OUT_0=~i)
+        self.specials += [
+            Instance("SB_IO",
+                p_PIN_TYPE     = C(0b011000, 6),
+                p_IO_STANDARD  = "SB_LVCMOS",
+                io_PACKAGE_PIN = o_n,
+                i_D_OUT_0      = ~i
+            )
+        ]
 
 
 class LatticeiCE40DifferentialOutput:
@@ -154,17 +180,22 @@ class LatticeiCE40DifferentialOutput:
         return LatticeiCE40DifferentialOutputImpl(dr.i, dr.o_p, dr.o_n)
 
 
+# iCE40 DDR Output ---------------------------------------------------------------------------------
+
 class LatticeiCE40DDROutputImpl(Module):
     def __init__(self, i1, i2, o, clk):
-        self.specials += Instance("SB_IO",
-            p_PIN_TYPE=C(0b010000, 6),
-            p_IO_STANDARD="SB_LVCMOS",
-            io_PACKAGE_PIN=o,
-            i_CLOCK_ENABLE=1,
-            i_OUTPUT_CLK=clk,
-            i_OUTPUT_ENABLE=1,
-            i_D_OUT_0=i1,
-            i_D_OUT_1=i2)
+        self.specials += [
+            Instance("SB_IO",
+                p_PIN_TYPE      = C(0b010000, 6),
+                p_IO_STANDARD   = "SB_LVCMOS",
+                io_PACKAGE_PIN  = o,
+                i_CLOCK_ENABLE  = 1,
+                i_OUTPUT_CLK    = clk,
+                i_OUTPUT_ENABLE = 1,
+                i_D_OUT_0       = i1,
+                i_D_OUT_1       = i2
+            )
+        ]
 
 
 class LatticeiCE40DDROutput:
@@ -172,6 +203,7 @@ class LatticeiCE40DDROutput:
     def lower(dr):
         return LatticeiCE40DDROutputImpl(dr.i1, dr.i2, dr.o, dr.clk)
 
+# iCE40 Trellis Special Overrides ------------------------------------------------------------------
 
 lattice_ice40_special_overrides = {
     AsyncResetSynchronizer: LatticeiCE40AsyncResetSynchronizer,
index 624495aefce01d6cf3b8919e741dc84b9e224bf9..c3b17662f49668d0060bddaa151c632d797239c1 100644 (file)
@@ -14,10 +14,12 @@ from litex.build.generic_platform import *
 from litex.build import tools
 from litex.build.lattice import common
 
+# Helpers ------------------------------------------------------------------------------------------
 
 def _produces_jedec(device):
     return device.startswith("LCMX")
 
+# IO Constraints (.lpf) ----------------------------------------------------------------------------
 
 def _format_constraint(c):
     if isinstance(c, Pins):
@@ -30,35 +32,54 @@ def _format_constraint(c):
 
 def _format_lpf(signame, pin, others, resname):
     fmt_c = [_format_constraint(c) for c in ([Pins(pin)] + others)]
-    r = ""
+    lpf = []
     for pre, suf in fmt_c:
-        r += pre + "\"" + signame + "\"" + suf + ";\n"
-    return r
+        lpf.append(pre + "\"" + signame + "\"" + suf + ";")
+    return "\n".join(lpf)
 
 
-def _build_lpf(named_sc, named_pc):
-    r = "BLOCK RESETPATHS;\n"
-    r += "BLOCK ASYNCPATHS;\n"
+def _build_lpf(named_sc, named_pc, build_name):
+    lpf = []
+    lpf.append("BLOCK RESETPATHS;")
+    lpf.append("BLOCK ASYNCPATHS;")
     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)
+                lpf.append(_format_lpf(sig + "[" + str(i) + "]", p, others, resname))
         else:
-            r += _format_lpf(sig, pins[0], others, resname)
+            lpf.append(_format_lpf(sig, pins[0], others, resname))
     if named_pc:
-        r += "\n" + "\n\n".join(named_pc)
-    return r
+        lpf.append("\n\n".join(named_pc))
+    tools.write_to_file(build_name + ".lpf", "\n".join(lpf))
 
+# Project (.tcl) -----------------------------------------------------------------------------------
 
-def _build_files(device, sources, vincpaths, build_name):
+def _build_tcl(device, sources, vincpaths, build_name):
     tcl = []
-    tcl.append("prj_project new -name \"{}\" -impl \"impl\" -dev {} -synthesis \"synplify\"".format(build_name, device))
+    # Create project
+    tcl.append(" ".join([
+        "prj_project",
+        "new -name \"{}\"".format(build_name),
+        "-impl \"impl\"",
+        "-dev {}".format(device),
+        "-synthesis \"synplify\""
+    ]))
+
+    # Add include paths
     for path in vincpaths:
         tcl.append("prj_impl option {include path} {\"" + path + "\"}")
+
+    # Add sources
     for filename, language, library in sources:
         tcl.append("prj_src add \"" + filename.replace("\\", "/") + "\" -work " + library)
+
+    # Set top level
     tcl.append("prj_impl option top \"{}\"".format(build_name))
+
+    # Save project
     tcl.append("prj_project save")
+
+    # Build flow
     tcl.append("prj_run Synthesis -impl impl -forceOne")
     tcl.append("prj_run Translate -impl impl")
     tcl.append("prj_run Map -impl impl")
@@ -68,43 +89,40 @@ def _build_files(device, sources, vincpaths, build_name):
         tcl.append("prj_run Export -impl impl -task Jedecgen")
     tools.write_to_file(build_name + ".tcl", "\n".join(tcl))
 
+# Script -------------------------------------------------------------------------------------------
 
 def _build_script(build_name, device, toolchain_path, ver=None):
     if sys.platform in ("win32", "cygwin"):
         script_ext = ".bat"
-        build_script_contents = "@echo off\nrem Autogenerated by LiteX / git: " + tools.get_litex_git_revision() + "\n\n"
+        script_contents = "@echo off\nrem Autogenerated by LiteX / git: " + tools.get_litex_git_revision() + "\n\n"
         copy_stmt = "copy"
         fail_stmt = " || exit /b"
     else:
         script_ext = ".sh"
-        build_script_contents = "# Autogenerated by LiteX / git: " + tools.get_litex_git_revision() + "\nset -e\n"
+        script_contents = "# Autogenerated by LiteX / git: " + tools.get_litex_git_revision() + "\nset -e\n"
         copy_stmt = "cp"
         fail_stmt = ""
 
     if sys.platform not in ("win32", "cygwin"):
-        build_script_contents += "bindir={}\n".format(toolchain_path)
-        build_script_contents += ". ${{bindir}}/diamond_env{fail_stmt}\n".format(
-            fail_stmt=fail_stmt)
-    build_script_contents += "{pnmainc} {tcl_script}{fail_stmt}\n".format(
-        pnmainc=os.path.join(toolchain_path, "pnmainc"),
-        tcl_script=build_name + ".tcl",
-        fail_stmt=fail_stmt)
+        script_contents += "bindir={}\n".format(toolchain_path)
+        script_contents += ". ${{bindir}}/diamond_env{fail_stmt}\n".format(fail_stmt=fail_stmt)
+    script_contents += "{pnmainc} {tcl_script}{fail_stmt}\n".format(
+        pnmainc    = os.path.join(toolchain_path, "pnmainc"),
+        tcl_script = build_name + ".tcl",
+        fail_stmt  = fail_stmt)
     for ext in (".bit", ".jed"):
         if ext == ".jed" and not _produces_jedec(device):
             continue
-        build_script_contents += "{copy_stmt} {diamond_product} {migen_product}" \
-                                 "{fail_stmt}\n".format(
-            copy_stmt=copy_stmt,
-            fail_stmt=fail_stmt,
-            diamond_product=os.path.join("impl", build_name + "_impl" + ext),
-            migen_product=build_name + ext)
+        script_contents += "{copy_stmt} {diamond_product} {migen_product} {fail_stmt}\n".format(
+            copy_stmt       = copy_stmt,
+            fail_stmt       = fail_stmt,
+            diamond_product = os.path.join("impl", build_name + "_impl" + ext),
+            migen_product   = build_name + ext)
 
     build_script_file = "build_" + build_name + script_ext
-    tools.write_to_file(build_script_file, build_script_contents,
-                        force_unix=False)
+    tools.write_to_file(build_script_file, script_contents, force_unix=False)
     return build_script_file
 
-
 def _run_script(script):
     if sys.platform in ("win32", "cygwin"):
         shell = ["cmd", "/c"]
@@ -114,45 +132,60 @@ def _run_script(script):
     if subprocess.call(shell + [script]) != 0:
         raise OSError("Subprocess failed")
 
+# LatticeDiamondToolchain --------------------------------------------------------------------------
 
 class LatticeDiamondToolchain:
     attr_translate = {
         # FIXME: document
-        "keep": ("syn_keep", "true"),
-        "no_retiming": ("syn_no_retiming", "true"),
-        "async_reg": None,
-        "mr_ff": None,
-        "mr_false_path": None,
-        "ars_ff1": None,
-        "ars_ff2": None,
-        "ars_false_path": None,
+        "keep":             ("syn_keep", "true"),
+        "no_retiming":      ("syn_no_retiming", "true"),
+        "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 build(self, platform, fragment, build_dir="build", build_name="top",
-              toolchain_path=None, run=True, **kwargs):
+    def build(self, platform, fragment,
+        build_dir      = "build",
+        build_name     = "top",
+        toolchain_path = None,
+        run            = True,
+        **kwargs):
+
+        # Create build directory
         if toolchain_path is None:
             toolchain_path = "/opt/Diamond"
         os.makedirs(build_dir, exist_ok=True)
         cwd = os.getcwd()
         os.chdir(build_dir)
 
+        # Finalize design
         if not isinstance(fragment, _Fragment):
             fragment = fragment.get_fragment()
         platform.finalize(fragment)
 
+        # Generate verilog
         v_output = platform.get_verilog(fragment, name=build_name, **kwargs)
         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)
-        _build_files(platform.device, platform.sources, platform.verilog_include_paths, build_name)
 
-        tools.write_to_file(build_name + ".lpf", _build_lpf(named_sc, named_pc))
+        # Generate design script file (.tcl)
+        _build_tcl(platform.device, platform.sources, platform.verilog_include_paths, build_name)
 
+        # Generate design timing constraints file (.lpf)
+        _build_lpf(named_sc, named_pc, build_name)
+
+        # Generate build script
         script = _build_script(build_name, platform.device, toolchain_path)
+
+        # Run
         if run:
             _run_script(script)
 
@@ -162,4 +195,5 @@ class LatticeDiamondToolchain:
 
     def add_period_constraint(self, platform, clk, period):
         # TODO: handle differential clk
-        platform.add_platform_command("""FREQUENCY PORT "{clk}" {freq} MHz;""".format(freq=str(float(1/period)*1000), clk="{clk}"), clk=clk)
+        platform.add_platform_command("""FREQUENCY PORT "{clk}" {freq} MHz;""".format(
+            freq=str(float(1/period)*1000), clk="{clk}"), clk=clk)
index 3fe31ceb673823f2190cdc4c402d80184fbdd81f..389f49172de0114ec096b3face181783d66d21ef 100644 (file)
@@ -1,10 +1,11 @@
-# This file is Copyright (c) 2015-2018 Florent Kermarrec <florent@enjoy-digital.fr>
+# This file is Copyright (c) 2015-2019 Florent Kermarrec <florent@enjoy-digital.fr>
 # This file is Copyright (c) 2017 William D. Jones <thor0505@comcast.net>
 # License: BSD
 
 from litex.build.generic_platform import GenericPlatform
 from litex.build.lattice import common, diamond, icestorm, trellis
 
+# LatticePlatform ----------------------------------------------------------------------------------
 
 class LatticePlatform(GenericPlatform):
     bitstream_ext = ".bit"
@@ -22,7 +23,7 @@ class LatticePlatform(GenericPlatform):
             raise ValueError("Unknown toolchain")
 
     def get_verilog(self, *args, special_overrides=dict(), **kwargs):
-        so = dict()  # No common overrides between ECP and ice40.
+        so = dict()  # No common overrides between ECPX and iCE40.
         so.update(self.toolchain.special_overrides)
         so.update(special_overrides)
         return GenericPlatform.get_verilog(self, *args, special_overrides=so,