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)