amaranth/nmigen/vendor/fpga/lattice_ice40.py
whitequark 6fae06aea9 build.{dsl,plat,res}: allow dir="oe".
Although a dir="oe" pin is generally equivalent to dir="io" pin with
the i* signal(s) disconnected, they are not equivalent, because some
pins may not be able to support input buffers at all, either because
there are no input buffers, or because the input buffers are consumed
by some other resource.

E.g. this can happen on iCE40 when the input buffer is consumed by
a PLL.
2019-06-03 04:42:55 +00:00

200 lines
7.5 KiB
Python

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):
self._check_feature("single-ended input", pin, extras,
valid_xdrs=(0,), valid_extras=True)
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):
self._check_feature("single-ended output", pin, extras,
valid_xdrs=(0,), valid_extras=True)
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):
self._check_feature("single-ended tristate", pin, extras,
valid_xdrs=(0,), valid_extras=True)
return self._get_io_buffer(port, extras, lambda bit: [
# PIN_OUTPUT_TRISTATE|PIN_INPUT_REGISTERED
("p", "PIN_TYPE", 0b1010_00),
("i", "D_OUT_0", pin.o[bit]),
("i", "OUTPUT_ENABLE", pin.oe),
])
def get_input_output(self, pin, port, extras):
self._check_feature("single-ended input/output", pin, extras,
valid_xdrs=(0,), valid_extras=True)
return self._get_io_buffer(port, extras, lambda bit: [
# PIN_OUTPUT_TRISTATE|PIN_INPUT
("p", "PIN_TYPE", 0b1010_01),
("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\": \"<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)