From 1eee7cd76f4c9a79329c185a25003fd2d5bf4c47 Mon Sep 17 00:00:00 2001 From: whitequark Date: Mon, 3 Jun 2019 04:28:53 +0000 Subject: [PATCH] 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. --- nmigen/lib/io.py | 22 ++++++++++---------- nmigen/test/test_lib_io.py | 41 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+), 10 deletions(-) diff --git a/nmigen/lib/io.py b/nmigen/lib/io.py index 3b65f07..81f16c5 100644 --- a/nmigen/lib/io.py +++ b/nmigen/lib/io.py @@ -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 diff --git a/nmigen/test/test_lib_io.py b/nmigen/test/test_lib_io.py index 1a91a29..0e05277 100644 --- a/nmigen/test/test_lib_io.py +++ b/nmigen/test/test_lib_io.py @@ -27,6 +27,19 @@ class PinLayoutCombTestCase(FHDLTestCase): "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 @@ class PinLayoutSDRTestCase(FHDLTestCase): "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 @@ class PinLayoutDDRTestCase(FHDLTestCase): "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, { -- 2.30.2