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()
72 device
= self
.device
.upper()
73 if device
.startswith("XC3S"):
74 if device
.endswith("A"):
76 elif device
.endswith("E"):
77 raise NotImplementedError("""Spartan 3E family is not supported
78 as a nMigen platform.""")
80 raise NotImplementedError("""Spartan 3 family is not supported
81 as a nMigen platform.""")
82 elif device
.startswith("XC6S"):
88 **TemplatedPlatform
.build_script_templates
,
89 "build_{{name}}.sh": r
"""
91 set -e{{verbose("x")}}
92 if [ -z "$BASH" ] ; then exec /bin/bash "$0" "$@"; fi
93 [ -n "${{platform._toolchain_env_var}}" ] && . "${{platform._toolchain_env_var}}"
94 {{emit_commands("sh")}}
97 /* {{autogenerated}} */
100 "{{name}}.debug.v": r
"""
101 /* {{autogenerated}} */
102 {{emit_debug_verilog()}}
106 {% for file in platform.iter_extra_files(".vhd", ".vhdl") -%}
109 {% for file in platform.iter_extra_files(".v") -%}
110 verilog work {{file}}
112 verilog work {{name}}.v
120 {% if platform.family in ["3", "3E", "3A"] %}
123 -p {{platform.device}}{{platform.package}}-{{platform.speed}}
124 {{get_override("script_after_run")|default("# (script_after_run placeholder)")}}
128 {% for port_name, pin_name, attrs in platform.iter_port_constraints_bits() -%}
129 {% set port_name = port_name|replace("[", "<")|replace("]", ">") -%}
130 NET "{{port_name}}" LOC={{pin_name}};
131 {% for attr_name, attr_value in attrs.items() -%}
132 NET "{{port_name}}" {{attr_name}}={{attr_value}};
135 {% for net_signal, port_signal, frequency in platform.iter_clock_constraints() -%}
136 NET "{{net_signal|hierarchy("/")}}" TNM_NET="PRD{{net_signal|hierarchy("/")}}";
137 TIMESPEC "TS{{net_signal|hierarchy("/")}}"=PERIOD "PRD{{net_signal|hierarchy("/")}}" {{1000000000/frequency}} ns HIGH 50%;
139 {{get_override("add_constraints")|default("# (add_constraints placeholder)")}}
142 command_templates
= [
144 {{invoke_tool("xst")}}
145 {{get_override("xst_opts")|options}}
149 {{invoke_tool("ngdbuild")}}
151 {{verbose("-verbose")}}
152 {{get_override("ngdbuild_opts")|options}}
157 {{invoke_tool("map")}}
158 {{verbose("-detail")}}
159 {{get_override("map_opts")|default([])|options}}
166 {{invoke_tool("par")}}
167 {{get_override("par_opts")|default([])|options}}
174 {{invoke_tool("bitgen")}}
175 {{get_override("bitgen_opts")|default(["-g Compress"])|options}}
183 def create_missing_domain(self
, name
):
184 # Xilinx devices have a global write enable (GWE) signal that asserted during configuraiton
185 # and deasserted once it ends. Because it is an asynchronous signal (GWE is driven by logic
186 # syncronous to configuration clock, which is not used by most designs), even though it is
187 # a low-skew global network, its deassertion may violate a setup/hold constraint with
188 # relation to a user clock. The recommended solution is to use a BUFGCE driven by the EOS
189 # signal (if available). For details, see:
190 # * https://www.xilinx.com/support/answers/44174.html
191 # * https://www.xilinx.com/support/documentation/white_papers/wp272.pdf
192 if self
.family
!= "6":
193 # Spartan 3 lacks a STARTUP primitive with EOS output; use a simple ResetSynchronizer
194 # in that case, as is the default.
195 return super().create_missing_domain(name
)
197 if name
== "sync" and self
.default_clk
is not None:
198 clk_i
= self
.request(self
.default_clk
).i
199 if self
.default_rst
is not None:
200 rst_i
= self
.request(self
.default_rst
).i
204 m
.submodules
+= Instance("STARTUP_SPARTAN6", o_EOS
=eos
)
205 m
.domains
+= ClockDomain("sync", reset_less
=self
.default_rst
is None)
206 m
.submodules
+= Instance("BUFGCE", i_CE
=eos
, i_I
=clk_i
, o_O
=ClockSignal("sync"))
207 if self
.default_rst
is not None:
208 m
.submodules
.reset_sync
= ResetSynchronizer(rst_i
, domain
="sync")
211 def _get_xdr_buffer(self
, m
, pin
, *, i_invert
=False, o_invert
=False):
212 def get_dff(clk
, d
, q
):
213 # SDR I/O is performed by packing a flip-flop into the pad IOB.
214 for bit
in range(len(q
)):
215 m
.submodules
+= Instance("FDCE",
224 def get_iddr(clk
, d
, q0
, q1
):
225 for bit
in range(len(q0
)):
226 m
.submodules
+= Instance("IDDR2",
227 p_DDR_ALIGNMENT
="C0",
229 p_INIT_Q0
=0, p_INIT_Q1
=0,
232 i_S
=Const(0), i_R
=Const(0),
234 o_Q0
=q0
[bit
], o_Q1
=q1
[bit
]
237 def get_oddr(clk
, d0
, d1
, q
):
238 for bit
in range(len(q
)):
239 m
.submodules
+= Instance("ODDR2",
240 p_DDR_ALIGNMENT
="C0",
245 i_S
=Const(0), i_R
=Const(0),
246 i_D0
=d0
[bit
], i_D1
=d1
[bit
],
250 def get_ineg(y
, invert
):
252 a
= Signal
.like(y
, name_suffix
="_n")
258 def get_oneg(a
, invert
):
260 y
= Signal
.like(a
, name_suffix
="_n")
268 pin_i
= get_ineg(pin
.i
, i_invert
)
270 pin_i0
= get_ineg(pin
.i0
, i_invert
)
271 pin_i1
= get_ineg(pin
.i1
, i_invert
)
274 pin_o
= get_oneg(pin
.o
, o_invert
)
276 pin_o0
= get_oneg(pin
.o0
, o_invert
)
277 pin_o1
= get_oneg(pin
.o1
, o_invert
)
281 i
= Signal(pin
.width
, name
="{}_xdr_i".format(pin
.name
))
283 o
= Signal(pin
.width
, name
="{}_xdr_o".format(pin
.name
))
284 if pin
.dir in ("oe", "io"):
285 t
= Signal(1, name
="{}_xdr_t".format(pin
.name
))
292 if pin
.dir in ("oe", "io"):
296 get_dff(pin
.i_clk
, i
, pin_i
)
298 get_dff(pin
.o_clk
, pin_o
, o
)
299 if pin
.dir in ("oe", "io"):
300 get_dff(pin
.o_clk
, ~pin
.oe
, t
)
303 # Re-register first input before it enters fabric. This allows both inputs to
304 # enter fabric on the same clock edge, and adds one cycle of latency.
305 i0_ff
= Signal
.like(pin_i0
, name_suffix
="_ff")
306 get_dff(pin
.i_clk
, i0_ff
, pin_i0
)
307 get_iddr(pin
.i_clk
, i
, i0_ff
, pin_i1
)
309 get_oddr(pin
.o_clk
, pin_o0
, pin_o1
, o
)
310 if pin
.dir in ("oe", "io"):
311 get_dff(pin
.o_clk
, ~pin
.oe
, t
)
317 def get_input(self
, pin
, port
, attrs
, invert
):
318 self
._check
_feature
("single-ended input", pin
, attrs
,
319 valid_xdrs
=(0, 1, 2), valid_attrs
=True)
321 i
, o
, t
= self
._get
_xdr
_buffer
(m
, pin
, i_invert
=invert
)
322 for bit
in range(len(port
)):
323 m
.submodules
["{}_{}".format(pin
.name
, bit
)] = Instance("IBUF",
329 def get_output(self
, pin
, port
, attrs
, invert
):
330 self
._check
_feature
("single-ended output", pin
, attrs
,
331 valid_xdrs
=(0, 1, 2), valid_attrs
=True)
333 i
, o
, t
= self
._get
_xdr
_buffer
(m
, pin
, o_invert
=invert
)
334 for bit
in range(len(port
)):
335 m
.submodules
["{}_{}".format(pin
.name
, bit
)] = Instance("OBUF",
341 def get_tristate(self
, pin
, port
, attrs
, invert
):
342 self
._check
_feature
("single-ended tristate", pin
, attrs
,
343 valid_xdrs
=(0, 1, 2), valid_attrs
=True)
345 i
, o
, t
= self
._get
_xdr
_buffer
(m
, pin
, o_invert
=invert
)
346 for bit
in range(len(port
)):
347 m
.submodules
["{}_{}".format(pin
.name
, bit
)] = Instance("OBUFT",
354 def get_input_output(self
, pin
, port
, attrs
, invert
):
355 self
._check
_feature
("single-ended input/output", pin
, attrs
,
356 valid_xdrs
=(0, 1, 2), valid_attrs
=True)
358 i
, o
, t
= self
._get
_xdr
_buffer
(m
, pin
, i_invert
=invert
, o_invert
=invert
)
359 for bit
in range(len(port
)):
360 m
.submodules
["{}_{}".format(pin
.name
, bit
)] = Instance("IOBUF",
368 def get_diff_input(self
, pin
, p_port
, n_port
, attrs
, invert
):
369 self
._check
_feature
("differential input", pin
, attrs
,
370 valid_xdrs
=(0, 1, 2), valid_attrs
=True)
372 i
, o
, t
= self
._get
_xdr
_buffer
(m
, pin
, i_invert
=invert
)
373 for bit
in range(len(p_port
)):
374 m
.submodules
["{}_{}".format(pin
.name
, bit
)] = Instance("IBUFDS",
375 i_I
=p_port
[bit
], i_IB
=n_port
[bit
],
380 def get_diff_output(self
, pin
, p_port
, n_port
, attrs
, invert
):
381 self
._check
_feature
("differential output", pin
, attrs
,
382 valid_xdrs
=(0, 1, 2), valid_attrs
=True)
384 i
, o
, t
= self
._get
_xdr
_buffer
(m
, pin
, o_invert
=invert
)
385 for bit
in range(len(p_port
)):
386 m
.submodules
["{}_{}".format(pin
.name
, bit
)] = Instance("OBUFDS",
388 o_O
=p_port
[bit
], o_OB
=n_port
[bit
]
392 def get_diff_tristate(self
, pin
, p_port
, n_port
, attrs
, invert
):
393 self
._check
_feature
("differential tristate", pin
, attrs
,
394 valid_xdrs
=(0, 1, 2), valid_attrs
=True)
396 i
, o
, t
= self
._get
_xdr
_buffer
(m
, pin
, o_invert
=invert
)
397 for bit
in range(len(p_port
)):
398 m
.submodules
["{}_{}".format(pin
.name
, bit
)] = Instance("OBUFTDS",
401 o_O
=p_port
[bit
], o_OB
=n_port
[bit
]
405 def get_diff_input_output(self
, pin
, p_port
, n_port
, attrs
, invert
):
406 self
._check
_feature
("differential input/output", pin
, attrs
,
407 valid_xdrs
=(0, 1, 2), valid_attrs
=True)
409 i
, o
, t
= self
._get
_xdr
_buffer
(m
, pin
, i_invert
=invert
, o_invert
=invert
)
410 for bit
in range(len(p_port
)):
411 m
.submodules
["{}_{}".format(pin
.name
, bit
)] = Instance("IOBUFDS",
415 io_IO
=p_port
[bit
], io_IOB
=n_port
[bit
]
419 # The synchronizer implementations below apply the ASYNC_REG attribute. This attribute
420 # prevents inference of shift registers from synchronizer FFs, and constraints the FFs
421 # to be placed as close as possible, ideally in one CLB. This attribute only affects
422 # the synchronizer FFs themselves.
424 def get_ff_sync(self
, ff_sync
):
425 if ff_sync
._max
_input
_delay
is not None:
426 raise NotImplementedError("Platform '{}' does not support constraining input delay "
428 .format(type(self
).__name
__))
431 flops
= [Signal(ff_sync
.i
.shape(), name
="stage{}".format(index
),
432 reset
=ff_sync
._reset
, reset_less
=ff_sync
._reset
_less
,
433 attrs
={"ASYNC_REG": "TRUE"})
434 for index
in range(ff_sync
._stages
)]
435 for i
, o
in zip((ff_sync
.i
, *flops
), flops
):
436 m
.d
[ff_sync
._o
_domain
] += o
.eq(i
)
437 m
.d
.comb
+= ff_sync
.o
.eq(flops
[-1])
440 def get_async_ff_sync(self
, async_ff_sync
):
441 if self
._max
_input
_delay
is not None:
442 raise NotImplementedError("Platform '{}' does not support constraining input delay "
443 "for AsyncFFSynchronizer"
444 .format(type(self
).__name
__))
447 m
.domains
+= ClockDomain("async_ff", async_reset
=True, local
=True)
448 flops
= [Signal(1, name
="stage{}".format(index
), reset
=1,
449 attrs
={"ASYNC_REG": "TRUE"})
450 for index
in range(async_ff_sync
._stages
)]
451 for i
, o
in zip((0, *flops
), flops
):
452 m
.d
.async_ff
+= o
.eq(i
)
454 if async_ff_sync
._edge
== "pos":
455 m
.d
.comb
+= ResetSignal("async_ff").eq(async_ff_sync
.i
)
457 m
.d
.comb
+= ResetSignal("async_ff").eq(~async_ff_sync
.i
)
460 ClockSignal("async_ff").eq(ClockSignal(async_ff_sync
._domain
)),
461 async_ff_sync
.o
.eq(flops
[-1])
466 XilinxSpartan3APlatform
= XilinxSpartan3Or6Platform
467 XilinxSpartan6Platform
= XilinxSpartan3Or6Platform