1 from abc
import abstractproperty
4 from ..lib
.cdc
import ResetSynchronizer
8 __all__
= ["XilinxPlatform"]
11 class XilinxPlatform(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.
61 The environment is populated by running the script specified in the environment variable
62 ``NMIGEN_ENV_ISE``, if present.
65 * ``script_after_run``: inserts commands after ``run`` in XST script.
66 * ``add_constraints``: inserts commands in UCF file.
67 * ``xst_opts``: adds extra options for ``xst``.
68 * ``ngdbuild_opts``: adds extra options for ``ngdbuild``.
69 * ``map_opts``: adds extra options for ``map``.
70 * ``par_opts``: adds extra options for ``par``.
71 * ``bitgen_opts``: adds extra and overrides default options for ``bitgen``;
72 default options: ``-g Compress``.
75 * ``{{name}}.srp``: synthesis report.
76 * ``{{name}}.ngc``: synthesized RTL.
77 * ``{{name}}.bld``: NGDBuild log.
78 * ``{{name}}.ngd``: design database.
79 * ``{{name}}_map.map``: MAP log.
80 * ``{{name}}_map.mrp``: mapping report.
81 * ``{{name}}_map.ncd``: mapped netlist.
82 * ``{{name}}.pcf``: physical constraints.
83 * ``{{name}}_par.par``: PAR log.
84 * ``{{name}}_par_pad.txt``: I/O usage report.
85 * ``{{name}}_par.ncd``: place and routed netlist.
86 * ``{{name}}.drc``: DRC report.
87 * ``{{name}}.bgn``: BitGen log.
88 * ``{{name}}.bit``: binary bitstream with metadata.
89 * ``{{name}}.bin``: raw binary bitstream.
99 * ``symbiflow_write_fasm``
100 * ``symbiflow_write_bitstream``
102 The environment is populated by running the script specified in the environment variable
103 ``NMIGEN_ENV_Symbiflow``, if present.
106 * ``add_constraints``: inserts commands in XDC file.
109 toolchain
= None # selected when creating platform
111 device
= abstractproperty()
112 package
= abstractproperty()
113 speed
= abstractproperty()
117 if self
.family
in {"ultrascale", "ultrascaleplus"}:
118 return "{}-{}-{}".format(self
.device
, self
.package
, self
.speed
)
120 return "{}{}-{}".format(self
.device
, self
.package
, self
.speed
)
124 _vivado_required_tools
= ["vivado"]
125 _vivado_file_templates
= {
126 **TemplatedPlatform
.build_script_templates
,
127 "build_{{name}}.sh": r
"""
129 set -e{{verbose("x")}}
130 if [ -z "$BASH" ] ; then exec /bin/bash "$0" "$@"; fi
131 [ -n "${{platform._toolchain_env_var}}" ] && . "${{platform._toolchain_env_var}}"
132 {{emit_commands("sh")}}
135 /* {{autogenerated}} */
138 "{{name}}.debug.v": r
"""
139 /* {{autogenerated}} */
140 {{emit_debug_verilog()}}
144 create_project -force -name {{name}} -part {{platform._part}}
145 {% for file in platform.iter_files(".v", ".sv", ".vhd", ".vhdl") -%}
146 add_files {{file|tcl_escape}}
149 read_xdc {{name}}.xdc
150 {% for file in platform.iter_files(".xdc") -%}
151 read_xdc {{file|tcl_escape}}
153 {{get_override("script_after_read")|default("# (script_after_read placeholder)")}}
154 synth_design -top {{name}}
155 foreach cell [get_cells -quiet -hier -filter {nmigen.vivado.false_path == "TRUE"}] {
156 set_false_path -to $cell
158 foreach cell [get_cells -quiet -hier -filter {nmigen.vivado.max_delay != ""}] {
159 set clock [get_clocks -of_objects \
160 [all_fanin -flat -startpoints_only [get_pin $cell/D]]]
161 if {[llength $clock] != 0} {
162 set_max_delay -datapath_only -from $clock \
163 -to [get_cells $cell] [get_property nmigen.vivado.max_delay $cell]
166 {{get_override("script_after_synth")|default("# (script_after_synth placeholder)")}}
167 report_timing_summary -file {{name}}_timing_synth.rpt
168 report_utilization -hierarchical -file {{name}}_utilization_hierarchical_synth.rpt
169 report_utilization -file {{name}}_utilization_synth.rpt
172 {{get_override("script_after_place")|default("# (script_after_place placeholder)")}}
173 report_utilization -hierarchical -file {{name}}_utilization_hierarchical_place.rpt
174 report_utilization -file {{name}}_utilization_place.rpt
175 report_io -file {{name}}_io.rpt
176 report_control_sets -verbose -file {{name}}_control_sets.rpt
177 report_clock_utilization -file {{name}}_clock_utilization.rpt
179 {{get_override("script_after_route")|default("# (script_after_route placeholder)")}}
181 report_timing_summary -no_header -no_detailed_paths
182 write_checkpoint -force {{name}}_route.dcp
183 report_route_status -file {{name}}_route_status.rpt
184 report_drc -file {{name}}_drc.rpt
185 report_methodology -file {{name}}_methodology.rpt
186 report_timing_summary -datasheet -max_paths 10 -file {{name}}_timing.rpt
187 report_power -file {{name}}_power.rpt
188 {{get_override("script_before_bitstream")|default("# (script_before_bitstream placeholder)")}}
189 write_bitstream -force -bin_file {{name}}.bit
190 {{get_override("script_after_bitstream")|default("# (script_after_bitstream placeholder)")}}
195 {% for port_name, pin_name, attrs in platform.iter_port_constraints_bits() -%}
196 set_property LOC {{pin_name}} [get_ports {{port_name|tcl_escape}}]
197 {% for attr_name, attr_value in attrs.items() -%}
198 set_property {{attr_name}} {{attr_value|tcl_escape}} [get_ports {{port_name|tcl_escape}}]
201 {% for net_signal, port_signal, frequency in platform.iter_clock_constraints() -%}
202 {% if port_signal is not none -%}
203 create_clock -name {{port_signal.name|ascii_escape}} -period {{1000000000/frequency}} [get_ports {{port_signal.name|tcl_escape}}]
205 create_clock -name {{net_signal.name|ascii_escape}} -period {{1000000000/frequency}} [get_nets {{net_signal|hierarchy("/")|tcl_escape}}]
208 {{get_override("add_constraints")|default("# (add_constraints placeholder)")}}
211 _vivado_command_templates
= [
213 {{invoke_tool("vivado")}}
214 {{verbose("-verbose")}}
215 {{get_override("vivado_opts")|options}}
224 _ise_required_tools
= [
231 _ise_file_templates
= {
232 **TemplatedPlatform
.build_script_templates
,
233 "build_{{name}}.sh": r
"""
235 set -e{{verbose("x")}}
236 if [ -z "$BASH" ] ; then exec /bin/bash "$0" "$@"; fi
237 [ -n "${{platform._toolchain_env_var}}" ] && . "${{platform._toolchain_env_var}}"
238 {{emit_commands("sh")}}
241 /* {{autogenerated}} */
244 "{{name}}.debug.v": r
"""
245 /* {{autogenerated}} */
246 {{emit_debug_verilog()}}
250 {% for file in platform.iter_files(".vhd", ".vhdl") -%}
253 {% for file in platform.iter_files(".v") -%}
254 verilog work {{file}}
256 verilog work {{name}}.v
265 -p {{platform.device}}{{platform.package}}-{{platform.speed}}
266 {{get_override("script_after_run")|default("# (script_after_run placeholder)")}}
270 {% for port_name, pin_name, attrs in platform.iter_port_constraints_bits() -%}
271 {% set port_name = port_name|replace("[", "<")|replace("]", ">") -%}
272 NET "{{port_name}}" LOC={{pin_name}};
273 {% for attr_name, attr_value in attrs.items() -%}
274 NET "{{port_name}}" {{attr_name}}={{attr_value}};
277 {% for net_signal, port_signal, frequency in platform.iter_clock_constraints() -%}
278 NET "{{net_signal|hierarchy("/")}}" TNM_NET="PRD{{net_signal|hierarchy("/")}}";
279 TIMESPEC "TS{{net_signal|hierarchy("__")}}"=PERIOD "PRD{{net_signal|hierarchy("/")}}" {{1000000000/frequency}} ns HIGH 50%;
281 {{get_override("add_constraints")|default("# (add_constraints placeholder)")}}
284 _ise_command_templates
= [
286 {{invoke_tool("xst")}}
287 {{get_override("xst_opts")|options}}
291 {{invoke_tool("ngdbuild")}}
293 {{verbose("-verbose")}}
294 {{get_override("ngdbuild_opts")|options}}
299 {{invoke_tool("map")}}
300 {{verbose("-detail")}}
301 {{get_override("map_opts")|default([])|options}}
308 {{invoke_tool("par")}}
309 {{get_override("par_opts")|default([])|options}}
316 {{invoke_tool("bitgen")}}
317 {{get_override("bitgen_opts")|default(["-g Compress"])|options}}
325 # Symbiflow templates
327 _symbiflow_part_map
= {
328 "xc7a35ticsg324-1L": "xc7a35tcsg324-1", # Arty-A7
331 _symbiflow_required_tools
= [
336 "symbiflow_write_fasm",
337 "symbiflow_write_bitstream"
339 _symbiflow_file_templates
= {
340 **TemplatedPlatform
.build_script_templates
,
342 /* {{autogenerated}} */
345 "{{name}}.debug.v": r
"""
346 /* {{autogenerated}} */
347 {{emit_debug_verilog()}}
351 {% for port_name, pin_name, attrs in platform.iter_port_constraints_bits() -%}
352 set_io {{port_name}} {{pin_name}}
357 {% for port_name, pin_name, attrs in platform.iter_port_constraints_bits() -%}
358 {% for attr_name, attr_value in attrs.items() -%}
359 set_property {{attr_name}} {{attr_value}} [get_ports {{port_name|tcl_escape}} }]
362 {{get_override("add_constraints")|default("# (add_constraints placeholder)")}}
366 {% for net_signal, port_signal, frequency in platform.iter_clock_constraints() -%}
367 {% if port_signal is none -%}
368 create_clock -period {{1000000000/frequency}} {{net_signal.name|ascii_escape}}
373 _symbiflow_command_templates
= [
375 {{invoke_tool("symbiflow_synth")}}
377 -v {% for file in platform.iter_files(".v", ".sv", ".vhd", ".vhdl") -%} {{file}} {% endfor %} {{name}}.v
378 -p {{platform._symbiflow_part_map.get(platform._part, platform._part)}}
382 {{invoke_tool("symbiflow_pack")}}
384 -P {{platform._symbiflow_part_map.get(platform._part, platform._part)}}
388 {{invoke_tool("symbiflow_place")}}
392 -P {{platform._symbiflow_part_map.get(platform._part, platform._part)}}
396 {{invoke_tool("symbiflow_route")}}
398 -P {{platform._symbiflow_part_map.get(platform._part, platform._part)}}
402 {{invoke_tool("symbiflow_write_fasm")}}
404 -P {{platform._symbiflow_part_map.get(platform._part, platform._part)}}
407 {{invoke_tool("symbiflow_write_bitstream")}}
409 -p {{platform._symbiflow_part_map.get(platform._part, platform._part)}}
416 def __init__(self
, *, toolchain
=None):
419 # Determine device family.
420 device
= self
.device
.lower()
422 if device
.startswith("xc"):
424 elif device
.startswith("xa"):
426 elif device
.startswith("xqr"):
428 elif device
.startswith("xq"):
431 raise ValueError("Device '{}' is not recognized".format(self
.device
))
432 # Do actual name matching.
433 if device
.startswith("2vp"):
434 self
.family
= "virtex2p"
435 elif device
.startswith("2v"):
436 self
.family
= "virtex2"
437 elif device
.startswith("3sd"):
438 self
.family
= "spartan3adsp"
439 elif device
.startswith("3s"):
440 if device
.endswith("a"):
441 self
.family
= "spartan3a"
442 elif device
.endswith("e"):
443 self
.family
= "spartan3e"
445 self
.family
= "spartan3"
446 elif device
.startswith("4v"):
447 self
.family
= "virtex4"
448 elif device
.startswith("5v"):
449 self
.family
= "virtex5"
450 elif device
.startswith("6v"):
451 self
.family
= "virtex6"
452 elif device
.startswith("6s"):
453 self
.family
= "spartan6"
454 elif device
.startswith("7"):
455 self
.family
= "series7"
456 elif device
.startswith(("vu", "ku")):
457 if device
.endswith("p"):
458 self
.family
= "ultrascaleplus"
460 self
.family
= "ultrascale"
461 elif device
.startswith(("zu", "u", "k26")):
462 self
.family
= "ultrascaleplus"
463 elif device
.startswith(("v", "2s")):
464 # Match last to avoid conflict with ultrascale.
465 # Yes, Spartan 2 is the same thing as Virtex.
466 if device
.endswith("e"):
467 self
.family
= "virtexe"
469 self
.family
= "virtex"
474 "virtex2", "virtex2p",
475 "spartan3", "spartan3e", "spartan3a", "spartan3adsp",
481 if toolchain
is None:
482 if self
.family
in ISE_FAMILIES
:
487 assert toolchain
in ("Vivado", "ISE", "Symbiflow")
488 if toolchain
== "Vivado":
489 if self
.family
in ISE_FAMILIES
:
490 raise ValueError("Family '{}' is not supported by the Vivado toolchain, please use ISE instead".format(self
.family
))
491 elif toolchain
== "ISE":
492 if self
.family
not in ISE_FAMILIES
and self
.family
!= "series7":
493 raise ValueError("Family '{}' is not supported by the ISE toolchain, please use Vivado instead".format(self
.family
))
494 elif toolchain
== "Symbiflow":
495 if self
.family
!= "series7":
496 raise ValueError("Family '{}' is not supported by the Symbiflow toolchain".format(self
.family
))
497 self
.toolchain
= toolchain
500 def required_tools(self
):
501 if self
.toolchain
== "Vivado":
502 return self
._vivado
_required
_tools
503 if self
.toolchain
== "ISE":
504 return self
._ise
_required
_tools
505 if self
.toolchain
== "Symbiflow":
506 return self
._symbiflow
_required
_tools
510 def file_templates(self
):
511 if self
.toolchain
== "Vivado":
512 return self
._vivado
_file
_templates
513 if self
.toolchain
== "ISE":
514 return self
._ise
_file
_templates
515 if self
.toolchain
== "Symbiflow":
516 return self
._symbiflow
_file
_templates
520 def command_templates(self
):
521 if self
.toolchain
== "Vivado":
522 return self
._vivado
_command
_templates
523 if self
.toolchain
== "ISE":
524 return self
._ise
_command
_templates
525 if self
.toolchain
== "Symbiflow":
526 return self
._symbiflow
_command
_templates
529 def create_missing_domain(self
, name
):
530 # Xilinx devices have a global write enable (GWE) signal that asserted during configuraiton
531 # and deasserted once it ends. Because it is an asynchronous signal (GWE is driven by logic
532 # syncronous to configuration clock, which is not used by most designs), even though it is
533 # a low-skew global network, its deassertion may violate a setup/hold constraint with
534 # relation to a user clock. The recommended solution is to use a BUFGCE driven by the EOS
535 # signal (if available). For details, see:
536 # * https://www.xilinx.com/support/answers/44174.html
537 # * https://www.xilinx.com/support/documentation/white_papers/wp272.pdf
539 STARTUP_PRIMITIVE
= {
540 "spartan6": "STARTUP_SPARTAN6",
541 "virtex4": "STARTUP_VIRTEX4",
542 "virtex5": "STARTUP_VIRTEX5",
543 "virtex6": "STARTUP_VIRTEX6",
544 "series7": "STARTUPE2",
545 "ultrascale": "STARTUPE3",
546 "ultrascaleplus": "STARTUPE3",
549 if self
.family
not in STARTUP_PRIMITIVE
or self
.toolchain
== "Symbiflow":
550 # Spartan 3 and before lacks a STARTUP primitive with EOS output; use a simple ResetSynchronizer
551 # in that case, as is the default.
552 # Symbiflow does not support the STARTUPE2 primitive.
553 return super().create_missing_domain(name
)
555 if name
== "sync" and self
.default_clk
is not None:
556 clk_i
= self
.request(self
.default_clk
).i
557 if self
.default_rst
is not None:
558 rst_i
= self
.request(self
.default_rst
).i
562 m
.submodules
+= Instance(STARTUP_PRIMITIVE
[self
.family
], o_EOS
=ready
)
563 m
.domains
+= ClockDomain("sync", reset_less
=self
.default_rst
is None)
564 if self
.toolchain
!= "Vivado":
565 m
.submodules
+= Instance("BUFGCE", i_CE
=ready
, i_I
=clk_i
, o_O
=ClockSignal("sync"))
566 elif self
.family
== "series7":
567 # Actually use BUFGCTRL configured as BUFGCE, since using BUFGCE causes
568 # sim/synth mismatches with Vivado 2019.2, and the suggested workaround
569 # (SIM_DEVICE parameter) breaks Vivado 2017.4.
570 m
.submodules
+= Instance("BUFGCTRL",
571 p_SIM_DEVICE
="7SERIES",
572 i_I0
=clk_i
, i_S0
=C(1, 1), i_CE0
=ready
, i_IGNORE0
=C(0, 1),
573 i_I1
=C(1, 1), i_S1
=C(0, 1), i_CE1
=C(0, 1), i_IGNORE1
=C(1, 1),
574 o_O
=ClockSignal("sync")
577 m
.submodules
+= Instance("BUFGCE",
578 p_SIM_DEVICE
="ULTRASCALE",
581 o_O
=ClockSignal("sync")
583 if self
.default_rst
is not None:
584 m
.submodules
.reset_sync
= ResetSynchronizer(rst_i
, domain
="sync")
587 def add_clock_constraint(self
, clock
, frequency
):
588 super().add_clock_constraint(clock
, frequency
)
589 clock
.attrs
["keep"] = "TRUE"
591 def _get_xdr_buffer(self
, m
, pin
, iostd
, *, i_invert
=False, o_invert
=False):
614 def get_iob_dff(clk
, d
, q
):
615 # SDR I/O is performed by packing a flip-flop into the pad IOB.
616 for bit
in range(len(q
)):
617 m
.submodules
+= Instance("FDCE",
626 def get_dff(clk
, d
, q
):
627 for bit
in range(len(q
)):
628 m
.submodules
+= Instance("FDCE",
636 def get_ifddr(clk
, io
, q0
, q1
):
637 assert self
.family
in XFDDR_FAMILIES
638 for bit
in range(len(q0
)):
639 m
.submodules
+= Instance("IFDDRCPE",
642 i_CLR
=Const(0), i_PRE
=Const(0),
644 o_Q0
=q0
[bit
], o_Q1
=q1
[bit
]
647 def get_iddr2(clk
, d
, q0
, q1
, alignment
):
648 assert self
.family
in XDDR2_FAMILIES
649 for bit
in range(len(q0
)):
650 m
.submodules
+= Instance("IDDR2",
651 p_DDR_ALIGNMENT
=alignment
,
653 p_INIT_Q0
=C(0, 1), p_INIT_Q1
=C(0, 1),
656 i_S
=Const(0), i_R
=Const(0),
658 o_Q0
=q0
[bit
], o_Q1
=q1
[bit
]
661 def get_iddr(clk
, d
, q1
, q2
):
662 assert self
.family
in XDDR_FAMILIES
or self
.family
in XDDRE1_FAMILIES
663 for bit
in range(len(q1
)):
664 if self
.family
in XDDR_FAMILIES
:
665 m
.submodules
+= Instance("IDDR",
666 p_DDR_CLK_EDGE
="SAME_EDGE_PIPELINED",
668 p_INIT_Q1
=C(0, 1), p_INIT_Q2
=C(0, 1),
671 i_S
=Const(0), i_R
=Const(0),
673 o_Q1
=q1
[bit
], o_Q2
=q2
[bit
]
676 m
.submodules
+= Instance("IDDRE1",
677 p_DDR_CLK_EDGE
="SAME_EDGE_PIPELINED",
678 p_IS_C_INVERTED
=C(0, 1), p_IS_CB_INVERTED
=C(1, 1),
682 o_Q1
=q1
[bit
], o_Q2
=q2
[bit
]
685 def get_fddr(clk
, d0
, d1
, q
):
686 for bit
in range(len(q
)):
687 if self
.family
in XFDDR_FAMILIES
:
688 m
.submodules
+= Instance("FDDRCPE",
691 i_PRE
=Const(0), i_CLR
=Const(0),
692 i_D0
=d0
[bit
], i_D1
=d1
[bit
],
696 m
.submodules
+= Instance("ODDR2",
697 p_DDR_ALIGNMENT
="NONE",
702 i_S
=Const(0), i_R
=Const(0),
703 i_D0
=d0
[bit
], i_D1
=d1
[bit
],
707 def get_oddr(clk
, d1
, d2
, q
):
708 for bit
in range(len(q
)):
709 if self
.family
in XDDR2_FAMILIES
:
710 m
.submodules
+= Instance("ODDR2",
711 p_DDR_ALIGNMENT
="C0",
716 i_S
=Const(0), i_R
=Const(0),
717 i_D0
=d1
[bit
], i_D1
=d2
[bit
],
720 elif self
.family
in XDDR_FAMILIES
:
721 m
.submodules
+= Instance("ODDR",
722 p_DDR_CLK_EDGE
="SAME_EDGE",
727 i_S
=Const(0), i_R
=Const(0),
728 i_D1
=d1
[bit
], i_D2
=d2
[bit
],
731 elif self
.family
in XDDRE1_FAMILIES
:
732 m
.submodules
+= Instance("ODDRE1",
736 i_D1
=d1
[bit
], i_D2
=d2
[bit
],
740 def get_ineg(y
, invert
):
742 a
= Signal
.like(y
, name_suffix
="_n")
748 def get_oneg(a
, invert
):
750 y
= Signal
.like(a
, name_suffix
="_n")
758 pin_i
= get_ineg(pin
.i
, i_invert
)
760 pin_i0
= get_ineg(pin
.i0
, i_invert
)
761 pin_i1
= get_ineg(pin
.i1
, i_invert
)
764 pin_o
= get_oneg(pin
.o
, o_invert
)
766 pin_o0
= get_oneg(pin
.o0
, o_invert
)
767 pin_o1
= get_oneg(pin
.o1
, o_invert
)
771 i
= Signal(pin
.width
, name
="{}_xdr_i".format(pin
.name
))
773 o
= Signal(pin
.width
, name
="{}_xdr_o".format(pin
.name
))
774 if pin
.dir in ("oe", "io"):
775 t
= Signal(1, name
="{}_xdr_t".format(pin
.name
))
782 if pin
.dir in ("oe", "io"):
786 get_iob_dff(pin
.i_clk
, i
, pin_i
)
788 get_iob_dff(pin
.o_clk
, pin_o
, o
)
789 if pin
.dir in ("oe", "io"):
790 get_iob_dff(pin
.o_clk
, ~pin
.oe
, t
)
792 # On Spartan 3E/3A, the situation with DDR registers is messy: while the hardware
793 # supports same-edge alignment, it does so by borrowing the resources of the other
794 # pin in the differential pair (if any). Since we cannot be sure if the other pin
795 # is actually unused (or if the pin is even part of a differential pair in the first
796 # place), we only use the hardware alignment feature in two cases:
798 # - differential inputs (since the other pin's input registers will be unused)
799 # - true differential outputs (since they use only one pin's output registers,
800 # as opposed to pseudo-differential outputs that use both)
802 "LVDS_33", "LVDS_25",
803 "MINI_LVDS_33", "MINI_LVDS_25",
804 "RSDS_33", "RSDS_25",
805 "PPDS_33", "PPDS_25",
808 DIFF_S3EA
= TRUE_DIFF_S3EA |
{
823 if self
.family
in XFDDR_FAMILIES
:
824 # First-generation input DDR register: basically just two FFs with opposite
825 # clocks. Add a register on both outputs, so that they enter fabric on
826 # the same clock edge, adding one cycle of latency.
827 i0_ff
= Signal
.like(pin_i0
, name_suffix
="_ff")
828 i1_ff
= Signal
.like(pin_i1
, name_suffix
="_ff")
829 get_dff(pin
.i_clk
, i0_ff
, pin_i0
)
830 get_dff(pin
.i_clk
, i1_ff
, pin_i1
)
831 get_iob_dff(pin
.i_clk
, i
, i0_ff
)
832 get_iob_dff(~pin
.i_clk
, i
, i1_ff
)
833 elif self
.family
in XDDR2_FAMILIES
:
834 if self
.family
== 'spartan6' or iostd
in DIFF_S3EA
:
835 # Second-generation input DDR register: hw realigns i1 to positive clock edge,
836 # but also misaligns it with i0 input. Re-register first input before it
837 # enters fabric. This allows both inputs to enter fabric on the same clock
838 # edge, and adds one cycle of latency.
839 i0_ff
= Signal
.like(pin_i0
, name_suffix
="_ff")
840 get_dff(pin
.i_clk
, i0_ff
, pin_i0
)
841 get_iddr2(pin
.i_clk
, i
, i0_ff
, pin_i1
, "C0")
843 # No extra register available for hw alignment, use extra registers.
844 i0_ff
= Signal
.like(pin_i0
, name_suffix
="_ff")
845 i1_ff
= Signal
.like(pin_i1
, name_suffix
="_ff")
846 get_dff(pin
.i_clk
, i0_ff
, pin_i0
)
847 get_dff(pin
.i_clk
, i1_ff
, pin_i1
)
848 get_iddr2(pin
.i_clk
, i
, i0_ff
, i1_ff
, "NONE")
850 # Third-generation input DDR register: does all of the above on its own.
851 get_iddr(pin
.i_clk
, i
, pin_i0
, pin_i1
)
853 if self
.family
in XFDDR_FAMILIES
or self
.family
== "spartan3e" or (self
.family
.startswith("spartan3a") and iostd
not in TRUE_DIFF_S3EA
):
854 # For this generation, we need to realign o1 input ourselves.
855 o1_ff
= Signal
.like(pin_o1
, name_suffix
="_ff")
856 get_dff(pin
.o_clk
, pin_o1
, o1_ff
)
857 get_fddr(pin
.o_clk
, pin_o0
, o1_ff
, o
)
859 get_oddr(pin
.o_clk
, pin_o0
, pin_o1
, o
)
860 if pin
.dir in ("oe", "io"):
861 if self
.family
== "spartan6":
862 get_oddr(pin
.o_clk
, ~pin
.oe
, ~pin
.oe
, t
)
864 get_iob_dff(pin
.o_clk
, ~pin
.oe
, t
)
870 def _get_valid_xdrs(self
):
871 if self
.family
in {"virtex", "virtexe"}:
876 def get_input(self
, pin
, port
, attrs
, invert
):
877 self
._check
_feature
("single-ended input", pin
, attrs
,
878 valid_xdrs
=self
._get
_valid
_xdrs
(), valid_attrs
=True)
880 i
, o
, t
= self
._get
_xdr
_buffer
(m
, pin
, attrs
.get("IOSTANDARD"), i_invert
=invert
)
881 for bit
in range(pin
.width
):
882 m
.submodules
["{}_{}".format(pin
.name
, bit
)] = Instance("IBUF",
888 def get_output(self
, pin
, port
, attrs
, invert
):
889 self
._check
_feature
("single-ended output", pin
, attrs
,
890 valid_xdrs
=self
._get
_valid
_xdrs
(), valid_attrs
=True)
892 i
, o
, t
= self
._get
_xdr
_buffer
(m
, pin
, attrs
.get("IOSTANDARD"), o_invert
=invert
)
893 if self
.toolchain
!= "Symbiflow":
894 for bit
in range(pin
.width
):
895 m
.submodules
["{}_{}".format(pin
.name
, bit
)] = Instance("OBUF",
900 m
.d
.comb
+= port
.eq(self
._invert
_if
(invert
, o
))
903 def get_tristate(self
, pin
, port
, attrs
, invert
):
904 if self
.toolchain
== "Symbiflow":
905 return super().get_tristate(pin
, port
, attrs
, invert
)
907 self
._check
_feature
("single-ended tristate", pin
, attrs
,
908 valid_xdrs
=self
._get
_valid
_xdrs
(), valid_attrs
=True)
910 i
, o
, t
= self
._get
_xdr
_buffer
(m
, pin
, attrs
.get("IOSTANDARD"), o_invert
=invert
)
911 for bit
in range(pin
.width
):
912 m
.submodules
["{}_{}".format(pin
.name
, bit
)] = Instance("OBUFT",
919 def get_input_output(self
, pin
, port
, attrs
, invert
):
920 if self
.toolchain
== "Symbiflow":
921 return super().get_input_output(pin
, port
, attrs
, invert
)
923 self
._check
_feature
("single-ended input/output", pin
, attrs
,
924 valid_xdrs
=self
._get
_valid
_xdrs
(), valid_attrs
=True)
926 i
, o
, t
= self
._get
_xdr
_buffer
(m
, pin
, attrs
.get("IOSTANDARD"), i_invert
=invert
, o_invert
=invert
)
927 for bit
in range(pin
.width
):
928 m
.submodules
["{}_{}".format(pin
.name
, bit
)] = Instance("IOBUF",
936 def get_diff_input(self
, pin
, port
, attrs
, invert
):
937 if self
.toolchain
== "Symbiflow":
938 return super().get_diff_input(pin
, port
, attrs
, invert
)
940 self
._check
_feature
("differential input", pin
, attrs
,
941 valid_xdrs
=self
._get
_valid
_xdrs
(), valid_attrs
=True)
943 i
, o
, t
= self
._get
_xdr
_buffer
(m
, pin
, attrs
.get("IOSTANDARD", "LVDS_25"), i_invert
=invert
)
944 for bit
in range(pin
.width
):
945 m
.submodules
["{}_{}".format(pin
.name
, bit
)] = Instance("IBUFDS",
946 i_I
=port
.p
[bit
], i_IB
=port
.n
[bit
],
951 def get_diff_output(self
, pin
, port
, attrs
, invert
):
952 if self
.toolchain
== "Symbiflow":
953 return super().get_diff_output(pin
, port
, attrs
, invert
)
955 self
._check
_feature
("differential output", pin
, attrs
,
956 valid_xdrs
=self
._get
_valid
_xdrs
(), valid_attrs
=True)
958 i
, o
, t
= self
._get
_xdr
_buffer
(m
, pin
, attrs
.get("IOSTANDARD", "LVDS_25"), o_invert
=invert
)
959 for bit
in range(pin
.width
):
960 m
.submodules
["{}_{}".format(pin
.name
, bit
)] = Instance("OBUFDS",
962 o_O
=port
.p
[bit
], o_OB
=port
.n
[bit
]
966 def get_diff_tristate(self
, pin
, port
, attrs
, invert
):
967 if self
.toolchain
== "Symbiflow":
968 return super().get_diff_tristate(pin
, port
, attrs
, invert
)
970 self
._check
_feature
("differential tristate", pin
, attrs
,
971 valid_xdrs
=self
._get
_valid
_xdrs
(), valid_attrs
=True)
973 i
, o
, t
= self
._get
_xdr
_buffer
(m
, pin
, attrs
.get("IOSTANDARD", "LVDS_25"), o_invert
=invert
)
974 for bit
in range(pin
.width
):
975 m
.submodules
["{}_{}".format(pin
.name
, bit
)] = Instance("OBUFTDS",
978 o_O
=port
.p
[bit
], o_OB
=port
.n
[bit
]
982 def get_diff_input_output(self
, pin
, port
, attrs
, invert
):
983 if self
.toolchain
== "Symbiflow":
984 return super().get_diff_input_output(pin
, port
, attrs
, invert
)
986 self
._check
_feature
("differential input/output", pin
, attrs
,
987 valid_xdrs
=self
._get
_valid
_xdrs
(), valid_attrs
=True)
989 i
, o
, t
= self
._get
_xdr
_buffer
(m
, pin
, attrs
.get("IOSTANDARD", "LVDS_25"), i_invert
=invert
, o_invert
=invert
)
990 for bit
in range(pin
.width
):
991 m
.submodules
["{}_{}".format(pin
.name
, bit
)] = Instance("IOBUFDS",
995 io_IO
=port
.p
[bit
], io_IOB
=port
.n
[bit
]
999 # The synchronizer implementations below apply two separate but related timing constraints.
1001 # First, the ASYNC_REG attribute prevents inference of shift registers from synchronizer FFs,
1002 # and constraints the FFs to be placed as close as possible, ideally in one CLB. This attribute
1003 # only affects the synchronizer FFs themselves.
1005 # Second, for Vivado only, the nmigen.vivado.false_path or nmigen.vivado.max_delay attribute
1006 # affects the path into the synchronizer. If maximum input delay is specified, a datapath-only
1007 # maximum delay constraint is applied, limiting routing delay (and therefore skew) at
1008 # the synchronizer input. Otherwise, a false path constraint is used to omit the input path
1009 # from the timing analysis.
1011 def get_ff_sync(self
, ff_sync
):
1013 flops
= [Signal(ff_sync
.i
.shape(), name
="stage{}".format(index
),
1014 reset
=ff_sync
._reset
, reset_less
=ff_sync
._reset
_less
,
1015 attrs
={"ASYNC_REG": "TRUE"})
1016 for index
in range(ff_sync
._stages
)]
1017 if self
.toolchain
== "Vivado":
1018 if ff_sync
._max
_input
_delay
is None:
1019 flops
[0].attrs
["nmigen.vivado.false_path"] = "TRUE"
1021 flops
[0].attrs
["nmigen.vivado.max_delay"] = str(ff_sync
._max
_input
_delay
* 1e9
)
1022 elif ff_sync
._max
_input
_delay
is not None:
1023 raise NotImplementedError("Platform '{}' does not support constraining input delay "
1024 "for FFSynchronizer"
1025 .format(type(self
).__name
__))
1026 for i
, o
in zip((ff_sync
.i
, *flops
), flops
):
1027 m
.d
[ff_sync
._o
_domain
] += o
.eq(i
)
1028 m
.d
.comb
+= ff_sync
.o
.eq(flops
[-1])
1032 def get_async_ff_sync(self
, async_ff_sync
):
1034 m
.domains
+= ClockDomain("async_ff", async_reset
=True, local
=True)
1035 flops
= [Signal(1, name
="stage{}".format(index
), reset
=1,
1036 attrs
={"ASYNC_REG": "TRUE"})
1037 for index
in range(async_ff_sync
._stages
)]
1038 if self
.toolchain
== "Vivado":
1039 if async_ff_sync
._max
_input
_delay
is None:
1040 flops
[0].attrs
["nmigen.vivado.false_path"] = "TRUE"
1042 flops
[0].attrs
["nmigen.vivado.max_delay"] = str(async_ff_sync
._max
_input
_delay
* 1e9
)
1043 elif async_ff_sync
._max
_input
_delay
is not None:
1044 raise NotImplementedError("Platform '{}' does not support constraining input delay "
1045 "for AsyncFFSynchronizer"
1046 .format(type(self
).__name
__))
1047 for i
, o
in zip((0, *flops
), flops
):
1048 m
.d
.async_ff
+= o
.eq(i
)
1050 if async_ff_sync
._edge
== "pos":
1051 m
.d
.comb
+= ResetSignal("async_ff").eq(async_ff_sync
.i
)
1053 m
.d
.comb
+= ResetSignal("async_ff").eq(~async_ff_sync
.i
)
1056 ClockSignal("async_ff").eq(ClockSignal(async_ff_sync
._o
_domain
)),
1057 async_ff_sync
.o
.eq(flops
[-1])