From: whitequark Date: Tue, 4 Jun 2019 09:47:04 +0000 (+0000) Subject: vendor.board: split off into nmigen-boards package. X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=33cd18c9c047241b3b0d76320b7577214f734ff7;p=nmigen.git vendor.board: split off into nmigen-boards package. 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. --- diff --git a/LICENSE.txt b/LICENSE.txt index da06945..147ab4f 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -1,4 +1,4 @@ -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: diff --git a/README.md b/README.md index 52b4c56..3b4e07d 100644 --- a/README.md +++ b/README.md @@ -25,6 +25,7 @@ Thanks [LambdaConcept][] for being a sponsor of this project! Contact sb [at] m- ### Installation pip install git+https://github.com/m-labs/nmigen.git + pip install git+https://github.com/m-labs/nmigen-boards.git ### Introduction diff --git a/examples/board/blinky.py b/examples/board/blinky.py index 3228163..53f49d0 100644 --- a/examples/board/blinky.py +++ b/examples/board/blinky.py @@ -1,5 +1,5 @@ from nmigen import * -from nmigen.vendor.board.ice40_hx1k_blink_evn import * +from nmigen_boards.ice40_hx1k_blink_evn import * class Blinky(Elaboratable): diff --git a/nmigen/vendor/board/__init__.py b/nmigen/vendor/board/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/nmigen/vendor/board/ice40_hx1k_blink_evn.py b/nmigen/vendor/board/ice40_hx1k_blink_evn.py deleted file mode 100644 index 1039527..0000000 --- a/nmigen/vendor/board/ice40_hx1k_blink_evn.py +++ /dev/null @@ -1,34 +0,0 @@ -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 - ] diff --git a/nmigen/vendor/board/icestick.py b/nmigen/vendor/board/icestick.py deleted file mode 100644 index 0d2ae75..0000000 --- a/nmigen/vendor/board/icestick.py +++ /dev/null @@ -1,56 +0,0 @@ -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" diff --git a/nmigen/vendor/board/tinyfpga_bx.py b/nmigen/vendor/board/tinyfpga_bx.py deleted file mode 100644 index 0031bb7..0000000 --- a/nmigen/vendor/board/tinyfpga_bx.py +++ /dev/null @@ -1,55 +0,0 @@ -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"), - ] diff --git a/nmigen/vendor/conn/__init__.py b/nmigen/vendor/conn/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/nmigen/vendor/conn/pmod.py b/nmigen/vendor/conn/pmod.py deleted file mode 100644 index 5566c55..0000000 --- a/nmigen/vendor/conn/pmod.py +++ /dev/null @@ -1,94 +0,0 @@ -# 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 - ) diff --git a/nmigen/vendor/fpga/__init__.py b/nmigen/vendor/fpga/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/nmigen/vendor/fpga/lattice_ice40.py b/nmigen/vendor/fpga/lattice_ice40.py deleted file mode 100644 index 7355f6d..0000000 --- a/nmigen/vendor/fpga/lattice_ice40.py +++ /dev/null @@ -1,322 +0,0 @@ -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\": \"\"})" - .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) diff --git a/nmigen/vendor/lattice_ice40.py b/nmigen/vendor/lattice_ice40.py new file mode 100644 index 0000000..ab33856 --- /dev/null +++ b/nmigen/vendor/lattice_ice40.py @@ -0,0 +1,286 @@ +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. diff --git a/setup.py b/setup.py index 5307f88..35211a9 100644 --- a/setup.py +++ b/setup.py @@ -19,7 +19,7 @@ setup( 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", },