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()
55 required_tools
= ["vivado"]
57 **TemplatedPlatform
.build_script_templates
,
58 "build_{{name}}.sh": r
"""
60 set -e{{verbose("x")}}
61 if [ -z "$BASH" ] ; then exec /bin/bash "$0" "$@"; fi
62 [ -n "${{platform._toolchain_env_var}}" ] && . "${{platform._toolchain_env_var}}"
63 {{emit_commands("sh")}}
66 /* {{autogenerated}} */
69 "{{name}}.debug.v": r
"""
70 /* {{autogenerated}} */
71 {{emit_debug_verilog()}}
75 create_project -force -name {{name}} -part {{platform.device}}-{{platform.package}}-{{platform.speed}}
76 {% for file in platform.iter_extra_files(".v", ".sv", ".vhd", ".vhdl") -%}
77 add_files {{file|tcl_escape}}
81 {% for file in platform.iter_extra_files(".xdc") -%}
82 read_xdc {{file|tcl_escape}}
84 {{get_override("script_after_read")|default("# (script_after_read placeholder)")}}
85 synth_design -top {{name}}
86 foreach cell [get_cells -quiet -hier -filter {nmigen.vivado.false_path == "TRUE"}] {
87 set_false_path -to $cell
89 foreach cell [get_cells -quiet -hier -filter {nmigen.vivado.max_delay != ""}] {
90 set clock [get_clocks -of_objects \
91 [all_fanin -flat -startpoints_only [get_pin $cell/D]]]
92 if {[llength $clock] != 0} {
93 set_max_delay -datapath_only -from $clock \
94 -to [get_cells $cell] [get_property nmigen.vivado.max_delay $cell]
97 {{get_override("script_after_synth")|default("# (script_after_synth placeholder)")}}
98 report_timing_summary -file {{name}}_timing_synth.rpt
99 report_utilization -hierarchical -file {{name}}_utilization_hierachical_synth.rpt
100 report_utilization -file {{name}}_utilization_synth.rpt
103 {{get_override("script_after_place")|default("# (script_after_place placeholder)")}}
104 report_utilization -hierarchical -file {{name}}_utilization_hierarchical_place.rpt
105 report_utilization -file {{name}}_utilization_place.rpt
106 report_io -file {{name}}_io.rpt
107 report_control_sets -verbose -file {{name}}_control_sets.rpt
108 report_clock_utilization -file {{name}}_clock_utilization.rpt
110 {{get_override("script_after_route")|default("# (script_after_route placeholder)")}}
112 report_timing_summary -no_header -no_detailed_paths
113 write_checkpoint -force {{name}}_route.dcp
114 report_route_status -file {{name}}_route_status.rpt
115 report_drc -file {{name}}_drc.rpt
116 report_methodology -file {{name}}_methodology.rpt
117 report_timing_summary -datasheet -max_paths 10 -file {{name}}_timing.rpt
118 report_power -file {{name}}_power.rpt
119 {{get_override("script_before_bitstream")|default("# (script_before_bitstream placeholder)")}}
120 write_bitstream -force -bin_file {{name}}.bit
121 {{get_override("script_after_bitstream")|default("# (script_after_bitstream placeholder)")}}
126 {% for port_name, pin_name, attrs in platform.iter_port_constraints_bits() -%}
127 set_property LOC {{pin_name}} [get_ports {{port_name|tcl_escape}}]
128 {% for attr_name, attr_value in attrs.items() -%}
129 set_property {{attr_name}} {{attr_value|tcl_escape}} [get_ports {{port_name|tcl_escape}}]
132 {% for net_signal, port_signal, frequency in platform.iter_clock_constraints() -%}
133 {% if port_signal is not none -%}
134 create_clock -name {{port_signal.name|ascii_escape}} -period {{1000000000/frequency}} [get_ports {{port_signal.name|tcl_escape}}]
136 create_clock -name {{net_signal.name|ascii_escape}} -period {{1000000000/frequency}} [get_nets {{net_signal|hierarchy("/")|tcl_escape}}]
139 {{get_override("add_constraints")|default("# (add_constraints placeholder)")}}
142 command_templates
= [
144 {{invoke_tool("vivado")}}
145 {{verbose("-verbose")}}
146 {{get_override("vivado_opts")|options}}
153 def create_missing_domain(self
, name
):
154 # Xilinx devices have a global write enable (GWE) signal that asserted during configuraiton
155 # and deasserted once it ends. Because it is an asynchronous signal (GWE is driven by logic
156 # syncronous to configuration clock, which is not used by most designs), even though it is
157 # a low-skew global network, its deassertion may violate a setup/hold constraint with
158 # relation to a user clock. The recommended solution is to use a BUFGCE driven by the EOS
159 # signal. For details, see:
160 # * https://www.xilinx.com/support/answers/44174.html
161 # * https://www.xilinx.com/support/documentation/white_papers/wp272.pdf
162 if name
== "sync" and self
.default_clk
is not None:
163 clk_i
= self
.request(self
.default_clk
).i
164 if self
.default_rst
is not None:
165 rst_i
= self
.request(self
.default_rst
).i
169 m
.submodules
+= Instance("STARTUPE3", o_EOS
=ready
)
170 m
.domains
+= ClockDomain("sync", reset_less
=self
.default_rst
is None)
171 # Actually use BUFGCTRL configured as BUFGCE, since using BUFGCE causes sim/synth
172 # mismatches with Vivado 2019.2, and the suggested workaround (SIM_DEVICE parameter)
173 # breaks Vivado 2017.4.
174 m
.submodules
+= Instance("BUFGCTRL",
175 i_I0
=clk_i
, i_S0
=C(1, 1), i_CE0
=ready
, i_IGNORE0
=C(0, 1),
176 i_I1
=C(1, 1), i_S1
=C(0, 1), i_CE1
=C(0, 1), i_IGNORE1
=C(1, 1),
177 o_O
=ClockSignal("sync")
179 if self
.default_rst
is not None:
180 m
.submodules
.reset_sync
= ResetSynchronizer(rst_i
, domain
="sync")
183 def add_clock_constraint(self
, clock
, frequency
):
184 super().add_clock_constraint(clock
, frequency
)
185 clock
.attrs
["keep"] = "TRUE"
187 def _get_xdr_buffer(self
, m
, pin
, *, i_invert
=False, o_invert
=False):
188 def get_dff(clk
, d
, q
):
189 # SDR I/O is performed by packing a flip-flop into the pad IOB.
190 for bit
in range(len(q
)):
191 m
.submodules
+= Instance("FDCE",
200 def get_iddr(clk
, d
, q1
, q2
):
201 for bit
in range(len(q1
)):
202 m
.submodules
+= Instance("IDDRE1",
203 p_DDR_CLK_EDGE
="SAME_EDGE_PIPELINED",
204 p_IS_C_INVERTED
=0, p_IS_CB_INVERTED
=1,
208 o_Q1
=q1
[bit
], o_Q2
=q2
[bit
]
211 def get_oddr(clk
, d1
, d2
, q
):
212 for bit
in range(len(q
)):
213 m
.submodules
+= Instance("ODDRE1",
214 p_DDR_CLK_EDGE
="SAME_EDGE",
218 i_D1
=d1
[bit
], i_D2
=d2
[bit
],
222 def get_ineg(y
, invert
):
224 a
= Signal
.like(y
, name_suffix
="_n")
230 def get_oneg(a
, invert
):
232 y
= Signal
.like(a
, name_suffix
="_n")
240 pin_i
= get_ineg(pin
.i
, i_invert
)
242 pin_i0
= get_ineg(pin
.i0
, i_invert
)
243 pin_i1
= get_ineg(pin
.i1
, i_invert
)
246 pin_o
= get_oneg(pin
.o
, o_invert
)
248 pin_o0
= get_oneg(pin
.o0
, o_invert
)
249 pin_o1
= get_oneg(pin
.o1
, o_invert
)
253 i
= Signal(pin
.width
, name
="{}_xdr_i".format(pin
.name
))
255 o
= Signal(pin
.width
, name
="{}_xdr_o".format(pin
.name
))
256 if pin
.dir in ("oe", "io"):
257 t
= Signal(1, name
="{}_xdr_t".format(pin
.name
))
264 if pin
.dir in ("oe", "io"):
268 get_dff(pin
.i_clk
, i
, pin_i
)
270 get_dff(pin
.o_clk
, pin_o
, o
)
271 if pin
.dir in ("oe", "io"):
272 get_dff(pin
.o_clk
, ~pin
.oe
, t
)
275 get_iddr(pin
.i_clk
, i
, pin_i0
, pin_i1
)
277 get_oddr(pin
.o_clk
, pin_o0
, pin_o1
, o
)
278 if pin
.dir in ("oe", "io"):
279 get_dff(pin
.o_clk
, ~pin
.oe
, t
)
285 def get_input(self
, pin
, port
, attrs
, invert
):
286 self
._check
_feature
("single-ended input", pin
, attrs
,
287 valid_xdrs
=(0, 1, 2), valid_attrs
=True)
289 i
, o
, t
= self
._get
_xdr
_buffer
(m
, pin
, i_invert
=invert
)
290 for bit
in range(pin
.width
):
291 m
.submodules
["{}_{}".format(pin
.name
, bit
)] = Instance("IBUF",
297 def get_output(self
, pin
, port
, attrs
, invert
):
298 self
._check
_feature
("single-ended output", pin
, attrs
,
299 valid_xdrs
=(0, 1, 2), valid_attrs
=True)
301 i
, o
, t
= self
._get
_xdr
_buffer
(m
, pin
, o_invert
=invert
)
302 for bit
in range(pin
.width
):
303 m
.submodules
["{}_{}".format(pin
.name
, bit
)] = Instance("OBUF",
309 def get_tristate(self
, pin
, port
, attrs
, invert
):
310 self
._check
_feature
("single-ended tristate", pin
, attrs
,
311 valid_xdrs
=(0, 1, 2), valid_attrs
=True)
313 i
, o
, t
= self
._get
_xdr
_buffer
(m
, pin
, o_invert
=invert
)
314 for bit
in range(pin
.width
):
315 m
.submodules
["{}_{}".format(pin
.name
, bit
)] = Instance("OBUFT",
322 def get_input_output(self
, pin
, port
, attrs
, invert
):
323 self
._check
_feature
("single-ended input/output", pin
, attrs
,
324 valid_xdrs
=(0, 1, 2), valid_attrs
=True)
326 i
, o
, t
= self
._get
_xdr
_buffer
(m
, pin
, i_invert
=invert
, o_invert
=invert
)
327 for bit
in range(pin
.width
):
328 m
.submodules
["{}_{}".format(pin
.name
, bit
)] = Instance("IOBUF",
336 def get_diff_input(self
, pin
, port
, attrs
, invert
):
337 self
._check
_feature
("differential input", pin
, attrs
,
338 valid_xdrs
=(0, 1, 2), valid_attrs
=True)
340 i
, o
, t
= self
._get
_xdr
_buffer
(m
, pin
, i_invert
=invert
)
341 for bit
in range(pin
.width
):
342 m
.submodules
["{}_{}".format(pin
.name
, bit
)] = Instance("IBUFDS",
343 i_I
=port
.p
[bit
], i_IB
=port
.n
[bit
],
348 def get_diff_output(self
, pin
, port
, attrs
, invert
):
349 self
._check
_feature
("differential output", pin
, attrs
,
350 valid_xdrs
=(0, 1, 2), valid_attrs
=True)
352 i
, o
, t
= self
._get
_xdr
_buffer
(m
, pin
, o_invert
=invert
)
353 for bit
in range(pin
.width
):
354 m
.submodules
["{}_{}".format(pin
.name
, bit
)] = Instance("OBUFDS",
356 o_O
=port
.p
[bit
], o_OB
=port
.n
[bit
]
360 def get_diff_tristate(self
, pin
, port
, attrs
, invert
):
361 self
._check
_feature
("differential tristate", pin
, attrs
,
362 valid_xdrs
=(0, 1, 2), valid_attrs
=True)
364 i
, o
, t
= self
._get
_xdr
_buffer
(m
, pin
, o_invert
=invert
)
365 for bit
in range(pin
.width
):
366 m
.submodules
["{}_{}".format(pin
.name
, bit
)] = Instance("OBUFTDS",
369 o_O
=port
.p
[bit
], o_OB
=port
.n
[bit
]
373 def get_diff_input_output(self
, pin
, port
, attrs
, invert
):
374 self
._check
_feature
("differential input/output", pin
, attrs
,
375 valid_xdrs
=(0, 1, 2), valid_attrs
=True)
377 i
, o
, t
= self
._get
_xdr
_buffer
(m
, pin
, i_invert
=invert
, o_invert
=invert
)
378 for bit
in range(pin
.width
):
379 m
.submodules
["{}_{}".format(pin
.name
, bit
)] = Instance("IOBUFDS",
383 io_IO
=port
.p
[bit
], io_IOB
=port
.n
[bit
]
387 # The synchronizer implementations below apply two separate but related timing constraints.
389 # First, the ASYNC_REG attribute prevents inference of shift registers from synchronizer FFs,
390 # and constraints the FFs to be placed as close as possible, ideally in one CLB. This attribute
391 # only affects the synchronizer FFs themselves.
393 # Second, the nmigen.vivado.false_path or nmigen.vivado.max_delay attribute affects the path
394 # into the synchronizer. If maximum input delay is specified, a datapath-only maximum delay
395 # constraint is applied, limiting routing delay (and therefore skew) at the synchronizer input.
396 # Otherwise, a false path constraint is used to omit the input path from the timing analysis.
398 def get_ff_sync(self
, ff_sync
):
400 flops
= [Signal(ff_sync
.i
.shape(), name
="stage{}".format(index
),
401 reset
=ff_sync
._reset
, reset_less
=ff_sync
._reset
_less
,
402 attrs
={"ASYNC_REG": "TRUE"})
403 for index
in range(ff_sync
._stages
)]
404 if ff_sync
._max
_input
_delay
is None:
405 flops
[0].attrs
["nmigen.vivado.false_path"] = "TRUE"
407 flops
[0].attrs
["nmigen.vivado.max_delay"] = str(ff_sync
._max
_input
_delay
* 1e9
)
408 for i
, o
in zip((ff_sync
.i
, *flops
), flops
):
409 m
.d
[ff_sync
._o
_domain
] += o
.eq(i
)
410 m
.d
.comb
+= ff_sync
.o
.eq(flops
[-1])
413 def get_async_ff_sync(self
, async_ff_sync
):
415 m
.domains
+= ClockDomain("async_ff", async_reset
=True, local
=True)
416 flops
= [Signal(1, name
="stage{}".format(index
), reset
=1,
417 attrs
={"ASYNC_REG": "TRUE"})
418 for index
in range(async_ff_sync
._stages
)]
419 if async_ff_sync
._max
_input
_delay
is None:
420 flops
[0].attrs
["nmigen.vivado.false_path"] = "TRUE"
422 flops
[0].attrs
["nmigen.vivado.max_delay"] = str(async_ff_sync
._max
_input
_delay
* 1e9
)
423 for i
, o
in zip((0, *flops
), flops
):
424 m
.d
.async_ff
+= o
.eq(i
)
426 if async_ff_sync
._edge
== "pos":
427 m
.d
.comb
+= ResetSignal("async_ff").eq(async_ff_sync
.i
)
429 m
.d
.comb
+= ResetSignal("async_ff").eq(~async_ff_sync
.i
)
432 ClockSignal("async_ff").eq(ClockSignal(async_ff_sync
._o
_domain
)),
433 async_ff_sync
.o
.eq(flops
[-1])