1 from abc
import abstractproperty
4 from ..lib
.cdc
import ResetSynchronizer
8 __all__
= ["Xilinx7SeriesPlatform"]
11 class Xilinx7SeriesPlatform(TemplatedPlatform
):
19 The environment is populated by running the script specified in the environment variable
20 ``NMIGEN_ENV_Vivado``, if present.
23 * ``script_after_read``: inserts commands after ``read_xdc`` in Tcl script.
24 * ``script_after_synth``: inserts commands after ``synth_design`` in Tcl script.
25 * ``script_after_place``: inserts commands after ``place_design`` in Tcl script.
26 * ``script_after_route``: inserts commands after ``route_design`` in Tcl script.
27 * ``script_before_bitstream``: inserts commands before ``write_bitstream`` in Tcl script.
28 * ``script_after_bitstream``: inserts commands after ``write_bitstream`` in Tcl script.
29 * ``add_constraints``: inserts commands in XDC file.
30 * ``vivado_opts``: adds extra options for ``vivado``.
33 * ``{{name}}.log``: Vivado log.
34 * ``{{name}}_timing_synth.rpt``: Vivado report.
35 * ``{{name}}_utilization_hierarchical_synth.rpt``: Vivado report.
36 * ``{{name}}_utilization_synth.rpt``: Vivado report.
37 * ``{{name}}_utilization_hierarchical_place.rpt``: Vivado report.
38 * ``{{name}}_utilization_place.rpt``: Vivado report.
39 * ``{{name}}_io.rpt``: Vivado report.
40 * ``{{name}}_control_sets.rpt``: Vivado report.
41 * ``{{name}}_clock_utilization.rpt``: Vivado report.
42 * ``{{name}}_route_status.rpt``: Vivado report.
43 * ``{{name}}_drc.rpt``: Vivado report.
44 * ``{{name}}_methodology.rpt``: Vivado report.
45 * ``{{name}}_timing.rpt``: Vivado report.
46 * ``{{name}}_power.rpt``: Vivado report.
47 * ``{{name}}_route.dcp``: Vivado design checkpoint.
48 * ``{{name}}.bit``: binary bitstream with metadata.
49 * ``{{name}}.bin``: binary bitstream.
62 The environment is populated by running the script specified in the environment variable
63 ``NMIGEN_ENV_Symbiflow``, if present.
66 * ``add_constraints``: inserts commands in XDC file.
69 toolchain
= None # selected when creating platform
71 device
= abstractproperty()
72 package
= abstractproperty()
73 speed
= abstractproperty()
77 return "{}{}-{}".format(self
.device
, self
.package
, self
.speed
)
81 _vivado_required_tools
= ["vivado"]
82 _vivado_file_templates
= {
83 **TemplatedPlatform
.build_script_templates
,
84 "build_{{name}}.sh": r
"""
86 set -e{{verbose("x")}}
87 if [ -z "$BASH" ] ; then exec /bin/bash "$0" "$@"; fi
88 [ -n "${{platform._toolchain_env_var}}" ] && . "${{platform._toolchain_env_var}}"
89 {{emit_commands("sh")}}
92 /* {{autogenerated}} */
95 "{{name}}.debug.v": r
"""
96 /* {{autogenerated}} */
97 {{emit_debug_verilog()}}
101 create_project -force -name {{name}} -part {{platform._part}}
102 {% for file in platform.iter_files(".v", ".sv", ".vhd", ".vhdl") -%}
103 add_files {{file|tcl_escape}}
106 read_xdc {{name}}.xdc
107 {% for file in platform.iter_files(".xdc") -%}
108 read_xdc {{file|tcl_escape}}
110 {{get_override("script_after_read")|default("# (script_after_read placeholder)")}}
111 synth_design -top {{name}}
112 foreach cell [get_cells -quiet -hier -filter {nmigen.vivado.false_path == "TRUE"}] {
113 set_false_path -to $cell
115 foreach cell [get_cells -quiet -hier -filter {nmigen.vivado.max_delay != ""}] {
116 set clock [get_clocks -of_objects \
117 [all_fanin -flat -startpoints_only [get_pin $cell/D]]]
118 if {[llength $clock] != 0} {
119 set_max_delay -datapath_only -from $clock \
120 -to [get_cells $cell] [get_property nmigen.vivado.max_delay $cell]
123 {{get_override("script_after_synth")|default("# (script_after_synth placeholder)")}}
124 report_timing_summary -file {{name}}_timing_synth.rpt
125 report_utilization -hierarchical -file {{name}}_utilization_hierachical_synth.rpt
126 report_utilization -file {{name}}_utilization_synth.rpt
129 {{get_override("script_after_place")|default("# (script_after_place placeholder)")}}
130 report_utilization -hierarchical -file {{name}}_utilization_hierarchical_place.rpt
131 report_utilization -file {{name}}_utilization_place.rpt
132 report_io -file {{name}}_io.rpt
133 report_control_sets -verbose -file {{name}}_control_sets.rpt
134 report_clock_utilization -file {{name}}_clock_utilization.rpt
136 {{get_override("script_after_route")|default("# (script_after_route placeholder)")}}
138 report_timing_summary -no_header -no_detailed_paths
139 write_checkpoint -force {{name}}_route.dcp
140 report_route_status -file {{name}}_route_status.rpt
141 report_drc -file {{name}}_drc.rpt
142 report_methodology -file {{name}}_methodology.rpt
143 report_timing_summary -datasheet -max_paths 10 -file {{name}}_timing.rpt
144 report_power -file {{name}}_power.rpt
145 {{get_override("script_before_bitstream")|default("# (script_before_bitstream placeholder)")}}
146 write_bitstream -force -bin_file {{name}}.bit
147 {{get_override("script_after_bitstream")|default("# (script_after_bitstream placeholder)")}}
152 {% for port_name, pin_name, attrs in platform.iter_port_constraints_bits() -%}
153 set_property LOC {{pin_name}} [get_ports {{port_name|tcl_escape}}]
154 {% for attr_name, attr_value in attrs.items() -%}
155 set_property {{attr_name}} {{attr_value|tcl_escape}} [get_ports {{port_name|tcl_escape}}]
158 {% for net_signal, port_signal, frequency in platform.iter_clock_constraints() -%}
159 {% if port_signal is not none -%}
160 create_clock -name {{port_signal.name|ascii_escape}} -period {{1000000000/frequency}} [get_ports {{port_signal.name|tcl_escape}}]
162 create_clock -name {{net_signal.name|ascii_escape}} -period {{1000000000/frequency}} [get_nets {{net_signal|hierarchy("/")|tcl_escape}}]
165 {{get_override("add_constraints")|default("# (add_constraints placeholder)")}}
168 _vivado_command_templates
= [
170 {{invoke_tool("vivado")}}
171 {{verbose("-verbose")}}
172 {{get_override("vivado_opts")|options}}
179 # Symbiflow templates
181 _symbiflow_part_map
= {
182 "xc7a35ticsg324-1L": "xc7a35tcsg324-1", # Arty-A7
185 _symbiflow_required_tools
= [
193 _symbiflow_file_templates
= {
194 **TemplatedPlatform
.build_script_templates
,
196 /* {{autogenerated}} */
199 "{{name}}.debug.v": r
"""
200 /* {{autogenerated}} */
201 {{emit_debug_verilog()}}
205 {% for port_name, pin_name, attrs in platform.iter_port_constraints_bits() -%}
206 set_io {{port_name}} {{pin_name}}
211 {% for port_name, pin_name, attrs in platform.iter_port_constraints_bits() -%}
212 {% for attr_name, attr_value in attrs.items() -%}
213 set_property {{attr_name}} {{attr_value}} [get_ports {{port_name|tcl_escape}} }]
216 {{get_override("add_constraints")|default("# (add_constraints placeholder)")}}
220 {% for net_signal, port_signal, frequency in platform.iter_clock_constraints() -%}
221 {% if port_signal is none -%}
222 create_clock -period {{1000000000/frequency}} {{net_signal.name|ascii_escape}}
227 _symbiflow_command_templates
= [
229 {{invoke_tool("synth")}}
231 -v {% for file in platform.iter_files(".v", ".sv", ".vhd", ".vhdl") -%} {{file}} {% endfor %} {{name}}.v
232 -p {{platform._symbiflow_part_map.get(platform._part, platform._part)}}
236 {{invoke_tool("pack")}}
238 -P {{platform._symbiflow_part_map.get(platform._part, platform._part)}}
242 {{invoke_tool("place")}}
246 -P {{platform._symbiflow_part_map.get(platform._part, platform._part)}}
250 {{invoke_tool("route")}}
252 -P {{platform._symbiflow_part_map.get(platform._part, platform._part)}}
256 {{invoke_tool("write_fasm")}}
258 -P {{platform._symbiflow_part_map.get(platform._part, platform._part)}}
261 {{invoke_tool("write_bitstream")}}
263 -p {{platform._symbiflow_part_map.get(platform._part, platform._part)}}
270 def __init__(self
, *, toolchain
="Vivado"):
273 assert toolchain
in ("Vivado", "Symbiflow")
274 self
.toolchain
= toolchain
277 def required_tools(self
):
278 if self
.toolchain
== "Vivado":
279 return self
._vivado
_required
_tools
280 if self
.toolchain
== "Symbiflow":
281 return self
._symbiflow
_required
_tools
285 def file_templates(self
):
286 if self
.toolchain
== "Vivado":
287 return self
._vivado
_file
_templates
288 if self
.toolchain
== "Symbiflow":
289 return self
._symbiflow
_file
_templates
293 def command_templates(self
):
294 if self
.toolchain
== "Vivado":
295 return self
._vivado
_command
_templates
296 if self
.toolchain
== "Symbiflow":
297 return self
._symbiflow
_command
_templates
300 def create_missing_domain(self
, name
):
301 # Xilinx devices have a global write enable (GWE) signal that asserted during configuraiton
302 # and deasserted once it ends. Because it is an asynchronous signal (GWE is driven by logic
303 # syncronous to configuration clock, which is not used by most designs), even though it is
304 # a low-skew global network, its deassertion may violate a setup/hold constraint with
305 # relation to a user clock. The recommended solution is to use a BUFGCE driven by the EOS
306 # signal. For details, see:
307 # * https://www.xilinx.com/support/answers/44174.html
308 # * https://www.xilinx.com/support/documentation/white_papers/wp272.pdf
309 if name
== "sync" and self
.default_clk
is not None:
310 clk_i
= self
.request(self
.default_clk
).i
311 if self
.default_rst
is not None:
312 rst_i
= self
.request(self
.default_rst
).i
316 if self
.toolchain
== "Vivado":
318 m
.submodules
+= Instance("STARTUPE2", o_EOS
=ready
)
319 m
.domains
+= ClockDomain("sync", reset_less
=self
.default_rst
is None)
320 # Actually use BUFGCTRL configured as BUFGCE, since using BUFGCE causes
321 # sim/synth mismatches with Vivado 2019.2, and the suggested workaround
322 # (SIM_DEVICE parameter) breaks Vivado 2017.4.
323 m
.submodules
+= Instance("BUFGCTRL",
324 p_SIM_DEVICE
="7SERIES",
325 i_I0
=clk_i
, i_S0
=C(1, 1), i_CE0
=ready
, i_IGNORE0
=C(0, 1),
326 i_I1
=C(1, 1), i_S1
=C(0, 1), i_CE1
=C(0, 1), i_IGNORE1
=C(1, 1),
327 o_O
=ClockSignal("sync")
329 elif self
.toolchain
== "Symbiflow":
330 cd_sync
= ClockDomain("sync", reset_less
=self
.default_rst
is None)
332 m
.submodules
+= Instance("BUFG", i_I
=clk_i
, o_O
=cd_sync
.clk
)
333 self
.add_clock_constraint(cd_sync
.clk
, self
.default_clk_frequency
)
337 if self
.default_rst
is not None:
338 m
.submodules
.reset_sync
= ResetSynchronizer(rst_i
, domain
="sync")
341 def add_clock_constraint(self
, clock
, frequency
):
342 super().add_clock_constraint(clock
, frequency
)
343 clock
.attrs
["keep"] = "TRUE"
345 def _get_xdr_buffer(self
, m
, pin
, *, i_invert
=False, o_invert
=False):
346 def get_dff(clk
, d
, q
):
347 # SDR I/O is performed by packing a flip-flop into the pad IOB.
348 for bit
in range(len(q
)):
349 m
.submodules
+= Instance("FDCE",
358 def get_iddr(clk
, d
, q1
, q2
):
359 for bit
in range(len(q1
)):
360 m
.submodules
+= Instance("IDDR",
361 p_DDR_CLK_EDGE
="SAME_EDGE_PIPELINED",
363 p_INIT_Q1
=0, p_INIT_Q2
=0,
366 i_S
=Const(0), i_R
=Const(0),
368 o_Q1
=q1
[bit
], o_Q2
=q2
[bit
]
371 def get_oddr(clk
, d1
, d2
, q
):
372 for bit
in range(len(q
)):
373 m
.submodules
+= Instance("ODDR",
374 p_DDR_CLK_EDGE
="SAME_EDGE",
379 i_S
=Const(0), i_R
=Const(0),
380 i_D1
=d1
[bit
], i_D2
=d2
[bit
],
384 def get_ineg(y
, invert
):
386 a
= Signal
.like(y
, name_suffix
="_n")
392 def get_oneg(a
, invert
):
394 y
= Signal
.like(a
, name_suffix
="_n")
402 pin_i
= get_ineg(pin
.i
, i_invert
)
404 pin_i0
= get_ineg(pin
.i0
, i_invert
)
405 pin_i1
= get_ineg(pin
.i1
, i_invert
)
408 pin_o
= get_oneg(pin
.o
, o_invert
)
410 pin_o0
= get_oneg(pin
.o0
, o_invert
)
411 pin_o1
= get_oneg(pin
.o1
, o_invert
)
415 i
= Signal(pin
.width
, name
="{}_xdr_i".format(pin
.name
))
417 o
= Signal(pin
.width
, name
="{}_xdr_o".format(pin
.name
))
418 if pin
.dir in ("oe", "io"):
419 t
= Signal(1, name
="{}_xdr_t".format(pin
.name
))
426 if pin
.dir in ("oe", "io"):
430 get_dff(pin
.i_clk
, i
, pin_i
)
432 get_dff(pin
.o_clk
, pin_o
, o
)
433 if pin
.dir in ("oe", "io"):
434 get_dff(pin
.o_clk
, ~pin
.oe
, t
)
437 get_iddr(pin
.i_clk
, i
, pin_i0
, pin_i1
)
439 get_oddr(pin
.o_clk
, pin_o0
, pin_o1
, o
)
440 if pin
.dir in ("oe", "io"):
441 get_dff(pin
.o_clk
, ~pin
.oe
, t
)
447 def get_input(self
, pin
, port
, attrs
, invert
):
448 self
._check
_feature
("single-ended input", pin
, attrs
,
449 valid_xdrs
=(0, 1, 2), valid_attrs
=True)
451 i
, o
, t
= self
._get
_xdr
_buffer
(m
, pin
, i_invert
=invert
)
452 for bit
in range(pin
.width
):
453 m
.submodules
["{}_{}".format(pin
.name
, bit
)] = Instance("IBUF",
459 def get_output(self
, pin
, port
, attrs
, invert
):
460 self
._check
_feature
("single-ended output", pin
, attrs
,
461 valid_xdrs
=(0, 1, 2), valid_attrs
=True)
463 i
, o
, t
= self
._get
_xdr
_buffer
(m
, pin
, o_invert
=invert
)
464 if self
.toolchain
== "Vivado":
465 for bit
in range(pin
.width
):
466 m
.submodules
["{}_{}".format(pin
.name
, bit
)] = Instance("OBUF",
470 elif self
.toolchain
== "Symbiflow":
471 m
.d
.comb
+= port
.eq(self
._invert
_if
(invert
, o
))
476 def get_tristate(self
, pin
, port
, attrs
, invert
):
477 if self
.toolchain
== "Symbiflow":
478 return super().get_tristate(pin
, port
, attrs
, invert
)
480 self
._check
_feature
("single-ended tristate", pin
, attrs
,
481 valid_xdrs
=(0, 1, 2), valid_attrs
=True)
483 i
, o
, t
= self
._get
_xdr
_buffer
(m
, pin
, o_invert
=invert
)
484 for bit
in range(pin
.width
):
485 m
.submodules
["{}_{}".format(pin
.name
, bit
)] = Instance("OBUFT",
492 def get_input_output(self
, pin
, port
, attrs
, invert
):
493 if self
.toolchain
== "Symbiflow":
494 return super().get_input_output(pin
, port
, attrs
, invert
)
496 self
._check
_feature
("single-ended input/output", pin
, attrs
,
497 valid_xdrs
=(0, 1, 2), valid_attrs
=True)
499 i
, o
, t
= self
._get
_xdr
_buffer
(m
, pin
, i_invert
=invert
, o_invert
=invert
)
500 for bit
in range(pin
.width
):
501 m
.submodules
["{}_{}".format(pin
.name
, bit
)] = Instance("IOBUF",
509 def get_diff_input(self
, pin
, port
, attrs
, invert
):
510 if self
.toolchain
== "Symbiflow":
511 return super().get_diff_input(pin
, port
, attrs
, invert
)
513 self
._check
_feature
("differential input", pin
, attrs
,
514 valid_xdrs
=(0, 1, 2), valid_attrs
=True)
516 i
, o
, t
= self
._get
_xdr
_buffer
(m
, pin
, i_invert
=invert
)
517 for bit
in range(pin
.width
):
518 m
.submodules
["{}_{}".format(pin
.name
, bit
)] = Instance("IBUFDS",
519 i_I
=port
.p
[bit
], i_IB
=port
.n
[bit
],
524 def get_diff_output(self
, pin
, port
, attrs
, invert
):
525 if self
.toolchain
== "Symbiflow":
526 return super().get_diff_output(pin
, port
, attrs
, invert
)
528 self
._check
_feature
("differential output", pin
, attrs
,
529 valid_xdrs
=(0, 1, 2), valid_attrs
=True)
531 i
, o
, t
= self
._get
_xdr
_buffer
(m
, pin
, o_invert
=invert
)
532 for bit
in range(pin
.width
):
533 m
.submodules
["{}_{}".format(pin
.name
, bit
)] = Instance("OBUFDS",
535 o_O
=port
.p
[bit
], o_OB
=port
.n
[bit
]
539 def get_diff_tristate(self
, pin
, port
, attrs
, invert
):
540 if self
.toolchain
== "Symbiflow":
541 return super().get_diff_tristate(pin
, port
, attrs
, invert
)
543 self
._check
_feature
("differential tristate", pin
, attrs
,
544 valid_xdrs
=(0, 1, 2), valid_attrs
=True)
546 i
, o
, t
= self
._get
_xdr
_buffer
(m
, pin
, o_invert
=invert
)
547 for bit
in range(pin
.width
):
548 m
.submodules
["{}_{}".format(pin
.name
, bit
)] = Instance("OBUFTDS",
551 o_O
=port
.p
[bit
], o_OB
=port
.n
[bit
]
555 def get_diff_input_output(self
, pin
, port
, attrs
, invert
):
556 if self
.toolchain
== "Symbiflow":
557 return super().get_diff_input_output(pin
, port
, attrs
, invert
)
559 self
._check
_feature
("differential input/output", pin
, attrs
,
560 valid_xdrs
=(0, 1, 2), valid_attrs
=True)
562 i
, o
, t
= self
._get
_xdr
_buffer
(m
, pin
, i_invert
=invert
, o_invert
=invert
)
563 for bit
in range(pin
.width
):
564 m
.submodules
["{}_{}".format(pin
.name
, bit
)] = Instance("IOBUFDS",
568 io_IO
=port
.p
[bit
], io_IOB
=port
.n
[bit
]
572 # The synchronizer implementations below apply two separate but related timing constraints.
574 # First, the ASYNC_REG attribute prevents inference of shift registers from synchronizer FFs,
575 # and constraints the FFs to be placed as close as possible, ideally in one CLB. This attribute
576 # only affects the synchronizer FFs themselves.
578 # Second, the nmigen.vivado.false_path or nmigen.vivado.max_delay attribute affects the path
579 # into the synchronizer. If maximum input delay is specified, a datapath-only maximum delay
580 # constraint is applied, limiting routing delay (and therefore skew) at the synchronizer input.
581 # Otherwise, a false path constraint is used to omit the input path from the timing analysis.
583 def get_ff_sync(self
, ff_sync
):
585 flops
= [Signal(ff_sync
.i
.shape(), name
="stage{}".format(index
),
586 reset
=ff_sync
._reset
, reset_less
=ff_sync
._reset
_less
,
587 attrs
={"ASYNC_REG": "TRUE"})
588 for index
in range(ff_sync
._stages
)]
589 if ff_sync
._max
_input
_delay
is None:
590 flops
[0].attrs
["nmigen.vivado.false_path"] = "TRUE"
592 flops
[0].attrs
["nmigen.vivado.max_delay"] = str(ff_sync
._max
_input
_delay
* 1e9
)
593 for i
, o
in zip((ff_sync
.i
, *flops
), flops
):
594 m
.d
[ff_sync
._o
_domain
] += o
.eq(i
)
595 m
.d
.comb
+= ff_sync
.o
.eq(flops
[-1])
598 def get_async_ff_sync(self
, async_ff_sync
):
600 m
.domains
+= ClockDomain("async_ff", async_reset
=True, local
=True)
601 flops
= [Signal(1, name
="stage{}".format(index
), reset
=1,
602 attrs
={"ASYNC_REG": "TRUE"})
603 for index
in range(async_ff_sync
._stages
)]
604 if async_ff_sync
._max
_input
_delay
is None:
605 flops
[0].attrs
["nmigen.vivado.false_path"] = "TRUE"
607 flops
[0].attrs
["nmigen.vivado.max_delay"] = str(async_ff_sync
._max
_input
_delay
* 1e9
)
608 for i
, o
in zip((0, *flops
), flops
):
609 m
.d
.async_ff
+= o
.eq(i
)
611 if async_ff_sync
._edge
== "pos":
612 m
.d
.comb
+= ResetSignal("async_ff").eq(async_ff_sync
.i
)
614 m
.d
.comb
+= ResetSignal("async_ff").eq(~async_ff_sync
.i
)
617 ClockSignal("async_ff").eq(ClockSignal(async_ff_sync
._o
_domain
)),
618 async_ff_sync
.o
.eq(flops
[-1])