vendor: `yosys` is not a required tool for proprietary toolchains.
[nmigen.git] / nmigen / vendor / xilinx_spartan_3_6.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__ = ["XilinxSpartan3APlatform", "XilinxSpartan6Platform"]
9
10
11 # The interface to Spartan 3 and 6 are substantially the same. Handle
12 # differences internally using one class and expose user-aliases for
13 # convenience.
14 class XilinxSpartan3Or6Platform(TemplatedPlatform):
15 """
16 Required tools:
17 * ISE toolchain:
18 * ``xst``
19 * ``ngdbuild``
20 * ``map``
21 * ``par``
22 * ``bitgen``
23
24 The environment is populated by running the script specified in the environment variable
25 ``NMIGEN_ENV_ISE``, if present.
26
27 Available overrides:
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``.
36
37 Build products:
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.
53 """
54
55 toolchain = "ISE"
56
57 device = abstractproperty()
58 package = abstractproperty()
59 speed = abstractproperty()
60
61 required_tools = [
62 "xst",
63 "ngdbuild",
64 "map",
65 "par",
66 "bitgen",
67 ]
68
69 @property
70 def family(self):
71 device = self.device.upper()
72 if device.startswith("XC3S"):
73 if device.endswith("A"):
74 return "3A"
75 elif device.endswith("E"):
76 raise NotImplementedError("""Spartan 3E family is not supported
77 as a nMigen platform.""")
78 else:
79 raise NotImplementedError("""Spartan 3 family is not supported
80 as a nMigen platform.""")
81 elif device.startswith("XC6S"):
82 return "6"
83 else:
84 assert False
85
86 file_templates = {
87 **TemplatedPlatform.build_script_templates,
88 "build_{{name}}.sh": r"""
89 # {{autogenerated}}
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")}}
94 """,
95 "{{name}}.v": r"""
96 /* {{autogenerated}} */
97 {{emit_verilog()}}
98 """,
99 "{{name}}.debug.v": r"""
100 /* {{autogenerated}} */
101 {{emit_debug_verilog()}}
102 """,
103 "{{name}}.prj": r"""
104 # {{autogenerated}}
105 {% for file in platform.iter_extra_files(".vhd", ".vhdl") -%}
106 vhdl work {{file}}
107 {% endfor %}
108 {% for file in platform.iter_extra_files(".v") -%}
109 verilog work {{file}}
110 {% endfor %}
111 verilog work {{name}}.v
112 """,
113 "{{name}}.xst": r"""
114 # {{autogenerated}}
115 run
116 -ifn {{name}}.prj
117 -ofn {{name}}.ngc
118 -top {{name}}
119 {% if platform.family in ["3", "3E", "3A"] %}
120 -use_new_parser yes
121 {% endif %}
122 -p {{platform.device}}{{platform.package}}-{{platform.speed}}
123 {{get_override("script_after_run")|default("# (script_after_run placeholder)")}}
124 """,
125 "{{name}}.ucf": r"""
126 # {{autogenerated}}
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}};
132 {% endfor %}
133 {% endfor %}
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%;
137 {% endfor %}
138 {{get_override("add_constraints")|default("# (add_constraints placeholder)")}}
139 """
140 }
141 command_templates = [
142 r"""
143 {{invoke_tool("xst")}}
144 {{get_override("xst_opts")|options}}
145 -ifn {{name}}.xst
146 """,
147 r"""
148 {{invoke_tool("ngdbuild")}}
149 {{quiet("-quiet")}}
150 {{verbose("-verbose")}}
151 {{get_override("ngdbuild_opts")|options}}
152 -uc {{name}}.ucf
153 {{name}}.ngc
154 """,
155 r"""
156 {{invoke_tool("map")}}
157 {{verbose("-detail")}}
158 {{get_override("map_opts")|default([])|options}}
159 -w
160 -o {{name}}_map.ncd
161 {{name}}.ngd
162 {{name}}.pcf
163 """,
164 r"""
165 {{invoke_tool("par")}}
166 {{get_override("par_opts")|default([])|options}}
167 -w
168 {{name}}_map.ncd
169 {{name}}_par.ncd
170 {{name}}.pcf
171 """,
172 r"""
173 {{invoke_tool("bitgen")}}
174 {{get_override("bitgen_opts")|default(["-g Compress"])|options}}
175 -w
176 -g Binary:Yes
177 {{name}}_par.ncd
178 {{name}}.bit
179 """
180 ]
181
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)
195
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
200
201 m = Module()
202 eos = Signal()
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")
208 return m
209
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",
215 a_IOB="TRUE",
216 i_C=clk,
217 i_CE=Const(1),
218 i_CLR=Const(0),
219 i_D=d[bit],
220 o_Q=q[bit]
221 )
222
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",
227 p_SRTYPE="ASYNC",
228 p_INIT_Q0=0, p_INIT_Q1=0,
229 i_C0=clk, i_C1=~clk,
230 i_CE=Const(1),
231 i_S=Const(0), i_R=Const(0),
232 i_D=d[bit],
233 o_Q0=q0[bit], o_Q1=q1[bit]
234 )
235
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",
240 p_SRTYPE="ASYNC",
241 p_INIT=0,
242 i_C0=clk, i_C1=~clk,
243 i_CE=Const(1),
244 i_S=Const(0), i_R=Const(0),
245 i_D0=d0[bit], i_D1=d1[bit],
246 o_Q=q[bit]
247 )
248
249 def get_ineg(y, invert):
250 if invert:
251 a = Signal.like(y, name_suffix="_n")
252 m.d.comb += y.eq(~a)
253 return a
254 else:
255 return y
256
257 def get_oneg(a, invert):
258 if invert:
259 y = Signal.like(a, name_suffix="_n")
260 m.d.comb += y.eq(~a)
261 return y
262 else:
263 return a
264
265 if "i" in pin.dir:
266 if pin.xdr < 2:
267 pin_i = get_ineg(pin.i, i_invert)
268 elif pin.xdr == 2:
269 pin_i0 = get_ineg(pin.i0, i_invert)
270 pin_i1 = get_ineg(pin.i1, i_invert)
271 if "o" in pin.dir:
272 if pin.xdr < 2:
273 pin_o = get_oneg(pin.o, o_invert)
274 elif pin.xdr == 2:
275 pin_o0 = get_oneg(pin.o0, o_invert)
276 pin_o1 = get_oneg(pin.o1, o_invert)
277
278 i = o = t = None
279 if "i" in pin.dir:
280 i = Signal(pin.width, name="{}_xdr_i".format(pin.name))
281 if "o" in pin.dir:
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))
285
286 if pin.xdr == 0:
287 if "i" in pin.dir:
288 i = pin_i
289 if "o" in pin.dir:
290 o = pin_o
291 if pin.dir in ("oe", "io"):
292 t = ~pin.oe
293 elif pin.xdr == 1:
294 if "i" in pin.dir:
295 get_dff(pin.i_clk, i, pin_i)
296 if "o" in pin.dir:
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)
300 elif pin.xdr == 2:
301 if "i" in pin.dir:
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)
307 if "o" in pin.dir:
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)
311 else:
312 assert False
313
314 return (i, o, t)
315
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)
319 m = Module()
320 i, o, t = self._get_xdr_buffer(m, pin, i_invert=invert)
321 for bit in range(len(port)):
322 m.submodules["{}_{}".format(pin.name, bit)] = Instance("IBUF",
323 i_I=port[bit],
324 o_O=i[bit]
325 )
326 return m
327
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)
331 m = Module()
332 i, o, t = self._get_xdr_buffer(m, pin, o_invert=invert)
333 for bit in range(len(port)):
334 m.submodules["{}_{}".format(pin.name, bit)] = Instance("OBUF",
335 i_I=o[bit],
336 o_O=port[bit]
337 )
338 return m
339
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)
343 m = Module()
344 i, o, t = self._get_xdr_buffer(m, pin, o_invert=invert)
345 for bit in range(len(port)):
346 m.submodules["{}_{}".format(pin.name, bit)] = Instance("OBUFT",
347 i_T=t,
348 i_I=o[bit],
349 o_O=port[bit]
350 )
351 return m
352
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)
356 m = Module()
357 i, o, t = self._get_xdr_buffer(m, pin, i_invert=invert, o_invert=invert)
358 for bit in range(len(port)):
359 m.submodules["{}_{}".format(pin.name, bit)] = Instance("IOBUF",
360 i_T=t,
361 i_I=o[bit],
362 o_O=i[bit],
363 io_IO=port[bit]
364 )
365 return m
366
367 def get_diff_input(self, pin, p_port, n_port, attrs, invert):
368 self._check_feature("differential input", pin, attrs,
369 valid_xdrs=(0, 1, 2), valid_attrs=True)
370 m = Module()
371 i, o, t = self._get_xdr_buffer(m, pin, i_invert=invert)
372 for bit in range(len(p_port)):
373 m.submodules["{}_{}".format(pin.name, bit)] = Instance("IBUFDS",
374 i_I=p_port[bit], i_IB=n_port[bit],
375 o_O=i[bit]
376 )
377 return m
378
379 def get_diff_output(self, pin, p_port, n_port, attrs, invert):
380 self._check_feature("differential output", pin, attrs,
381 valid_xdrs=(0, 1, 2), valid_attrs=True)
382 m = Module()
383 i, o, t = self._get_xdr_buffer(m, pin, o_invert=invert)
384 for bit in range(len(p_port)):
385 m.submodules["{}_{}".format(pin.name, bit)] = Instance("OBUFDS",
386 i_I=o[bit],
387 o_O=p_port[bit], o_OB=n_port[bit]
388 )
389 return m
390
391 def get_diff_tristate(self, pin, p_port, n_port, attrs, invert):
392 self._check_feature("differential tristate", pin, attrs,
393 valid_xdrs=(0, 1, 2), valid_attrs=True)
394 m = Module()
395 i, o, t = self._get_xdr_buffer(m, pin, o_invert=invert)
396 for bit in range(len(p_port)):
397 m.submodules["{}_{}".format(pin.name, bit)] = Instance("OBUFTDS",
398 i_T=t,
399 i_I=o[bit],
400 o_O=p_port[bit], o_OB=n_port[bit]
401 )
402 return m
403
404 def get_diff_input_output(self, pin, p_port, n_port, attrs, invert):
405 self._check_feature("differential input/output", pin, attrs,
406 valid_xdrs=(0, 1, 2), valid_attrs=True)
407 m = Module()
408 i, o, t = self._get_xdr_buffer(m, pin, i_invert=invert, o_invert=invert)
409 for bit in range(len(p_port)):
410 m.submodules["{}_{}".format(pin.name, bit)] = Instance("IOBUFDS",
411 i_T=t,
412 i_I=o[bit],
413 o_O=i[bit],
414 io_IO=p_port[bit], io_IOB=n_port[bit]
415 )
416 return m
417
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.
422
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 "
426 "for FFSynchronizer"
427 .format(type(self).__name__))
428
429 m = Module()
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])
437 return m
438
439 def get_async_ff_sync(self, async_ff_sync):
440 if self._max_input_delay is not None:
441 raise NotImplementedError("Platform '{}' does not support constraining input delay "
442 "for AsyncFFSynchronizer"
443 .format(type(self).__name__))
444
445 m = Module()
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)
452
453 if async_ff_sync._edge == "pos":
454 m.d.comb += ResetSignal("async_ff").eq(async_ff_sync.i)
455 else:
456 m.d.comb += ResetSignal("async_ff").eq(~async_ff_sync.i)
457
458 m.d.comb += [
459 ClockSignal("async_ff").eq(ClockSignal(async_ff_sync._domain)),
460 async_ff_sync.o.eq(flops[-1])
461 ]
462
463 return m
464
465 XilinxSpartan3APlatform = XilinxSpartan3Or6Platform
466 XilinxSpartan6Platform = XilinxSpartan3Or6Platform