From c0fddb6561ecb6cbd900dde17165884af9ac430e Mon Sep 17 00:00:00 2001 From: Dolu1990 Date: Thu, 5 Sep 2024 15:21:12 +0200 Subject: [PATCH] build/efinix: add a few IO primitives, IO constraints, but mainly it rework how the SDC are handled --- litex/build/efinix/common.py | 141 +++++++++++++++++++++++++----- litex/build/efinix/efinity.py | 38 +++++--- litex/build/efinix/ifacewriter.py | 12 ++- litex/build/efinix/platform.py | 4 + litex/soc/cores/clock/efinix.py | 8 +- 5 files changed, 169 insertions(+), 34 deletions(-) diff --git a/litex/build/efinix/common.py b/litex/build/efinix/common.py index c52db5ee9..dbcfc66fd 100644 --- a/litex/build/efinix/common.py +++ b/litex/build/efinix/common.py @@ -139,24 +139,7 @@ class EfinixTristate(Module): def lower(dr): return EfinixTristateImpl(dr.platform, dr.target, dr.o, dr.oe, dr.i) -# Efinix SDRTristate ------------------------------------------------------------------------------- -class EfinixSDRTristateImpl(Module): - def __init__(self, platform, io, o, oe, i, clk): - _o = Signal() - _oe = Signal() - _i = Signal() - self.specials += SDROutput(o, _o, clk) - self.specials += SDRInput(_i, i, clk) - self.submodules += InferedSDRIO(oe, _oe, clk) - tristate = Tristate(io, _o, _oe, _i) - tristate.platform = platform - self.specials += tristate - -class EfinixSDRTristate(Module): - @staticmethod - def lower(dr): - return EfinixSDRTristateImpl(dr.platform, dr.io, dr.o, dr.oe, dr.i, dr.clk) # Efinix DifferentialOutput ------------------------------------------------------------------------ @@ -261,6 +244,122 @@ class EfinixDifferentialInput: def lower(dr): return EfinixDifferentialInputImpl(dr.platform, dr.i_p, dr.i_n, dr.o) + + + + +# Efinix DDRTristate --------------------------------------------------------------------------------- + +class EfinixDDRTristateImpl(Module): + def __init__(self, platform, io, o1, o2, oe1, oe2, i1, i2, clk): + assert oe1 == oe2 + io_name = platform.get_pin_name(io) + io_pad = platform.get_pin_location(io) + io_prop = platform.get_pin_properties(io) + io_prop_dict = dict(io_prop) + io_data_i_h = platform.add_iface_io(io_name + "_OUT_HI") + io_data_i_l = platform.add_iface_io(io_name + "_OUT_LO") + io_data_o_h = platform.add_iface_io(io_name + "_IN_HI") + io_data_o_l = platform.add_iface_io(io_name + "_IN_LO") + io_data_e = platform.add_iface_io(io_name + "_OE") + self.comb += io_data_i_h.eq(o1) + self.comb += io_data_i_l.eq(o2) + self.comb += io_data_e.eq(oe1) + self.comb += i1.eq(io_data_o_h) + self.comb += i2.eq(io_data_o_l) + block = { + "type" : "GPIO", + "mode" : "INOUT", + "name" : io_name, + "location" : io_pad, + "properties" : io_prop, + "size" : 1, + "in_reg" : "DDIO_RESYNC", + "in_clk_pin" : clk.name_override, # FIXME. + "out_reg" : "DDIO_RESYNC", + "out_clk_pin" : clk.name_override, # FIXME. + "oe_reg" : "REG", + "is_inclk_inverted" : False, + "drive_strength" : io_prop_dict.get("DRIVE_STRENGTH", "4") + } + platform.toolchain.ifacewriter.blocks.append(block) + platform.toolchain.excluded_ios.append(platform.get_pin(io)) + +class EfinixDDRTristate: + @staticmethod + def lower(dr): + return EfinixDDRTristateImpl(dr.platform, dr.io, dr.o1, dr.o2, dr.oe1, dr.oe2, dr.i1, dr.i2, dr.clk) + +# Efinix SDRTristate ------------------------------------------------------------------------------- + +class EfinixSDRTristateImpl(EfinixDDRTristateImpl): + def __init__(self, platform, io, o, oe, i, clk): + io_name = platform.get_pin_name(io) + io_pad = platform.get_pin_location(io) + io_prop = platform.get_pin_properties(io) + io_prop_dict = dict(io_prop) + io_data_i = platform.add_iface_io(io_name + "_OUT") + io_data_o = platform.add_iface_io(io_name + "_IN") + io_data_e = platform.add_iface_io(io_name + "_OE") + self.comb += io_data_i.eq(o) + self.comb += io_data_e.eq(oe) + self.comb += i.eq(io_data_o) + block = { + "type" : "GPIO", + "mode" : "INOUT", + "name" : io_name, + "location" : io_pad, + "properties" : io_prop, + "size" : 1, + "in_reg" : "REG", + "in_clk_pin" : clk.name_override, # FIXME. + "out_reg" : "REG", + "out_clk_pin" : clk.name_override, # FIXME. + "oe_reg" : "REG", + "is_inclk_inverted" : False, + "drive_strength" : io_prop_dict.get("DRIVE_STRENGTH", "4") + } + platform.toolchain.ifacewriter.blocks.append(block) + platform.toolchain.excluded_ios.append(platform.get_pin(io)) + + +class EfinixSDRTristate(Module): + @staticmethod + def lower(dr): + return EfinixSDRTristateImpl(dr.platform, dr.io, dr.o, dr.oe, dr.i, dr.clk) + +# Efinix SDROutput ------------------------------------------------------------------------------- + +class EfinixSDROutputImpl(Module): + def __init__(self, platform, i, o, clk): + io_name = platform.get_pin_name(o) + io_pad = platform.get_pin_location(o) + io_prop = platform.get_pin_properties(o) + io_prop_dict = dict(io_prop) + io_data_i = platform.add_iface_io(io_name) + self.comb += io_data_i.eq(i) + block = { + "type" : "GPIO", + "mode" : "OUTPUT", + "name" : io_name, + "location" : io_pad, + "properties" : io_prop, + "size" : 1, + "out_reg" : "REG", + "out_clk_pin" : clk.name_override, # FIXME. + "is_inclk_inverted" : False, + "drive_strength" : io_prop_dict.get("DRIVE_STRENGTH", "4") + } + platform.toolchain.ifacewriter.blocks.append(block) + platform.toolchain.excluded_ios.append(platform.get_pin(o)) + + +class EfinixSDROutput(Module): + @staticmethod + def lower(dr): + return EfinixSDROutputImpl(dr.platform, dr.i, dr.o, dr.clk) + + # Efinix DDROutput --------------------------------------------------------------------------------- class EfinixDDROutputImpl(Module): @@ -268,6 +367,7 @@ class EfinixDDROutputImpl(Module): io_name = platform.get_pin_name(o) io_pad = platform.get_pin_location(o) io_prop = platform.get_pin_properties(o) + io_prop_dict = dict(io_prop) io_data_h = platform.add_iface_io(io_name + "_HI") io_data_l = platform.add_iface_io(io_name + "_LO") self.comb += io_data_h.eq(i1) @@ -280,9 +380,9 @@ class EfinixDDROutputImpl(Module): "properties" : io_prop, "size" : 1, "out_reg" : "DDIO_RESYNC", - "out_clk_pin" : clk, # FIXME. + "out_clk_pin" : clk.name_override, # FIXME. "is_inclk_inverted" : False, - "drive_strength" : 4 # FIXME: Get it from constraints. + "drive_strength" : io_prop_dict.get("DRIVE_STRENGTH", "4") } platform.toolchain.ifacewriter.blocks.append(block) platform.toolchain.excluded_ios.append(platform.get_pin(o)) @@ -311,7 +411,7 @@ class EfinixDDRInputImpl(Module): "properties" : io_prop, "size" : 1, "in_reg" : "DDIO_RESYNC", - "in_clk_pin" : clk, # FIXME. + "in_clk_pin" : clk.name_override, # FIXME. "is_inclk_inverted" : False } platform.toolchain.ifacewriter.blocks.append(block) @@ -331,6 +431,7 @@ efinix_special_overrides = { Tristate : EfinixTristate, DifferentialOutput : EfinixDifferentialOutput, DifferentialInput : EfinixDifferentialInput, + SDROutput : EfinixSDROutput, SDRTristate : EfinixSDRTristate, DDROutput : EfinixDDROutput, DDRInput : EfinixDDRInput, diff --git a/litex/build/efinix/efinity.py b/litex/build/efinix/efinity.py index 0f782ac6b..01ebb6ac2 100644 --- a/litex/build/efinix/efinity.py +++ b/litex/build/efinix/efinity.py @@ -164,6 +164,10 @@ class EfinityToolchain(GenericToolchain): prop = "PULL_OPTION" val = c.misc + if c.misc == "SCHMITT_TRIGGER": + prop = "SCHMITT_TRIGGER" + val = "1" + if "DRIVE_STRENGTH" in c.misc: prop = "DRIVE_STRENGTH" val = c.misc.split("=")[1] @@ -267,7 +271,7 @@ class EfinityToolchain(GenericToolchain): # Add Timing Constraints. constraint_info = et.SubElement(root, "efx:constraint_info") - et.SubElement(constraint_info, "efx:sdc_file", name=f"{self._build_name}.sdc") + et.SubElement(constraint_info, "efx:sdc_file", name=f"{self._build_name}_merged.sdc") # Add Misc Info. misc_info = et.SubElement(root, "efx:misc_info") @@ -302,6 +306,26 @@ class EfinityToolchain(GenericToolchain): return "" # not used def run_script(self, script): + # Place and Route. + r = tools.subprocess_call_filtered([self.efinity_path + "/bin/python3", + self.efinity_path + "/scripts/efx_run_pt.py", + f"{self._build_name}", + self.platform.family, + self.platform.device + ], common.colors) + if r != 0: + raise OSError("Error occurred during efx_run_pt execution.") + + # Merge SDC + with open(f"{self._build_name}_merged.sdc", 'w') as outfile: + with open(f"outflow/{self._build_name}.pt.sdc") as infile: + outfile.write(infile.read()) + outfile.write("\n") + outfile.write("#########################\n") + outfile.write("\n") + with open(f"{self._build_name}.sdc") as infile: + outfile.write(infile.read()) + # Synthesis/Mapping. r = tools.subprocess_call_filtered([self.efinity_path + "/bin/efx_map", "--project", f"{self._build_name}", @@ -332,15 +356,7 @@ class EfinityToolchain(GenericToolchain): if r != 0: raise OSError("Error occurred during efx_map execution.") - # Place and Route. - r = tools.subprocess_call_filtered([self.efinity_path + "/bin/python3", - self.efinity_path + "/scripts/efx_run_pt.py", - f"{self._build_name}", - self.platform.family, - self.platform.device - ], common.colors) - if r != 0: - raise OSError("Error occurred during efx_run_pt execution.") + r = tools.subprocess_call_filtered([self.efinity_path + "/bin/efx_pnr", "--circuit", f"{self._build_name}", @@ -354,7 +370,7 @@ class EfinityToolchain(GenericToolchain): "--use_vdb_file", "on", "--place_file", f"outflow/{self._build_name}.place", "--route_file", f"outflow/{self._build_name}.route", - "--sdc_file", f"{self._build_name}.sdc", + "--sdc_file", f"{self._build_name}_merged.sdc", "--sync_file", f"outflow/{self._build_name}.interface.csv", "--seed", "1", "--work_dir", "work_pnr", diff --git a/litex/build/efinix/ifacewriter.py b/litex/build/efinix/ifacewriter.py index b2ca619fd..4993011e0 100644 --- a/litex/build/efinix/ifacewriter.py +++ b/litex/build/efinix/ifacewriter.py @@ -166,6 +166,9 @@ design.create("{2}", "{3}", "./../gateware", overwrite=True) cmd += f'design.assign_pkg_pin("{name}[{i}]","{pad}")\n' if "out_reg" in block: + cmd += f'design.set_property("{name}","oe_REG","{block["out_reg"]}")\n' + + if "oe_reg" in block: cmd += f'design.set_property("{name}","OUT_REG","{block["out_reg"]}")\n' cmd += f'design.set_property("{name}","OUT_CLK_PIN","{block["out_clk_pin"]}")\n' if "out_delay" in block: @@ -189,6 +192,11 @@ design.create("{2}", "{3}", "./../gateware", overwrite=True) if "oe_clk_pin" in block: cmd += f'design.set_property("{name}","OE_CLK_PIN","{block["oe_clk_pin"]}")\n' + if "drive_strength" in block: + cmd += 'design.set_property("{}","DRIVE_STRENGTH","{}")\n'.format(name, block["drive_strength"]) + if "slewrate" in block: + cmd += 'design.set_property("{}","SLEWRATE","{}")\n'.format(name, block["slewrate"]) + if prop: for p, val in prop: cmd += 'design.set_property("{}","{}","{}")\n'.format(name, p, val) @@ -234,7 +242,9 @@ design.create("{2}", "{3}", "./../gateware", overwrite=True) cmd += f'design.set_property("{name}","OE_CLK_PIN_INV","{block["out_clk_inv"]}")\n' if "drive_strength" in block: - cmd += 'design.set_property("{}","DRIVE_STRENGTH","4")\n'.format(name, block["drive_strength"]) + cmd += 'design.set_property("{}","DRIVE_STRENGTH","{}")\n'.format(name, block["drive_strength"]) + if "slewrate" in block: + cmd += 'design.set_property("{}","SLEWRATE","{}")\n'.format(name, block["slewrate"]) if prop: for p, val in prop: diff --git a/litex/build/efinix/platform.py b/litex/build/efinix/platform.py index 9808369ef..4b5777aa9 100644 --- a/litex/build/efinix/platform.py +++ b/litex/build/efinix/platform.py @@ -109,6 +109,10 @@ class EfinixPlatform(GenericPlatform): prop = "PULL_OPTION" val = o.misc ret.append((prop, val)) + if o.misc == "SCHMITT_TRIGGER": + prop = "SCHMITT_TRIGGER" + val = "1" + ret.append((prop, val)) if "DRIVE_STRENGTH" in o.misc: prop = "DRIVE_STRENGTH" val = o.misc.split("=")[1] diff --git a/litex/soc/cores/clock/efinix.py b/litex/soc/cores/clock/efinix.py index 0ca5ebf6e..04a012f88 100644 --- a/litex/soc/cores/clock/efinix.py +++ b/litex/soc/cores/clock/efinix.py @@ -107,11 +107,15 @@ class EFINIXPLL(LiteXModule): clk_out_name = f"{self.name}_clkout{self.nclkouts}" if name == "" else name if cd is not None: - self.platform.add_extension([(clk_out_name, 0, Pins(1))]) clk_name = f"{cd.name}_clk" + clk_out_name = clk_name # To unify constraints names + self.platform.add_extension([(clk_out_name, 0, Pins(1))]) clk_out = self.platform.request(clk_out_name) self.comb += cd.clk.eq(clk_out) - self.platform.add_period_constraint(clk=clk_out, period=1e9/freq, name=clk_name) + # Efinity will generate xxx.pt.sdc constraints automaticaly, + # so, the user realy need to use the toplevel pin from the pll instead of an intermediate signal + # This is a dirty workaround. But i don't have any better + cd.clk = clk_out if with_reset: self.specials += AsyncResetSynchronizer(cd, ~self.locked) self.platform.toolchain.excluded_ios.append(clk_out_name)