1 from abc
import abstractproperty
4 from ..lib
.cdc
import ResetSynchronizer
8 __all__
= ["XilinxSpartan3APlatform", "XilinxSpartan6Platform"]
11 # The interface to Spartan 3 and 6 are substantially the same. Handle
12 # differences internally using one class and expose user-aliases for
14 class XilinxSpartan3Or6Platform(TemplatedPlatform
):
24 The environment is populated by running the script specified in the environment variable
25 ``NMIGEN_ENV_ISE``, if present.
28 * ``script_after_run``: inserts commands after ``run`` in XST script.
29 * ``add_constraints``: inserts commands in UCF file.
30 * ``xst_opts``: adds extra options for ``xst``.
31 * ``ngdbuild_opts``: adds extra options for ``ngdbuild``.
32 * ``map_opts``: adds extra options for ``map``.
33 * ``par_opts``: adds extra options for ``par``.
34 * ``bitgen_opts``: adds extra and overrides default options for ``bitgen``;
35 default options: ``-g Compress``.
38 * ``{{name}}.srp``: synthesis report.
39 * ``{{name}}.ngc``: synthesized RTL.
40 * ``{{name}}.bld``: NGDBuild log.
41 * ``{{name}}.ngd``: design database.
42 * ``{{name}}_map.map``: MAP log.
43 * ``{{name}}_map.mrp``: mapping report.
44 * ``{{name}}_map.ncd``: mapped netlist.
45 * ``{{name}}.pcf``: physical constraints.
46 * ``{{name}}_par.par``: PAR log.
47 * ``{{name}}_par_pad.txt``: I/O usage report.
48 * ``{{name}}_par.ncd``: place and routed netlist.
49 * ``{{name}}.drc``: DRC report.
50 * ``{{name}}.bgn``: BitGen log.
51 * ``{{name}}.bit``: binary bitstream with metadata.
52 * ``{{name}}.bin``: raw binary bitstream.
57 device
= abstractproperty()
58 package
= abstractproperty()
59 speed
= abstractproperty()
71 device
= self
.device
.upper()
72 if device
.startswith("XC3S"):
73 if device
.endswith("A"):
75 elif device
.endswith("E"):
76 raise NotImplementedError("""Spartan 3E family is not supported
77 as a nMigen platform.""")
79 raise NotImplementedError("""Spartan 3 family is not supported
80 as a nMigen platform.""")
81 elif device
.startswith("XC6S"):
87 **TemplatedPlatform
.build_script_templates
,
88 "build_{{name}}.sh": r
"""
90 set -e{{verbose("x")}}
91 if [ -z "$BASH" ] ; then exec /bin/bash "$0" "$@"; fi
92 [ -n "${{platform._toolchain_env_var}}" ] && . "${{platform._toolchain_env_var}}"
93 {{emit_commands("sh")}}
96 /* {{autogenerated}} */
99 "{{name}}.debug.v": r
"""
100 /* {{autogenerated}} */
101 {{emit_debug_verilog()}}
105 {% for file in platform.iter_files(".vhd", ".vhdl") -%}
108 {% for file in platform.iter_files(".v") -%}
109 verilog work {{file}}
111 verilog work {{name}}.v
119 {% if platform.family in ["3", "3E", "3A"] %}
122 -p {{platform.device}}{{platform.package}}-{{platform.speed}}
123 {{get_override("script_after_run")|default("# (script_after_run placeholder)")}}
127 {% for port_name, pin_name, attrs in platform.iter_port_constraints_bits() -%}
128 {% set port_name = port_name|replace("[", "<")|replace("]", ">") -%}
129 NET "{{port_name}}" LOC={{pin_name}};
130 {% for attr_name, attr_value in attrs.items() -%}
131 NET "{{port_name}}" {{attr_name}}={{attr_value}};
134 {% for net_signal, port_signal, frequency in platform.iter_clock_constraints() -%}
135 NET "{{net_signal|hierarchy("/")}}" TNM_NET="PRD{{net_signal|hierarchy("/")}}";
136 TIMESPEC "TS{{net_signal|hierarchy("/")}}"=PERIOD "PRD{{net_signal|hierarchy("/")}}" {{1000000000/frequency}} ns HIGH 50%;
138 {{get_override("add_constraints")|default("# (add_constraints placeholder)")}}
141 command_templates
= [
143 {{invoke_tool("xst")}}
144 {{get_override("xst_opts")|options}}
148 {{invoke_tool("ngdbuild")}}
150 {{verbose("-verbose")}}
151 {{get_override("ngdbuild_opts")|options}}
156 {{invoke_tool("map")}}
157 {{verbose("-detail")}}
158 {{get_override("map_opts")|default([])|options}}
165 {{invoke_tool("par")}}
166 {{get_override("par_opts")|default([])|options}}
173 {{invoke_tool("bitgen")}}
174 {{get_override("bitgen_opts")|default(["-g Compress"])|options}}
182 def create_missing_domain(self
, name
):
183 # Xilinx devices have a global write enable (GWE) signal that asserted during configuraiton
184 # and deasserted once it ends. Because it is an asynchronous signal (GWE is driven by logic
185 # syncronous to configuration clock, which is not used by most designs), even though it is
186 # a low-skew global network, its deassertion may violate a setup/hold constraint with
187 # relation to a user clock. The recommended solution is to use a BUFGCE driven by the EOS
188 # signal (if available). For details, see:
189 # * https://www.xilinx.com/support/answers/44174.html
190 # * https://www.xilinx.com/support/documentation/white_papers/wp272.pdf
191 if self
.family
!= "6":
192 # Spartan 3 lacks a STARTUP primitive with EOS output; use a simple ResetSynchronizer
193 # in that case, as is the default.
194 return super().create_missing_domain(name
)
196 if name
== "sync" and self
.default_clk
is not None:
197 clk_i
= self
.request(self
.default_clk
).i
198 if self
.default_rst
is not None:
199 rst_i
= self
.request(self
.default_rst
).i
203 m
.submodules
+= Instance("STARTUP_SPARTAN6", o_EOS
=eos
)
204 m
.domains
+= ClockDomain("sync", reset_less
=self
.default_rst
is None)
205 m
.submodules
+= Instance("BUFGCE", i_CE
=eos
, i_I
=clk_i
, o_O
=ClockSignal("sync"))
206 if self
.default_rst
is not None:
207 m
.submodules
.reset_sync
= ResetSynchronizer(rst_i
, domain
="sync")
210 def _get_xdr_buffer(self
, m
, pin
, *, i_invert
=False, o_invert
=False):
211 def get_dff(clk
, d
, q
):
212 # SDR I/O is performed by packing a flip-flop into the pad IOB.
213 for bit
in range(len(q
)):
214 m
.submodules
+= Instance("FDCE",
223 def get_iddr(clk
, d
, q0
, q1
):
224 for bit
in range(len(q0
)):
225 m
.submodules
+= Instance("IDDR2",
226 p_DDR_ALIGNMENT
="C0",
228 p_INIT_Q0
=0, p_INIT_Q1
=0,
231 i_S
=Const(0), i_R
=Const(0),
233 o_Q0
=q0
[bit
], o_Q1
=q1
[bit
]
236 def get_oddr(clk
, d0
, d1
, q
):
237 for bit
in range(len(q
)):
238 m
.submodules
+= Instance("ODDR2",
239 p_DDR_ALIGNMENT
="C0",
244 i_S
=Const(0), i_R
=Const(0),
245 i_D0
=d0
[bit
], i_D1
=d1
[bit
],
249 def get_ineg(y
, invert
):
251 a
= Signal
.like(y
, name_suffix
="_n")
257 def get_oneg(a
, invert
):
259 y
= Signal
.like(a
, name_suffix
="_n")
267 pin_i
= get_ineg(pin
.i
, i_invert
)
269 pin_i0
= get_ineg(pin
.i0
, i_invert
)
270 pin_i1
= get_ineg(pin
.i1
, i_invert
)
273 pin_o
= get_oneg(pin
.o
, o_invert
)
275 pin_o0
= get_oneg(pin
.o0
, o_invert
)
276 pin_o1
= get_oneg(pin
.o1
, o_invert
)
280 i
= Signal(pin
.width
, name
="{}_xdr_i".format(pin
.name
))
282 o
= Signal(pin
.width
, name
="{}_xdr_o".format(pin
.name
))
283 if pin
.dir in ("oe", "io"):
284 t
= Signal(1, name
="{}_xdr_t".format(pin
.name
))
291 if pin
.dir in ("oe", "io"):
295 get_dff(pin
.i_clk
, i
, pin_i
)
297 get_dff(pin
.o_clk
, pin_o
, o
)
298 if pin
.dir in ("oe", "io"):
299 get_dff(pin
.o_clk
, ~pin
.oe
, t
)
302 # Re-register first input before it enters fabric. This allows both inputs to
303 # enter fabric on the same clock edge, and adds one cycle of latency.
304 i0_ff
= Signal
.like(pin_i0
, name_suffix
="_ff")
305 get_dff(pin
.i_clk
, i0_ff
, pin_i0
)
306 get_iddr(pin
.i_clk
, i
, i0_ff
, pin_i1
)
308 get_oddr(pin
.o_clk
, pin_o0
, pin_o1
, o
)
309 if pin
.dir in ("oe", "io"):
310 get_dff(pin
.o_clk
, ~pin
.oe
, t
)
316 def get_input(self
, pin
, port
, attrs
, invert
):
317 self
._check
_feature
("single-ended input", pin
, attrs
,
318 valid_xdrs
=(0, 1, 2), valid_attrs
=True)
320 i
, o
, t
= self
._get
_xdr
_buffer
(m
, pin
, i_invert
=invert
)
321 for bit
in range(pin
.width
):
322 m
.submodules
["{}_{}".format(pin
.name
, bit
)] = Instance("IBUF",
328 def get_output(self
, pin
, port
, attrs
, invert
):
329 self
._check
_feature
("single-ended output", pin
, attrs
,
330 valid_xdrs
=(0, 1, 2), valid_attrs
=True)
332 i
, o
, t
= self
._get
_xdr
_buffer
(m
, pin
, o_invert
=invert
)
333 for bit
in range(pin
.width
):
334 m
.submodules
["{}_{}".format(pin
.name
, bit
)] = Instance("OBUF",
340 def get_tristate(self
, pin
, port
, attrs
, invert
):
341 self
._check
_feature
("single-ended tristate", pin
, attrs
,
342 valid_xdrs
=(0, 1, 2), valid_attrs
=True)
344 i
, o
, t
= self
._get
_xdr
_buffer
(m
, pin
, o_invert
=invert
)
345 for bit
in range(pin
.width
):
346 m
.submodules
["{}_{}".format(pin
.name
, bit
)] = Instance("OBUFT",
353 def get_input_output(self
, pin
, port
, attrs
, invert
):
354 self
._check
_feature
("single-ended input/output", pin
, attrs
,
355 valid_xdrs
=(0, 1, 2), valid_attrs
=True)
357 i
, o
, t
= self
._get
_xdr
_buffer
(m
, pin
, i_invert
=invert
, o_invert
=invert
)
358 for bit
in range(pin
.width
):
359 m
.submodules
["{}_{}".format(pin
.name
, bit
)] = Instance("IOBUF",
367 def get_diff_input(self
, pin
, port
, attrs
, invert
):
368 self
._check
_feature
("differential input", pin
, attrs
,
369 valid_xdrs
=(0, 1, 2), valid_attrs
=True)
371 i
, o
, t
= self
._get
_xdr
_buffer
(m
, pin
, i_invert
=invert
)
372 for bit
in range(pin
.width
):
373 m
.submodules
["{}_{}".format(pin
.name
, bit
)] = Instance("IBUFDS",
374 i_I
=port
.p
[bit
], i_IB
=port
.n
[bit
],
379 def get_diff_output(self
, pin
, port
, attrs
, invert
):
380 self
._check
_feature
("differential output", pin
, attrs
,
381 valid_xdrs
=(0, 1, 2), valid_attrs
=True)
383 i
, o
, t
= self
._get
_xdr
_buffer
(m
, pin
, o_invert
=invert
)
384 for bit
in range(pin
.width
):
385 m
.submodules
["{}_{}".format(pin
.name
, bit
)] = Instance("OBUFDS",
387 o_O
=port
.p
[bit
], o_OB
=port
.n
[bit
]
391 def get_diff_tristate(self
, pin
, port
, attrs
, invert
):
392 self
._check
_feature
("differential tristate", pin
, attrs
,
393 valid_xdrs
=(0, 1, 2), valid_attrs
=True)
395 i
, o
, t
= self
._get
_xdr
_buffer
(m
, pin
, o_invert
=invert
)
396 for bit
in range(pin
.width
):
397 m
.submodules
["{}_{}".format(pin
.name
, bit
)] = Instance("OBUFTDS",
400 o_O
=port
.p
[bit
], o_OB
=port
.n
[bit
]
404 def get_diff_input_output(self
, pin
, port
, attrs
, invert
):
405 self
._check
_feature
("differential input/output", pin
, attrs
,
406 valid_xdrs
=(0, 1, 2), valid_attrs
=True)
408 i
, o
, t
= self
._get
_xdr
_buffer
(m
, pin
, i_invert
=invert
, o_invert
=invert
)
409 for bit
in range(pin
.width
):
410 m
.submodules
["{}_{}".format(pin
.name
, bit
)] = Instance("IOBUFDS",
414 io_IO
=port
.p
[bit
], io_IOB
=port
.n
[bit
]
418 # The synchronizer implementations below apply the ASYNC_REG attribute. This attribute
419 # prevents inference of shift registers from synchronizer FFs, and constraints the FFs
420 # to be placed as close as possible, ideally in one CLB. This attribute only affects
421 # the synchronizer FFs themselves.
423 def get_ff_sync(self
, ff_sync
):
424 if ff_sync
._max
_input
_delay
is not None:
425 raise NotImplementedError("Platform '{}' does not support constraining input delay "
427 .format(type(self
).__name
__))
430 flops
= [Signal(ff_sync
.i
.shape(), name
="stage{}".format(index
),
431 reset
=ff_sync
._reset
, reset_less
=ff_sync
._reset
_less
,
432 attrs
={"ASYNC_REG": "TRUE"})
433 for index
in range(ff_sync
._stages
)]
434 for i
, o
in zip((ff_sync
.i
, *flops
), flops
):
435 m
.d
[ff_sync
._o
_domain
] += o
.eq(i
)
436 m
.d
.comb
+= ff_sync
.o
.eq(flops
[-1])
439 def get_async_ff_sync(self
, async_ff_sync
):
440 if async_ff_sync
._max
_input
_delay
is not None:
441 raise NotImplementedError("Platform '{}' does not support constraining input delay "
442 "for AsyncFFSynchronizer"
443 .format(type(self
).__name
__))
446 m
.domains
+= ClockDomain("async_ff", async_reset
=True, local
=True)
447 flops
= [Signal(1, name
="stage{}".format(index
), reset
=1,
448 attrs
={"ASYNC_REG": "TRUE"})
449 for index
in range(async_ff_sync
._stages
)]
450 for i
, o
in zip((0, *flops
), flops
):
451 m
.d
.async_ff
+= o
.eq(i
)
453 if async_ff_sync
._edge
== "pos":
454 m
.d
.comb
+= ResetSignal("async_ff").eq(async_ff_sync
.i
)
456 m
.d
.comb
+= ResetSignal("async_ff").eq(~async_ff_sync
.i
)
459 ClockSignal("async_ff").eq(ClockSignal(async_ff_sync
._o
_domain
)),
460 async_ff_sync
.o
.eq(flops
[-1])
465 XilinxSpartan3APlatform
= XilinxSpartan3Or6Platform
466 XilinxSpartan6Platform
= XilinxSpartan3Or6Platform