lib.io: allow dir="oe".
authorwhitequark <cz@m-labs.hk>
Mon, 3 Jun 2019 04:28:53 +0000 (04:28 +0000)
committerwhitequark <cz@m-labs.hk>
Mon, 3 Jun 2019 04:28:53 +0000 (04:28 +0000)
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
nmigen/test/test_lib_io.py

index 3b65f07c0d7fb687ea9159aedc48fe8e5903b243..81f16c5f80f8cb532a4b9f176551e7e74085f832 100644 (file)
@@ -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
index 1a91a29435c8fad6f7da461d5b9e62b27ad39622..0e05277b19bc2579be35e6038a354e6699b01df0 100644 (file)
@@ -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, {