vendor.fpga.lattice_ice40: implement.
authorwhitequark <cz@m-labs.hk>
Sat, 1 Jun 2019 16:46:50 +0000 (16:46 +0000)
committerwhitequark <cz@m-labs.hk>
Sat, 1 Jun 2019 16:47:01 +0000 (16:47 +0000)
nmigen/vendor/__init__.py [new file with mode: 0644]
nmigen/vendor/fpga/__init__.py [new file with mode: 0644]
nmigen/vendor/fpga/lattice_ice40.py [new file with mode: 0644]

diff --git a/nmigen/vendor/__init__.py b/nmigen/vendor/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/nmigen/vendor/fpga/__init__.py b/nmigen/vendor/fpga/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/nmigen/vendor/fpga/lattice_ice40.py b/nmigen/vendor/fpga/lattice_ice40.py
new file mode 100644 (file)
index 0000000..3155999
--- /dev/null
@@ -0,0 +1,134 @@
+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)