build.plat: strip internal attributes from Verilog output.
[nmigen.git] / nmigen / vendor / lattice_ice40.py
1 from abc import abstractproperty
2
3 from ..hdl import *
4 from ..build import *
5
6
7 __all__ = ["LatticeICE40Platform"]
8
9
10 class LatticeICE40Platform(TemplatedPlatform):
11 """
12 IceStorm toolchain
13 ------------------
14
15 Required tools:
16 * ``yosys``
17 * ``nextpnr-ice40``
18 * ``icepack``
19
20 The environment is populated by running the script specified in the environment variable
21 ``NMIGEN_ENV_IceStorm``, if present.
22
23 Available overrides:
24 * ``verbose``: enables logging of informational messages to standard error.
25 * ``read_verilog_opts``: adds options for ``read_verilog`` Yosys command.
26 * ``synth_opts``: adds options for ``synth_ice40`` Yosys command.
27 * ``script_after_read``: inserts commands after ``read_ilang`` in Yosys script.
28 * ``script_after_synth``: inserts commands after ``synth_ice40`` in Yosys script.
29 * ``yosys_opts``: adds extra options for ``yosys``.
30 * ``nextpnr_opts``: adds extra options for ``nextpnr-ice40``.
31 * ``add_pre_pack``: inserts commands at the end in pre-pack Python script.
32 * ``add_constraints``: inserts commands at the end in the PCF file.
33
34 Build products:
35 * ``{{name}}.rpt``: Yosys log.
36 * ``{{name}}.json``: synthesized RTL.
37 * ``{{name}}.tim``: nextpnr log.
38 * ``{{name}}.asc``: ASCII bitstream.
39 * ``{{name}}.bin``: binary bitstream.
40
41 iCECube2 toolchain
42 ------------------
43
44 This toolchain comes in two variants: ``LSE-iCECube2`` and ``Synplify-iCECube2``.
45
46 Required tools:
47 * iCECube2 toolchain
48 * ``tclsh``
49
50 The environment is populated by setting the necessary environment variables based on
51 ``NMIGEN_ENV_iCECube2``, which must point to the root of the iCECube2 installation, and
52 is required.
53
54 Available overrides:
55 * ``verbose``: enables logging of informational messages to standard error.
56 * ``lse_opts``: adds options for LSE.
57 * ``script_after_add``: inserts commands after ``add_file`` in Synplify Tcl script.
58 * ``script_after_options``: inserts commands after ``set_option`` in Synplify Tcl script.
59 * ``add_constraints``: inserts commands in SDC file.
60 * ``script_after_flow``: inserts commands after ``run_sbt_backend_auto`` in SBT
61 Tcl script.
62
63 Build products:
64 * ``{{name}}_lse.log`` (LSE) or ``{{name}}_design/{{name}}.htm`` (Synplify): synthesis log.
65 * ``sbt/outputs/router/{{name}}_timing.rpt``: timing report.
66 * ``{{name}}.edf``: EDIF netlist.
67 * ``{{name}}.bin``: binary bitstream.
68 """
69
70 toolchain = None # selected when creating platform
71
72 device = abstractproperty()
73 package = abstractproperty()
74
75 # IceStorm templates
76
77 _nextpnr_device_options = {
78 "iCE40LP384": "--lp384",
79 "iCE40LP1K": "--lp1k",
80 "iCE40LP4K": "--lp8k",
81 "iCE40LP8K": "--lp8k",
82 "iCE40HX1K": "--hx1k",
83 "iCE40HX4K": "--hx8k",
84 "iCE40HX8K": "--hx8k",
85 "iCE40UP5K": "--up5k",
86 "iCE40UP3K": "--up5k",
87 "iCE5LP4K": "--u4k",
88 "iCE5LP2K": "--u4k",
89 "iCE5LP1K": "--u4k",
90 }
91 _nextpnr_package_options = {
92 "iCE40LP4K": ":4k",
93 "iCE40HX4K": ":4k",
94 "iCE40UP3K": "",
95 "iCE5LP2K": "",
96 "iCE5LP1K": "",
97 }
98
99 _icestorm_required_tools = [
100 "yosys",
101 "nextpnr-ice40",
102 "icepack",
103 ]
104 _icestorm_file_templates = {
105 **TemplatedPlatform.build_script_templates,
106 "{{name}}.il": r"""
107 # {{autogenerated}}
108 {{emit_rtlil()}}
109 """,
110 "{{name}}.ys": r"""
111 # {{autogenerated}}
112 {% for file in platform.iter_extra_files(".v") -%}
113 read_verilog {{get_override("read_opts")|options}} {{file}}
114 {% endfor %}
115 {% for file in platform.iter_extra_files(".sv") -%}
116 read_verilog -sv {{get_override("read_opts")|options}} {{file}}
117 {% endfor %}
118 read_ilang {{name}}.il
119 {{get_override("script_after_read")|default("# (script_after_read placeholder)")}}
120 synth_ice40 {{get_override("synth_opts")|options}} -top {{name}}
121 {{get_override("script_after_synth")|default("# (script_after_synth placeholder)")}}
122 write_json {{name}}.json
123 """,
124 "{{name}}_pre_pack.py": r"""
125 # {{autogenerated}}
126 {% for signal, frequency in platform.iter_clock_constraints() -%}
127 {# Clock in MHz #}
128 ctx.addClock("{{signal|hierarchy(".")}}", {{frequency/1000000}})
129 {% endfor%}
130 {{get_override("add_pre_pack")|default("# (add_pre_pack placeholder)")}}
131 """,
132 "{{name}}.pcf": r"""
133 # {{autogenerated}}
134 {% for port_name, pin_name, attrs in platform.iter_port_constraints_bits() -%}
135 set_io {{port_name}} {{pin_name}}
136 {% endfor %}
137 {{get_override("add_constraints")|default("# (add_constraints placeholder)")}}
138 """,
139 }
140 _icestorm_command_templates = [
141 r"""
142 {{get_tool("yosys")}}
143 {{quiet("-q")}}
144 {{get_override("yosys_opts")|options}}
145 -l {{name}}.rpt
146 {{name}}.ys
147 """,
148 r"""
149 {{get_tool("nextpnr-ice40")}}
150 {{quiet("--quiet")}}
151 {{get_override("nextpnr_opts")|options}}
152 --log {{name}}.tim
153 {{platform._nextpnr_device_options[platform.device]}}
154 --package
155 {{platform.package|lower}}{{platform._nextpnr_package_options[platform.device]}}
156 --json {{name}}.json
157 --pcf {{name}}.pcf
158 --pre-pack {{name}}_pre_pack.py
159 --asc {{name}}.asc
160 """,
161 r"""
162 {{get_tool("icepack")}}
163 {{verbose("-v")}}
164 {{name}}.asc
165 {{name}}.bin
166 """
167 ]
168
169 # iCECube2 templates
170
171 _icecube2_required_tools = [
172 "synthesis",
173 "synpwrap",
174 "tclsh",
175 ]
176 _icecube2_file_templates = {
177 **TemplatedPlatform.build_script_templates,
178 "build_{{name}}.sh": r"""
179 # {{autogenerated}}
180 set -e{{verbose("x")}}
181 if [ -n "${{platform._toolchain_env_var}}" ]; then
182 # LSE environment
183 export LD_LIBRARY_PATH=${{platform._toolchain_env_var}}/LSE/bin/lin64:$LD_LIBRARY_PATH
184 export PATH=${{platform._toolchain_env_var}}/LSE/bin/lin64:$PATH
185 export FOUNDRY=${{platform._toolchain_env_var}}/LSE
186 # Synplify environment
187 export LD_LIBRARY_PATH=${{platform._toolchain_env_var}}/sbt_backend/bin/linux/opt/synpwrap:$LD_LIBRARY_PATH
188 export PATH=${{platform._toolchain_env_var}}/sbt_backend/bin/linux/opt/synpwrap:$PATH
189 export SYNPLIFY_PATH=${{platform._toolchain_env_var}}/synpbase
190 # Common environment
191 export SBT_DIR=${{platform._toolchain_env_var}}/sbt_backend
192 else
193 echo "Variable ${{platform._toolchain_env_var}} must be set" >&2; exit 1
194 fi
195 {{emit_commands("sh")}}
196 """,
197 "{{name}}.v": r"""
198 /* {{autogenerated}} */
199 {{emit_verilog()}}
200 """,
201 "{{name}}.debug.v": r"""
202 /* {{autogenerated}} */
203 {{emit_debug_verilog()}}
204 """,
205 "{{name}}_lse.prj": r"""
206 # {{autogenerated}}
207 -a SBT{{platform.family}}
208 -d {{platform.device}}
209 -t {{platform.package}}
210 {{get_override("lse_opts")|options|default("# (lse_opts placeholder)")}}
211 {% for file in platform.iter_extra_files(".v") -%}
212 -ver {{file}}
213 {% endfor %}
214 -ver {{name}}.v
215 -sdc {{name}}.sdc
216 -top {{name}}
217 -output_edif {{name}}.edf
218 -logfile {{name}}_lse.log
219 """,
220 "{{name}}_syn.prj": r"""
221 # {{autogenerated}}
222 {% for file in platform.iter_extra_files(".v", ".sv", ".vhd", ".vhdl") -%}
223 add_file -verilog {{file}}
224 {% endfor %}
225 add_file -verilog {{name}}.v
226 add_file -constraint {{name}}.sdc
227 {{get_override("script_after_add")|default("# (script_after_add placeholder)")}}
228 impl -add {{name}}_design -type fpga
229 set_option -technology SBT{{platform.family}}
230 set_option -part {{platform.device}}
231 set_option -package {{platform.package}}
232 {{get_override("script_after_options")|default("# (script_after_options placeholder)")}}
233 project -result_format edif
234 project -result_file {{name}}.edf
235 impl -active {{name}}_design
236 project -run compile
237 project -run map
238 project -run fpga_mapper
239 file copy -force -- {{name}}_design/{{name}}.edf {{name}}.edf
240 """,
241 "{{name}}.sdc": r"""
242 # {{autogenerated}}
243 {% for signal, frequency in platform.iter_clock_constraints() -%}
244 create_clock -name {{signal.name}} -period {{1000000000/frequency}} [get_nets {{signal|hierarchy("/")}}]
245 {% endfor %}
246 {{get_override("add_constraints")|default("# (add_constraints placeholder)")}}
247 """,
248 "{{name}}.tcl": r"""
249 # {{autogenerated}}
250 set device {{platform.device}}-{{platform.package}}
251 set top_module {{name}}
252 set proj_dir .
253 set output_dir .
254 set edif_file {{name}}
255 set tool_options ":edifparser -y {{name}}.pcf"
256 set sbt_root $::env(SBT_DIR)
257 append sbt_tcl $sbt_root "/tcl/sbt_backend_synpl.tcl"
258 source $sbt_tcl
259 run_sbt_backend_auto $device $top_module $proj_dir $output_dir $tool_options $edif_file
260 {{get_override("script_after_file")|default("# (script_after_file placeholder)")}}
261 file copy -force -- sbt/outputs/bitmap/{{name}}_bitmap.bin {{name}}.bin
262 exit
263 """,
264 "{{name}}.pcf": r"""
265 # {{autogenerated}}
266 {% for port_name, pin_name, attrs in platform.iter_port_constraints_bits() -%}
267 set_io {{port_name}} {{pin_name}}
268 {% endfor %}
269 """,
270 }
271 _lse_icecube2_command_templates = [
272 r"""synthesis -f {{name}}_lse.prj""",
273 r"""tclsh {{name}}.tcl""",
274 ]
275 _synplify_icecube2_command_templates = [
276 r"""synpwrap -prj {{name}}_syn.prj -log {{name}}_syn.log""",
277 r"""tclsh {{name}}.tcl""",
278 ]
279
280 # Common logic
281
282 def __init__(self, *, toolchain="IceStorm"):
283 super().__init__()
284
285 assert toolchain in ("IceStorm", "LSE-iCECube2", "Synplify-iCECube2")
286 self.toolchain = toolchain
287
288 @property
289 def family(self):
290 if self.device.startswith("iCE40"):
291 return "iCE40"
292 if self.device.startswith("iCE5"):
293 return "iCE5"
294 assert False
295
296 @property
297 def _toolchain_env_var(self):
298 if self.toolchain == "IceStorm":
299 return f"NMIGEN_ENV_{self.toolchain}"
300 if self.toolchain in ("LSE-iCECube2", "Synplify-iCECube2"):
301 return f"NMIGEN_ENV_iCECube2"
302 assert False
303
304 @property
305 def required_tools(self):
306 if self.toolchain == "IceStorm":
307 return self._icestorm_required_tools
308 if self.toolchain in ("LSE-iCECube2", "Synplify-iCECube2"):
309 return self._icecube2_required_tools
310 assert False
311
312 @property
313 def file_templates(self):
314 if self.toolchain == "IceStorm":
315 return self._icestorm_file_templates
316 if self.toolchain in ("LSE-iCECube2", "Synplify-iCECube2"):
317 return self._icecube2_file_templates
318 assert False
319
320 @property
321 def command_templates(self):
322 if self.toolchain == "IceStorm":
323 return self._icestorm_command_templates
324 if self.toolchain == "LSE-iCECube2":
325 return self._lse_icecube2_command_templates
326 if self.toolchain == "Synplify-iCECube2":
327 return self._synplify_icecube2_command_templates
328 assert False
329
330 def create_missing_domain(self, name):
331 # For unknown reasons (no errata was ever published, and no documentation mentions this
332 # issue), iCE40 BRAMs read as zeroes for ~3 us after configuration and release of internal
333 # global reset. Note that this is a *time-based* delay, generated purely by the internal
334 # oscillator, which may not be observed nor influenced directly. For details, see links:
335 # * https://github.com/cliffordwolf/icestorm/issues/76#issuecomment-289270411
336 # * https://github.com/cliffordwolf/icotools/issues/2#issuecomment-299734673
337 #
338 # To handle this, it is necessary to have a global reset in any iCE40 design that may
339 # potentially instantiate BRAMs, and assert this reset for >3 us after configuration.
340 # (We add a margin of 5x to allow for PVT variation.) If the board includes a dedicated
341 # reset line, this line is ORed with the power on reset.
342 #
343 # The power-on reset timer counts up because the vendor tools do not support initialization
344 # of flip-flops.
345 if name == "sync" and self.default_clk is not None:
346 clk_i = self.request(self.default_clk).i
347 if self.default_rst is not None:
348 rst_i = self.request(self.default_rst).i
349
350 m = Module()
351 # Power-on-reset domain
352 m.domains += ClockDomain("por", reset_less=True, local=True)
353 delay = int(15e-6 * self.default_clk_frequency)
354 timer = Signal(max=delay)
355 ready = Signal()
356 m.d.comb += ClockSignal("por").eq(clk_i)
357 with m.If(timer == delay):
358 m.d.por += ready.eq(1)
359 with m.Else():
360 m.d.por += timer.eq(timer + 1)
361 # Primary domain
362 m.domains += ClockDomain("sync")
363 m.d.comb += ClockSignal("sync").eq(clk_i)
364 if self.default_rst is not None:
365 m.d.comb += ResetSignal("sync").eq(~ready | rst_i)
366 else:
367 m.d.comb += ResetSignal("sync").eq(~ready)
368 return m
369
370 def should_skip_port_component(self, port, attrs, component):
371 # On iCE40, a differential input is placed by only instantiating an SB_IO primitive for
372 # the pin with z=0, which is the non-inverting pin. The pinout unfortunately differs
373 # between LP/HX and UP series:
374 # * for LP/HX, z=0 is DPxxB (B is non-inverting, A is inverting)
375 # * for UP, z=0 is IOB_xxA (A is non-inverting, B is inverting)
376 if attrs.get("IO_STANDARD", "SB_LVCMOS") == "SB_LVDS_INPUT" and component == "n":
377 return True
378 return False
379
380 def _get_io_buffer(self, m, pin, port, attrs, *, i_invert=False, o_invert=False,
381 invert_lut=False):
382 def get_dff(clk, d, q):
383 m.submodules += Instance("$dff",
384 p_CLK_POLARITY=1,
385 p_WIDTH=len(d),
386 i_CLK=clk,
387 i_D=d,
388 o_Q=q)
389
390 def get_ineg(y, invert):
391 if invert_lut:
392 a = Signal.like(y, name_suffix="_x{}".format(1 if invert else 0))
393 for bit in range(len(y)):
394 m.submodules += Instance("SB_LUT4",
395 p_LUT_INIT=Const(0b01 if invert else 0b10, 16),
396 i_I0=a[bit],
397 i_I1=Const(0),
398 i_I2=Const(0),
399 i_I3=Const(0),
400 o_O=y[bit])
401 return a
402 elif invert:
403 a = Signal.like(y, name_suffix="_n")
404 m.d.comb += y.eq(~a)
405 return a
406 else:
407 return y
408
409 def get_oneg(a, invert):
410 if invert_lut:
411 y = Signal.like(a, name_suffix="_x{}".format(1 if invert else 0))
412 for bit in range(len(a)):
413 m.submodules += Instance("SB_LUT4",
414 p_LUT_INIT=Const(0b01 if invert else 0b10, 16),
415 i_I0=a[bit],
416 i_I1=Const(0),
417 i_I2=Const(0),
418 i_I3=Const(0),
419 o_O=y[bit])
420 return y
421 elif invert:
422 y = Signal.like(a, name_suffix="_n")
423 m.d.comb += y.eq(~a)
424 return y
425 else:
426 return a
427
428 if "GLOBAL" in attrs:
429 is_global_input = bool(attrs["GLOBAL"])
430 del attrs["GLOBAL"]
431 else:
432 is_global_input = False
433 assert not (is_global_input and i_invert)
434
435 if "i" in pin.dir:
436 if pin.xdr < 2:
437 pin_i = get_ineg(pin.i, i_invert)
438 elif pin.xdr == 2:
439 pin_i0 = get_ineg(pin.i0, i_invert)
440 pin_i1 = get_ineg(pin.i1, i_invert)
441 if "o" in pin.dir:
442 if pin.xdr < 2:
443 pin_o = get_oneg(pin.o, o_invert)
444 elif pin.xdr == 2:
445 pin_o0 = get_oneg(pin.o0, o_invert)
446 pin_o1 = get_oneg(pin.o1, o_invert)
447
448 if "i" in pin.dir and pin.xdr == 2:
449 i0_ff = Signal.like(pin_i0, name_suffix="_ff")
450 i1_ff = Signal.like(pin_i1, name_suffix="_ff")
451 get_dff(pin.i_clk, i0_ff, pin_i0)
452 get_dff(pin.i_clk, i1_ff, pin_i1)
453 if "o" in pin.dir and pin.xdr == 2:
454 o1_ff = Signal.like(pin_o1, name_suffix="_ff")
455 get_dff(pin.o_clk, pin_o1, o1_ff)
456
457 for bit in range(len(port)):
458 io_args = [
459 ("io", "PACKAGE_PIN", port[bit]),
460 *(("p", key, value) for key, value in attrs.items()),
461 ]
462
463 if "i" not in pin.dir:
464 # If no input pin is requested, it is important to use a non-registered input pin
465 # type, because an output-only pin would not have an input clock, and if its input
466 # is configured as registered, this would prevent a co-located input-capable pin
467 # from using an input clock.
468 i_type = 0b01 # PIN_INPUT
469 elif pin.xdr == 0:
470 i_type = 0b01 # PIN_INPUT
471 elif pin.xdr > 0:
472 i_type = 0b00 # PIN_INPUT_REGISTERED aka PIN_INPUT_DDR
473 if "o" not in pin.dir:
474 o_type = 0b0000 # PIN_NO_OUTPUT
475 elif pin.xdr == 0 and pin.dir == "o":
476 o_type = 0b0110 # PIN_OUTPUT
477 elif pin.xdr == 0:
478 o_type = 0b1010 # PIN_OUTPUT_TRISTATE
479 elif pin.xdr == 1 and pin.dir == "o":
480 o_type = 0b0101 # PIN_OUTPUT_REGISTERED
481 elif pin.xdr == 1:
482 o_type = 0b1101 # PIN_OUTPUT_REGISTERED_ENABLE_REGISTERED
483 elif pin.xdr == 2 and pin.dir == "o":
484 o_type = 0b0100 # PIN_OUTPUT_DDR
485 elif pin.xdr == 2:
486 o_type = 0b1100 # PIN_OUTPUT_DDR_ENABLE_REGISTERED
487 io_args.append(("p", "PIN_TYPE", C((o_type << 2) | i_type, 6)))
488
489 if hasattr(pin, "i_clk"):
490 io_args.append(("i", "INPUT_CLK", pin.i_clk))
491 if hasattr(pin, "o_clk"):
492 io_args.append(("i", "OUTPUT_CLK", pin.o_clk))
493
494 if "i" in pin.dir:
495 if pin.xdr == 0 and is_global_input:
496 io_args.append(("o", "GLOBAL_BUFFER_OUTPUT", pin.i[bit]))
497 elif pin.xdr < 2:
498 io_args.append(("o", "D_IN_0", pin_i[bit]))
499 elif pin.xdr == 2:
500 # Re-register both inputs before they enter fabric. This increases hold time
501 # to an entire cycle, and adds one cycle of latency.
502 io_args.append(("o", "D_IN_0", i0_ff[bit]))
503 io_args.append(("o", "D_IN_1", i1_ff[bit]))
504 if "o" in pin.dir:
505 if pin.xdr < 2:
506 io_args.append(("i", "D_OUT_0", pin_o[bit]))
507 elif pin.xdr == 2:
508 # Re-register negedge output after it leaves fabric. This increases setup time
509 # to an entire cycle, and doesn't add latency.
510 io_args.append(("i", "D_OUT_0", pin_o0[bit]))
511 io_args.append(("i", "D_OUT_1", o1_ff[bit]))
512
513 if pin.dir in ("oe", "io"):
514 io_args.append(("i", "OUTPUT_ENABLE", pin.oe))
515
516 if is_global_input:
517 m.submodules["{}_{}".format(pin.name, bit)] = Instance("SB_GB_IO", *io_args)
518 else:
519 m.submodules["{}_{}".format(pin.name, bit)] = Instance("SB_IO", *io_args)
520
521 def get_input(self, pin, port, attrs, invert):
522 self._check_feature("single-ended input", pin, attrs,
523 valid_xdrs=(0, 1, 2), valid_attrs=True)
524 m = Module()
525 self._get_io_buffer(m, pin, port, attrs, i_invert=invert)
526 return m
527
528 def get_output(self, pin, port, attrs, invert):
529 self._check_feature("single-ended output", pin, attrs,
530 valid_xdrs=(0, 1, 2), valid_attrs=True)
531 m = Module()
532 self._get_io_buffer(m, pin, port, attrs, o_invert=invert)
533 return m
534
535 def get_tristate(self, pin, port, attrs, invert):
536 self._check_feature("single-ended tristate", pin, attrs,
537 valid_xdrs=(0, 1, 2), valid_attrs=True)
538 m = Module()
539 self._get_io_buffer(m, pin, port, attrs, o_invert=invert)
540 return m
541
542 def get_input_output(self, pin, port, attrs, invert):
543 self._check_feature("single-ended input/output", pin, attrs,
544 valid_xdrs=(0, 1, 2), valid_attrs=True)
545 m = Module()
546 self._get_io_buffer(m, pin, port, attrs, i_invert=invert, o_invert=invert)
547 return m
548
549 def get_diff_input(self, pin, p_port, n_port, attrs, invert):
550 self._check_feature("differential input", pin, attrs,
551 valid_xdrs=(0, 1, 2), valid_attrs=True)
552 m = Module()
553 # See comment in should_skip_port_component above.
554 self._get_io_buffer(m, pin, p_port, attrs, i_invert=invert)
555 return m
556
557 def get_diff_output(self, pin, p_port, n_port, attrs, invert):
558 self._check_feature("differential output", pin, attrs,
559 valid_xdrs=(0, 1, 2), valid_attrs=True)
560 m = Module()
561 # Note that the non-inverting output pin is not driven the same way as a regular
562 # output pin. The inverter introduces a delay, so for a non-inverting output pin,
563 # an identical delay is introduced by instantiating a LUT. This makes the waveform
564 # perfectly symmetric in the xdr=0 case.
565 self._get_io_buffer(m, pin, p_port, attrs, o_invert= invert, invert_lut=True)
566 self._get_io_buffer(m, pin, n_port, attrs, o_invert=not invert, invert_lut=True)
567 return m
568
569 # Tristate and bidirectional buffers are not supported on iCE40 because it requires external
570 # termination, which is incompatible for input and output differential I/Os.
571
572 # CDC primitives are not currently specialized for iCE40. It is not known if iCECube2 supports
573 # the necessary attributes; nextpnr-ice40 does not.