build.{res,plat}: propagate extras to pin fragment factories.
authorwhitequark <cz@m-labs.hk>
Mon, 3 Jun 2019 01:58:43 +0000 (01:58 +0000)
committerwhitequark <cz@m-labs.hk>
Mon, 3 Jun 2019 01:58:43 +0000 (01:58 +0000)
This is necessary because on some platforms, like iCE40, extras
become parameters on an IO primitive, since the constraint file
format is not expressive enough for all of them.

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

index 2cd30f45116116d31dd8e9e85f9fd3b0a015e013..04e8378bbac88c1d09f38fddb337fc6284365705 100644 (file)
@@ -116,27 +116,27 @@ class Platform(ConstraintManager, metaclass=ABCMeta):
 
         fragment = Fragment.get(fragment, self)
 
-        pin_fragments = []
-        for pin, port in self._se_pins:
+        def add_pin_fragment(pin, pin_fragment):
+            pin_fragment = Fragment.get(pin_fragment, self)
+            if not isinstance(pin_fragment, Instance):
+                pin_fragment.flatten = True
+            fragment.add_subfragment(pin_fragment, name="pin_{}".format(pin.name))
+
+        for pin, port, extras in self.iter_single_ended_pins():
             if pin.dir == "i":
-                pin_fragments.append((pin.name, self.get_input(pin, port)))
+                add_pin_fragment(pin, self.get_input(pin, port, extras))
             if pin.dir == "o":
-                pin_fragments.append((pin.name, self.get_output(pin, port)))
+                add_pin_fragment(pin, self.get_output(pin, port, extras))
             if pin.dir == "io":
-                pin_fragments.append((pin.name, self.get_tristate(pin, port)))
-        for pin, p_port, n_port in self._dp_pins:
+                add_pin_fragment(pin, self.get_input(pin, port, extras))
+
+        for pin, p_port, n_port, extras in self.iter_differential_pins():
             if pin.dir == "i":
-                pin_fragments.append((pin.name, self.get_diff_input(pin, p_port, n_port)))
+                add_pin_fragment(pin, self.get_diff_input(pin, p_port, n_port))
             if pin.dir == "o":
-                pin_fragments.append((pin.name, self.get_diff_output(pin, p_port, n_port)))
+                add_pin_fragment(pin, self.get_diff_output(pin, p_port, n_port))
             if pin.dir == "io":
-                pin_fragments.append((pin.name, self.get_diff_tristate(pin, p_port, n_port)))
-
-        for pin_name, pin_fragment in pin_fragments:
-            pin_fragment = Fragment.get(pin_fragment, self)
-            if not isinstance(pin_fragment, Instance):
-                pin_fragment.flatten = True
-            fragment.add_subfragment(pin_fragment, name="pin_{}".format(pin_name))
+                add_pin_fragment(pin, self.get_diff_tristate(pin, p_port, n_port))
 
         return self.toolchain_prepare(fragment, name, **kwargs)
 
@@ -155,30 +155,37 @@ class Platform(ConstraintManager, metaclass=ABCMeta):
         raise NotImplementedError("Platform {} does not support programming"
                                   .format(self.__class__.__name__))
 
-    def _check_feature(self, feature, pin, xdrs):
-        if not xdrs:
+    def _check_feature(self, feature, pin, extras, valid_xdrs, valid_extras):
+        if not valid_xdrs:
             raise NotImplementedError("Platform {} does not support {}"
                                       .format(self.__class__.__name__, feature))
-        elif pin.xdr not in xdrs:
+        elif pin.xdr not in valid_xdrs:
             raise NotImplementedError("Platform {} does not support {} for XDR {}"
                                       .format(self.__class__.__name__, feature, pin.xdr))
 
-    def get_input(self, pin, port):
-        self._check_feature("single-ended input", pin, xdrs=(1,))
+        if not valid_extras and extras:
+            raise NotImplementedError("Platform {} does not support extras for {}"
+                                      .format(self.__class__.__name__, feature))
+
+    def get_input(self, pin, port, extras):
+        self._check_feature("single-ended input", pin, extras,
+                            valid_xdrs=(1,), valid_extras=None)
 
         m = Module()
         m.d.comb += pin.i.eq(port)
         return m
 
-    def get_output(self, pin, port):
-        self._check_feature("single-ended output", pin, xdrs=(1,))
+    def get_output(self, pin, port, extras):
+        self._check_feature("single-ended output", pin, extras,
+                            valid_xdrs=(1,), valid_extras=None)
 
         m = Module()
         m.d.comb += port.eq(pin.o)
         return m
 
-    def get_tristate(self, pin, port):
-        self._check_feature("single-ended tristate", pin, xdrs=(1,))
+    def get_tristate(self, pin, port, extras):
+        self._check_feature("single-ended tristate", pin, extras,
+                            valid_xdrs=(1,), valid_extras=None)
 
         m = Module()
         m.submodules += Instance("$tribuf",
@@ -190,14 +197,17 @@ class Platform(ConstraintManager, metaclass=ABCMeta):
         m.d.comb += pin.i.eq(port)
         return m
 
-    def get_diff_input(self, pin, p_port, n_port):
-        self._check_feature("differential input", pin, xdrs=())
+    def get_diff_input(self, pin, p_port, n_port, extras):
+        self._check_feature("differential input", pin, extras,
+                            valid_xdrs=(), valid_extras=None)
 
-    def get_diff_output(self, pin, p_port, n_port):
-        self._check_feature("differential output", pin, xdrs=())
+    def get_diff_output(self, pin, p_port, n_port, extras):
+        self._check_feature("differential output", pin, extras,
+                            valid_xdrs=(), valid_extras=None)
 
-    def get_diff_tristate(self, pin, p_port, n_port):
-        self._check_feature("differential tristate", pin, xdrs=())
+    def get_diff_tristate(self, pin, p_port, n_port, extras):
+        self._check_feature("differential tristate", pin, extras,
+                            valid_xdrs=(), valid_extras=None)
 
 
 class TemplatedPlatform(Platform):
index cf810f38a1591576e793859a342175ddff39007f..5ac3c64be95ff8373e11e6e073e20ecc79ccb5e6 100644 (file)
@@ -19,10 +19,7 @@ class ConstraintManager:
         self.resources  = OrderedDict()
         self.requested  = OrderedDict()
         self.clocks     = OrderedDict()
-
         self._ports     = []
-        self._se_pins   = []
-        self._dp_pins   = []
 
         self.add_resources(resources)
         for name_number, frequency in clocks:
@@ -113,19 +110,12 @@ 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))
-                    self._se_pins.append((pin, port))
-                    self._ports.append((port, phys.names, subsignal.extras))
-
                 if isinstance(phys, DiffPairs):
-                    p_port = Signal(pin.width, name="{}_p".format(pin.name))
-                    n_port = Signal(pin.width, name="{}_n".format(pin.name))
-                    self._dp_pins.append((pin, p_port, n_port))
-                    self._ports.append((p_port, phys.p.names, subsignal.extras))
-                    self._ports.append((n_port, phys.n.names, subsignal.extras))
-
+                    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
             else:
                 assert False # :nocov:
@@ -136,13 +126,42 @@ class ConstraintManager:
         self.requested[resource.name, resource.number] = value
         return value
 
+    def iter_single_ended_pins(self):
+        for resource, pin, port in self._ports:
+            if isinstance(resource.io[0], Pins):
+                yield pin, port, resource.extras
+
+    def iter_differential_pins(self):
+        for resource, pin, port in self._ports:
+            if isinstance(resource.io[0], DiffPairs):
+                p_port, n_port = port
+                yield pin, p_port, n_port, resource.extras
+
     def iter_ports(self):
-        for port, pins, extras in self._ports:
-            yield port
+        for resource, pin, port in self._ports:
+            if isinstance(resource.io[0], Pins):
+                yield port
+            elif isinstance(resource.io[0], DiffPairs):
+                p_port, n_port = port
+                yield p_port
+                yield n_port
+            else:
+                assert False
 
-    def iter_port_constraints(self):
-        for port, pins, extras in self._ports:
-            yield (port.name, pins, extras)
+    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
+            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
+                if "n" in diff_pins:
+                    yield n_port.name, resource.io[0].n.names, resource.extras
+            else:
+                assert False
 
     def iter_clock_constraints(self):
         for name, number in self.clocks.keys() & self.requested.keys():
index 5ffdac8e6485c5de7251990d38e04b8a6196c9ab..26acc7c12e0136e94dec1d8a0c7687c4b67919e9 100644 (file)
@@ -85,9 +85,9 @@ class ConstraintManagerTestCase(FHDLTestCase):
         self.assertEqual(ports[1].name, "i2c_0__sda_io")
         self.assertEqual(ports[1].nbits, 1)
 
-        self.assertEqual(self.cm._se_pins, [
-            (i2c.scl, scl),
-            (i2c.sda, sda),
+        self.assertEqual(list(self.cm.iter_single_ended_pins()), [
+            (i2c.scl, scl, {}),
+            (i2c.sda, sda, {}),
         ])
         self.assertEqual(list(self.cm.iter_port_constraints()), [
             ("i2c_0__scl_io", ["N10"], {}),
@@ -108,12 +108,18 @@ class ConstraintManagerTestCase(FHDLTestCase):
         self.assertEqual(n.name, "clk100_0_n")
         self.assertEqual(n.nbits, clk100.width)
 
-        self.assertEqual(self.cm._dp_pins, [
-            (clk100, p, n),
+        self.assertEqual(list(self.cm.iter_differential_pins()), [
+            (clk100, p, n, {}),
         ])
         self.assertEqual(list(self.cm.iter_port_constraints()), [
             ("clk100_0_p", ["H1"], {}),
-            ("clk100_0_n", ["H2"], {})
+            ("clk100_0_n", ["H2"], {}),
+        ])
+        self.assertEqual(list(self.cm.iter_port_constraints(diff_pins="p")), [
+            ("clk100_0_p", ["H1"], {}),
+        ])
+        self.assertEqual(list(self.cm.iter_port_constraints(diff_pins="n")), [
+            ("clk100_0_n", ["H2"], {}),
         ])
 
     def test_add_clock(self):