1 from abc
import abstractproperty
4 from ..lib
.cdc
import ResetSynchronizer
8 __all__
= ["Xilinx7SeriesPlatform"]
11 class Xilinx7SeriesPlatform(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()
57 return "{}{}-{}".format(self
.device
, self
.package
, self
.speed
)
59 required_tools
= ["vivado"]
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._part}}
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("STARTUPE2", o_EOS
=ready
)
174 m
.domains
+= ClockDomain("sync", reset_less
=self
.default_rst
is None)
175 # Actually use BUFGCTRL configured as BUFGCE, since using BUFGCE causes sim/synth
176 # mismatches with Vivado 2019.2, and the suggested workaround (SIM_DEVICE parameter)
177 # breaks Vivado 2017.4.
178 m
.submodules
+= Instance("BUFGCTRL",
179 i_I0
=clk_i
, i_S0
=C(1, 1), i_CE0
=ready
, i_IGNORE0
=C(0, 1),
180 i_I1
=C(1, 1), i_S1
=C(0, 1), i_CE1
=C(0, 1), i_IGNORE1
=C(1, 1),
181 o_O
=ClockSignal("sync")
183 if self
.default_rst
is not None:
184 m
.submodules
.reset_sync
= ResetSynchronizer(rst_i
, domain
="sync")
187 def add_clock_constraint(self
, clock
, frequency
):
188 super().add_clock_constraint(clock
, frequency
)
189 clock
.attrs
["keep"] = "TRUE"
191 def _get_xdr_buffer(self
, m
, pin
, *, i_invert
=False, o_invert
=False):
192 def get_dff(clk
, d
, q
):
193 # SDR I/O is performed by packing a flip-flop into the pad IOB.
194 for bit
in range(len(q
)):
195 m
.submodules
+= Instance("FDCE",
204 def get_iddr(clk
, d
, q1
, q2
):
205 for bit
in range(len(q1
)):
206 m
.submodules
+= Instance("IDDR",
207 p_DDR_CLK_EDGE
="SAME_EDGE_PIPELINED",
209 p_INIT_Q1
=0, p_INIT_Q2
=0,
212 i_S
=Const(0), i_R
=Const(0),
214 o_Q1
=q1
[bit
], o_Q2
=q2
[bit
]
217 def get_oddr(clk
, d1
, d2
, q
):
218 for bit
in range(len(q
)):
219 m
.submodules
+= Instance("ODDR",
220 p_DDR_CLK_EDGE
="SAME_EDGE",
225 i_S
=Const(0), i_R
=Const(0),
226 i_D1
=d1
[bit
], i_D2
=d2
[bit
],
230 def get_ineg(y
, invert
):
232 a
= Signal
.like(y
, name_suffix
="_n")
238 def get_oneg(a
, invert
):
240 y
= Signal
.like(a
, name_suffix
="_n")
248 pin_i
= get_ineg(pin
.i
, i_invert
)
250 pin_i0
= get_ineg(pin
.i0
, i_invert
)
251 pin_i1
= get_ineg(pin
.i1
, i_invert
)
254 pin_o
= get_oneg(pin
.o
, o_invert
)
256 pin_o0
= get_oneg(pin
.o0
, o_invert
)
257 pin_o1
= get_oneg(pin
.o1
, o_invert
)
261 i
= Signal(pin
.width
, name
="{}_xdr_i".format(pin
.name
))
263 o
= Signal(pin
.width
, name
="{}_xdr_o".format(pin
.name
))
264 if pin
.dir in ("oe", "io"):
265 t
= Signal(1, name
="{}_xdr_t".format(pin
.name
))
272 if pin
.dir in ("oe", "io"):
276 get_dff(pin
.i_clk
, i
, pin_i
)
278 get_dff(pin
.o_clk
, pin_o
, o
)
279 if pin
.dir in ("oe", "io"):
280 get_dff(pin
.o_clk
, ~pin
.oe
, t
)
283 get_iddr(pin
.i_clk
, i
, pin_i0
, pin_i1
)
285 get_oddr(pin
.o_clk
, pin_o0
, pin_o1
, o
)
286 if pin
.dir in ("oe", "io"):
287 get_dff(pin
.o_clk
, ~pin
.oe
, t
)
293 def get_input(self
, pin
, port
, attrs
, invert
):
294 self
._check
_feature
("single-ended input", pin
, attrs
,
295 valid_xdrs
=(0, 1, 2), valid_attrs
=True)
297 i
, o
, t
= self
._get
_xdr
_buffer
(m
, pin
, i_invert
=invert
)
298 for bit
in range(pin
.width
):
299 m
.submodules
["{}_{}".format(pin
.name
, bit
)] = Instance("IBUF",
305 def get_output(self
, pin
, port
, attrs
, invert
):
306 self
._check
_feature
("single-ended output", pin
, attrs
,
307 valid_xdrs
=(0, 1, 2), valid_attrs
=True)
309 i
, o
, t
= self
._get
_xdr
_buffer
(m
, pin
, o_invert
=invert
)
310 for bit
in range(pin
.width
):
311 m
.submodules
["{}_{}".format(pin
.name
, bit
)] = Instance("OBUF",
317 def get_tristate(self
, pin
, port
, attrs
, invert
):
318 self
._check
_feature
("single-ended tristate", pin
, attrs
,
319 valid_xdrs
=(0, 1, 2), valid_attrs
=True)
321 i
, o
, t
= self
._get
_xdr
_buffer
(m
, pin
, o_invert
=invert
)
322 for bit
in range(pin
.width
):
323 m
.submodules
["{}_{}".format(pin
.name
, bit
)] = Instance("OBUFT",
330 def get_input_output(self
, pin
, port
, attrs
, invert
):
331 self
._check
_feature
("single-ended input/output", pin
, attrs
,
332 valid_xdrs
=(0, 1, 2), valid_attrs
=True)
334 i
, o
, t
= self
._get
_xdr
_buffer
(m
, pin
, i_invert
=invert
, o_invert
=invert
)
335 for bit
in range(pin
.width
):
336 m
.submodules
["{}_{}".format(pin
.name
, bit
)] = Instance("IOBUF",
344 def get_diff_input(self
, pin
, port
, attrs
, invert
):
345 self
._check
_feature
("differential input", pin
, attrs
,
346 valid_xdrs
=(0, 1, 2), valid_attrs
=True)
348 i
, o
, t
= self
._get
_xdr
_buffer
(m
, pin
, i_invert
=invert
)
349 for bit
in range(pin
.width
):
350 m
.submodules
["{}_{}".format(pin
.name
, bit
)] = Instance("IBUFDS",
351 i_I
=port
.p
[bit
], i_IB
=port
.n
[bit
],
356 def get_diff_output(self
, pin
, port
, attrs
, invert
):
357 self
._check
_feature
("differential output", pin
, attrs
,
358 valid_xdrs
=(0, 1, 2), valid_attrs
=True)
360 i
, o
, t
= self
._get
_xdr
_buffer
(m
, pin
, o_invert
=invert
)
361 for bit
in range(pin
.width
):
362 m
.submodules
["{}_{}".format(pin
.name
, bit
)] = Instance("OBUFDS",
364 o_O
=port
.p
[bit
], o_OB
=port
.n
[bit
]
368 def get_diff_tristate(self
, pin
, port
, attrs
, invert
):
369 self
._check
_feature
("differential tristate", pin
, attrs
,
370 valid_xdrs
=(0, 1, 2), valid_attrs
=True)
372 i
, o
, t
= self
._get
_xdr
_buffer
(m
, pin
, o_invert
=invert
)
373 for bit
in range(pin
.width
):
374 m
.submodules
["{}_{}".format(pin
.name
, bit
)] = Instance("OBUFTDS",
377 o_O
=port
.p
[bit
], o_OB
=port
.n
[bit
]
381 def get_diff_input_output(self
, pin
, port
, attrs
, invert
):
382 self
._check
_feature
("differential input/output", pin
, attrs
,
383 valid_xdrs
=(0, 1, 2), valid_attrs
=True)
385 i
, o
, t
= self
._get
_xdr
_buffer
(m
, pin
, i_invert
=invert
, o_invert
=invert
)
386 for bit
in range(pin
.width
):
387 m
.submodules
["{}_{}".format(pin
.name
, bit
)] = Instance("IOBUFDS",
391 io_IO
=port
.p
[bit
], io_IOB
=port
.n
[bit
]
395 # The synchronizer implementations below apply two separate but related timing constraints.
397 # First, the ASYNC_REG attribute prevents inference of shift registers from synchronizer FFs,
398 # and constraints the FFs to be placed as close as possible, ideally in one CLB. This attribute
399 # only affects the synchronizer FFs themselves.
401 # Second, the nmigen.vivado.false_path or nmigen.vivado.max_delay attribute affects the path
402 # into the synchronizer. If maximum input delay is specified, a datapath-only maximum delay
403 # constraint is applied, limiting routing delay (and therefore skew) at the synchronizer input.
404 # Otherwise, a false path constraint is used to omit the input path from the timing analysis.
406 def get_ff_sync(self
, ff_sync
):
408 flops
= [Signal(ff_sync
.i
.shape(), name
="stage{}".format(index
),
409 reset
=ff_sync
._reset
, reset_less
=ff_sync
._reset
_less
,
410 attrs
={"ASYNC_REG": "TRUE"})
411 for index
in range(ff_sync
._stages
)]
412 if ff_sync
._max
_input
_delay
is None:
413 flops
[0].attrs
["nmigen.vivado.false_path"] = "TRUE"
415 flops
[0].attrs
["nmigen.vivado.max_delay"] = str(ff_sync
._max
_input
_delay
* 1e9
)
416 for i
, o
in zip((ff_sync
.i
, *flops
), flops
):
417 m
.d
[ff_sync
._o
_domain
] += o
.eq(i
)
418 m
.d
.comb
+= ff_sync
.o
.eq(flops
[-1])
421 def get_async_ff_sync(self
, async_ff_sync
):
423 m
.domains
+= ClockDomain("async_ff", async_reset
=True, local
=True)
424 flops
= [Signal(1, name
="stage{}".format(index
), reset
=1,
425 attrs
={"ASYNC_REG": "TRUE"})
426 for index
in range(async_ff_sync
._stages
)]
427 if async_ff_sync
._max
_input
_delay
is None:
428 flops
[0].attrs
["nmigen.vivado.false_path"] = "TRUE"
430 flops
[0].attrs
["nmigen.vivado.max_delay"] = str(async_ff_sync
._max
_input
_delay
* 1e9
)
431 for i
, o
in zip((0, *flops
), flops
):
432 m
.d
.async_ff
+= o
.eq(i
)
434 if async_ff_sync
._edge
== "pos":
435 m
.d
.comb
+= ResetSignal("async_ff").eq(async_ff_sync
.i
)
437 m
.d
.comb
+= ResetSignal("async_ff").eq(~async_ff_sync
.i
)
440 ClockSignal("async_ff").eq(ClockSignal(async_ff_sync
._domain
)),
441 async_ff_sync
.o
.eq(flops
[-1])