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_extra_files(".v", ".sv", ".vhd", ".vhdl") -%}
103 add_files {{file|tcl_escape}}
106 read_xdc {{name}}.xdc
107 {% for file in platform.iter_extra_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_extra_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 sim/synth
321 # mismatches with Vivado 2019.2, and the suggested workaround (SIM_DEVICE parameter)
322 # breaks Vivado 2017.4.
323 m
.submodules
+= Instance("BUFGCTRL",
324 i_I0
=clk_i
, i_S0
=C(1, 1), i_CE0
=ready
, i_IGNORE0
=C(0, 1),
325 i_I1
=C(1, 1), i_S1
=C(0, 1), i_CE1
=C(0, 1), i_IGNORE1
=C(1, 1),
326 o_O
=ClockSignal("sync")
328 elif self
.toolchain
== "Symbiflow":
329 cd_sync
= ClockDomain("sync", reset_less
=self
.default_rst
is None)
331 m
.submodules
+= Instance("BUFG", i_I
=clk_i
, o_O
=cd_sync
.clk
)
332 self
.add_clock_constraint(cd_sync
.clk
, self
.default_clk_frequency
)
336 if self
.default_rst
is not None:
337 m
.submodules
.reset_sync
= ResetSynchronizer(rst_i
, domain
="sync")
340 def add_clock_constraint(self
, clock
, frequency
):
341 super().add_clock_constraint(clock
, frequency
)
342 clock
.attrs
["keep"] = "TRUE"
344 def _get_xdr_buffer(self
, m
, pin
, *, i_invert
=False, o_invert
=False):
345 def get_dff(clk
, d
, q
):
346 # SDR I/O is performed by packing a flip-flop into the pad IOB.
347 for bit
in range(len(q
)):
348 m
.submodules
+= Instance("FDCE",
357 def get_iddr(clk
, d
, q1
, q2
):
358 for bit
in range(len(q1
)):
359 m
.submodules
+= Instance("IDDR",
360 p_DDR_CLK_EDGE
="SAME_EDGE_PIPELINED",
362 p_INIT_Q1
=0, p_INIT_Q2
=0,
365 i_S
=Const(0), i_R
=Const(0),
367 o_Q1
=q1
[bit
], o_Q2
=q2
[bit
]
370 def get_oddr(clk
, d1
, d2
, q
):
371 for bit
in range(len(q
)):
372 m
.submodules
+= Instance("ODDR",
373 p_DDR_CLK_EDGE
="SAME_EDGE",
378 i_S
=Const(0), i_R
=Const(0),
379 i_D1
=d1
[bit
], i_D2
=d2
[bit
],
383 def get_ineg(y
, invert
):
385 a
= Signal
.like(y
, name_suffix
="_n")
391 def get_oneg(a
, invert
):
393 y
= Signal
.like(a
, name_suffix
="_n")
401 pin_i
= get_ineg(pin
.i
, i_invert
)
403 pin_i0
= get_ineg(pin
.i0
, i_invert
)
404 pin_i1
= get_ineg(pin
.i1
, i_invert
)
407 pin_o
= get_oneg(pin
.o
, o_invert
)
409 pin_o0
= get_oneg(pin
.o0
, o_invert
)
410 pin_o1
= get_oneg(pin
.o1
, o_invert
)
414 i
= Signal(pin
.width
, name
="{}_xdr_i".format(pin
.name
))
416 o
= Signal(pin
.width
, name
="{}_xdr_o".format(pin
.name
))
417 if pin
.dir in ("oe", "io"):
418 t
= Signal(1, name
="{}_xdr_t".format(pin
.name
))
425 if pin
.dir in ("oe", "io"):
429 get_dff(pin
.i_clk
, i
, pin_i
)
431 get_dff(pin
.o_clk
, pin_o
, o
)
432 if pin
.dir in ("oe", "io"):
433 get_dff(pin
.o_clk
, ~pin
.oe
, t
)
436 get_iddr(pin
.i_clk
, i
, pin_i0
, pin_i1
)
438 get_oddr(pin
.o_clk
, pin_o0
, pin_o1
, o
)
439 if pin
.dir in ("oe", "io"):
440 get_dff(pin
.o_clk
, ~pin
.oe
, t
)
446 def get_input(self
, pin
, port
, attrs
, invert
):
447 self
._check
_feature
("single-ended input", pin
, attrs
,
448 valid_xdrs
=(0, 1, 2), valid_attrs
=True)
450 i
, o
, t
= self
._get
_xdr
_buffer
(m
, pin
, i_invert
=invert
)
451 for bit
in range(pin
.width
):
452 m
.submodules
["{}_{}".format(pin
.name
, bit
)] = Instance("IBUF",
458 def get_output(self
, pin
, port
, attrs
, invert
):
459 self
._check
_feature
("single-ended output", pin
, attrs
,
460 valid_xdrs
=(0, 1, 2), valid_attrs
=True)
462 i
, o
, t
= self
._get
_xdr
_buffer
(m
, pin
, o_invert
=invert
)
463 if self
.toolchain
== "Vivado":
464 for bit
in range(pin
.width
):
465 m
.submodules
["{}_{}".format(pin
.name
, bit
)] = Instance("OBUF",
469 elif self
.toolchain
== "Symbiflow":
470 m
.d
.comb
+= port
.eq(self
._invert
_if
(invert
, o
))
475 def get_tristate(self
, pin
, port
, attrs
, invert
):
476 if toolchain
== "Symbiflow":
477 return super().get_tristate(pin
, port
, attrs
, invert
)
479 self
._check
_feature
("single-ended tristate", pin
, attrs
,
480 valid_xdrs
=(0, 1, 2), valid_attrs
=True)
482 i
, o
, t
= self
._get
_xdr
_buffer
(m
, pin
, o_invert
=invert
)
483 for bit
in range(pin
.width
):
484 m
.submodules
["{}_{}".format(pin
.name
, bit
)] = Instance("OBUFT",
491 def get_input_output(self
, pin
, port
, attrs
, invert
):
492 if toolchain
== "Symbiflow":
493 return super().get_input_output(pin
, port
, attrs
, invert
)
495 self
._check
_feature
("single-ended input/output", pin
, attrs
,
496 valid_xdrs
=(0, 1, 2), valid_attrs
=True)
498 i
, o
, t
= self
._get
_xdr
_buffer
(m
, pin
, i_invert
=invert
, o_invert
=invert
)
499 for bit
in range(pin
.width
):
500 m
.submodules
["{}_{}".format(pin
.name
, bit
)] = Instance("IOBUF",
508 def get_diff_input(self
, pin
, port
, attrs
, invert
):
509 if toolchain
== "Symbiflow":
510 return super().get_diff_input(pin
, port
, attrs
, invert
)
512 self
._check
_feature
("differential input", pin
, attrs
,
513 valid_xdrs
=(0, 1, 2), valid_attrs
=True)
515 i
, o
, t
= self
._get
_xdr
_buffer
(m
, pin
, i_invert
=invert
)
516 for bit
in range(pin
.width
):
517 m
.submodules
["{}_{}".format(pin
.name
, bit
)] = Instance("IBUFDS",
518 i_I
=port
.p
[bit
], i_IB
=port
.n
[bit
],
523 def get_diff_output(self
, pin
, port
, attrs
, invert
):
524 if toolchain
== "Symbiflow":
525 return super().get_diff_output(pin
, port
, attrs
, invert
)
527 self
._check
_feature
("differential output", pin
, attrs
,
528 valid_xdrs
=(0, 1, 2), valid_attrs
=True)
530 i
, o
, t
= self
._get
_xdr
_buffer
(m
, pin
, o_invert
=invert
)
531 for bit
in range(pin
.width
):
532 m
.submodules
["{}_{}".format(pin
.name
, bit
)] = Instance("OBUFDS",
534 o_O
=port
.p
[bit
], o_OB
=port
.n
[bit
]
538 def get_diff_tristate(self
, pin
, port
, attrs
, invert
):
539 if toolchain
== "Symbiflow":
540 return super().get_diff_tristate(pin
, port
, attrs
, invert
)
542 self
._check
_feature
("differential tristate", pin
, attrs
,
543 valid_xdrs
=(0, 1, 2), valid_attrs
=True)
545 i
, o
, t
= self
._get
_xdr
_buffer
(m
, pin
, o_invert
=invert
)
546 for bit
in range(pin
.width
):
547 m
.submodules
["{}_{}".format(pin
.name
, bit
)] = Instance("OBUFTDS",
550 o_O
=port
.p
[bit
], o_OB
=port
.n
[bit
]
554 def get_diff_input_output(self
, pin
, port
, attrs
, invert
):
555 if toolchain
== "Symbiflow":
556 return super().get_diff_input_output(pin
, port
, attrs
, invert
)
558 self
._check
_feature
("differential input/output", pin
, attrs
,
559 valid_xdrs
=(0, 1, 2), valid_attrs
=True)
561 i
, o
, t
= self
._get
_xdr
_buffer
(m
, pin
, i_invert
=invert
, o_invert
=invert
)
562 for bit
in range(pin
.width
):
563 m
.submodules
["{}_{}".format(pin
.name
, bit
)] = Instance("IOBUFDS",
567 io_IO
=port
.p
[bit
], io_IOB
=port
.n
[bit
]
571 # The synchronizer implementations below apply two separate but related timing constraints.
573 # First, the ASYNC_REG attribute prevents inference of shift registers from synchronizer FFs,
574 # and constraints the FFs to be placed as close as possible, ideally in one CLB. This attribute
575 # only affects the synchronizer FFs themselves.
577 # Second, the nmigen.vivado.false_path or nmigen.vivado.max_delay attribute affects the path
578 # into the synchronizer. If maximum input delay is specified, a datapath-only maximum delay
579 # constraint is applied, limiting routing delay (and therefore skew) at the synchronizer input.
580 # Otherwise, a false path constraint is used to omit the input path from the timing analysis.
582 def get_ff_sync(self
, ff_sync
):
584 flops
= [Signal(ff_sync
.i
.shape(), name
="stage{}".format(index
),
585 reset
=ff_sync
._reset
, reset_less
=ff_sync
._reset
_less
,
586 attrs
={"ASYNC_REG": "TRUE"})
587 for index
in range(ff_sync
._stages
)]
588 if ff_sync
._max
_input
_delay
is None:
589 flops
[0].attrs
["nmigen.vivado.false_path"] = "TRUE"
591 flops
[0].attrs
["nmigen.vivado.max_delay"] = str(ff_sync
._max
_input
_delay
* 1e9
)
592 for i
, o
in zip((ff_sync
.i
, *flops
), flops
):
593 m
.d
[ff_sync
._o
_domain
] += o
.eq(i
)
594 m
.d
.comb
+= ff_sync
.o
.eq(flops
[-1])
597 def get_async_ff_sync(self
, async_ff_sync
):
599 m
.domains
+= ClockDomain("async_ff", async_reset
=True, local
=True)
600 flops
= [Signal(1, name
="stage{}".format(index
), reset
=1,
601 attrs
={"ASYNC_REG": "TRUE"})
602 for index
in range(async_ff_sync
._stages
)]
603 if async_ff_sync
._max
_input
_delay
is None:
604 flops
[0].attrs
["nmigen.vivado.false_path"] = "TRUE"
606 flops
[0].attrs
["nmigen.vivado.max_delay"] = str(async_ff_sync
._max
_input
_delay
* 1e9
)
607 for i
, o
in zip((0, *flops
), flops
):
608 m
.d
.async_ff
+= o
.eq(i
)
610 if async_ff_sync
._edge
== "pos":
611 m
.d
.comb
+= ResetSignal("async_ff").eq(async_ff_sync
.i
)
613 m
.d
.comb
+= ResetSignal("async_ff").eq(~async_ff_sync
.i
)
616 ClockSignal("async_ff").eq(ClockSignal(async_ff_sync
._o
_domain
)),
617 async_ff_sync
.o
.eq(flops
[-1])