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.
This commit is contained in:
whitequark 2019-06-03 04:28:53 +00:00
parent 9ba2efd86b
commit 1eee7cd76f
2 changed files with 53 additions and 10 deletions

View file

@ -15,8 +15,8 @@ def pin_layout(width, dir, xdr=1):
if not isinstance(width, int) or width < 1: if not isinstance(width, int) or width < 1:
raise TypeError("Width must be a positive integer, not '{!r}'" raise TypeError("Width must be a positive integer, not '{!r}'"
.format(width)) .format(width))
if dir not in ("i", "o", "io"): if dir not in ("i", "o", "oe", "io"):
raise TypeError("Direction must be one of \"i\", \"o\" or \"io\", not '{!r}'""" raise TypeError("Direction must be one of \"i\", \"o\", \"io\", or \"oe\", not '{!r}'"""
.format(dir)) .format(dir))
if not isinstance(xdr, int) or xdr < 0: if not isinstance(xdr, int) or xdr < 0:
raise TypeError("Gearing ratio must be a non-negative integer, not '{!r}'" raise TypeError("Gearing ratio must be a non-negative integer, not '{!r}'"
@ -29,13 +29,13 @@ def pin_layout(width, dir, xdr=1):
else: else:
for n in range(xdr): for n in range(xdr):
fields.append(("i{}".format(n), width)) fields.append(("i{}".format(n), width))
if dir in ("o", "io"): if dir in ("o", "oe", "io"):
if xdr in (0, 1): if xdr in (0, 1):
fields.append(("o", width)) fields.append(("o", width))
else: else:
for n in range(xdr): for n in range(xdr):
fields.append(("o{}".format(n), width)) fields.append(("o{}".format(n), width))
if dir == "io": if dir in ("oe", "io"):
fields.append(("oe", 1)) fields.append(("oe", 1))
return Layout(fields) return Layout(fields)
@ -54,11 +54,12 @@ class Pin(Record):
---------- ----------
width : int width : int
Width of the ``i``/``iN`` and ``o``/``oN`` signals. 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 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"`` present. If ``"o"`` is specified, only the ``o``/``oN`` signals are present. If ``"oe"`` is
is specified, both the ``i``/``iN`` and ``o``/``oN`` signals are present, and an ``oe`` specified, the ``o``/``oN`` signals are present, and an ``oe`` signal is present.
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 xdr : int
Gearbox ratio. If equal to 0, the I/O buffer is combinatorial, and only ``i``/``o`` 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 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 I/O buffer outputs, with gearing. Present if ``dir="o"`` or ``dir="io"``, and ``xdr`` is
greater than 1. greater than 1.
oe : Signal, in oe : Signal, in
I/O buffer output enable. Present if ``dir="io"``. Buffers generally cannot change I/O buffer output enable. Present if ``dir="io"`` or ``dir="oe"``. Buffers generally
direction more than once per cycle, so at most one output enable signal is present. 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): def __init__(self, width, dir, xdr=0, name=None):
self.width = width self.width = width

View file

@ -27,6 +27,19 @@ class PinLayoutCombTestCase(FHDLTestCase):
"o": ((2, False), DIR_NONE), "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): def test_pin_layout_io(self):
layout_1 = pin_layout(1, dir="io") layout_1 = pin_layout(1, dir="io")
self.assertEqual(layout_1.fields, { self.assertEqual(layout_1.fields, {
@ -66,6 +79,19 @@ class PinLayoutSDRTestCase(FHDLTestCase):
"o": ((2, False), DIR_NONE), "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): def test_pin_layout_io(self):
layout_1 = pin_layout(1, dir="io", xdr=1) layout_1 = pin_layout(1, dir="io", xdr=1)
self.assertEqual(layout_1.fields, { self.assertEqual(layout_1.fields, {
@ -109,6 +135,21 @@ class PinLayoutDDRTestCase(FHDLTestCase):
"o1": ((2, False), DIR_NONE), "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): def test_pin_layout_io(self):
layout_1 = pin_layout(1, dir="io", xdr=2) layout_1 = pin_layout(1, dir="io", xdr=2)
self.assertEqual(layout_1.fields, { self.assertEqual(layout_1.fields, {