c1442296b7146a52cba06d6bb2cbeb90b16b1b36
1 from abc
import abstractproperty
4 from ..lib
.cdc
import ResetSynchronizer
8 __all__
= ["XilinxUltraScalePlatform"]
11 class XilinxUltraScalePlatform(TemplatedPlatform
):
16 The environment is populated by running the script specified in the environment variable
17 ``NMIGEN_ENV_Vivado``, if present.
20 * ``script_after_read``: inserts commands after ``read_xdc`` in Tcl script.
21 * ``script_after_synth``: inserts commands after ``synth_design`` in Tcl script.
22 * ``script_after_place``: inserts commands after ``place_design`` in Tcl script.
23 * ``script_after_route``: inserts commands after ``route_design`` in Tcl script.
24 * ``script_before_bitstream``: inserts commands before ``write_bitstream`` in Tcl script.
25 * ``script_after_bitstream``: inserts commands after ``write_bitstream`` in Tcl script.
26 * ``add_constraints``: inserts commands in XDC file.
27 * ``vivado_opts``: adds extra options for ``vivado``.
30 * ``{{name}}.log``: Vivado log.
31 * ``{{name}}_timing_synth.rpt``: Vivado report.
32 * ``{{name}}_utilization_hierarchical_synth.rpt``: Vivado report.
33 * ``{{name}}_utilization_synth.rpt``: Vivado report.
34 * ``{{name}}_utilization_hierarchical_place.rpt``: Vivado report.
35 * ``{{name}}_utilization_place.rpt``: Vivado report.
36 * ``{{name}}_io.rpt``: Vivado report.
37 * ``{{name}}_control_sets.rpt``: Vivado report.
38 * ``{{name}}_clock_utilization.rpt``: Vivado report.
39 * ``{{name}}_route_status.rpt``: Vivado report.
40 * ``{{name}}_drc.rpt``: Vivado report.
41 * ``{{name}}_methodology.rpt``: Vivado report.
42 * ``{{name}}_timing.rpt``: Vivado report.
43 * ``{{name}}_power.rpt``: Vivado report.
44 * ``{{name}}_route.dcp``: Vivado design checkpoint.
45 * ``{{name}}.bit``: binary bitstream with metadata.
46 * ``{{name}}.bin``: binary bitstream.
51 device
= abstractproperty()
52 package
= abstractproperty()
53 speed
= abstractproperty()
61 **TemplatedPlatform
.build_script_templates
,
62 "build_{{name}}.sh": r
"""
64 set -e{{verbose("x")}}
65 if [ -z "$BASH" ] ; then exec /bin/bash "$0" "$@"; fi
66 [ -n "${{platform._toolchain_env_var}}" ] && . "${{platform._toolchain_env_var}}"
67 {{emit_commands("sh")}}
70 /* {{autogenerated}} */
73 "{{name}}.debug.v": r
"""
74 /* {{autogenerated}} */
75 {{emit_debug_verilog()}}
79 create_project -force -name {{name}} -part {{platform.device}}-{{platform.package}}-{{platform.speed}}{{"-" + platform.grade if platform.grade else ""}}
80 {% for file in platform.iter_extra_files(".v", ".sv", ".vhd", ".vhdl") -%}
81 add_files {{file|tcl_escape}}
85 {% for file in platform.iter_extra_files(".xdc") -%}
86 read_xdc {{file|tcl_escape}}
88 {{get_override("script_after_read")|default("# (script_after_read placeholder)")}}
89 synth_design -top {{name}}
90 foreach cell [get_cells -quiet -hier -filter {nmigen.vivado.false_path == "TRUE"}] {
91 set_false_path -to $cell
93 foreach cell [get_cells -quiet -hier -filter {nmigen.vivado.max_delay != ""}] {
94 set clock [get_clocks -of_objects \
95 [all_fanin -flat -startpoints_only [get_pin $cell/D]]]
96 if {[llength $clock] != 0} {
97 set_max_delay -datapath_only -from $clock \
98 -to [get_cells $cell] [get_property nmigen.vivado.max_delay $cell]
101 {{get_override("script_after_synth")|default("# (script_after_synth placeholder)")}}
102 report_timing_summary -file {{name}}_timing_synth.rpt
103 report_utilization -hierarchical -file {{name}}_utilization_hierachical_synth.rpt
104 report_utilization -file {{name}}_utilization_synth.rpt
107 {{get_override("script_after_place")|default("# (script_after_place placeholder)")}}
108 report_utilization -hierarchical -file {{name}}_utilization_hierarchical_place.rpt
109 report_utilization -file {{name}}_utilization_place.rpt
110 report_io -file {{name}}_io.rpt
111 report_control_sets -verbose -file {{name}}_control_sets.rpt
112 report_clock_utilization -file {{name}}_clock_utilization.rpt
114 {{get_override("script_after_route")|default("# (script_after_route placeholder)")}}
116 report_timing_summary -no_header -no_detailed_paths
117 write_checkpoint -force {{name}}_route.dcp
118 report_route_status -file {{name}}_route_status.rpt
119 report_drc -file {{name}}_drc.rpt
120 report_methodology -file {{name}}_methodology.rpt
121 report_timing_summary -datasheet -max_paths 10 -file {{name}}_timing.rpt
122 report_power -file {{name}}_power.rpt
123 {{get_override("script_before_bitstream")|default("# (script_before_bitstream placeholder)")}}
124 write_bitstream -force -bin_file {{name}}.bit
125 {{get_override("script_after_bitstream")|default("# (script_after_bitstream placeholder)")}}
130 {% for port_name, pin_name, attrs in platform.iter_port_constraints_bits() -%}
131 set_property LOC {{pin_name}} [get_ports {{port_name|tcl_escape}}]
132 {% for attr_name, attr_value in attrs.items() -%}
133 set_property {{attr_name}} {{attr_value|tcl_escape}} [get_ports {{port_name|tcl_escape}}]
136 {% for net_signal, port_signal, frequency in platform.iter_clock_constraints() -%}
137 {% if port_signal is not none -%}
138 create_clock -name {{port_signal.name|ascii_escape}} -period {{1000000000/frequency}} [get_ports {{port_signal.name|tcl_escape}}]
140 create_clock -name {{net_signal.name|ascii_escape}} -period {{1000000000/frequency}} [get_nets {{net_signal|hierarchy("/")|tcl_escape}}]
143 {{get_override("add_constraints")|default("# (add_constraints placeholder)")}}
146 command_templates
= [
148 {{invoke_tool("vivado")}}
149 {{verbose("-verbose")}}
150 {{get_override("vivado_opts")|options}}
157 def create_missing_domain(self
, name
):
158 # Xilinx devices have a global write enable (GWE) signal that asserted during configuraiton
159 # and deasserted once it ends. Because it is an asynchronous signal (GWE is driven by logic
160 # syncronous to configuration clock, which is not used by most designs), even though it is
161 # a low-skew global network, its deassertion may violate a setup/hold constraint with
162 # relation to a user clock. The recommended solution is to use a BUFGCE driven by the EOS
163 # signal. For details, see:
164 # * https://www.xilinx.com/support/answers/44174.html
165 # * https://www.xilinx.com/support/documentation/white_papers/wp272.pdf
166 if name
== "sync" and self
.default_clk
is not None:
167 clk_i
= self
.request(self
.default_clk
).i
168 if self
.default_rst
is not None:
169 rst_i
= self
.request(self
.default_rst
).i
173 m
.submodules
+= Instance("STARTUPE3", o_EOS
=ready
)
174 m
.domains
+= ClockDomain("sync", reset_less
=self
.default_rst
is None)
175 m
.submodules
+= Instance("BUFGCE", i_CE
=ready
, i_I
=clk_i
, o_O
=ClockSignal("sync"))
176 if self
.default_rst
is not None:
177 m
.submodules
.reset_sync
= ResetSynchronizer(rst_i
, domain
="sync")
180 def add_clock_constraint(self
, clock
, frequency
):
181 super().add_clock_constraint(clock
, frequency
)
182 clock
.attrs
["keep"] = "TRUE"
184 def _get_xdr_buffer(self
, m
, pin
, *, i_invert
=False, o_invert
=False):
185 def get_dff(clk
, d
, q
):
186 # SDR I/O is performed by packing a flip-flop into the pad IOB.
187 for bit
in range(len(q
)):
188 m
.submodules
+= Instance("FDCE",
197 def get_iddr(clk
, d
, q1
, q2
):
198 for bit
in range(len(q1
)):
199 m
.submodules
+= Instance("IDDRE1",
200 p_DDR_CLK_EDGE
="SAME_EDGE_PIPELINED",
201 p_IS_C_INVERTED
=0, p_IS_CB_INVERTED
=1,
205 o_Q1
=q1
[bit
], o_Q2
=q2
[bit
]
208 def get_oddr(clk
, d1
, d2
, q
):
209 for bit
in range(len(q
)):
210 m
.submodules
+= Instance("ODDRE1",
211 p_DDR_CLK_EDGE
="SAME_EDGE",
215 i_D1
=d1
[bit
], i_D2
=d2
[bit
],
219 def get_ineg(y
, invert
):
221 a
= Signal
.like(y
, name_suffix
="_n")
227 def get_oneg(a
, invert
):
229 y
= Signal
.like(a
, name_suffix
="_n")
237 pin_i
= get_ineg(pin
.i
, i_invert
)
239 pin_i0
= get_ineg(pin
.i0
, i_invert
)
240 pin_i1
= get_ineg(pin
.i1
, i_invert
)
243 pin_o
= get_oneg(pin
.o
, o_invert
)
245 pin_o0
= get_oneg(pin
.o0
, o_invert
)
246 pin_o1
= get_oneg(pin
.o1
, o_invert
)
250 i
= Signal(pin
.width
, name
="{}_xdr_i".format(pin
.name
))
252 o
= Signal(pin
.width
, name
="{}_xdr_o".format(pin
.name
))
253 if pin
.dir in ("oe", "io"):
254 t
= Signal(1, name
="{}_xdr_t".format(pin
.name
))
261 if pin
.dir in ("oe", "io"):
265 get_dff(pin
.i_clk
, i
, pin_i
)
267 get_dff(pin
.o_clk
, pin_o
, o
)
268 if pin
.dir in ("oe", "io"):
269 get_dff(pin
.o_clk
, ~pin
.oe
, t
)
272 get_iddr(pin
.i_clk
, i
, pin_i0
, pin_i1
)
274 get_oddr(pin
.o_clk
, pin_o0
, pin_o1
, o
)
275 if pin
.dir in ("oe", "io"):
276 get_dff(pin
.o_clk
, ~pin
.oe
, t
)
282 def get_input(self
, pin
, port
, attrs
, invert
):
283 self
._check
_feature
("single-ended input", pin
, attrs
,
284 valid_xdrs
=(0, 1, 2), valid_attrs
=True)
286 i
, o
, t
= self
._get
_xdr
_buffer
(m
, pin
, i_invert
=invert
)
287 for bit
in range(len(port
)):
288 m
.submodules
["{}_{}".format(pin
.name
, bit
)] = Instance("IBUF",
294 def get_output(self
, pin
, port
, attrs
, invert
):
295 self
._check
_feature
("single-ended output", pin
, attrs
,
296 valid_xdrs
=(0, 1, 2), valid_attrs
=True)
298 i
, o
, t
= self
._get
_xdr
_buffer
(m
, pin
, o_invert
=invert
)
299 for bit
in range(len(port
)):
300 m
.submodules
["{}_{}".format(pin
.name
, bit
)] = Instance("OBUF",
306 def get_tristate(self
, pin
, port
, attrs
, invert
):
307 self
._check
_feature
("single-ended tristate", pin
, attrs
,
308 valid_xdrs
=(0, 1, 2), valid_attrs
=True)
310 i
, o
, t
= self
._get
_xdr
_buffer
(m
, pin
, o_invert
=invert
)
311 for bit
in range(len(port
)):
312 m
.submodules
["{}_{}".format(pin
.name
, bit
)] = Instance("OBUFT",
319 def get_input_output(self
, pin
, port
, attrs
, invert
):
320 self
._check
_feature
("single-ended input/output", pin
, attrs
,
321 valid_xdrs
=(0, 1, 2), valid_attrs
=True)
323 i
, o
, t
= self
._get
_xdr
_buffer
(m
, pin
, i_invert
=invert
, o_invert
=invert
)
324 for bit
in range(len(port
)):
325 m
.submodules
["{}_{}".format(pin
.name
, bit
)] = Instance("IOBUF",
333 def get_diff_input(self
, pin
, p_port
, n_port
, attrs
, invert
):
334 self
._check
_feature
("differential input", pin
, attrs
,
335 valid_xdrs
=(0, 1, 2), valid_attrs
=True)
337 i
, o
, t
= self
._get
_xdr
_buffer
(m
, pin
, i_invert
=invert
)
338 for bit
in range(len(p_port
)):
339 m
.submodules
["{}_{}".format(pin
.name
, bit
)] = Instance("IBUFDS",
340 i_I
=p_port
[bit
], i_IB
=n_port
[bit
],
345 def get_diff_output(self
, pin
, p_port
, n_port
, attrs
, invert
):
346 self
._check
_feature
("differential output", pin
, attrs
,
347 valid_xdrs
=(0, 1, 2), valid_attrs
=True)
349 i
, o
, t
= self
._get
_xdr
_buffer
(m
, pin
, o_invert
=invert
)
350 for bit
in range(len(p_port
)):
351 m
.submodules
["{}_{}".format(pin
.name
, bit
)] = Instance("OBUFDS",
353 o_O
=p_port
[bit
], o_OB
=n_port
[bit
]
357 def get_diff_tristate(self
, pin
, p_port
, n_port
, attrs
, invert
):
358 self
._check
_feature
("differential tristate", pin
, attrs
,
359 valid_xdrs
=(0, 1, 2), valid_attrs
=True)
361 i
, o
, t
= self
._get
_xdr
_buffer
(m
, pin
, o_invert
=invert
)
362 for bit
in range(len(p_port
)):
363 m
.submodules
["{}_{}".format(pin
.name
, bit
)] = Instance("OBUFTDS",
366 o_O
=p_port
[bit
], o_OB
=n_port
[bit
]
370 def get_diff_input_output(self
, pin
, p_port
, n_port
, attrs
, invert
):
371 self
._check
_feature
("differential input/output", pin
, attrs
,
372 valid_xdrs
=(0, 1, 2), valid_attrs
=True)
374 i
, o
, t
= self
._get
_xdr
_buffer
(m
, pin
, i_invert
=invert
, o_invert
=invert
)
375 for bit
in range(len(p_port
)):
376 m
.submodules
["{}_{}".format(pin
.name
, bit
)] = Instance("IOBUFDS",
380 io_IO
=p_port
[bit
], io_IOB
=n_port
[bit
]
384 # The synchronizer implementations below apply two separate but related timing constraints.
386 # First, the ASYNC_REG attribute prevents inference of shift registers from synchronizer FFs,
387 # and constraints the FFs to be placed as close as possible, ideally in one CLB. This attribute
388 # only affects the synchronizer FFs themselves.
390 # Second, the nmigen.vivado.false_path or nmigen.vivado.max_delay attribute affects the path
391 # into the synchronizer. If maximum input delay is specified, a datapath-only maximum delay
392 # constraint is applied, limiting routing delay (and therefore skew) at the synchronizer input.
393 # Otherwise, a false path constraint is used to omit the input path from the timing analysis.
395 def get_ff_sync(self
, ff_sync
):
397 flops
= [Signal(ff_sync
.i
.shape(), name
="stage{}".format(index
),
398 reset
=ff_sync
._reset
, reset_less
=ff_sync
._reset
_less
,
399 attrs
={"ASYNC_REG": "TRUE"})
400 for index
in range(ff_sync
._stages
)]
401 if ff_sync
._max
_input
_delay
is None:
402 flops
[0].attrs
["nmigen.vivado.false_path"] = "TRUE"
404 flops
[0].attrs
["nmigen.vivado.max_delay"] = str(ff_sync
._max
_input
_delay
* 1e9
)
405 for i
, o
in zip((ff_sync
.i
, *flops
), flops
):
406 m
.d
[ff_sync
._o
_domain
] += o
.eq(i
)
407 m
.d
.comb
+= ff_sync
.o
.eq(flops
[-1])
410 def get_async_ff_sync(self
, async_ff_sync
):
412 m
.domains
+= ClockDomain("async_ff", async_reset
=True, local
=True)
413 flops
= [Signal(1, name
="stage{}".format(index
), reset
=1,
414 attrs
={"ASYNC_REG": "TRUE"})
415 for index
in range(async_ff_sync
._stages
)]
416 if async_ff_sync
._max
_input
_delay
is None:
417 flops
[0].attrs
["nmigen.vivado.false_path"] = "TRUE"
419 flops
[0].attrs
["nmigen.vivado.max_delay"] = str(async_ff_sync
._max
_input
_delay
* 1e9
)
420 for i
, o
in zip((0, *flops
), flops
):
421 m
.d
.async_ff
+= o
.eq(i
)
423 if async_ff_sync
._edge
== "pos":
424 m
.d
.comb
+= ResetSignal("async_ff").eq(async_ff_sync
.i
)
426 m
.d
.comb
+= ResetSignal("async_ff").eq(~async_ff_sync
.i
)
429 ClockSignal("async_ff").eq(ClockSignal(async_ff_sync
._domain
)),
430 async_ff_sync
.o
.eq(flops
[-1])