--- /dev/null
+from abc import abstractproperty
+import os
+import subprocess
+import tempfile
+
+from ...build import *
+
+
+__all__ = ["LatticeICE40Platform", "IceStormProgrammerMixin", "IceBurnProgrammerMixin"]
+
+
+class LatticeICE40Platform(TemplatedPlatform):
+ """
+ Required tools:
+ * ``yosys``
+ * ``nextpnr-ice40``
+ * ``icepack``
+
+ Available overrides:
+ * ``verbose``: enables logging of informational messages to standard error.
+ * ``read_opts``: adds options for ``read`` Yosys command.
+ * ``synth_opts``: adds options for ``synth_ice40`` Yosys command.
+ * ``script_after_read``: inserts commands after ``read_ilang`` in Yosys script.
+ * ``script_after_synth``: inserts commands after ``synth_ice40`` in Yosys script.
+ * ``yosys_opts``: overrides default options (``-q``) for Yosys.
+ * ``nextpnr_opts``: overrides default options (``-q --placer heap``).
+
+ Build products:
+ * ``{{name}}.rpt``: Yosys log.
+ * ``{{name}}.json``: synthesized RTL.
+ * ``{{name}}.tim``: nextpnr log.
+ * ``{{name}}.asc``: ASCII bitstream.
+ * ``{{name}}.bin``: binary bitstream.
+ """
+
+ device = abstractproperty()
+ package = abstractproperty()
+
+ file_templates = {
+ **TemplatedPlatform.build_script_templates,
+ "{{name}}.il": r"""
+ # {{autogenerated}}
+ {{emit_design("rtlil")}}
+ """,
+ "{{name}}.ys": r"""
+ # {{autogenerated}}
+ {% for file in platform.extra_files %}
+ {% if file.endswith(".v") -%}
+ read_verilog {{get_override("read_opts")|join(" ")}} {{file}}
+ {% elif file.endswith(".sv") -%}
+ read_verilog {{get_override("read_opts")|join(" ")}} {{file}}
+ {% endif %}
+ {% endfor %}
+ read_ilang {{name}}.il
+ {{get_override("script_after_read")|default("# (script_after_read placeholder)")}}
+ synth_ice40 {{get_override("synth_opts")|join(" ")}} -top {{name}}
+ {{get_override("script_after_synth")|default("# (script_after_synth placeholder)")}}
+ write_json {{name}}.json
+ """,
+ "{{name}}.pcf": r"""
+ # {{autogenerated}}
+ {% for port, pins, extra in platform.iter_port_constraints() %}
+ {% if pins|count > 1 %}
+ {% for bit in range -%}
+ set_io {{port}}[{{bit}}] {{pins[bit]}}
+ {% endfor %}
+ {% else -%}
+ set_io {{port}} {{pins[0]}}
+ {% endif %}
+ {% endfor %}
+ """,
+ "{{name}}_pre_pack.py": r"""
+ # {{autogenerated}}
+ {% for port, frequency in platform.iter_clock_constraints() -%}
+ {# Clock in MHz #}
+ ctx.addClock("{{port}}", {{frequency/1000000}})
+ {% endfor%}
+ """,
+ }
+ command_templates = [
+ r"""
+ {{get_tool("yosys")}}
+ {{quiet("-q")}}
+ {{get_override("yosys_opts")|join(" ")}}
+ -l {{name}}.rpt
+ {{name}}.ys
+ """,
+ r"""
+ {{get_tool("nextpnr-ice40")}}
+ {{quiet("-q")}}
+ {{get_override("nextpnr_opts")|default(["--placer","heap"])|join(" ")}}
+ -l {{name}}.tim
+ --{{platform.device}}
+ --package {{platform.package}}
+ --json {{name}}.json
+ --pcf {{name}}.pcf
+ --pre-pack {{name}}_pre_pack.py
+ --asc {{name}}.asc
+ """,
+ r"""
+ {{get_tool("icepack")}}
+ {{name}}.asc
+ {{name}}.bin
+ """
+ ]
+
+
+class IceStormProgrammerMixin:
+ def toolchain_program(self, products, name, *, mode=None):
+ if mode is None and hasattr(self, "prog_mode"):
+ mode = self.prog_mode
+ if mode not in ("sram", "flash"):
+ raise ValueError("iceprog mode must be one of \"sram\" or \"flash\", not {!r}; "
+ "specify it using .build(..., program_opts={\"mode\": \"<mode>\"})"
+ .format(mode))
+
+ iceprog = os.environ.get("ICEPROG", "iceprog")
+ bitstream = products.get("{}.bin".format(name))
+ if mode == "sram":
+ options = ["-S"]
+ if mode == "flash":
+ options = []
+ with tempfile.NamedTemporaryFile(prefix="nmigen_iceprog_") as bitstream_file:
+ bitstream_file.write(bitstream)
+ subprocess.run([iceprog, *options, bitstream_file.name], check=True)
+
+
+class IceBurnProgrammerMixin:
+ def toolchain_program(self, products, name):
+ iceburn = os.environ.get("ICEBURN", "iCEburn")
+ bitstream = products.get("{}.bin".format(name))
+ with tempfile.NamedTemporaryFile(prefix="nmigen_iceburn_") as bitstream_file:
+ bitstream_file.write(bitstream)
+ subprocess.run([iceburn, "-evw", bitstream_file.name], check=True)