From 946478a71e2a7d47545665732c7a35bab19831fa Mon Sep 17 00:00:00 2001 From: Florent Kermarrec Date: Fri, 6 Dec 2019 12:13:20 +0100 Subject: [PATCH] build/lattice: cleanup/simplify --- litex/build/lattice/common.py | 142 +++++++++++++++++++------------- litex/build/lattice/diamond.py | 120 +++++++++++++++++---------- litex/build/lattice/platform.py | 5 +- 3 files changed, 167 insertions(+), 100 deletions(-) diff --git a/litex/build/lattice/common.py b/litex/build/lattice/common.py index 005d9ec0..2a99f848 100644 --- a/litex/build/lattice/common.py +++ b/litex/build/lattice/common.py @@ -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, diff --git a/litex/build/lattice/diamond.py b/litex/build/lattice/diamond.py index 624495ae..c3b17662 100644 --- a/litex/build/lattice/diamond.py +++ b/litex/build/lattice/diamond.py @@ -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) diff --git a/litex/build/lattice/platform.py b/litex/build/lattice/platform.py index 3fe31ceb..389f4917 100644 --- a/litex/build/lattice/platform.py +++ b/litex/build/lattice/platform.py @@ -1,10 +1,11 @@ -# This file is Copyright (c) 2015-2018 Florent Kermarrec +# This file is Copyright (c) 2015-2019 Florent Kermarrec # This file is Copyright (c) 2017 William D. Jones # 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, -- 2.30.2