build.{dsl,plat,res}: allow dir="oe".
authorwhitequark <cz@m-labs.hk>
Mon, 3 Jun 2019 04:39:05 +0000 (04:39 +0000)
committerwhitequark <cz@m-labs.hk>
Mon, 3 Jun 2019 04:42:55 +0000 (04:42 +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/build/dsl.py
nmigen/build/plat.py
nmigen/build/res.py
nmigen/test/test_build_dsl.py
nmigen/test/test_build_res.py
nmigen/vendor/fpga/lattice_ice40.py

index 7b3c38d1c8d057a0c41fb9cad7b15a55b7e67b9f..222193a79c2bb12933ae53716881f00137c1d5b7 100644 (file)
@@ -9,7 +9,7 @@ class Pins:
         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
 
index f2ff7898d7e6b30a0b98176d342f644f965944af..0dfa752aa147a254ed9aa84c0b7995786bfd163a 100644 (file)
@@ -127,16 +127,20 @@ class Platform(ConstraintManager, metaclass=ABCMeta):
                 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 @@ class Platform(ConstraintManager, metaclass=ABCMeta):
         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 @@ class Platform(ConstraintManager, metaclass=ABCMeta):
         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()
index 88e600658b5437176e76c6dc9fceb1d0211fde61..49852931614750e47711565925f509c1d5545f62 100644 (file)
@@ -84,14 +84,14 @@ class ConstraintManager:
                     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}"
index 15ef9ad34f334139b690c4aa835f9fbe66ccc344..a3f75a4c83fce117fdbf091d3de77cec2d4041c0 100644 (file)
@@ -17,7 +17,7 @@ class PinsTestCase(FHDLTestCase):
 
     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")
 
 
index 1455619659647f010fa1852c22c551714cbc02c0..c7cff5725d273873bdad826a5228e8c37dfaaca7 100644 (file)
@@ -197,13 +197,14 @@ class ConstraintManagerTestCase(FHDLTestCase):
 
     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")
 
index 3f55bc3cd9e1d4205add89646c5dd1aea3db48d2..0583451421d3f3c1803d43bbcbfb2db45981a751 100644 (file)
@@ -9,7 +9,8 @@ from ...hdl.ir import *
 from ...build import *
 
 
-__all__ = ["LatticeICE40Platform", "IceStormProgrammerMixin", "IceBurnProgrammerMixin", "TinyProgrammerMixin"]
+__all__ = ["LatticeICE40Platform",
+           "IceStormProgrammerMixin", "IceBurnProgrammerMixin", "TinyProgrammerMixin"]
 
 
 class LatticeICE40Platform(TemplatedPlatform):
@@ -140,6 +141,16 @@ class LatticeICE40Platform(TemplatedPlatform):
         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),