vendor.lattice_ecp5: Add support for io with xdr=4
authorKonrad Beckmann <konrad.beckmann@gmail.com>
Mon, 6 Jul 2020 14:01:19 +0000 (16:01 +0200)
committerLuke Kenneth Casson Leighton <lkcl@lkcl.net>
Fri, 31 Dec 2021 14:43:43 +0000 (14:43 +0000)
This adds support for IOs with xdr=4 using the
IDDRX2F and ODDRX2F primitives.

nmigen/lib/io.py
nmigen/vendor/lattice_ecp5.py

index 7c950ae7d3c8f2aea3d3f9b21b0391e543c707fa..9776effc1bf17780cf3a73d43d78b50c8bf402b0 100644 (file)
@@ -26,6 +26,8 @@ def pin_layout(width, dir, xdr=0):
     if dir in ("i", "io"):
         if xdr > 0:
             fields.append(("i_clk", 1))
+        if xdr > 2:
+            fields.append(("i_fclk", 1))
         if xdr in (0, 1):
             fields.append(("i", width))
         else:
@@ -34,6 +36,8 @@ def pin_layout(width, dir, xdr=0):
     if dir in ("o", "oe", "io"):
         if xdr > 0:
             fields.append(("o_clk", 1))
+        if xdr > 2:
+            fields.append(("o_fclk", 1))
         if xdr in (0, 1):
             fields.append(("o", width))
         else:
@@ -78,6 +82,9 @@ class Pin(Record):
     ----------
     i_clk:
         I/O buffer input clock. Synchronizes `i*`. Present if ``xdr`` is nonzero.
+    i_fclk:
+        I/O buffer input fast clock. Synchronizes `i*` on higer gearbox ratios. Present if ``xdr``
+        is greater than 2.
     i : Signal, out
         I/O buffer input, without gearing. Present if ``dir="i"`` or ``dir="io"``, and ``xdr`` is
         equal to 0 or 1.
@@ -86,6 +93,9 @@ class Pin(Record):
         greater than 1.
     o_clk:
         I/O buffer output clock. Synchronizes `o*`, including `oe`. Present if ``xdr`` is nonzero.
+    o_fclk:
+        I/O buffer output fast clock. Synchronizes `o*` on higher gearbox ratios. Present if
+        ``xdr`` is greater than 2.
     o : Signal, in
         I/O buffer output, without gearing. Present if ``dir="o"`` or ``dir="io"``, and ``xdr`` is
         equal to 0 or 1.
index c1a1366148903e3ac3d0deaf8bbf691681cecfc7..0abeba95dc1b830efa1c7835be581908438d81d9 100644 (file)
@@ -389,6 +389,16 @@ class LatticeECP5Platform(TemplatedPlatform):
                     o_Q0=q0[bit], o_Q1=q1[bit]
                 )
 
+        def get_iddrx2(sclk, eclk, d, q0, q1, q2, q3):
+            for bit in range(len(d)):
+                m.submodules += Instance("IDDRX2F",
+                    i_SCLK=sclk,
+                    i_ECLK=eclk,
+                    i_RST=Const(0),
+                    i_D=d[bit],
+                    o_Q0=q0[bit], o_Q1=q1[bit], o_Q2=q2[bit], o_Q3=q3[bit]
+                )
+
         def get_oddr(sclk, d0, d1, q):
             for bit in range(len(q)):
                 m.submodules += Instance("ODDRX1F",
@@ -398,6 +408,16 @@ class LatticeECP5Platform(TemplatedPlatform):
                     o_Q=q[bit]
                 )
 
+        def get_oddrx2(sclk, eclk, d0, d1, d2, d3, q):
+            for bit in range(len(q)):
+                m.submodules += Instance("ODDRX2F",
+                    i_SCLK=sclk,
+                    i_ECLK=eclk,
+                    i_RST=Const(0),
+                    i_D0=d0[bit], i_D1=d1[bit], i_D2=d2[bit], i_D3=d3[bit],
+                    o_Q=q[bit]
+                )
+
         def get_ineg(z, invert):
             if invert:
                 a = Signal.like(z, name_suffix="_n")
@@ -420,12 +440,22 @@ class LatticeECP5Platform(TemplatedPlatform):
             elif pin.xdr == 2:
                 pin_i0 = get_ineg(pin.i0, i_invert)
                 pin_i1 = get_ineg(pin.i1, i_invert)
+            elif pin.xdr == 4:
+                pin_i0 = get_ineg(pin.i0, i_invert)
+                pin_i1 = get_ineg(pin.i1, i_invert)
+                pin_i2 = get_ineg(pin.i2, i_invert)
+                pin_i3 = get_ineg(pin.i3, i_invert)
         if "o" in pin.dir:
             if pin.xdr < 2:
                 pin_o  = get_oneg(pin.o,  o_invert)
             elif pin.xdr == 2:
                 pin_o0 = get_oneg(pin.o0, o_invert)
                 pin_o1 = get_oneg(pin.o1, o_invert)
+            elif pin.xdr == 4:
+                pin_o0 = get_oneg(pin.o0, o_invert)
+                pin_o1 = get_oneg(pin.o1, o_invert)
+                pin_o2 = get_oneg(pin.o2, o_invert)
+                pin_o3 = get_oneg(pin.o3, o_invert)
 
         i = o = t = None
         if "i" in pin.dir:
@@ -460,6 +490,13 @@ class LatticeECP5Platform(TemplatedPlatform):
                 # It is not clear what is the recommended set of primitives for this task.
                 # Similarly, nextpnr will not pack anything as a tristate register in a DDR PIO.
                 get_oreg(pin.o_clk, ~pin.oe, t)
+        elif pin.xdr == 4:
+            if "i" in pin.dir:
+                get_iddrx2(pin.i_clk, pin.i_fclk, i, pin_i0, pin_i1, pin_i2, pin_i3)
+            if "o" in pin.dir:
+                get_oddrx2(pin.o_clk, pin.o_fclk, pin_o0, pin_o1, pin_o2, pin_o3, o)
+            if pin.dir in ("oe", "io"):
+                get_oreg(pin.o_clk, ~pin.oe, t)
         else:
             assert False
 
@@ -467,7 +504,7 @@ class LatticeECP5Platform(TemplatedPlatform):
 
     def get_input(self, pin, port, attrs, invert):
         self._check_feature("single-ended input", pin, attrs,
-                            valid_xdrs=(0, 1, 2), valid_attrs=True)
+                            valid_xdrs=(0, 1, 2, 4), valid_attrs=True)
         m = Module()
         i, o, t = self._get_xdr_buffer(m, pin, i_invert=invert)
         for bit in range(len(port)):
@@ -479,7 +516,7 @@ class LatticeECP5Platform(TemplatedPlatform):
 
     def get_output(self, pin, port, attrs, invert):
         self._check_feature("single-ended output", pin, attrs,
-                            valid_xdrs=(0, 1, 2), valid_attrs=True)
+                            valid_xdrs=(0, 1, 2, 4), valid_attrs=True)
         m = Module()
         i, o, t = self._get_xdr_buffer(m, pin, o_invert=invert)
         for bit in range(len(port)):
@@ -491,7 +528,7 @@ class LatticeECP5Platform(TemplatedPlatform):
 
     def get_tristate(self, pin, port, attrs, invert):
         self._check_feature("single-ended tristate", pin, attrs,
-                            valid_xdrs=(0, 1, 2), valid_attrs=True)
+                            valid_xdrs=(0, 1, 2, 4), valid_attrs=True)
         m = Module()
         i, o, t = self._get_xdr_buffer(m, pin, o_invert=invert)
         for bit in range(len(port)):
@@ -504,7 +541,7 @@ class LatticeECP5Platform(TemplatedPlatform):
 
     def get_input_output(self, pin, port, attrs, invert):
         self._check_feature("single-ended input/output", pin, attrs,
-                            valid_xdrs=(0, 1, 2), valid_attrs=True)
+                            valid_xdrs=(0, 1, 2, 4), valid_attrs=True)
         m = Module()
         i, o, t = self._get_xdr_buffer(m, pin, i_invert=invert, o_invert=invert)
         for bit in range(len(port)):
@@ -518,7 +555,7 @@ class LatticeECP5Platform(TemplatedPlatform):
 
     def get_diff_input(self, pin, p_port, n_port, attrs, invert):
         self._check_feature("differential input", pin, attrs,
-                            valid_xdrs=(0, 1, 2), valid_attrs=True)
+                            valid_xdrs=(0, 1, 2, 4), valid_attrs=True)
         m = Module()
         i, o, t = self._get_xdr_buffer(m, pin, i_invert=invert)
         for bit in range(len(p_port)):
@@ -530,7 +567,7 @@ class LatticeECP5Platform(TemplatedPlatform):
 
     def get_diff_output(self, pin, p_port, n_port, attrs, invert):
         self._check_feature("differential output", pin, attrs,
-                            valid_xdrs=(0, 1, 2), valid_attrs=True)
+                            valid_xdrs=(0, 1, 2, 4), valid_attrs=True)
         m = Module()
         i, o, t = self._get_xdr_buffer(m, pin, o_invert=invert)
         for bit in range(len(p_port)):
@@ -542,7 +579,7 @@ class LatticeECP5Platform(TemplatedPlatform):
 
     def get_diff_tristate(self, pin, p_port, n_port, attrs, invert):
         self._check_feature("differential tristate", pin, attrs,
-                            valid_xdrs=(0, 1, 2), valid_attrs=True)
+                            valid_xdrs=(0, 1, 2, 4), valid_attrs=True)
         m = Module()
         i, o, t = self._get_xdr_buffer(m, pin, o_invert=invert)
         for bit in range(len(p_port)):
@@ -555,7 +592,7 @@ class LatticeECP5Platform(TemplatedPlatform):
 
     def get_diff_input_output(self, pin, p_port, n_port, attrs, invert):
         self._check_feature("differential input/output", pin, attrs,
-                            valid_xdrs=(0, 1, 2), valid_attrs=True)
+                            valid_xdrs=(0, 1, 2, 4), valid_attrs=True)
         m = Module()
         i, o, t = self._get_xdr_buffer(m, pin, i_invert=invert, o_invert=invert)
         for bit in range(len(p_port)):