The iCE40 programmers are also moved, since they're board-specific.
(It looks like iceprog isn't, but it only works with Lattice
evaluation kits.)
Fixes #80.
-Copyright (C) 2011-2018 M-Labs Limited
+Copyright (C) 2011-2019 M-Labs Limited
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
### Installation
pip install git+https://github.com/m-labs/nmigen.git
+ pip install git+https://github.com/m-labs/nmigen-boards.git
### Introduction
from nmigen import *
-from nmigen.vendor.board.ice40_hx1k_blink_evn import *
+from nmigen_boards.ice40_hx1k_blink_evn import *
class Blinky(Elaboratable):
+++ /dev/null
-from ...build import *
-from ..fpga.lattice_ice40 import LatticeICE40Platform, IceBurnProgrammerMixin
-
-
-__all__ = ["ICE40HX1KBlinkEVNPlatform"]
-
-
-class ICE40HX1KBlinkEVNPlatform(IceBurnProgrammerMixin, LatticeICE40Platform):
- device = "hx1k"
- package = "vq100"
- clocks = [
- ("clk3p3", 3.3e6),
- ]
- resources = [
- Resource("clk3p3", 0, Pins("13", dir="i"),
- extras={"GLOBAL": "1", "IO_STANDARD": "SB_LVCMOS33"}),
-
- Resource("user_led", 0, Pins("59", dir="o"), extras={"IO_STANDARD": "SB_LVCMOS33"}),
- Resource("user_led", 1, Pins("56", dir="o"), extras={"IO_STANDARD": "SB_LVCMOS33"}),
- Resource("user_led", 2, Pins("53", dir="o"), extras={"IO_STANDARD": "SB_LVCMOS33"}),
- Resource("user_led", 3, Pins("51", dir="o"), extras={"IO_STANDARD": "SB_LVCMOS33"}),
-
- Resource("user_btn", 0, Pins("60"), extras={"IO_STANDARD": "SB_LVCMOS33"}),
- Resource("user_btn", 1, Pins("57"), extras={"IO_STANDARD": "SB_LVCMOS33"}),
- Resource("user_btn", 2, Pins("54"), extras={"IO_STANDARD": "SB_LVCMOS33"}),
- Resource("user_btn", 3, Pins("52"), extras={"IO_STANDARD": "SB_LVCMOS33"}),
- ]
- connectors = [
- Connector("pmod", 1, "10 9 8 7 - - 4 3 2 1 - -"), # J1
- Connector("pmod", 5, "40 42 62 64 - - 37 41 63 45 - -"), # J5
- Connector("pmod", 6, "25 24 21 20 - - 26 27 28 33 - -"), # J6
- Connector("pmod", 11, "49 45 46 48 - -"), # J11
- Connector("pmod", 12, "59 56 53 51 - -"), # J12
- ]
+++ /dev/null
-from ...build import *
-from ..fpga.lattice_ice40 import LatticeICE40Platform, IceStormProgrammerMixin
-
-
-__all__ = ["ICEStickPlatform"]
-
-
-class ICEStickPlatform(IceStormProgrammerMixin, LatticeICE40Platform):
- device = "hx1k"
- package = "tq144"
- clocks = [
- ("clk12", 12e6),
- ]
- resources = [
- Resource("clk12", 0, Pins("21", dir="i"),
- extras={"GLOBAL": "1", "IO_STANDARD": "SB_LVCMOS33"}),
-
- Resource("user_led", 0, Pins("99", dir="o"), extras={"IO_STANDARD": "SB_LVCMOS33"}),
- Resource("user_led", 1, Pins("98", dir="o"), extras={"IO_STANDARD": "SB_LVCMOS33"}),
- Resource("user_led", 2, Pins("97", dir="o"), extras={"IO_STANDARD": "SB_LVCMOS33"}),
- Resource("user_led", 3, Pins("96", dir="o"), extras={"IO_STANDARD": "SB_LVCMOS33"}),
- Resource("user_led", 4, Pins("95", dir="o"), extras={"IO_STANDARD": "SB_LVCMOS33"}),
-
- Resource("serial", 0,
- Subsignal("rx", Pins("9", dir="i")),
- Subsignal("tx", Pins("8", dir="o")),
- Subsignal("rts", Pins("7", dir="o")),
- Subsignal("cts", Pins("4", dir="i")),
- Subsignal("dtr", Pins("3", dir="o")),
- Subsignal("dsr", Pins("2", dir="i")),
- Subsignal("dcd", Pins("1", dir="i")),
- extras={"IO_STANDARD": "SB_LVTTL", "PULLUP": "1"}
- ),
-
- Resource("irda", 0,
- Subsignal("rx", Pins("106", dir="i")),
- Subsignal("tx", Pins("105", dir="o")),
- Subsignal("sd", Pins("107", dir="o")),
- extras={"IO_STANDARD": "SB_LVCMOS33"}
- ),
-
- Resource("spiflash", 0,
- Subsignal("cs_n", Pins("71", dir="o")),
- Subsignal("clk", Pins("70", dir="o")),
- Subsignal("mosi", Pins("67", dir="o")),
- Subsignal("miso", Pins("68", dir="i")),
- extras={"IO_STANDARD": "SB_LVCMOS33"}
- ),
- ]
- connectors = [
- Connector("pmod", 0, "78 79 80 81 - - 87 88 90 91 - -"), # J2
-
- Connector("j", 1, "- - 112 113 114 115 116 117 118 119"), # J1
- Connector("j", 3, "- - 62 61 60 56 48 47 45 44"), # J3
- ]
- prog_mode = "flash"
+++ /dev/null
-from ...build import *
-from ..fpga.lattice_ice40 import LatticeICE40Platform, TinyProgrammerMixin
-
-
-__all__ = ["TinyFPGABXPlatform"]
-
-
-class TinyFPGABXPlatform(TinyProgrammerMixin, LatticeICE40Platform):
- device = "lp8k"
- package = "cm81"
- clocks = [
- ("clk16", 16e6),
- ]
- resources = [
- Resource("clk16", 0, Pins("B2", dir="i"),
- extras={"IO_STANDARD": "SB_LVCMOS33"}),
-
- Resource("user_led", 0, Pins("B3", dir="o"), extras={"IO_STANDARD": "SB_LVCMOS33"}),
-
- Resource("usb", 0,
- Subsignal("d_p", Pins("B4", dir="io")),
- Subsignal("d_n", Pins("A4", dir="io")),
- Subsignal("pullup", Pins("A3", dir="o")),
- extras={"IO_STANDARD": "SB_LVCMOS33"}
- ),
-
- Resource("spiflash", 0,
- Subsignal("cs_n", Pins("F7", dir="o")),
- Subsignal("clk", Pins("G7", dir="o")),
- Subsignal("mosi", Pins("G6", dir="o")),
- Subsignal("miso", Pins("H7", dir="i")),
- Subsignal("wp", Pins("H4", dir="o")),
- Subsignal("hold", Pins("J8", dir="o")),
- extras={"IO_STANDARD": "SB_LVCMOS33"}
- ),
-
- Resource("spiflash4x", 0,
- Subsignal("cs_n", Pins("F7", dir="o")),
- Subsignal("clk", Pins("G7", dir="o")),
- Subsignal("dq", Pins("G6 H7 H4 J8", dir="io")),
- extras={"IO_STANDARD": "SB_LVCMOS33"}
- ),
- ]
- connectors = [
- Connector("gpio", 0,
- # Left side of the board
- # 1 2 3 4 5 6 7 8 9 10 11 12 13
- " A2 A1 B1 C2 C1 D2 D1 E2 E1 G2 H1 J1 H2"
- # Right side of the board
- # 14 15 16 17 18 19 20 21 22 23 24
- " H9 D9 D8 B8 A9 B8 A8 B7 A7 B6 A6"
- # Bottom of the board
- # 25 26 27 28 29 30 31
- "G1 J3 J4 G9 J9 E8 J2"),
- ]
+++ /dev/null
-# Reference: https://www.digilentinc.com/Pmods/Digilent-Pmod_%20Interface_Specification.pdf
-
-from ...build import *
-
-
-__all__ = [
- "PmodGPIOType1Resource",
- "PmodSPIType2Resource",
- "PmodSPIType2AResource",
- "PmodUARTType3Resource",
- "PmodUARTType4Resource",
- "PmodUARTType4AResource",
- "PmodHBridgeType5Resource",
- "PmodDualHBridgeType6Resource",
-]
-
-
-def PmodGPIOType1Resource(name, number, *, pmod, extras=None):
- return Resource(name, number,
- Pins("1 2 3 4", dir="io", conn=("pmod", pmod)),
- extras=extras
- )
-
-
-def PmodSPIType2Resource(name, number, *, pmod, extras=None):
- return Resource(name, number,
- Subsignal("cs_n", Pins("1", dir="o", conn=("pmod", pmod))),
- Subsignal("clk", Pins("2", dir="o", conn=("pmod", pmod))),
- Subsignal("mosi", Pins("3", dir="o", conn=("pmod", pmod))),
- Subsignal("miso", Pins("4", dir="i", conn=("pmod", pmod))),
- extras=extras
- )
-
-
-def PmodSPIType2AResource(name, number, *, pmod, extras=None):
- return Resource(name, number,
- Subsignal("cs_n", Pins("1", dir="o", conn=("pmod", pmod))),
- Subsignal("clk", Pins("2", dir="o", conn=("pmod", pmod))),
- Subsignal("mosi", Pins("3", dir="o", conn=("pmod", pmod))),
- Subsignal("miso", Pins("4", dir="i", conn=("pmod", pmod))),
- Subsignal("int", Pins("7", dir="i", conn=("pmod", pmod))),
- Subsignal("reset", Pins("8", dir="o", conn=("pmod", pmod))),
- extras=extras
- )
-
-
-def PmodUARTType3Resource(name, number, *, pmod, extras=None):
- return Resource(name, number,
- Subsignal("cts", Pins("1", dir="o", conn=("pmod", pmod))),
- Subsignal("rts", Pins("2", dir="i", conn=("pmod", pmod))),
- Subsignal("rx", Pins("3", dir="i", conn=("pmod", pmod))),
- Subsignal("tx", Pins("4", dir="o", conn=("pmod", pmod))),
- extras=extras
- )
-
-
-def PmodUARTType4Resource(name, number, *, pmod, extras=None):
- return Resource(name, number,
- Subsignal("cts", Pins("1", dir="i", conn=("pmod", pmod))),
- Subsignal("tx", Pins("2", dir="o", conn=("pmod", pmod))),
- Subsignal("rx", Pins("3", dir="i", conn=("pmod", pmod))),
- Subsignal("rts", Pins("4", dir="o", conn=("pmod", pmod))),
- extras=extras
- )
-
-
-def PmodUARTType4AResource(name, number, *, pmod, extras=None):
- return Resource(name, number,
- Subsignal("cts", Pins("1", dir="i", conn=("pmod", pmod))),
- Subsignal("tx", Pins("2", dir="o", conn=("pmod", pmod))),
- Subsignal("rx", Pins("3", dir="i", conn=("pmod", pmod))),
- Subsignal("rts", Pins("4", dir="o", conn=("pmod", pmod))),
- Subsignal("int", Pins("7", dir="i", conn=("pmod", pmod))),
- Subsignal("reset", Pins("8", dir="o", conn=("pmod", pmod))),
- extras=extras
- )
-
-
-def PmodHBridgeType5Resource(name, number, *, pmod, extras=None):
- return Resource(name, number,
- Subsignal("dir", Pins("1", dir="o", conn=("pmod", pmod))),
- Subsignal("en", Pins("2", dir="o", conn=("pmod", pmod))),
- Subsignal("sa", Pins("3", dir="i", conn=("pmod", pmod))),
- Subsignal("sb", Pins("4", dir="i", conn=("pmod", pmod))),
- extras=extras
- )
-
-
-def PmodDualHBridgeType6Resource(name, number, *, pmod, extras=None):
- return Resource(name, number,
- Subsignal("dir", Pins("1 3", dir="o", conn=("pmod", pmod))),
- Subsignal("en", Pins("2 4", dir="o", conn=("pmod", pmod))),
- extras=extras
- )
+++ /dev/null
-from abc import abstractproperty
-import os
-import subprocess
-import tempfile
-
-from ...hdl 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``: adds extra options for Yosys.
- * ``nextpnr_opts``: adds extra and overrides default options (``--placer heap``)
- for nextpnr.
-
- 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_name, pin_name, extras in platform.iter_port_constraints_bits() -%}
- set_io {{port_name}} {{pin_name}}
- {% 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")}}
- {{verbose("-v")}}
- {{name}}.asc
- {{name}}.bin
- """
- ]
-
- def iter_ports(self):
- for res, pin, port in self._ports:
- if isinstance(res.io[0], Pins):
- yield port.io
- elif isinstance(res.io[0], DiffPairs):
- if res.extras.get("IO_STANDARD", "SB_LVCMOS") == "SB_LVDS_INPUT":
- yield port.p
- else:
- yield port.p
- yield port.n
- else:
- assert False
-
- def iter_port_constraints(self):
- for res, pin, port in self._ports:
- if isinstance(res.io[0], Pins):
- yield port.io.name, list(res.io[0].map_names(self._mapping, res)), res.extras
- elif isinstance(res.io[0], DiffPairs):
- if res.extras.get("IO_STANDARD", "SB_LVCMOS") == "SB_LVDS_INPUT":
- yield port.p.name, list(res.io[0].p.map_names(self._mapping, res)), res.extras
- else:
- yield port.p.name, list(res.io[0].p.map_names(self._mapping, res)), res.extras
- yield port.n.name, list(res.io[0].n.map_names(self._mapping, res)), res.extras
- else:
- assert False
-
- def _get_io_buffer(self, m, pin, port, extras, o_invert=None):
- def _get_dff(clk, d, q):
- m.submodules += Instance("$dff",
- p_CLK_POLARITY=0,
- p_WIDTH=len(d),
- i_CLK=clk,
- i_D=d,
- o_Q=q)
-
- def _get_inverter(a, invert):
- if invert is None:
- return a
- else:
- y = Signal.like(a, name="{}_x{}".format(a.name, 1 if invert else 0))
- for bit in range(len(a)):
- m.submodules += Instance("SB_LUT4",
- p_LUT_INIT=0b01 if invert else 0b10,
- i_I0=a[bit],
- i_I1=Const(0),
- i_I2=Const(0),
- i_I3=Const(0),
- o_O=y[bit])
- return y
-
- if "GLOBAL" in extras:
- is_global_input = bool(extras["GLOBAL"])
- del extras["GLOBAL"]
- else:
- is_global_input = False
-
- if "o" in pin.dir:
- if pin.xdr < 2:
- pin_o = _get_inverter(pin.o, o_invert)
- elif pin.xdr == 2:
- pin_o0 = _get_inverter(pin.o0, o_invert)
- pin_o1 = _get_inverter(pin.o1, o_invert)
-
- if "i" in pin.dir and pin.xdr == 2:
- i0_ff = Signal.like(pin.i0, name="{}_ff".format(pin.i0.name))
- i1_ff = Signal.like(pin.i1, name="{}_ff".format(pin.i1.name))
- _get_dff(pin.i_clk, i0_ff, pin.i0)
- _get_dff(pin.i_clk, i1_ff, pin.i1)
- if "o" in pin.dir and pin.xdr == 2:
- o1_ff = Signal.like(pin.o1, name="{}_ff".format(pin.o1.name))
- _get_dff(pin.o_clk, pin_o1, o1_ff)
-
- for bit in range(len(port)):
- io_args = [
- ("io", "PACKAGE_PIN", port[bit]),
- *(("p", key, value) for key, value in extras.items()),
- ]
-
- if "i" not in pin.dir:
- i_type = 0b00 # PIN_NO_INPUT aka PIN_INPUT_REGISTERED
- elif pin.xdr == 0:
- i_type = 0b01 # PIN_INPUT
- elif pin.xdr > 0:
- i_type = 0b00 # PIN_INPUT_REGISTERED
- if "o" not in pin.dir:
- o_type = 0b0000 # PIN_NO_OUTPUT
- elif pin.xdr == 0 and pin.dir == "o":
- o_type = 0b0110 # PIN_OUTPUT
- elif pin.xdr == 0:
- o_type = 0b1010 # PIN_OUTPUT_TRISTATE
- elif pin.xdr == 1 and pin.dir == "o":
- o_type = 0b0101 # PIN_OUTPUT_REGISTERED
- elif pin.xdr == 1:
- o_type = 0b1101 # PIN_OUTPUT_REGISTERED_ENABLE_REGISTERED
- elif pin.xdr == 2 and pin.dir == "o":
- o_type = 0b0100 # PIN_OUTPUT_DDR
- elif pin.xdr == 2:
- o_type = 0b1100 # PIN_OUTPUT_DDR_ENABLE_REGISTERED
- io_args.append(("p", "PIN_TYPE", (o_type << 2) | i_type))
-
- if hasattr(pin, "i_clk"):
- io_args.append(("i", "INPUT_CLK", pin.i_clk))
- if hasattr(pin, "o_clk"):
- io_args.append(("i", "OUTPUT_CLK", pin.o_clk))
-
- if "i" in pin.dir:
- if pin.xdr == 0 and is_global_input:
- io_args.append(("o", "GLOBAL_BUFFER_OUTPUT", pin.i[bit]))
- elif pin.xdr < 2:
- io_args.append(("o", "D_IN_0", pin.i[bit]))
- elif pin.xdr == 2:
- # Re-register both inputs before they enter fabric. This increases hold time
- # to an entire cycle, and adds one cycle of latency.
- io_args.append(("o", "D_IN_0", i0_ff))
- io_args.append(("o", "D_IN_1", i1_ff))
- if "o" in pin.dir:
- if pin.xdr < 2:
- io_args.append(("i", "D_OUT_0", pin_o[bit]))
- elif pin.xdr == 2:
- # Re-register negedge output after it leaves fabric. This increases setup time
- # to an entire cycle, and doesn't add latency.
- io_args.append(("i", "D_OUT_0", pin_o0[bit]))
- io_args.append(("i", "D_OUT_1", o1_ff))
-
- if pin.dir in ("oe", "io"):
- io_args.append(("i", "OUTPUT_ENABLE", pin.oe))
-
- if is_global_input:
- m.submodules += Instance("SB_GB_IO", *io_args)
- else:
- m.submodules += Instance("SB_IO", *io_args)
-
- def get_input(self, pin, port, extras):
- self._check_feature("single-ended input", pin, extras,
- valid_xdrs=(0, 1, 2), valid_extras=True)
- m = Module()
- self._get_io_buffer(m, pin, port, extras)
- return m
-
- def get_output(self, pin, port, extras):
- self._check_feature("single-ended output", pin, extras,
- valid_xdrs=(0, 1, 2), valid_extras=True)
- m = Module()
- self._get_io_buffer(m, pin, port, extras)
- return m
-
- def get_tristate(self, pin, port, extras):
- self._check_feature("single-ended tristate", pin, extras,
- valid_xdrs=(0, 1, 2), valid_extras=True)
- m = Module()
- self._get_io_buffer(m, pin, port, extras)
- return m
-
- def get_input_output(self, pin, port, extras):
- self._check_feature("single-ended input/output", pin, extras,
- valid_xdrs=(0, 1, 2), valid_extras=True)
- m = Module()
- self._get_io_buffer(m, pin, port, extras)
- return m
-
- def get_diff_input(self, pin, p_port, n_port, extras):
- self._check_feature("differential input", pin, extras,
- valid_xdrs=(0, 1, 2), valid_extras=True)
- # On iCE40, a differential input is placed by only instantiating an SB_IO primitive for
- # the pin with z=0, which is the non-inverting pin. The pinout unfortunately differs
- # between LP/HX and UP series:
- # * for LP/HX, z=0 is DPxxB (B is non-inverting, A is inverting)
- # * for UP, z=0 is IOB_xxA (A is non-inverting, B is inverting)
- m = Module()
- self._get_io_buffer(m, pin, p_port, extras)
- return m
-
- def get_diff_output(self, pin, p_port, n_port, extras):
- self._check_feature("differential output", pin, extras,
- valid_xdrs=(0, 1, 2), valid_extras=True)
- m = Module()
- # Note that the non-inverting output pin is not driven the same way as a regular
- # output pin. The inverter introduces a delay, so for a non-inverting output pin,
- # an identical delay is introduced by instantiating a LUT. This makes the waveform
- # perfectly symmetric in the xdr=0 case.
- self._get_io_buffer(m, pin, p_port, extras, o_invert=False)
- self._get_io_buffer(m, pin, n_port, extras, o_invert=True)
- return m
-
- # Tristate and bidirectional buffers are not supported on iCE40 because it requires external
- # termination, which is incompatible for input and output differential I/Os.
-
-
-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")
- if mode == "sram":
- options = ["-S"]
- if mode == "flash":
- options = []
- with products.extract("{}.bin".format(name)) as bitstream_filename:
- subprocess.run([iceprog, *options, bitstream_filename], check=True)
-
-
-class IceBurnProgrammerMixin:
- def toolchain_program(self, products, name):
- iceburn = os.environ.get("ICEBURN", "iCEburn")
- with products.extract("{}.bin".format(name)) as bitstream_filename:
- subprocess.run([iceburn, "-evw", bitstream_filename], check=True)
-
-
-class TinyProgrammerMixin:
- def toolchain_program(self, products, name):
- tinyprog = os.environ.get("TINYPROG", "tinyprog")
- with products.extract("{}.bin".format(name)) as bitstream_filename:
- subprocess.run([tinyprog, "-p", bitstream_filename], check=True)
--- /dev/null
+from abc import abstractproperty
+
+from ..hdl import *
+from ..build import *
+
+
+__all__ = ["LatticeICE40Platform"]
+
+
+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``: adds extra options for Yosys.
+ * ``nextpnr_opts``: adds extra and overrides default options (``--placer heap``)
+ for nextpnr.
+
+ 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_name, pin_name, extras in platform.iter_port_constraints_bits() -%}
+ set_io {{port_name}} {{pin_name}}
+ {% 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")}}
+ {{verbose("-v")}}
+ {{name}}.asc
+ {{name}}.bin
+ """
+ ]
+
+ def iter_ports(self):
+ for res, pin, port in self._ports:
+ if isinstance(res.io[0], Pins):
+ yield port.io
+ elif isinstance(res.io[0], DiffPairs):
+ if res.extras.get("IO_STANDARD", "SB_LVCMOS") == "SB_LVDS_INPUT":
+ yield port.p
+ else:
+ yield port.p
+ yield port.n
+ else:
+ assert False
+
+ def iter_port_constraints(self):
+ for res, pin, port in self._ports:
+ if isinstance(res.io[0], Pins):
+ yield port.io.name, list(res.io[0].map_names(self._mapping, res)), res.extras
+ elif isinstance(res.io[0], DiffPairs):
+ if res.extras.get("IO_STANDARD", "SB_LVCMOS") == "SB_LVDS_INPUT":
+ yield port.p.name, list(res.io[0].p.map_names(self._mapping, res)), res.extras
+ else:
+ yield port.p.name, list(res.io[0].p.map_names(self._mapping, res)), res.extras
+ yield port.n.name, list(res.io[0].n.map_names(self._mapping, res)), res.extras
+ else:
+ assert False
+
+ def _get_io_buffer(self, m, pin, port, extras, o_invert=None):
+ def _get_dff(clk, d, q):
+ m.submodules += Instance("$dff",
+ p_CLK_POLARITY=0,
+ p_WIDTH=len(d),
+ i_CLK=clk,
+ i_D=d,
+ o_Q=q)
+
+ def _get_inverter(a, invert):
+ if invert is None:
+ return a
+ else:
+ y = Signal.like(a, name="{}_x{}".format(a.name, 1 if invert else 0))
+ for bit in range(len(a)):
+ m.submodules += Instance("SB_LUT4",
+ p_LUT_INIT=0b01 if invert else 0b10,
+ i_I0=a[bit],
+ i_I1=Const(0),
+ i_I2=Const(0),
+ i_I3=Const(0),
+ o_O=y[bit])
+ return y
+
+ if "GLOBAL" in extras:
+ is_global_input = bool(extras["GLOBAL"])
+ del extras["GLOBAL"]
+ else:
+ is_global_input = False
+
+ if "o" in pin.dir:
+ if pin.xdr < 2:
+ pin_o = _get_inverter(pin.o, o_invert)
+ elif pin.xdr == 2:
+ pin_o0 = _get_inverter(pin.o0, o_invert)
+ pin_o1 = _get_inverter(pin.o1, o_invert)
+
+ if "i" in pin.dir and pin.xdr == 2:
+ i0_ff = Signal.like(pin.i0, name="{}_ff".format(pin.i0.name))
+ i1_ff = Signal.like(pin.i1, name="{}_ff".format(pin.i1.name))
+ _get_dff(pin.i_clk, i0_ff, pin.i0)
+ _get_dff(pin.i_clk, i1_ff, pin.i1)
+ if "o" in pin.dir and pin.xdr == 2:
+ o1_ff = Signal.like(pin.o1, name="{}_ff".format(pin.o1.name))
+ _get_dff(pin.o_clk, pin_o1, o1_ff)
+
+ for bit in range(len(port)):
+ io_args = [
+ ("io", "PACKAGE_PIN", port[bit]),
+ *(("p", key, value) for key, value in extras.items()),
+ ]
+
+ if "i" not in pin.dir:
+ i_type = 0b00 # PIN_NO_INPUT aka PIN_INPUT_REGISTERED
+ elif pin.xdr == 0:
+ i_type = 0b01 # PIN_INPUT
+ elif pin.xdr > 0:
+ i_type = 0b00 # PIN_INPUT_REGISTERED
+ if "o" not in pin.dir:
+ o_type = 0b0000 # PIN_NO_OUTPUT
+ elif pin.xdr == 0 and pin.dir == "o":
+ o_type = 0b0110 # PIN_OUTPUT
+ elif pin.xdr == 0:
+ o_type = 0b1010 # PIN_OUTPUT_TRISTATE
+ elif pin.xdr == 1 and pin.dir == "o":
+ o_type = 0b0101 # PIN_OUTPUT_REGISTERED
+ elif pin.xdr == 1:
+ o_type = 0b1101 # PIN_OUTPUT_REGISTERED_ENABLE_REGISTERED
+ elif pin.xdr == 2 and pin.dir == "o":
+ o_type = 0b0100 # PIN_OUTPUT_DDR
+ elif pin.xdr == 2:
+ o_type = 0b1100 # PIN_OUTPUT_DDR_ENABLE_REGISTERED
+ io_args.append(("p", "PIN_TYPE", (o_type << 2) | i_type))
+
+ if hasattr(pin, "i_clk"):
+ io_args.append(("i", "INPUT_CLK", pin.i_clk))
+ if hasattr(pin, "o_clk"):
+ io_args.append(("i", "OUTPUT_CLK", pin.o_clk))
+
+ if "i" in pin.dir:
+ if pin.xdr == 0 and is_global_input:
+ io_args.append(("o", "GLOBAL_BUFFER_OUTPUT", pin.i[bit]))
+ elif pin.xdr < 2:
+ io_args.append(("o", "D_IN_0", pin.i[bit]))
+ elif pin.xdr == 2:
+ # Re-register both inputs before they enter fabric. This increases hold time
+ # to an entire cycle, and adds one cycle of latency.
+ io_args.append(("o", "D_IN_0", i0_ff))
+ io_args.append(("o", "D_IN_1", i1_ff))
+ if "o" in pin.dir:
+ if pin.xdr < 2:
+ io_args.append(("i", "D_OUT_0", pin_o[bit]))
+ elif pin.xdr == 2:
+ # Re-register negedge output after it leaves fabric. This increases setup time
+ # to an entire cycle, and doesn't add latency.
+ io_args.append(("i", "D_OUT_0", pin_o0[bit]))
+ io_args.append(("i", "D_OUT_1", o1_ff))
+
+ if pin.dir in ("oe", "io"):
+ io_args.append(("i", "OUTPUT_ENABLE", pin.oe))
+
+ if is_global_input:
+ m.submodules += Instance("SB_GB_IO", *io_args)
+ else:
+ m.submodules += Instance("SB_IO", *io_args)
+
+ def get_input(self, pin, port, extras):
+ self._check_feature("single-ended input", pin, extras,
+ valid_xdrs=(0, 1, 2), valid_extras=True)
+ m = Module()
+ self._get_io_buffer(m, pin, port, extras)
+ return m
+
+ def get_output(self, pin, port, extras):
+ self._check_feature("single-ended output", pin, extras,
+ valid_xdrs=(0, 1, 2), valid_extras=True)
+ m = Module()
+ self._get_io_buffer(m, pin, port, extras)
+ return m
+
+ def get_tristate(self, pin, port, extras):
+ self._check_feature("single-ended tristate", pin, extras,
+ valid_xdrs=(0, 1, 2), valid_extras=True)
+ m = Module()
+ self._get_io_buffer(m, pin, port, extras)
+ return m
+
+ def get_input_output(self, pin, port, extras):
+ self._check_feature("single-ended input/output", pin, extras,
+ valid_xdrs=(0, 1, 2), valid_extras=True)
+ m = Module()
+ self._get_io_buffer(m, pin, port, extras)
+ return m
+
+ def get_diff_input(self, pin, p_port, n_port, extras):
+ self._check_feature("differential input", pin, extras,
+ valid_xdrs=(0, 1, 2), valid_extras=True)
+ # On iCE40, a differential input is placed by only instantiating an SB_IO primitive for
+ # the pin with z=0, which is the non-inverting pin. The pinout unfortunately differs
+ # between LP/HX and UP series:
+ # * for LP/HX, z=0 is DPxxB (B is non-inverting, A is inverting)
+ # * for UP, z=0 is IOB_xxA (A is non-inverting, B is inverting)
+ m = Module()
+ self._get_io_buffer(m, pin, p_port, extras)
+ return m
+
+ def get_diff_output(self, pin, p_port, n_port, extras):
+ self._check_feature("differential output", pin, extras,
+ valid_xdrs=(0, 1, 2), valid_extras=True)
+ m = Module()
+ # Note that the non-inverting output pin is not driven the same way as a regular
+ # output pin. The inverter introduces a delay, so for a non-inverting output pin,
+ # an identical delay is introduced by instantiating a LUT. This makes the waveform
+ # perfectly symmetric in the xdr=0 case.
+ self._get_io_buffer(m, pin, p_port, extras, o_invert=False)
+ self._get_io_buffer(m, pin, n_port, extras, o_invert=True)
+ return m
+
+ # Tristate and bidirectional buffers are not supported on iCE40 because it requires external
+ # termination, which is incompatible for input and output differential I/Os.
packages=find_packages(),
python_requires=">=3.6",
project_urls={
- #"Documentation": "https://glasgow.readthedocs.io/",
+ #"Documentation": "https://nmigen.readthedocs.io/",
"Source Code": "https://github.com/m-labs/nmigen",
"Bug Tracker": "https://github.com/m-labs/nmigen/issues",
},