from abc import abstractproperty import os import subprocess import tempfile from ...hdl.ast import * from ...hdl.dsl import * from ...hdl.ir import * from ...build import * __all__ = ["LatticeICE40Platform", "IceStormProgrammerMixin", "IceBurnProgrammerMixin", "TinyProgrammerMixin"] 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 -sv {{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 """ ] def _get_io_buffer(self, port, extras, fn): m = Module() for bit in range(len(port)): m.submodules += Instance("SB_IO", ("io", "PACKAGE_PIN", port[bit]), *fn(bit), *(("p", key, value) for key, value in extras.items())) return m def get_input(self, pin, port, extras): return self._get_io_buffer(port, extras, lambda bit: [ # PIN_NO_OUTPUT|PIN_INPUT ("p", "PIN_TYPE", 0b0000_01), ("o", "D_IN_0", pin.i[bit]), ]) def get_output(self, pin, port, extras): return self._get_io_buffer(port, extras, lambda bit: [ # PIN_OUTPUT|PIN_INPUT_REGISTERED ("p", "PIN_TYPE", 0b0110_00), ("i", "D_OUT_0", pin.o[bit]), ]) def get_tristate(self, pin, port, extras): return self._get_io_buffer(port, extras, lambda bit: [ # PIN_OUTPUT_TRISTATE|PIN_INPUT_REGISTERED ("p", "PIN_TYPE", 0b1010_00), ("o", "D_IN_0", pin.i[bit]), ("i", "D_OUT_0", pin.o[bit]), ("i", "OUTPUT_ENABLE", pin.oe), ]) 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_", suffix=".bin") 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_", suffix=".bin") as bitstream_file: bitstream_file.write(bitstream) subprocess.run([iceburn, "-evw", bitstream_file.name], check=True) class TinyProgrammerMixin: def toolchain_program(self, products, name): tinyprog = os.environ.get("TINYPROG", "tinyprog") options = ["-p"] bitstream = products.get("{}.bin".format(name)) with tempfile.NamedTemporaryFile(prefix="nmigen_tinyprog_", suffix=".bin") as bitstream_file: bitstream_file.write(bitstream) subprocess.run([tinyprog, *options, bitstream_file.name], check=True)