Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

build/efinix: add a few IO primitives, IO constraints, sdc rework #2060

Merged
merged 5 commits into from
Sep 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
144 changes: 123 additions & 21 deletions litex/build/efinix/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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 ------------------------------------------------------------------------

Expand Down Expand Up @@ -261,13 +244,130 @@ 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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this assert doesn't work right if oe1 and oe2 are a _Slice, like in the litespi ddr phy

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If i remember well, one issue is that the efinix primitive only support 1 output enable signal (in ddr mode).

  • Either when oe1 != oe2 it could fall back to DDR tristate emulation (et get very bad timings)
  • Either it could be ignored and hope for the best
  • Either it can keep the assert

Not sure what is the best.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another options would be to update litespi to avoid the usage of 2 oe signals

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it was already with changes like: litex-hub/litespi#60

The problem is this: oe1 = dq_oe[i], oe2 = dq_oe[i], with dq_oe = Signal(len(pads.dq))
where oe1 and oe2 are only a `_Slice``od a Signal and not a whole Signal. the error comes from here: https://github.com/m-labs/migen/blob/832a7240ba32af9cbd4fdd519ddcb4f912534726/migen/fhdl/structure.py#L41

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hoo then would just need to do a dq_oe[i] once before , and refer twice to its result ?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this should fix it m-labs/migen#292

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):
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)
Expand All @@ -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))
Expand Down Expand Up @@ -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)
Expand All @@ -331,7 +431,9 @@ def lower(dr):
Tristate : EfinixTristate,
DifferentialOutput : EfinixDifferentialOutput,
DifferentialInput : EfinixDifferentialInput,
SDROutput : EfinixSDROutput,
SDRTristate : EfinixSDRTristate,
DDROutput : EfinixDDROutput,
DDRInput : EfinixDDRInput,
Dolu1990 marked this conversation as resolved.
Show resolved Hide resolved
DDRTristate : EfinixDDRTristate,
}
38 changes: 27 additions & 11 deletions litex/build/efinix/efinity.py
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down Expand Up @@ -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")
Expand Down Expand Up @@ -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}",
Expand Down Expand Up @@ -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}",
Expand All @@ -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",
Expand Down
17 changes: 16 additions & 1 deletion litex/build/efinix/ifacewriter.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -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)
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -429,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"]
Expand Down
5 changes: 5 additions & 0 deletions litex/build/efinix/platform.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -109,6 +110,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]
Expand Down
18 changes: 15 additions & 3 deletions litex/soc/cores/clock/efinix.py
Original file line number Diff line number Diff line change
Expand Up @@ -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"))
Expand All @@ -107,11 +114,16 @@ 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}_{self.name}_clk"
clk_out_name = clk_name # To unify constraints names
self.platform.add_extension([(clk_out_name, 0, Pins(1))])
clk_name = f"{cd.name}_clk"
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
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)
Expand Down
Loading