lib.cdc: in AsyncFFSynchronizer(), rename domain= to o_domain=.
[nmigen.git] / nmigen / vendor / xilinx_7series.py
1 from abc import abstractproperty
2
3 from ..hdl import *
4 from ..lib.cdc import ResetSynchronizer
5 from ..build import *
6
7
8 __all__ = ["Xilinx7SeriesPlatform"]
9
10
11 class Xilinx7SeriesPlatform(TemplatedPlatform):
12 """
13 Vivado toolchain
14 ----------------
15
16 Required tools:
17 * ``vivado``
18
19 The environment is populated by running the script specified in the environment variable
20 ``NMIGEN_ENV_Vivado``, if present.
21
22 Available overrides:
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``.
31
32 Build products:
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.
50
51 Symbiflow toolchain
52 -------------------
53
54 Required tools:
55 * ``synth``
56 * ``pack``
57 * ``place``
58 * ``route``
59 * ``write_fasm``
60 * ``write_bitstream``
61
62 The environment is populated by running the script specified in the environment variable
63 ``NMIGEN_ENV_Symbiflow``, if present.
64
65 Available overrides:
66 * ``add_constraints``: inserts commands in XDC file.
67 """
68
69 toolchain = None # selected when creating platform
70
71 device = abstractproperty()
72 package = abstractproperty()
73 speed = abstractproperty()
74
75 @property
76 def _part(self):
77 return "{}{}-{}".format(self.device, self.package, self.speed)
78
79 # Vivado templates
80
81 _vivado_required_tools = ["vivado"]
82 _vivado_file_templates = {
83 **TemplatedPlatform.build_script_templates,
84 "build_{{name}}.sh": r"""
85 # {{autogenerated}}
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")}}
90 """,
91 "{{name}}.v": r"""
92 /* {{autogenerated}} */
93 {{emit_verilog()}}
94 """,
95 "{{name}}.debug.v": r"""
96 /* {{autogenerated}} */
97 {{emit_debug_verilog()}}
98 """,
99 "{{name}}.tcl": r"""
100 # {{autogenerated}}
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}}
104 {% endfor %}
105 add_files {{name}}.v
106 read_xdc {{name}}.xdc
107 {% for file in platform.iter_extra_files(".xdc") -%}
108 read_xdc {{file|tcl_escape}}
109 {% endfor %}
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
114 }
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]
121 }
122 }
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
127 opt_design
128 place_design
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
135 route_design
136 {{get_override("script_after_route")|default("# (script_after_route placeholder)")}}
137 phys_opt_design
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)")}}
148 quit
149 """,
150 "{{name}}.xdc": r"""
151 # {{autogenerated}}
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}}]
156 {% endfor %}
157 {% endfor %}
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}}]
161 {% else -%}
162 create_clock -name {{net_signal.name|ascii_escape}} -period {{1000000000/frequency}} [get_nets {{net_signal|hierarchy("/")|tcl_escape}}]
163 {% endif %}
164 {% endfor %}
165 {{get_override("add_constraints")|default("# (add_constraints placeholder)")}}
166 """
167 }
168 _vivado_command_templates = [
169 r"""
170 {{invoke_tool("vivado")}}
171 {{verbose("-verbose")}}
172 {{get_override("vivado_opts")|options}}
173 -mode batch
174 -log {{name}}.log
175 -source {{name}}.tcl
176 """
177 ]
178
179 # Symbiflow templates
180
181 _symbiflow_part_map = {
182 "xc7a35ticsg324-1L": "xc7a35tcsg324-1", # Arty-A7
183 }
184
185 _symbiflow_required_tools = [
186 "synth",
187 "pack",
188 "place",
189 "route",
190 "write_fasm",
191 "write_bitstream"
192 ]
193 _symbiflow_file_templates = {
194 **TemplatedPlatform.build_script_templates,
195 "{{name}}.v": r"""
196 /* {{autogenerated}} */
197 {{emit_verilog()}}
198 """,
199 "{{name}}.debug.v": r"""
200 /* {{autogenerated}} */
201 {{emit_debug_verilog()}}
202 """,
203 "{{name}}.pcf": r"""
204 # {{autogenerated}}
205 {% for port_name, pin_name, attrs in platform.iter_port_constraints_bits() -%}
206 set_io {{port_name}} {{pin_name}}
207 {% endfor %}
208 """,
209 "{{name}}.xdc": r"""
210 # {{autogenerated}}
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}} }]
214 {% endfor %}
215 {% endfor %}
216 {{get_override("add_constraints")|default("# (add_constraints placeholder)")}}
217 """,
218 "{{name}}.sdc": r"""
219 # {{autogenerated}}
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}}
223 {% endif %}
224 {% endfor %}
225 """
226 }
227 _symbiflow_command_templates = [
228 r"""
229 {{invoke_tool("synth")}}
230 -t {{name}}
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)}}
233 -x {{name}}.xdc
234 """,
235 r"""
236 {{invoke_tool("pack")}}
237 -e {{name}}.eblif
238 -P {{platform._symbiflow_part_map.get(platform._part, platform._part)}}
239 -s {{name}}.sdc
240 """,
241 r"""
242 {{invoke_tool("place")}}
243 -e {{name}}.eblif
244 -p {{name}}.pcf
245 -n {{name}}.net
246 -P {{platform._symbiflow_part_map.get(platform._part, platform._part)}}
247 -s {{name}}.sdc
248 """,
249 r"""
250 {{invoke_tool("route")}}
251 -e {{name}}.eblif
252 -P {{platform._symbiflow_part_map.get(platform._part, platform._part)}}
253 -s {{name}}.sdc
254 """,
255 r"""
256 {{invoke_tool("write_fasm")}}
257 -e {{name}}.eblif
258 -P {{platform._symbiflow_part_map.get(platform._part, platform._part)}}
259 """,
260 r"""
261 {{invoke_tool("write_bitstream")}}
262 -f {{name}}.fasm
263 -p {{platform._symbiflow_part_map.get(platform._part, platform._part)}}
264 -b {{name}}.bit
265 """
266 ]
267
268 # Common logic
269
270 def __init__(self, *, toolchain="Vivado"):
271 super().__init__()
272
273 assert toolchain in ("Vivado", "Symbiflow")
274 self.toolchain = toolchain
275
276 @property
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
282 assert False
283
284 @property
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
290 assert False
291
292 @property
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
298 assert False
299
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
313
314 m = Module()
315
316 if self.toolchain == "Vivado":
317 ready = Signal()
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")
327 )
328 elif self.toolchain == "Symbiflow":
329 cd_sync = ClockDomain("sync", reset_less=self.default_rst is None)
330 m.domains += cd_sync
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)
333 else:
334 assert False
335
336 if self.default_rst is not None:
337 m.submodules.reset_sync = ResetSynchronizer(rst_i, domain="sync")
338 return m
339
340 def add_clock_constraint(self, clock, frequency):
341 super().add_clock_constraint(clock, frequency)
342 clock.attrs["keep"] = "TRUE"
343
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",
349 a_IOB="TRUE",
350 i_C=clk,
351 i_CE=Const(1),
352 i_CLR=Const(0),
353 i_D=d[bit],
354 o_Q=q[bit]
355 )
356
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",
361 p_SRTYPE="ASYNC",
362 p_INIT_Q1=0, p_INIT_Q2=0,
363 i_C=clk,
364 i_CE=Const(1),
365 i_S=Const(0), i_R=Const(0),
366 i_D=d[bit],
367 o_Q1=q1[bit], o_Q2=q2[bit]
368 )
369
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",
374 p_SRTYPE="ASYNC",
375 p_INIT=0,
376 i_C=clk,
377 i_CE=Const(1),
378 i_S=Const(0), i_R=Const(0),
379 i_D1=d1[bit], i_D2=d2[bit],
380 o_Q=q[bit]
381 )
382
383 def get_ineg(y, invert):
384 if invert:
385 a = Signal.like(y, name_suffix="_n")
386 m.d.comb += y.eq(~a)
387 return a
388 else:
389 return y
390
391 def get_oneg(a, invert):
392 if invert:
393 y = Signal.like(a, name_suffix="_n")
394 m.d.comb += y.eq(~a)
395 return y
396 else:
397 return a
398
399 if "i" in pin.dir:
400 if pin.xdr < 2:
401 pin_i = get_ineg(pin.i, i_invert)
402 elif pin.xdr == 2:
403 pin_i0 = get_ineg(pin.i0, i_invert)
404 pin_i1 = get_ineg(pin.i1, i_invert)
405 if "o" in pin.dir:
406 if pin.xdr < 2:
407 pin_o = get_oneg(pin.o, o_invert)
408 elif pin.xdr == 2:
409 pin_o0 = get_oneg(pin.o0, o_invert)
410 pin_o1 = get_oneg(pin.o1, o_invert)
411
412 i = o = t = None
413 if "i" in pin.dir:
414 i = Signal(pin.width, name="{}_xdr_i".format(pin.name))
415 if "o" in pin.dir:
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))
419
420 if pin.xdr == 0:
421 if "i" in pin.dir:
422 i = pin_i
423 if "o" in pin.dir:
424 o = pin_o
425 if pin.dir in ("oe", "io"):
426 t = ~pin.oe
427 elif pin.xdr == 1:
428 if "i" in pin.dir:
429 get_dff(pin.i_clk, i, pin_i)
430 if "o" in pin.dir:
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)
434 elif pin.xdr == 2:
435 if "i" in pin.dir:
436 get_iddr(pin.i_clk, i, pin_i0, pin_i1)
437 if "o" in pin.dir:
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)
441 else:
442 assert False
443
444 return (i, o, t)
445
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)
449 m = Module()
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",
453 i_I=port.io[bit],
454 o_O=i[bit]
455 )
456 return m
457
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)
461 m = Module()
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",
466 i_I=o[bit],
467 o_O=port.io[bit]
468 )
469 elif self.toolchain == "Symbiflow":
470 m.d.comb += port.eq(self._invert_if(invert, o))
471 else:
472 assert False
473 return m
474
475 def get_tristate(self, pin, port, attrs, invert):
476 if toolchain == "Symbiflow":
477 return super().get_tristate(pin, port, attrs, invert)
478
479 self._check_feature("single-ended tristate", pin, attrs,
480 valid_xdrs=(0, 1, 2), valid_attrs=True)
481 m = Module()
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",
485 i_T=t,
486 i_I=o[bit],
487 o_O=port.io[bit]
488 )
489 return m
490
491 def get_input_output(self, pin, port, attrs, invert):
492 if toolchain == "Symbiflow":
493 return super().get_input_output(pin, port, attrs, invert)
494
495 self._check_feature("single-ended input/output", pin, attrs,
496 valid_xdrs=(0, 1, 2), valid_attrs=True)
497 m = Module()
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",
501 i_T=t,
502 i_I=o[bit],
503 o_O=i[bit],
504 io_IO=port.io[bit]
505 )
506 return m
507
508 def get_diff_input(self, pin, port, attrs, invert):
509 if toolchain == "Symbiflow":
510 return super().get_diff_input(pin, port, attrs, invert)
511
512 self._check_feature("differential input", pin, attrs,
513 valid_xdrs=(0, 1, 2), valid_attrs=True)
514 m = Module()
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],
519 o_O=i[bit]
520 )
521 return m
522
523 def get_diff_output(self, pin, port, attrs, invert):
524 if toolchain == "Symbiflow":
525 return super().get_diff_output(pin, port, attrs, invert)
526
527 self._check_feature("differential output", pin, attrs,
528 valid_xdrs=(0, 1, 2), valid_attrs=True)
529 m = Module()
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",
533 i_I=o[bit],
534 o_O=port.p[bit], o_OB=port.n[bit]
535 )
536 return m
537
538 def get_diff_tristate(self, pin, port, attrs, invert):
539 if toolchain == "Symbiflow":
540 return super().get_diff_tristate(pin, port, attrs, invert)
541
542 self._check_feature("differential tristate", pin, attrs,
543 valid_xdrs=(0, 1, 2), valid_attrs=True)
544 m = Module()
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",
548 i_T=t,
549 i_I=o[bit],
550 o_O=port.p[bit], o_OB=port.n[bit]
551 )
552 return m
553
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)
557
558 self._check_feature("differential input/output", pin, attrs,
559 valid_xdrs=(0, 1, 2), valid_attrs=True)
560 m = Module()
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",
564 i_T=t,
565 i_I=o[bit],
566 o_O=i[bit],
567 io_IO=port.p[bit], io_IOB=port.n[bit]
568 )
569 return m
570
571 # The synchronizer implementations below apply two separate but related timing constraints.
572 #
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.
576 #
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.
581
582 def get_ff_sync(self, ff_sync):
583 m = Module()
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"
590 else:
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])
595 return m
596
597 def get_async_ff_sync(self, async_ff_sync):
598 m = Module()
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"
605 else:
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)
609
610 if async_ff_sync._edge == "pos":
611 m.d.comb += ResetSignal("async_ff").eq(async_ff_sync.i)
612 else:
613 m.d.comb += ResetSignal("async_ff").eq(~async_ff_sync.i)
614
615 m.d.comb += [
616 ClockSignal("async_ff").eq(ClockSignal(async_ff_sync._o_domain)),
617 async_ff_sync.o.eq(flops[-1])
618 ]
619
620 return m