From 928f55abf74c0692ea03513d05fb0974753904d0 Mon Sep 17 00:00:00 2001 From: whitequark Date: Sat, 1 Jun 2019 16:46:50 +0000 Subject: [PATCH] vendor.fpga.lattice_ice40: implement. --- nmigen/vendor/__init__.py | 0 nmigen/vendor/fpga/__init__.py | 0 nmigen/vendor/fpga/lattice_ice40.py | 134 ++++++++++++++++++++++++++++ 3 files changed, 134 insertions(+) create mode 100644 nmigen/vendor/__init__.py create mode 100644 nmigen/vendor/fpga/__init__.py create mode 100644 nmigen/vendor/fpga/lattice_ice40.py diff --git a/nmigen/vendor/__init__.py b/nmigen/vendor/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/nmigen/vendor/fpga/__init__.py b/nmigen/vendor/fpga/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/nmigen/vendor/fpga/lattice_ice40.py b/nmigen/vendor/fpga/lattice_ice40.py new file mode 100644 index 0000000..3155999 --- /dev/null +++ b/nmigen/vendor/fpga/lattice_ice40.py @@ -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\": \"\"})" + .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) -- 2.30.2