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
Changes from 1 commit
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
Next Next commit
build/efinix: add a few IO primitives, IO constraints, but mainly it …
…rework how the SDC are handled
Dolu1990 committed Sep 5, 2024

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
commit c0fddb6561ecb6cbd900dde17165884af9ac430e
141 changes: 121 additions & 20 deletions litex/build/efinix/common.py
Original file line number Diff line number Diff line change
@@ -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,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)
@@ -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,
Dolu1990 marked this conversation as resolved.
Show resolved Hide resolved
38 changes: 27 additions & 11 deletions litex/build/efinix/efinity.py
Original file line number Diff line number Diff line change
@@ -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",
12 changes: 11 additions & 1 deletion litex/build/efinix/ifacewriter.py
Original file line number Diff line number Diff line change
@@ -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:
4 changes: 4 additions & 0 deletions litex/build/efinix/platform.py
Original file line number Diff line number Diff line change
@@ -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]
8 changes: 6 additions & 2 deletions litex/soc/cores/clock/efinix.py
Original file line number Diff line number Diff line change
@@ -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)