Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: m-labs/nmigen
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: 9ba2efd86b52
Choose a base ref
...
head repository: m-labs/nmigen
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: 6fae06aea9b7
Choose a head ref
  • 2 commits
  • 8 files changed
  • 1 contributor

Commits on Jun 3, 2019

  1. lib.io: 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.
    whitequark committed Jun 3, 2019
    Copy the full SHA
    1eee7cd View commit details
  2. 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.
    whitequark committed Jun 3, 2019
    Copy the full SHA
    6fae06a View commit details
Showing with 98 additions and 22 deletions.
  1. +1 −1 nmigen/build/dsl.py
  2. +23 −2 nmigen/build/plat.py
  3. +5 −5 nmigen/build/res.py
  4. +12 −10 nmigen/lib/io.py
  5. +1 −1 nmigen/test/test_build_dsl.py
  6. +3 −2 nmigen/test/test_build_res.py
  7. +41 −0 nmigen/test/test_lib_io.py
  8. +12 −1 nmigen/vendor/fpga/lattice_ice40.py
2 changes: 1 addition & 1 deletion nmigen/build/dsl.py
Original file line number Diff line number Diff line change
@@ -9,7 +9,7 @@ def __init__(self, names, dir="io"):
self.names = names.split()

if dir not in ("i", "o", "io"):
raise TypeError("Direction must be one of \"i\", \"o\" or \"io\", not {!r}"
raise TypeError("Direction must be one of \"i\", \"o\", \"oe\", or \"io\", not {!r}"
.format(dir))
self.dir = dir

25 changes: 23 additions & 2 deletions nmigen/build/plat.py
Original file line number Diff line number Diff line change
@@ -127,16 +127,20 @@ def add_pin_fragment(pin, pin_fragment):
add_pin_fragment(pin, self.get_input(pin, port, extras))
if pin.dir == "o":
add_pin_fragment(pin, self.get_output(pin, port, extras))
if pin.dir == "oe":
add_pin_fragment(pin, self.get_tristate(pin, port, extras))
if pin.dir == "io":
add_pin_fragment(pin, self.get_input(pin, port, extras))
add_pin_fragment(pin, self.get_input_output(pin, port, extras))

for pin, p_port, n_port, extras in self.iter_differential_pins():
if pin.dir == "i":
add_pin_fragment(pin, self.get_diff_input(pin, p_port, n_port))
if pin.dir == "o":
add_pin_fragment(pin, self.get_diff_output(pin, p_port, n_port))
if pin.dir == "io":
if pin.dir == "oe":
add_pin_fragment(pin, self.get_diff_tristate(pin, p_port, n_port))
if pin.dir == "io":
add_pin_fragment(pin, self.get_diff_input_output(pin, p_port, n_port))

return self.toolchain_prepare(fragment, name, **kwargs)

@@ -187,6 +191,19 @@ def get_tristate(self, pin, port, extras):
self._check_feature("single-ended tristate", pin, extras,
valid_xdrs=(0,), valid_extras=None)

m = Module()
m.submodules += Instance("$tribuf",
p_WIDTH=pin.width,
i_EN=pin.oe,
i_A=pin.o,
o_Y=port,
)
return m

def get_input_output(self, pin, port, extras):
self._check_feature("single-ended input/output", pin, extras,
valid_xdrs=(0,), valid_extras=None)

m = Module()
m.submodules += Instance("$tribuf",
p_WIDTH=pin.width,
@@ -209,6 +226,10 @@ def get_diff_tristate(self, pin, p_port, n_port, extras):
self._check_feature("differential tristate", pin, extras,
valid_xdrs=(), valid_extras=None)

def get_diff_input_output(self, pin, p_port, n_port, extras):
self._check_feature("differential input/output", pin, extras,
valid_xdrs=(), valid_extras=None)


class TemplatedPlatform(Platform):
file_templates = abstractproperty()
10 changes: 5 additions & 5 deletions nmigen/build/res.py
Original file line number Diff line number Diff line change
@@ -84,14 +84,14 @@ def merge_options(subsignal, dir, xdr):
dir = subsignal.io[0].dir
if xdr is None:
xdr = 0
if dir not in ("i", "o", "io", "-"):
raise TypeError("Direction must be one of \"i\", \"o\", \"io\", or \"-\", "
"not {!r}"
if dir not in ("i", "o", "oe", "io", "-"):
raise TypeError("Direction must be one of \"i\", \"o\", \"oe\", \"io\", "
"or \"-\", not {!r}"
.format(dir))
if dir != subsignal.io[0].dir and not (subsignal.io[0].dir == "io" or dir == "-"):
raise ValueError("Direction of {!r} cannot be changed from \"{}\" to \"{}\"; "
"direction can be changed from \"io\" to \"i\", from \"io\""
"to \"o\", or from anything to \"-\""
"direction can be changed from \"io\" to \"i\", \"o\", or "
"\"oe\", or from anything to \"-\""
.format(subsignal.io[0], subsignal.io[0].dir, dir))
if not isinstance(xdr, int) or xdr < 0:
raise ValueError("Data rate of {!r} must be a non-negative integer, not {!r}"
22 changes: 12 additions & 10 deletions nmigen/lib/io.py
Original file line number Diff line number Diff line change
@@ -15,8 +15,8 @@ def pin_layout(width, dir, xdr=1):
if not isinstance(width, int) or width < 1:
raise TypeError("Width must be a positive integer, not '{!r}'"
.format(width))
if dir not in ("i", "o", "io"):
raise TypeError("Direction must be one of \"i\", \"o\" or \"io\", not '{!r}'"""
if dir not in ("i", "o", "oe", "io"):
raise TypeError("Direction must be one of \"i\", \"o\", \"io\", or \"oe\", not '{!r}'"""
.format(dir))
if not isinstance(xdr, int) or xdr < 0:
raise TypeError("Gearing ratio must be a non-negative integer, not '{!r}'"
@@ -29,13 +29,13 @@ def pin_layout(width, dir, xdr=1):
else:
for n in range(xdr):
fields.append(("i{}".format(n), width))
if dir in ("o", "io"):
if dir in ("o", "oe", "io"):
if xdr in (0, 1):
fields.append(("o", width))
else:
for n in range(xdr):
fields.append(("o{}".format(n), width))
if dir == "io":
if dir in ("oe", "io"):
fields.append(("oe", 1))
return Layout(fields)

@@ -54,11 +54,12 @@ class Pin(Record):
----------
width : int
Width of the ``i``/``iN`` and ``o``/``oN`` signals.
dir : ``"i"``, ``"o"``, ``"io"``
dir : ``"i"``, ``"o"``, ``"io"``, ``"oe"``
Direction of the buffers. If ``"i"`` is specified, only the ``i``/``iN`` signals are
present. If ``"o"`` is specified, only the ``o``/``oN`` signals are present. If ``"io"``
is specified, both the ``i``/``iN`` and ``o``/``oN`` signals are present, and an ``oe``
signal is present.
present. If ``"o"`` is specified, only the ``o``/``oN`` signals are present. If ``"oe"`` is
specified, the ``o``/``oN`` signals are present, and an ``oe`` signal is present.
If ``"io"`` is specified, both the ``i``/``iN`` and ``o``/``oN`` signals are present, and
an ``oe`` signal is present.
xdr : int
Gearbox ratio. If equal to 0, the I/O buffer is combinatorial, and only ``i``/``o``
signals are present. If equal to 1, the I/O buffer is SDR, and only ``i``/``o`` signals are
@@ -84,8 +85,9 @@ class Pin(Record):
I/O buffer outputs, with gearing. Present if ``dir="o"`` or ``dir="io"``, and ``xdr`` is
greater than 1.
oe : Signal, in
I/O buffer output enable. Present if ``dir="io"``. Buffers generally cannot change
direction more than once per cycle, so at most one output enable signal is present.
I/O buffer output enable. Present if ``dir="io"`` or ``dir="oe"``. Buffers generally
cannot change direction more than once per cycle, so at most one output enable signal
is present.
"""
def __init__(self, width, dir, xdr=0, name=None):
self.width = width
2 changes: 1 addition & 1 deletion nmigen/test/test_build_dsl.py
Original file line number Diff line number Diff line change
@@ -17,7 +17,7 @@ def test_wrong_names(self):

def test_wrong_dir(self):
with self.assertRaises(TypeError,
msg="Direction must be one of \"i\", \"o\" or \"io\", not 'wrong'"):
msg="Direction must be one of \"i\", \"o\", \"oe\", or \"io\", not 'wrong'"):
p = Pins("A0 A1", dir="wrong")


5 changes: 3 additions & 2 deletions nmigen/test/test_build_res.py
Original file line number Diff line number Diff line change
@@ -197,13 +197,14 @@ def test_wrong_request_duplicate(self):

def test_wrong_request_with_dir(self):
with self.assertRaises(TypeError,
msg="Direction must be one of \"i\", \"o\", \"io\", or \"-\", not 'wrong'"):
msg="Direction must be one of \"i\", \"o\", \"oe\", \"io\", or \"-\", "
"not 'wrong'"):
user_led = self.cm.request("user_led", 0, dir="wrong")

def test_wrong_request_with_dir_io(self):
with self.assertRaises(ValueError,
msg="Direction of (pins o A0) cannot be changed from \"o\" to \"i\"; direction "
"can be changed from \"io\" to \"i\", from \"io\"to \"o\", or from anything "
"can be changed from \"io\" to \"i\", \"o\", or \"oe\", or from anything "
"to \"-\""):
user_led = self.cm.request("user_led", 0, dir="i")

41 changes: 41 additions & 0 deletions nmigen/test/test_lib_io.py
Original file line number Diff line number Diff line change
@@ -27,6 +27,19 @@ def test_pin_layout_o(self):
"o": ((2, False), DIR_NONE),
})

def test_pin_layout_oe(self):
layout_1 = pin_layout(1, dir="oe")
self.assertEqual(layout_1.fields, {
"o": ((1, False), DIR_NONE),
"oe": ((1, False), DIR_NONE),
})

layout_2 = pin_layout(2, dir="oe")
self.assertEqual(layout_2.fields, {
"o": ((2, False), DIR_NONE),
"oe": ((1, False), DIR_NONE),
})

def test_pin_layout_io(self):
layout_1 = pin_layout(1, dir="io")
self.assertEqual(layout_1.fields, {
@@ -66,6 +79,19 @@ def test_pin_layout_o(self):
"o": ((2, False), DIR_NONE),
})

def test_pin_layout_oe(self):
layout_1 = pin_layout(1, dir="oe", xdr=1)
self.assertEqual(layout_1.fields, {
"o": ((1, False), DIR_NONE),
"oe": ((1, False), DIR_NONE),
})

layout_2 = pin_layout(2, dir="oe", xdr=1)
self.assertEqual(layout_2.fields, {
"o": ((2, False), DIR_NONE),
"oe": ((1, False), DIR_NONE),
})

def test_pin_layout_io(self):
layout_1 = pin_layout(1, dir="io", xdr=1)
self.assertEqual(layout_1.fields, {
@@ -109,6 +135,21 @@ def test_pin_layout_o(self):
"o1": ((2, False), DIR_NONE),
})

def test_pin_layout_oe(self):
layout_1 = pin_layout(1, dir="oe", xdr=2)
self.assertEqual(layout_1.fields, {
"o0": ((1, False), DIR_NONE),
"o1": ((1, False), DIR_NONE),
"oe": ((1, False), DIR_NONE),
})

layout_2 = pin_layout(2, dir="oe", xdr=2)
self.assertEqual(layout_2.fields, {
"o0": ((2, False), DIR_NONE),
"o1": ((2, False), DIR_NONE),
"oe": ((1, False), DIR_NONE),
})

def test_pin_layout_io(self):
layout_1 = pin_layout(1, dir="io", xdr=2)
self.assertEqual(layout_1.fields, {
13 changes: 12 additions & 1 deletion nmigen/vendor/fpga/lattice_ice40.py
Original file line number Diff line number Diff line change
@@ -9,7 +9,8 @@
from ...build import *


__all__ = ["LatticeICE40Platform", "IceStormProgrammerMixin", "IceBurnProgrammerMixin", "TinyProgrammerMixin"]
__all__ = ["LatticeICE40Platform",
"IceStormProgrammerMixin", "IceBurnProgrammerMixin", "TinyProgrammerMixin"]


class LatticeICE40Platform(TemplatedPlatform):
@@ -140,6 +141,16 @@ 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),
("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),