a4b4d999db97e617278971a97db5c2d7e262a402
[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 "yosys",
63 "xst",
64 "ngdbuild",
65 "map",
66 "par",
67 "bitgen",
68 ]
69
70 @property
71 def family(self):
72 device = self.device.upper()
73 if device.startswith("XC3S"):
74 if device.endswith("A"):
75 return "3A"
76 elif device.endswith("E"):
77 raise NotImplementedError("""Spartan 3E family is not supported
78 as a nMigen platform.""")
79 else:
80 raise NotImplementedError("""Spartan 3 family is not supported
81 as a nMigen platform.""")
82 elif device.startswith("XC6S"):
83 return "6"
84 else:
85 assert False
86
87 file_templates = {
88 **TemplatedPlatform.build_script_templates,
89 "build_{{name}}.sh": r"""
90 # {{autogenerated}}
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")}}
95 """,
96 "{{name}}.v": r"""
97 /* {{autogenerated}} */
98 {{emit_verilog()}}
99 """,
100 "{{name}}.debug.v": r"""
101 /* {{autogenerated}} */
102 {{emit_debug_verilog()}}
103 """,
104 "{{name}}.prj": r"""
105 # {{autogenerated}}
106 {% for file in platform.iter_extra_files(".vhd", ".vhdl") -%}
107 vhdl work {{file}}
108 {% endfor %}
109 {% for file in platform.iter_extra_files(".v") -%}
110 verilog work {{file}}
111 {% endfor %}
112 verilog work {{name}}.v
113 """,
114 "{{name}}.xst": r"""
115 # {{autogenerated}}
116 run
117 -ifn {{name}}.prj
118 -ofn {{name}}.ngc
119 -top {{name}}
120 {% if platform.family in ["3", "3E", "3A"] %}
121 -use_new_parser yes
122 {% endif %}
123 -p {{platform.device}}{{platform.package}}-{{platform.speed}}
124 {{get_override("script_after_run")|default("# (script_after_run placeholder)")}}
125 """,
126 "{{name}}.ucf": r"""
127 # {{autogenerated}}
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}};
133 {% endfor %}
134 {% endfor %}
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%;
138 {% endfor %}
139 {{get_override("add_constraints")|default("# (add_constraints placeholder)")}}
140 """
141 }
142 command_templates = [
143 r"""
144 {{invoke_tool("xst")}}
145 {{get_override("xst_opts")|options}}
146 -ifn {{name}}.xst
147 """,
148 r"""
149 {{invoke_tool("ngdbuild")}}
150 {{quiet("-quiet")}}
151 {{verbose("-verbose")}}
152 {{get_override("ngdbuild_opts")|options}}
153 -uc {{name}}.ucf
154 {{name}}.ngc
155 """,
156 r"""
157 {{invoke_tool("map")}}
158 {{verbose("-detail")}}
159 {{get_override("map_opts")|default([])|options}}
160 -w
161 -o {{name}}_map.ncd
162 {{name}}.ngd
163 {{name}}.pcf
164 """,
165 r"""
166 {{invoke_tool("par")}}
167 {{get_override("par_opts")|default([])|options}}
168 -w
169 {{name}}_map.ncd
170 {{name}}_par.ncd
171 {{name}}.pcf
172 """,
173 r"""
174 {{invoke_tool("bitgen")}}
175 {{get_override("bitgen_opts")|default(["-g Compress"])|options}}
176 -w
177 -g Binary:Yes
178 {{name}}_par.ncd
179 {{name}}.bit
180 """
181 ]
182
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)
196
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
201
202 m = Module()
203 eos = Signal()
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")
209 return m
210
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",
216 a_IOB="TRUE",
217 i_C=clk,
218 i_CE=Const(1),
219 i_CLR=Const(0),
220 i_D=d[bit],
221 o_Q=q[bit]
222 )
223
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",
228 p_SRTYPE="ASYNC",
229 p_INIT_Q0=0, p_INIT_Q1=0,
230 i_C0=clk, i_C1=~clk,
231 i_CE=Const(1),
232 i_S=Const(0), i_R=Const(0),
233 i_D=d[bit],
234 o_Q0=q0[bit], o_Q1=q1[bit]
235 )
236
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",
241 p_SRTYPE="ASYNC",
242 p_INIT=0,
243 i_C0=clk, i_C1=~clk,
244 i_CE=Const(1),
245 i_S=Const(0), i_R=Const(0),
246 i_D0=d0[bit], i_D1=d1[bit],
247 o_Q=q[bit]
248 )
249
250 def get_ineg(y, invert):
251 if invert:
252 a = Signal.like(y, name_suffix="_n")
253 m.d.comb += y.eq(~a)
254 return a
255 else:
256 return y
257
258 def get_oneg(a, invert):
259 if invert:
260 y = Signal.like(a, name_suffix="_n")
261 m.d.comb += y.eq(~a)
262 return y
263 else:
264 return a
265
266 if "i" in pin.dir:
267 if pin.xdr < 2:
268 pin_i = get_ineg(pin.i, i_invert)
269 elif pin.xdr == 2:
270 pin_i0 = get_ineg(pin.i0, i_invert)
271 pin_i1 = get_ineg(pin.i1, i_invert)
272 if "o" in pin.dir:
273 if pin.xdr < 2:
274 pin_o = get_oneg(pin.o, o_invert)
275 elif pin.xdr == 2:
276 pin_o0 = get_oneg(pin.o0, o_invert)
277 pin_o1 = get_oneg(pin.o1, o_invert)
278
279 i = o = t = None
280 if "i" in pin.dir:
281 i = Signal(pin.width, name="{}_xdr_i".format(pin.name))
282 if "o" in pin.dir:
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))
286
287 if pin.xdr == 0:
288 if "i" in pin.dir:
289 i = pin_i
290 if "o" in pin.dir:
291 o = pin_o
292 if pin.dir in ("oe", "io"):
293 t = ~pin.oe
294 elif pin.xdr == 1:
295 if "i" in pin.dir:
296 get_dff(pin.i_clk, i, pin_i)
297 if "o" in pin.dir:
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)
301 elif pin.xdr == 2:
302 if "i" in pin.dir:
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)
308 if "o" in pin.dir:
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)
312 else:
313 assert False
314
315 return (i, o, t)
316
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)
320 m = Module()
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",
324 i_I=port[bit],
325 o_O=i[bit]
326 )
327 return m
328
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)
332 m = Module()
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",
336 i_I=o[bit],
337 o_O=port[bit]
338 )
339 return m
340
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)
344 m = Module()
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",
348 i_T=t,
349 i_I=o[bit],
350 o_O=port[bit]
351 )
352 return m
353
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)
357 m = Module()
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",
361 i_T=t,
362 i_I=o[bit],
363 o_O=i[bit],
364 io_IO=port[bit]
365 )
366 return m
367
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)
371 m = Module()
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],
376 o_O=i[bit]
377 )
378 return m
379
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)
383 m = Module()
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",
387 i_I=o[bit],
388 o_O=p_port[bit], o_OB=n_port[bit]
389 )
390 return m
391
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)
395 m = Module()
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",
399 i_T=t,
400 i_I=o[bit],
401 o_O=p_port[bit], o_OB=n_port[bit]
402 )
403 return m
404
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)
408 m = Module()
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",
412 i_T=t,
413 i_I=o[bit],
414 o_O=i[bit],
415 io_IO=p_port[bit], io_IOB=n_port[bit]
416 )
417 return m
418
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.
423
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 "
427 "for FFSynchronizer"
428 .format(type(self).__name__))
429
430 m = Module()
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])
438 return m
439
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__))
445
446 m = Module()
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)
453
454 if async_ff_sync._edge == "pos":
455 m.d.comb += ResetSignal("async_ff").eq(async_ff_sync.i)
456 else:
457 m.d.comb += ResetSignal("async_ff").eq(~async_ff_sync.i)
458
459 m.d.comb += [
460 ClockSignal("async_ff").eq(ClockSignal(async_ff_sync._domain)),
461 async_ff_sync.o.eq(flops[-1])
462 ]
463
464 return m
465
466 XilinxSpartan3APlatform = XilinxSpartan3Or6Platform
467 XilinxSpartan6Platform = XilinxSpartan3Or6Platform