vendor.fpga.lattice_ice40: implement differential input buffers.
authorwhitequark <cz@m-labs.hk>
Mon, 3 Jun 2019 08:38:12 +0000 (08:38 +0000)
committerwhitequark <cz@m-labs.hk>
Mon, 3 Jun 2019 08:38:12 +0000 (08:38 +0000)
nmigen/build/plat.py
nmigen/build/res.py
nmigen/test/test_build_res.py
nmigen/vendor/fpga/lattice_ice40.py

index 0dfa752aa147a254ed9aa84c0b7995786bfd163a..6b4ee3093f13a8bfea121d99ac849ed93d80f1de 100644 (file)
@@ -134,13 +134,13 @@ class Platform(ConstraintManager, metaclass=ABCMeta):
 
         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))
+                add_pin_fragment(pin, self.get_diff_input(pin, p_port, n_port, extras))
             if pin.dir == "o":
-                add_pin_fragment(pin, self.get_diff_output(pin, p_port, n_port))
+                add_pin_fragment(pin, self.get_diff_output(pin, p_port, n_port, extras))
             if pin.dir == "oe":
-                add_pin_fragment(pin, self.get_diff_tristate(pin, p_port, n_port))
+                add_pin_fragment(pin, self.get_diff_tristate(pin, p_port, n_port, extras))
             if pin.dir == "io":
-                add_pin_fragment(pin, self.get_diff_input_output(pin, p_port, n_port))
+                add_pin_fragment(pin, self.get_diff_input_output(pin, p_port, n_port, extras))
 
         return self.toolchain_prepare(fragment, name, **kwargs)
 
index 49852931614750e47711565925f509c1d5545f62..9cea2376803d9919344936c9e34b24c43f754a69 100644 (file)
@@ -156,17 +156,13 @@ class ConstraintManager:
             else:
                 assert False
 
-    def iter_port_constraints(self, diff_pins="pn"):
+    def iter_port_constraints(self):
         for resource, pin, port in self._ports:
             if isinstance(resource.io[0], Pins):
                 yield port.io.name, resource.io[0].names, resource.extras
             elif isinstance(resource.io[0], DiffPairs):
-                # 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 port.p.name, resource.io[0].p.names, resource.extras
-                if "n" in diff_pins:
-                    yield port.n.name, resource.io[0].n.names, resource.extras
+                yield port.p.name, resource.io[0].p.names, resource.extras
+                yield port.n.name, resource.io[0].n.names, resource.extras
             else:
                 assert False
 
index c7cff5725d273873bdad826a5228e8c37dfaaca7..d81584b76a1c2e2efd6bb9120ac3eba9844a5682 100644 (file)
@@ -115,12 +115,6 @@ class ConstraintManagerTestCase(FHDLTestCase):
             ("clk100_0__p", ["H1"], {}),
             ("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_request_raw(self):
         clk50 = self.cm.request("clk50", 0, dir="-")
index b8852d1375177ef3530b2401e4c11f8ddd7f2c37..3ae605c46e7695099bed1ea9322acc219e37d07c 100644 (file)
@@ -108,6 +108,32 @@ class LatticeICE40Platform(TemplatedPlatform):
         """
     ]
 
+    def iter_ports(self):
+        for resource, pin, port in self._ports:
+            if isinstance(resource.io[0], Pins):
+                yield port.io
+            elif isinstance(resource.io[0], DiffPairs):
+                if resource.extras.get("IO_STANDARD", "SB_LVCMOS") == "SB_LVDS_INPUT":
+                    yield port.p
+                else:
+                    yield port.p
+                    yield port.n
+            else:
+                assert False
+
+    def iter_port_constraints(self):
+        for resource, pin, port in self._ports:
+            if isinstance(resource.io[0], Pins):
+                yield port.io.name, resource.io[0].names, resource.extras
+            elif isinstance(resource.io[0], DiffPairs):
+                if resource.extras.get("IO_STANDARD", "SB_LVCMOS") == "SB_LVDS_INPUT":
+                    yield port.p.name, resource.io[0].p.names, resource.extras
+                else:
+                    yield port.p.name, resource.io[0].p.names, resource.extras
+                    yield port.n.name, resource.io[0].n.names, resource.extras
+            else:
+                assert False
+
     def _get_dff(self, clk, d, q):
         return Instance("$dff",
             p_CLK_POLARITY=0,
@@ -216,6 +242,16 @@ class LatticeICE40Platform(TemplatedPlatform):
                             valid_xdrs=(0, 1, 2), valid_extras=True)
         return self._get_io_buffer(pin, port, extras)
 
+    def get_diff_input(self, pin, p_port, n_port, extras):
+        self._check_feature("differential input", pin, extras,
+                            valid_xdrs=(0, 1, 2), valid_extras=True)
+        # On iCE40, a differential input is placed by only instantiating an SB_IO primitive for
+        # the pin with z=0, which is the non-inverting pin. The pinout unfortunately differs
+        # between LP/HX and UP series:
+        #  * for LP/HX, z=0 is DPxxB   (B is non-inverting, A is inverting)
+        #  * for UP,    z=0 is IOB_xxA (A is non-inverting, B is inverting)
+        return self._get_io_buffer(pin, p_port, extras)
+
 
 class IceStormProgrammerMixin:
     def toolchain_program(self, products, name, *, mode=None):