vendor.intel: implement `add_settings` (QSF) and `add_constraints` (SDC) overrides.
[nmigen.git] / nmigen / vendor / intel.py
1 from abc import abstractproperty
2
3 from ..hdl import *
4 from ..build import *
5
6
7 __all__ = ["IntelPlatform"]
8
9
10 class IntelPlatform(TemplatedPlatform):
11 """
12 Required tools:
13 * ``quartus_map``
14 * ``quartus_fit``
15 * ``quartus_asm``
16 * ``quartus_sta``
17
18 The environment is populated by running the script specified in the environment variable
19 ``NMIGEN_ENV_Quartus``, if present.
20
21 Available overrides:
22 * ``add_settings``: inserts commands at the end of the QSF file.
23 * ``add_constraints``: inserts commands at the end of the SDC file.
24 * ``nproc``: sets the number of cores used by all tools.
25 * ``quartus_map_opts``: adds extra options for ``quartus_map``.
26 * ``quartus_fit_opts``: adds extra options for ``quartus_fit``.
27 * ``quartus_asm_opts``: adds extra options for ``quartus_asm``.
28 * ``quartus_sta_opts``: adds extra options for ``quartus_sta``.
29
30 Build products:
31 * ``*.rpt``: toolchain reports.
32 * ``{{name}}.sof``: bitstream as SRAM object file.
33 * ``{{name}}.rbf``: bitstream as raw binary file.
34 """
35
36 toolchain = "Quartus"
37
38 device = abstractproperty()
39 package = abstractproperty()
40 speed = abstractproperty()
41 suffix = ""
42
43 quartus_suppressed_warnings = [
44 10264, # All case item expressions in this case statement are onehot
45 10270, # Incomplete Verilog case statement has no default case item
46 10335, # Unrecognized synthesis attribute
47 10763, # Verilog case statement has overlapping case item expressions with non-constant or don't care bits
48 10935, # Verilog casex/casez overlaps with a previous casex/vasez item expression
49 12125, # Using design file which is not specified as a design file for the current project, but contains definitions used in project
50 18236, # Number of processors not specified in QSF
51 292013, # Feature is only available with a valid subscription license
52 ]
53
54 required_tools = [
55 "quartus_map",
56 "quartus_fit",
57 "quartus_asm",
58 "quartus_sta",
59 ]
60
61 file_templates = {
62 **TemplatedPlatform.build_script_templates,
63 "build_{{name}}.sh": r"""
64 # {{autogenerated}}
65 if [ -n "${{platform._toolchain_env_var}}" ]; then
66 QUARTUS_ROOTDIR=$(dirname $(dirname "${{platform._toolchain_env_var}}"))
67 # Quartus' qenv.sh does not work with `set -e`.
68 . "${{platform._toolchain_env_var}}"
69 fi
70 set -e{{verbose("x")}}
71 {{emit_commands("sh")}}
72 """,
73 "{{name}}.v": r"""
74 /* {{autogenerated}} */
75 {{emit_verilog()}}
76 """,
77 "{{name}}.debug.v": r"""
78 /* {{autogenerated}} */
79 {{emit_debug_verilog()}}
80 """,
81 "{{name}}.qsf": r"""
82 # {{autogenerated}}
83 {% if get_override("nproc") -%}
84 set_global_assignment -name NUM_PARALLEL_PROCESSORS {{get_override("nproc")}}
85 {% endif %}
86
87 {% for file in platform.iter_files(".v") -%}
88 set_global_assignment -name VERILOG_FILE {{file|tcl_quote}}
89 {% endfor %}
90 {% for file in platform.iter_files(".sv") -%}
91 set_global_assignment -name SYSTEMVERILOG_FILE {{file|tcl_quote}}
92 {% endfor %}
93 {% for file in platform.iter_files(".vhd", ".vhdl") -%}
94 set_global_assignment -name VHDL_FILE {{file|tcl_quote}}
95 {% endfor %}
96 set_global_assignment -name VERILOG_FILE {{name}}.v
97 set_global_assignment -name TOP_LEVEL_ENTITY {{name}}
98
99 set_global_assignment -name DEVICE {{platform.device}}{{platform.package}}{{platform.speed}}{{platform.suffix}}
100 {% for port_name, pin_name, attrs in platform.iter_port_constraints_bits() -%}
101 set_location_assignment -to {{port_name|tcl_quote}} PIN_{{pin_name}}
102 {% for key, value in attrs.items() -%}
103 set_instance_assignment -to {{port_name|tcl_quote}} -name {{key}} {{value|tcl_quote}}
104 {% endfor %}
105 {% endfor %}
106
107 set_global_assignment -name GENERATE_RBF_FILE ON
108
109 {{get_override("add_settings")|default("# (add_settings placeholder)")}}
110 """,
111 "{{name}}.sdc": r"""
112 {% for net_signal, port_signal, frequency in platform.iter_clock_constraints() -%}
113 {% if port_signal is not none -%}
114 create_clock -name {{port_signal.name|tcl_quote}} -period {{1000000000/frequency}} [get_ports {{port_signal.name|tcl_quote}}]
115 {% else -%}
116 create_clock -name {{net_signal.name|tcl_quote}} -period {{1000000000/frequency}} [get_nets {{net_signal|hierarchy("|")|tcl_quote}}]
117 {% endif %}
118 {% endfor %}
119 {{get_override("add_constraints")|default("# (add_constraints placeholder)")}}
120 """,
121 "{{name}}.srf": r"""
122 {% for warning in platform.quartus_suppressed_warnings %}
123 { "" "" "" "{{name}}.v" { } { } 0 {{warning}} "" 0 0 "Design Software" 0 -1 0 ""}
124 {% endfor %}
125 """,
126 }
127 command_templates = [
128 r"""
129 {{invoke_tool("quartus_map")}}
130 {{get_override("quartus_map_opts")|options}}
131 --rev={{name}} {{name}}
132 """,
133 r"""
134 {{invoke_tool("quartus_fit")}}
135 {{get_override("quartus_fit_opts")|options}}
136 --rev={{name}} {{name}}
137 """,
138 r"""
139 {{invoke_tool("quartus_asm")}}
140 {{get_override("quartus_asm_opts")|options}}
141 --rev={{name}} {{name}}
142 """,
143 r"""
144 {{invoke_tool("quartus_sta")}}
145 {{get_override("quartus_sta_opts")|options}}
146 --rev={{name}} {{name}}
147 """,
148 ]
149
150 def add_clock_constraint(self, clock, frequency):
151 super().add_clock_constraint(clock, frequency)
152 clock.attrs["keep"] = "true"
153
154 @property
155 def default_clk_constraint(self):
156 # Internal high-speed oscillator on Cyclone V devices.
157 # It is specified to not be faster than 100MHz, but the actual
158 # frequency seems to vary a lot between devices. Measurements
159 # of 78 to 84 MHz have been observed.
160 if self.default_clk == "cyclonev_oscillator":
161 assert self.device.startswith("5C")
162 return Clock(100e6)
163 # Otherwise, use the defined Clock resource.
164 return super().default_clk_constraint
165
166 def create_missing_domain(self, name):
167 if name == "sync" and self.default_clk == "cyclonev_oscillator":
168 # Use the internal high-speed oscillator for Cyclone V devices
169 assert self.device.startswith("5C")
170 m = Module()
171 m.domains += ClockDomain("sync")
172 m.submodules += Instance("cyclonev_oscillator",
173 i_oscena=Const(1),
174 o_clkout=ClockSignal("sync"))
175 return m
176 else:
177 return super().create_missing_domain(name)
178
179 # The altiobuf_* and altddio_* primitives are explained in the following Intel documents:
180 # * https://www.intel.com/content/dam/www/programmable/us/en/pdfs/literature/ug/ug_altiobuf.pdf
181 # * https://www.intel.com/content/dam/www/programmable/us/en/pdfs/literature/ug/ug_altddio.pdf
182 # See also errata mentioned in: https://www.intel.com/content/www/us/en/programmable/support/support-resources/knowledge-base/solutions/rd11192012_735.html.
183
184 @staticmethod
185 def _get_ireg(m, pin, invert):
186 def get_ineg(i):
187 if invert:
188 i_neg = Signal.like(i, name_suffix="_neg")
189 m.d.comb += i.eq(~i_neg)
190 return i_neg
191 else:
192 return i
193
194 if pin.xdr == 0:
195 return get_ineg(pin.i)
196 elif pin.xdr == 1:
197 i_sdr = Signal(pin.width, name="{}_i_sdr")
198 m.submodules += Instance("$dff",
199 p_CLK_POLARITY=1,
200 p_WIDTH=pin.width,
201 i_CLK=pin.i_clk,
202 i_D=i_sdr,
203 o_Q=get_ineg(pin.i),
204 )
205 return i_sdr
206 elif pin.xdr == 2:
207 i_ddr = Signal(pin.width, name="{}_i_ddr".format(pin.name))
208 m.submodules["{}_i_ddr".format(pin.name)] = Instance("altddio_in",
209 p_width=pin.width,
210 i_datain=i_ddr,
211 i_inclock=pin.i_clk,
212 o_dataout_h=get_ineg(pin.i0),
213 o_dataout_l=get_ineg(pin.i1),
214 )
215 return i_ddr
216 assert False
217
218 @staticmethod
219 def _get_oreg(m, pin, invert):
220 def get_oneg(o):
221 if invert:
222 o_neg = Signal.like(o, name_suffix="_neg")
223 m.d.comb += o_neg.eq(~o)
224 return o_neg
225 else:
226 return o
227
228 if pin.xdr == 0:
229 return get_oneg(pin.o)
230 elif pin.xdr == 1:
231 o_sdr = Signal(pin.width, name="{}_o_sdr".format(pin.name))
232 m.submodules += Instance("$dff",
233 p_CLK_POLARITY=1,
234 p_WIDTH=pin.width,
235 i_CLK=pin.o_clk,
236 i_D=get_oneg(pin.o),
237 o_Q=o_sdr,
238 )
239 return o_sdr
240 elif pin.xdr == 2:
241 o_ddr = Signal(pin.width, name="{}_o_ddr".format(pin.name))
242 m.submodules["{}_o_ddr".format(pin.name)] = Instance("altddio_out",
243 p_width=pin.width,
244 o_dataout=o_ddr,
245 i_outclock=pin.o_clk,
246 i_datain_h=get_oneg(pin.o0),
247 i_datain_l=get_oneg(pin.o1),
248 )
249 return o_ddr
250 assert False
251
252 @staticmethod
253 def _get_oereg(m, pin):
254 # altiobuf_ requires an output enable signal for each pin, but pin.oe is 1 bit wide.
255 if pin.xdr == 0:
256 return Repl(pin.oe, pin.width)
257 elif pin.xdr in (1, 2):
258 oe_reg = Signal(pin.width, name="{}_oe_reg".format(pin.name))
259 oe_reg.attrs["useioff"] = "1"
260 m.submodules += Instance("$dff",
261 p_CLK_POLARITY=1,
262 p_WIDTH=pin.width,
263 i_CLK=pin.o_clk,
264 i_D=pin.oe,
265 o_Q=oe_reg,
266 )
267 return oe_reg
268 assert False
269
270 def get_input(self, pin, port, attrs, invert):
271 self._check_feature("single-ended input", pin, attrs,
272 valid_xdrs=(0, 1, 2), valid_attrs=True)
273 if pin.xdr == 1:
274 port.attrs["useioff"] = 1
275
276 m = Module()
277 m.submodules[pin.name] = Instance("altiobuf_in",
278 p_enable_bus_hold="FALSE",
279 p_number_of_channels=pin.width,
280 p_use_differential_mode="FALSE",
281 i_datain=port.io,
282 o_dataout=self._get_ireg(m, pin, invert)
283 )
284 return m
285
286 def get_output(self, pin, port, attrs, invert):
287 self._check_feature("single-ended output", pin, attrs,
288 valid_xdrs=(0, 1, 2), valid_attrs=True)
289 if pin.xdr == 1:
290 port.attrs["useioff"] = 1
291
292 m = Module()
293 m.submodules[pin.name] = Instance("altiobuf_out",
294 p_enable_bus_hold="FALSE",
295 p_number_of_channels=pin.width,
296 p_use_differential_mode="FALSE",
297 p_use_oe="FALSE",
298 i_datain=self._get_oreg(m, pin, invert),
299 o_dataout=port.io,
300 )
301 return m
302
303 def get_tristate(self, pin, port, attrs, invert):
304 self._check_feature("single-ended tristate", pin, attrs,
305 valid_xdrs=(0, 1, 2), valid_attrs=True)
306 if pin.xdr == 1:
307 port.attrs["useioff"] = 1
308
309 m = Module()
310 m.submodules[pin.name] = Instance("altiobuf_out",
311 p_enable_bus_hold="FALSE",
312 p_number_of_channels=pin.width,
313 p_use_differential_mode="FALSE",
314 p_use_oe="TRUE",
315 i_datain=self._get_oreg(m, pin, invert),
316 o_dataout=port.io,
317 i_oe=self._get_oereg(m, pin)
318 )
319 return m
320
321 def get_input_output(self, pin, port, attrs, invert):
322 self._check_feature("single-ended input/output", pin, attrs,
323 valid_xdrs=(0, 1, 2), valid_attrs=True)
324 if pin.xdr == 1:
325 port.attrs["useioff"] = 1
326
327 m = Module()
328 m.submodules[pin.name] = Instance("altiobuf_bidir",
329 p_enable_bus_hold="FALSE",
330 p_number_of_channels=pin.width,
331 p_use_differential_mode="FALSE",
332 i_datain=self._get_oreg(m, pin, invert),
333 io_dataio=port.io,
334 o_dataout=self._get_ireg(m, pin, invert),
335 i_oe=self._get_oereg(m, pin),
336 )
337 return m
338
339 def get_diff_input(self, pin, port, attrs, invert):
340 self._check_feature("differential input", pin, attrs,
341 valid_xdrs=(0, 1, 2), valid_attrs=True)
342 if pin.xdr == 1:
343 port.p.attrs["useioff"] = 1
344 port.n.attrs["useioff"] = 1
345
346 m = Module()
347 m.submodules[pin.name] = Instance("altiobuf_in",
348 p_enable_bus_hold="FALSE",
349 p_number_of_channels=pin.width,
350 p_use_differential_mode="TRUE",
351 i_datain=port.p,
352 i_datain_b=port.n,
353 o_dataout=self._get_ireg(m, pin, invert)
354 )
355 return m
356
357 def get_diff_output(self, pin, port, attrs, invert):
358 self._check_feature("differential output", pin, attrs,
359 valid_xdrs=(0, 1, 2), valid_attrs=True)
360 if pin.xdr == 1:
361 port.p.attrs["useioff"] = 1
362 port.n.attrs["useioff"] = 1
363
364 m = Module()
365 m.submodules[pin.name] = Instance("altiobuf_out",
366 p_enable_bus_hold="FALSE",
367 p_number_of_channels=pin.width,
368 p_use_differential_mode="TRUE",
369 p_use_oe="FALSE",
370 i_datain=self._get_oreg(m, pin, invert),
371 o_dataout=port.p,
372 o_dataout_b=port.n,
373 )
374 return m
375
376 def get_diff_tristate(self, pin, port, attrs, invert):
377 self._check_feature("differential tristate", pin, attrs,
378 valid_xdrs=(0, 1, 2), valid_attrs=True)
379 if pin.xdr == 1:
380 port.p.attrs["useioff"] = 1
381 port.n.attrs["useioff"] = 1
382
383 m = Module()
384 m.submodules[pin.name] = Instance("altiobuf_out",
385 p_enable_bus_hold="FALSE",
386 p_number_of_channels=pin.width,
387 p_use_differential_mode="TRUE",
388 p_use_oe="TRUE",
389 i_datain=self._get_oreg(m, pin, invert),
390 o_dataout=port.p,
391 o_dataout_b=port.n,
392 i_oe=self._get_oereg(m, pin),
393 )
394 return m
395
396 def get_diff_input_output(self, pin, port, attrs, invert):
397 self._check_feature("differential input/output", pin, attrs,
398 valid_xdrs=(0, 1, 2), valid_attrs=True)
399 if pin.xdr == 1:
400 port.p.attrs["useioff"] = 1
401 port.n.attrs["useioff"] = 1
402
403 m = Module()
404 m.submodules[pin.name] = Instance("altiobuf_bidir",
405 p_enable_bus_hold="FALSE",
406 p_number_of_channels=pin.width,
407 p_use_differential_mode="TRUE",
408 i_datain=self._get_oreg(m, pin, invert),
409 io_dataio=port.p,
410 io_dataio_b=port.n,
411 o_dataout=self._get_ireg(m, pin, invert),
412 i_oe=self._get_oereg(m, pin),
413 )
414 return m
415
416 # The altera_std_synchronizer{,_bundle} megafunctions embed SDC constraints that mark false
417 # paths, so use them instead of our default implementation.
418
419 def get_ff_sync(self, ff_sync):
420 return Instance("altera_std_synchronizer_bundle",
421 p_width=len(ff_sync.i),
422 p_depth=ff_sync._stages,
423 i_clk=ClockSignal(ff_sync._o_domain),
424 i_reset_n=Const(1),
425 i_din=ff_sync.i,
426 o_dout=ff_sync.o,
427 )
428
429 def get_async_ff_sync(self, async_ff_sync):
430 m = Module()
431 sync_output = Signal()
432 if async_ff_sync._edge == "pos":
433 m.submodules += Instance("altera_std_synchronizer",
434 p_depth=async_ff_sync._stages,
435 i_clk=ClockSignal(async_ff_sync._o_domain),
436 i_reset_n=~async_ff_sync.i,
437 i_din=Const(1),
438 o_dout=sync_output,
439 )
440 else:
441 m.submodules += Instance("altera_std_synchronizer",
442 p_depth=async_ff_sync._stages,
443 i_clk=ClockSignal(async_ff_sync._o_domain),
444 i_reset_n=async_ff_sync.i,
445 i_din=Const(1),
446 o_dout=sync_output,
447 )
448 m.d.comb += async_ff_sync.o.eq(~sync_output)
449 return m