From c0fddb6561ecb6cbd900dde17165884af9ac430e Mon Sep 17 00:00:00 2001 From: Dolu1990 Date: Thu, 5 Sep 2024 15:21:12 +0200 Subject: [PATCH 1/5] 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 c52db5ee90..dbcfc66fd1 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 @@ def __init__(self, platform, i1, i2, 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_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 @@ def __init__(self, platform, i1, i2, o, clk): "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 @@ def __init__(self, platform, i, o1, o2, clk): "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 @@ def lower(dr): 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 0f782ac6b1..01ebb6ac21 100644 --- a/litex/build/efinix/efinity.py +++ b/litex/build/efinix/efinity.py @@ -164,6 +164,10 @@ def _format_constraint(self, c, signame, fmt_r): 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 @@ def build_project(self): # 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 @@ def build_script(self): 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 @@ def run_script(self, script): 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 @@ def run_script(self, script): "--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 b2ca619fd6..4993011e0f 100644 --- a/litex/build/efinix/ifacewriter.py +++ b/litex/build/efinix/ifacewriter.py @@ -166,6 +166,9 @@ def generate_gpio(self, block, verbose=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 @@ def generate_gpio(self, block, verbose=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 @@ def generate_gpio(self, block, verbose=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 9808369efe..4b5777aa96 100644 --- a/litex/build/efinix/platform.py +++ b/litex/build/efinix/platform.py @@ -109,6 +109,10 @@ def get_pin_properties(self, sig): 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 0ca5ebf6ed..04a012f88d 100644 --- a/litex/soc/cores/clock/efinix.py +++ b/litex/soc/cores/clock/efinix.py @@ -107,11 +107,15 @@ def create_clkout(self, cd, freq, phase=0, margin=0, name="", with_reset=True, d 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) From 599c6dde3775dc0ada652a2fbed2a90b80b1f3f8 Mon Sep 17 00:00:00 2001 From: Dolu1990 Date: Thu, 5 Sep 2024 16:12:32 +0200 Subject: [PATCH 2/5] litex/build/efinix/common.py add EfinixDDRTristate binding MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Fin Maaß --- litex/build/efinix/common.py | 1 + 1 file changed, 1 insertion(+) diff --git a/litex/build/efinix/common.py b/litex/build/efinix/common.py index dbcfc66fd1..51885843a1 100644 --- a/litex/build/efinix/common.py +++ b/litex/build/efinix/common.py @@ -435,4 +435,5 @@ def lower(dr): SDRTristate : EfinixSDRTristate, DDROutput : EfinixDDROutput, DDRInput : EfinixDDRInput, + DDRTristate : EfinixDDRTristate, } From 109ae17e9e06bb98b01f4d49d9ced802003fc9c9 Mon Sep 17 00:00:00 2001 From: Gwenhael Goavec-Merou Date: Tue, 10 Sep 2024 17:56:49 +0200 Subject: [PATCH 3/5] build/efinix/common.py: replaced i as str by a ClockDomain --- litex/build/efinix/common.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/litex/build/efinix/common.py b/litex/build/efinix/common.py index 51885843a1..0c9d014b74 100644 --- a/litex/build/efinix/common.py +++ b/litex/build/efinix/common.py @@ -94,7 +94,7 @@ def __init__(self, platform, i, o): "size" : 1, "location" : platform.get_pin_location(o)[0], "properties" : platform.get_pin_properties(o), - "name" : i, # FIXME. + "name" : i.name_override, # FIXME "mode" : "OUTPUT_CLK", } platform.toolchain.ifacewriter.blocks.append(block) From ad09ffc1506dfa633be4d106cd4627656878f735 Mon Sep 17 00:00:00 2001 From: Gwenhael Goavec-Merou Date: Tue, 10 Sep 2024 18:01:40 +0200 Subject: [PATCH 4/5] soc/cores/clock/efinix.py: register_clkin: uses clkin.name_override as input_signal name when name is not provided and PLL is configured in CORE or INTERNAL mode, create_clkout: added PLL name in clk_name str --- litex/build/efinix/platform.py | 1 + litex/soc/cores/clock/efinix.py | 11 +++++++++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/litex/build/efinix/platform.py b/litex/build/efinix/platform.py index 4b5777aa96..4505c4caed 100644 --- a/litex/build/efinix/platform.py +++ b/litex/build/efinix/platform.py @@ -31,6 +31,7 @@ def __init__(self, *args, iobank_info=None, toolchain="efinity", spi_mode="activ self.iobank_info = iobank_info self.spi_mode = spi_mode self.spi_width = spi_width + self.clks = {} if self.device[:2] == "Ti": self.family = "Titanium" else: diff --git a/litex/soc/cores/clock/efinix.py b/litex/soc/cores/clock/efinix.py index 04a012f88d..18719c9875 100644 --- a/litex/soc/cores/clock/efinix.py +++ b/litex/soc/cores/clock/efinix.py @@ -89,9 +89,16 @@ def register_clkin(self, clkin, freq, name="", refclk_name="", lvds_input=False) self.logger.info("Clock source: {}, using EXT_CLK{}".format(block["input_clock"], clock_no)) self.platform.get_pll_resource(pll_res) else: + if name != "": + input_signal = name + elif clkin is not None: + input_signal = clkin.name_override + else: + self.logger.error("No clkin name nor clkin provided, can't continue") + quit() block["input_clock"] = "INTERNAL" if self.type == "TITANIUMPLL" else "CORE" block["resource"] = self.platform.get_free_pll_resource() - block["input_signal"] = name + block["input_signal"] = input_signal self.logger.info("Clock source: {}".format(block["input_clock"])) self.logger.info("PLL used : " + colorer(str(self.platform.pll_used), "cyan")) @@ -107,7 +114,7 @@ def create_clkout(self, cd, freq, phase=0, margin=0, name="", with_reset=True, d clk_out_name = f"{self.name}_clkout{self.nclkouts}" if name == "" else name if cd is not None: - clk_name = f"{cd.name}_clk" + clk_name = f"{cd.name}_{self.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) From a80f290d80a2a69a837fe23e1107c0c257b9fe8e Mon Sep 17 00:00:00 2001 From: Gwenhael Goavec-Merou Date: Tue, 10 Sep 2024 18:07:34 +0200 Subject: [PATCH 5/5] soc/cores/clock/efinix.py: fill platform.clks with clkout mapping cd/clk_out_name. litex/build/efinix/ifacewriter.py: generate_lvds: when slow_clk/fast_clk are ClockSignal uses platform.clks to map between domain and signal name --- litex/build/efinix/ifacewriter.py | 5 +++++ litex/soc/cores/clock/efinix.py | 1 + 2 files changed, 6 insertions(+) diff --git a/litex/build/efinix/ifacewriter.py b/litex/build/efinix/ifacewriter.py index 4993011e0f..f9e9cae781 100644 --- a/litex/build/efinix/ifacewriter.py +++ b/litex/build/efinix/ifacewriter.py @@ -439,6 +439,11 @@ def generate_lvds(self, block, verbose=True): half_rate= block.get("half_rate", "0") tx_output_load=block.get("output_load", "3") + if type(slow_clk) == ClockSignal: + slow_clk = self.platform.clks[slow_clk.cd] + if type(fast_clk) == ClockSignal: + fast_clk = self.platform.clks[fast_clk.cd] + if mode == "OUTPUT": block_type = "LVDS_TX" tx_mode = block["tx_mode"] diff --git a/litex/soc/cores/clock/efinix.py b/litex/soc/cores/clock/efinix.py index 18719c9875..8227074017 100644 --- a/litex/soc/cores/clock/efinix.py +++ b/litex/soc/cores/clock/efinix.py @@ -123,6 +123,7 @@ def create_clkout(self, cd, freq, phase=0, margin=0, name="", with_reset=True, d # 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 + self.platform.clks[cd.name] = clk_out_name if with_reset: self.specials += AsyncResetSynchronizer(cd, ~self.locked) self.platform.toolchain.excluded_ios.append(clk_out_name)