
Do this to make sure all buffers, tristate/differential or not, are instantiated the exact same way, and are subject to the same set of toolchain bugs, if any.
335 lines
13 KiB
Python
335 lines
13 KiB
Python
from abc import abstractproperty
|
|
|
|
from ..hdl.ast import *
|
|
from ..hdl.dsl import *
|
|
from ..hdl.ir import *
|
|
from ..build import *
|
|
|
|
|
|
__all__ = ["Xilinx7SeriesPlatform"]
|
|
|
|
|
|
class Xilinx7SeriesPlatform(TemplatedPlatform):
|
|
"""
|
|
Required tools:
|
|
* ``vivado``
|
|
|
|
Available overrides:
|
|
* ``script_after_read``: inserts commands after ``read_xdc`` in Tcl script.
|
|
* ``script_after_synth``: inserts commands after ``synth_design`` in Tcl script.
|
|
* ``script_after_place``: inserts commands after ``place_design`` in Tcl script.
|
|
* ``script_after_route``: inserts commands after ``route_design`` in Tcl script.
|
|
* ``script_before_bitstream``: inserts commands before ``write_bitstream`` in Tcl script.
|
|
* ``script_after_bitstream``: inserts commands after ``write_bitstream`` in Tcl script.
|
|
* ``add_constraints``: inserts commands in XDC file.
|
|
* ``vivado_opts``: adds extra options for Vivado.
|
|
|
|
Build products:
|
|
* ``{{name}}.log``: Vivado log.
|
|
* ``{{name}}_timing_synth.rpt``: Vivado report.
|
|
* ``{{name}}_utilization_hierarchical_synth.rpt``: Vivado report.
|
|
* ``{{name}}_utilization_synth.rpt``: Vivado report.
|
|
* ``{{name}}_utilization_hierarchical_place.rpt``: Vivado report.
|
|
* ``{{name}}_utilization_place.rpt``: Vivado report.
|
|
* ``{{name}}_io.rpt``: Vivado report.
|
|
* ``{{name}}_control_sets.rpt``: Vivado report.
|
|
* ``{{name}}_clock_utilization.rpt``: Vivado report.
|
|
* ``{{name}}_route_status.rpt``: Vivado report.
|
|
* ``{{name}}_drc.rpt``: Vivado report.
|
|
* ``{{name}}_timing.rpt``: Vivado report.
|
|
* ``{{name}}_power.rpt``: Vivado report.
|
|
* ``{{name}}_route.dcp``: Vivado design checkpoint.
|
|
* ``{{name}}.bit``: binary bitstream.
|
|
"""
|
|
|
|
device = abstractproperty()
|
|
package = abstractproperty()
|
|
speedgrade = abstractproperty()
|
|
|
|
file_templates = {
|
|
**TemplatedPlatform.build_script_templates,
|
|
"{{name}}.v": r"""
|
|
/* {{autogenerated}} */
|
|
{{emit_design("verilog")}}
|
|
""",
|
|
"{{name}}.tcl": r"""
|
|
# {{autogenerated}}
|
|
create_project -force -name {{name}} -part {{platform.device}}{{platform.package}}-{{platform.speedgrade}}
|
|
{% for file in platform.extra_files %}
|
|
{% if file.endswith((".v", ".sv")) -%}
|
|
add_files {{file}}
|
|
{% endif %}
|
|
{% endfor %}
|
|
add_files {{name}}.v
|
|
read_xdc {{name}}.xdc
|
|
{{get_override("script_after_read")|default("# (script_after_read placeholder)")}}
|
|
synth_design -top {{name}} -part {{platform.device}}{{platform.package}}-{{platform.speedgrade}}
|
|
{{get_override("script_after_synth")|default("# (script_after_synth placeholder)")}}
|
|
report_timing_summary -file {{name}}_timing_synth.rpt
|
|
report_utilization -hierarchical -file {{name}}_utilization_hierachical_synth.rpt
|
|
report_utilization -file {{name}}_utilization_synth.rpt
|
|
opt_design
|
|
place_design
|
|
{{get_override("script_after_place")|default("# (script_after_place placeholder)")}}
|
|
report_utilization -hierarchical -file {{name}}_utilization_hierarchical_place.rpt
|
|
report_utilization -file {{name}}_utilization_place.rpt
|
|
report_io -file {{name}}_io.rpt
|
|
report_control_sets -verbose -file {{name}}_control_sets.rpt
|
|
report_clock_utilization -file {{name}}_clock_utilization.rpt
|
|
route_design
|
|
{{get_override("script_after_route")|default("# (script_after_route placeholder)")}}
|
|
phys_opt_design
|
|
report_timing_summary -no_header -no_detailed_paths
|
|
write_checkpoint -force {{name}}_route.dcp
|
|
report_route_status -file {{name}}_route_status.rpt
|
|
report_drc -file {{name}}_drc.rpt
|
|
report_timing_summary -datasheet -max_paths 10 -file {{name}}_timing.rpt
|
|
report_power -file {{name}}_power.rpt
|
|
{{get_override("script_before_bitstream")|default("# (script_before_bitstream placeholder)")}}
|
|
write_bitstream -force {{name}}.bit
|
|
{{get_override("script_after_bitstream")|default("# (script_after_bitstream placeholder)")}}
|
|
quit
|
|
""",
|
|
"{{name}}.xdc": r"""
|
|
# {{autogenerated}}
|
|
{% for port_name, pin_name, attrs in platform.iter_port_constraints_bits() -%}
|
|
set_property LOC {{pin_name}} [get_ports {{port_name}}]
|
|
{% for attr_name, attr_value in attrs.items() -%}
|
|
set_property {{attr_name}} {{attr_value}} [get_ports {{port_name}}]
|
|
{% endfor %}
|
|
{% endfor %}
|
|
{% for signal, frequency in platform.iter_clock_constraints() -%}
|
|
create_clock -name {{signal.name}} -period {{1000000000/frequency}} [get_ports {{signal.name}}]
|
|
{% endfor %}
|
|
{{get_override("add_constraints")|default("# (add_constraints placeholder)")}}
|
|
"""
|
|
}
|
|
command_templates = [
|
|
r"""
|
|
{{get_tool("vivado")}}
|
|
{{verbose("-verbose")}}
|
|
{{get_override("vivado_opts")|join(" ")}}
|
|
-mode batch
|
|
-log {{name}}.log
|
|
-source {{name}}.tcl
|
|
"""
|
|
]
|
|
|
|
def _get_xdr_buffer(self, m, pin, i_invert=None, o_invert=None):
|
|
def get_dff(clk, d, q):
|
|
# SDR I/O is performed by packing a flip-flop into the pad IOB.
|
|
for bit in range(len(q)):
|
|
_q = Signal()
|
|
_q.attrs["IOB"] = "TRUE"
|
|
m.submodules += Instance("FDCE",
|
|
i_C=clk,
|
|
i_CE=Const(1),
|
|
i_CLR=Const(0),
|
|
i_D=d[bit],
|
|
o_Q=_q
|
|
)
|
|
m.d.comb += q[bit].eq(_q)
|
|
|
|
def get_iddr(clk, d, q1, q2):
|
|
for bit in range(len(q1)):
|
|
m.submodules += Instance("IDDR",
|
|
p_DDR_CLK_EDGE="SAME_EDGE_PIPELINED",
|
|
p_SRTYPE="ASYNC",
|
|
p_INIT_Q1=0, p_INIT_Q2=0,
|
|
i_C=clk,
|
|
i_CE=Const(1),
|
|
i_S=Const(0), i_R=Const(0),
|
|
i_D=d[bit],
|
|
o_Q1=q1[bit], o_Q2=q2[bit]
|
|
)
|
|
|
|
def get_oddr(clk, d1, d2, q):
|
|
for bit in range(len(q)):
|
|
m.submodules += Instance("ODDR",
|
|
p_DDR_CLK_EDGE="SAME_EDGE",
|
|
p_SRTYPE="ASYNC",
|
|
p_INIT=0,
|
|
i_C=clk,
|
|
i_CE=Const(1),
|
|
i_S=Const(0), i_R=Const(0),
|
|
i_D1=d1[bit], i_D2=d2[bit],
|
|
o_Q=q[bit]
|
|
)
|
|
|
|
def get_ixor(y, invert):
|
|
if invert is None:
|
|
return y
|
|
else:
|
|
a = Signal.like(y, name_suffix="_x{}".format(1 if invert else 0))
|
|
for bit in range(len(y)):
|
|
m.submodules += Instance("LUT1",
|
|
p_INIT=0b01 if invert else 0b10,
|
|
i_I0=a[bit],
|
|
o_O=y[bit]
|
|
)
|
|
return a
|
|
|
|
def get_oxor(a, invert):
|
|
if invert is None:
|
|
return a
|
|
else:
|
|
y = Signal.like(a, name_suffix="_x{}".format(1 if invert else 0))
|
|
for bit in range(len(a)):
|
|
m.submodules += Instance("LUT1",
|
|
p_INIT=0b01 if invert else 0b10,
|
|
i_I0=a[bit],
|
|
o_O=y[bit]
|
|
)
|
|
return y
|
|
|
|
if "i" in pin.dir:
|
|
if pin.xdr < 2:
|
|
pin_i = get_ixor(pin.i, i_invert)
|
|
elif pin.xdr == 2:
|
|
pin_i0 = get_ixor(pin.i0, i_invert)
|
|
pin_i1 = get_ixor(pin.i1, i_invert)
|
|
if "o" in pin.dir:
|
|
if pin.xdr < 2:
|
|
pin_o = get_oxor(pin.o, o_invert)
|
|
elif pin.xdr == 2:
|
|
pin_o0 = get_oxor(pin.o0, o_invert)
|
|
pin_o1 = get_oxor(pin.o1, o_invert)
|
|
|
|
i = o = t = None
|
|
if "i" in pin.dir:
|
|
i = Signal(pin.width, name="{}_xdr_i".format(pin.name))
|
|
if "o" in pin.dir:
|
|
o = Signal(pin.width, name="{}_xdr_o".format(pin.name))
|
|
if pin.dir in ("oe", "io"):
|
|
t = Signal(1, name="{}_xdr_t".format(pin.name))
|
|
|
|
if pin.xdr == 0:
|
|
if "i" in pin.dir:
|
|
i = pin_i
|
|
if "o" in pin.dir:
|
|
o = pin_o
|
|
if pin.dir in ("oe", "io"):
|
|
t = ~pin.oe
|
|
elif pin.xdr == 1:
|
|
if "i" in pin.dir:
|
|
get_dff(pin.i_clk, i, pin_i)
|
|
if "o" in pin.dir:
|
|
get_dff(pin.o_clk, pin_o, o)
|
|
if pin.dir in ("oe", "io"):
|
|
get_dff(pin.o_clk, ~pin.oe, t)
|
|
elif pin.xdr == 2:
|
|
if "i" in pin.dir:
|
|
get_iddr(pin.i_clk, i, pin_i0, pin_i1)
|
|
if "o" in pin.dir:
|
|
get_oddr(pin.o_clk, pin_o0, pin_o1, o)
|
|
if pin.dir in ("oe", "io"):
|
|
get_dff(pin.o_clk, ~pin.oe, t)
|
|
else:
|
|
assert False
|
|
|
|
return (i, o, t)
|
|
|
|
def get_input(self, pin, port, attrs, invert):
|
|
self._check_feature("single-ended input", pin, attrs,
|
|
valid_xdrs=(0, 1, 2), valid_attrs=True)
|
|
m = Module()
|
|
i, o, t = self._get_xdr_buffer(m, pin, i_invert=True if invert else None)
|
|
for bit in range(len(port)):
|
|
m.submodules += Instance("IBUF",
|
|
i_I=port[bit],
|
|
o_O=i[bit]
|
|
)
|
|
return m
|
|
|
|
def get_output(self, pin, port, attrs, invert):
|
|
self._check_feature("single-ended output", pin, attrs,
|
|
valid_xdrs=(0, 1, 2), valid_attrs=True)
|
|
m = Module()
|
|
i, o, t = self._get_xdr_buffer(m, pin, o_invert=True if invert else None)
|
|
for bit in range(len(port)):
|
|
m.submodules += Instance("OBUF",
|
|
i_I=o[bit],
|
|
o_O=port[bit]
|
|
)
|
|
return m
|
|
|
|
def get_tristate(self, pin, port, attrs, invert):
|
|
self._check_feature("single-ended tristate", pin, attrs,
|
|
valid_xdrs=(0, 1, 2), valid_attrs=True)
|
|
m = Module()
|
|
i, o, t = self._get_xdr_buffer(m, pin, o_invert=True if invert else None)
|
|
for bit in range(len(port)):
|
|
m.submodules += Instance("OBUFT",
|
|
i_T=t,
|
|
i_I=o[bit],
|
|
o_O=port[bit]
|
|
)
|
|
return m
|
|
|
|
def get_input_output(self, pin, port, attrs, invert):
|
|
self._check_feature("single-ended input/output", pin, attrs,
|
|
valid_xdrs=(0, 1, 2), valid_attrs=True)
|
|
m = Module()
|
|
i, o, t = self._get_xdr_buffer(m, pin, i_invert=True if invert else None,
|
|
o_invert=True if invert else None)
|
|
for bit in range(len(port)):
|
|
m.submodules += Instance("IOBUF",
|
|
i_T=t,
|
|
i_I=o[bit],
|
|
o_O=i[bit],
|
|
io_IO=port[bit]
|
|
)
|
|
return m
|
|
|
|
def get_diff_input(self, pin, p_port, n_port, attrs, invert):
|
|
self._check_feature("differential input", pin, attrs,
|
|
valid_xdrs=(0, 1, 2), valid_attrs=True)
|
|
m = Module()
|
|
i, o, t = self._get_xdr_buffer(m, pin, i_invert=True if invert else None)
|
|
for bit in range(len(p_port)):
|
|
m.submodules += Instance("IBUFDS",
|
|
i_I=p_port[bit], i_IB=n_port[bit],
|
|
o_O=i[bit]
|
|
)
|
|
return m
|
|
|
|
def get_diff_output(self, pin, p_port, n_port, attrs, invert):
|
|
self._check_feature("differential output", pin, attrs,
|
|
valid_xdrs=(0, 1, 2), valid_attrs=True)
|
|
m = Module()
|
|
i, o, t = self._get_xdr_buffer(m, pin, o_invert=True if invert else None)
|
|
for bit in range(len(p_port)):
|
|
m.submodules += Instance("OBUFDS",
|
|
i_I=o[bit],
|
|
o_O=p_port[bit], o_OB=n_port[bit]
|
|
)
|
|
return m
|
|
|
|
def get_diff_tristate(self, pin, p_port, n_port, attrs, invert):
|
|
self._check_feature("differential tristate", pin, attrs,
|
|
valid_xdrs=(0, 1, 2), valid_attrs=True)
|
|
m = Module()
|
|
i, o, t = self._get_xdr_buffer(m, pin, o_invert=True if invert else None)
|
|
for bit in range(len(p_port)):
|
|
m.submodules += Instance("OBUFTDS",
|
|
i_T=t,
|
|
i_I=o[bit],
|
|
o_O=p_port[bit], o_OB=n_port[bit]
|
|
)
|
|
return m
|
|
|
|
def get_diff_input_output(self, pin, p_port, n_port, attrs, invert):
|
|
self._check_feature("differential input/output", pin, attrs,
|
|
valid_xdrs=(0, 1, 2), valid_attrs=True)
|
|
m = Module()
|
|
i, o, t = self._get_xdr_buffer(m, pin, i_invert=True if invert else None,
|
|
o_invert=True if invert else None)
|
|
for bit in range(len(p_port)):
|
|
m.submodules += Instance("IOBUFDS",
|
|
i_T=t,
|
|
i_I=o[bit],
|
|
o_O=i[bit],
|
|
io_IO=p_port[bit], io_IOB=n_port[bit]
|
|
)
|
|
return m
|