build.res: allow requesting raw ports, with dir="-".
authorwhitequark <cz@m-labs.hk>
Mon, 3 Jun 2019 03:17:20 +0000 (03:17 +0000)
committerwhitequark <cz@m-labs.hk>
Mon, 3 Jun 2019 03:36:32 +0000 (03:36 +0000)
This provides an escape hatch for the case where the nMigen platform
code is not flexible enough, and a IO buffer primitive needs to be
instantiated directly.

nmigen/build/res.py
nmigen/test/test_build_res.py

index 44aa8fbd4fd2985ea69e7eabae9853b1976572d7..09707ef37dc9885c19ae24354cd7d98f2cfc4e8f 100644 (file)
@@ -84,13 +84,14 @@ class ConstraintManager:
                     dir = subsignal.io[0].dir
                 if xdr is None:
                     xdr = 1
-                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", "io", "-"):
+                    raise TypeError("Direction must be one of \"i\", \"o\", \"io\", or \"-\", "
+                                    "not {!r}"
                                     .format(dir))
-                if subsignal.io[0].dir != "io" and dir != subsignal.io[0].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\" or from \"io\""
-                                     "to \"o\""
+                                     "direction can be changed from \"io\" to \"i\", from \"io\""
+                                     "to \"o\", or from anything to \"-\""
                                      .format(subsignal.io[0], subsignal.io[0].dir, dir))
                 if not isinstance(xdr, int) or xdr < 1:
                     raise ValueError("Data rate of {!r} must be a positive integer, not {!r}"
@@ -109,14 +110,19 @@ class ConstraintManager:
 
             elif isinstance(subsignal.io[0], (Pins, DiffPairs)):
                 phys = subsignal.io[0]
-                pin  = Pin(len(phys), dir, xdr, name=name)
                 if isinstance(phys, Pins):
-                    port = Signal(pin.width, name="{}__io".format(pin.name))
+                    port = Record([("io", len(phys))], name=name)
                 if isinstance(phys, DiffPairs):
-                    port = (Signal(pin.width, name="{}__p".format(pin.name)),
-                            Signal(pin.width, name="{}__n".format(pin.name)))
-                self._ports.append((subsignal, pin, port))
-                return pin
+                    port = Record([("p", len(phys)),
+                                   ("n", len(phys))], name=name)
+                if dir == "-":
+                    self._ports.append((subsignal, None, port))
+                    return port
+                else:
+                    pin  = Pin(len(phys), dir, xdr, name=name)
+                    self._ports.append((subsignal, pin, port))
+                    return pin
+
             else:
                 assert False # :nocov:
 
@@ -128,38 +134,39 @@ class ConstraintManager:
 
     def iter_single_ended_pins(self):
         for resource, pin, port in self._ports:
+            if pin is None:
+                continue
             if isinstance(resource.io[0], Pins):
-                yield pin, port, resource.extras
+                yield pin, port.io, resource.extras
 
     def iter_differential_pins(self):
         for resource, pin, port in self._ports:
+            if pin is None:
+                continue
             if isinstance(resource.io[0], DiffPairs):
-                p_port, n_port = port
-                yield pin, p_port, n_port, resource.extras
+                yield pin, port.p, port.n, resource.extras
 
     def iter_ports(self):
         for resource, pin, port in self._ports:
             if isinstance(resource.io[0], Pins):
-                yield port
+                yield port.io
             elif isinstance(resource.io[0], DiffPairs):
-                p_port, n_port = port
-                yield p_port
-                yield n_port
+                yield port.p
+                yield port.n
             else:
                 assert False
 
     def iter_port_constraints(self, diff_pins="pn"):
         for resource, pin, port in self._ports:
             if isinstance(resource.io[0], Pins):
-                yield port.name, resource.io[0].names, resource.extras
+                yield port.io.name, resource.io[0].names, resource.extras
             elif isinstance(resource.io[0], DiffPairs):
-                p_port, n_port = port
                 # On some FPGAs like iCE40, only one pin out of two in a differential pair may be
                 # constrained. The other has to be completely disconnected.
                 if "p" in diff_pins:
-                    yield p_port.name, resource.io[0].p.names, resource.extras
+                    yield port.p.name, resource.io[0].p.names, resource.extras
                 if "n" in diff_pins:
-                    yield n_port.name, resource.io[0].n.names, resource.extras
+                    yield port.n.name, resource.io[0].n.names, resource.extras
             else:
                 assert False
 
index 13b30423a130d3eb57ff3dc640cf1c9b2161c656..1a14eb4f68ca6d663a3b2000e9e52b6ecbd94ebe 100644 (file)
@@ -122,6 +122,26 @@ class ConstraintManagerTestCase(FHDLTestCase):
             ("clk100_0__n", ["H2"], {}),
         ])
 
+    def test_request_raw(self):
+        clk50 = self.cm.request("clk50", 0, dir="-")
+        self.assertIsInstance(clk50, Record)
+        self.assertIsInstance(clk50.io, Signal)
+
+        ports = list(self.cm.iter_ports())
+        self.assertEqual(len(ports), 1)
+        self.assertIs(ports[0], clk50.io)
+
+    def test_request_raw_diffpairs(self):
+        clk100 = self.cm.request("clk100", 0, dir="-")
+        self.assertIsInstance(clk100, Record)
+        self.assertIsInstance(clk100.p, Signal)
+        self.assertIsInstance(clk100.n, Signal)
+
+        ports = list(self.cm.iter_ports())
+        self.assertEqual(len(ports), 2)
+        self.assertIs(ports[0], clk100.p)
+        self.assertIs(ports[1], clk100.n)
+
     def test_add_clock(self):
         self.cm.add_clock("clk100", 0, 10e6)
         self.assertEqual(self.cm.clocks["clk100", 0], 10e6)
@@ -177,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\" or \"io\", not 'wrong'"):
+                msg="Direction must be one of \"i\", \"o\", \"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\" or from \"io\"to \"o\""):
+                    "can be changed from \"io\" to \"i\", from \"io\"to \"o\", or from anything "
+                    "to \"-\""):
             user_led = self.cm.request("user_led", 0, dir="i")
 
     def test_wrong_request_with_dir_dict(self):