add input and output reset signals to Pin
authorLuke Kenneth Casson Leighton <lkcl@lkcl.net>
Fri, 15 Apr 2022 09:40:44 +0000 (10:40 +0100)
committerLuke Kenneth Casson Leighton <lkcl@lkcl.net>
Fri, 15 Apr 2022 09:40:44 +0000 (10:40 +0100)
https://gitlab.com/nmigen/nmigen/-/issues/2

the issue being encountered is that ECP5 2x, 4x and 7x phase-tapped
IO pads will come up 50% of the time in an incorrect phase
setting the RST line of IDDR71B and other IOpad instances to Const(0)
is the likely root cause.

unfortunately, to fix this, an actual Signal has to be passed in,
to the LatticeECP5Platform, for it to be able to pass it to the IOpad.
that in turn means that the Pin Record has to have a reset signal added.

current experiments with GRAM show that setting the reset signal equal
to the domain reset signal is not quite sufficient, but greatly improves
the number of times that the DQS and other DDR IOpads get a successful
lock.  this most likely because the 2x PLL has not been given enough
time to stabilise.  at least with the addition of reset signals
i_prst and o_prst to Pin, experimentation and investigation can proceed

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

index 9776effc1bf17780cf3a73d43d78b50c8bf402b0..acff790fb740026965adc52960fa471fda22286d 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 xdf >= 2:
+            fields.append(("i_prst", 1))
         if xdr > 2:
             fields.append(("i_fclk", 1))
         if xdr in (0, 1):
@@ -36,6 +38,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_prst", 1))
         if xdr > 2:
             fields.append(("o_fclk", 1))
         if xdr in (0, 1):
@@ -85,6 +89,10 @@ class Pin(Record):
     i_fclk:
         I/O buffer input fast clock. Synchronizes `i*` on higer gearbox ratios. Present if ``xdr``
         is greater than 2.
+    i_prst:
+        I/O buffer pad reset.  Some FPGAs (ECP5) have a built-in PLL that will
+        lock 180 out of phase 50% of the time without a proper reset.
+        Present if ``xdr`` is greater or equal to 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.
@@ -96,6 +104,10 @@ class Pin(Record):
     o_fclk:
         I/O buffer output fast clock. Synchronizes `o*` on higher gearbox ratios. Present if
         ``xdr`` is greater than 2.
+    o_prst:
+        I/O buffer pad reset.  Some FPGAs (ECP5) have a built-in PLL that will
+        lock 180 out of phase 50% of the time without a proper reset.
+        Present if ``xdr`` is greater or equal to 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 0972d0140caf1c1e69e0624dd7caac8e8c48d811..73f17b86b6e0e0e644e812ee945ee15915d5bad5 100644 (file)
@@ -396,61 +396,61 @@ class LatticeECP5Platform(TemplatedPlatform):
                     o_Q=q[bit]
                 )
 
-        def get_iddr(sclk, d, q0, q1):
+        def get_iddr(sclk, prst, d, q0, q1):
             for bit in range(len(d)):
                 m.submodules += Instance("IDDRX1F",
                     i_SCLK=sclk,
-                    i_RST=Const(0),
+                    i_RST=prst,
                     i_D=d[bit],
                     o_Q0=q0[bit], o_Q1=q1[bit]
                 )
 
-        def get_iddrx2(sclk, eclk, d, q0, q1, q2, q3):
+        def get_iddrx2(sclk, eclk, prst, 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_RST=prst,
                     i_D=d[bit],
                     o_Q0=q0[bit], o_Q1=q1[bit], o_Q2=q2[bit], o_Q3=q3[bit]
                 )
 
-        def get_iddr71b(sclk, eclk, d, q0, q1, q2, q3, q4, q5, q6):
+        def get_iddr71b(sclk, eclk, prst, d, q0, q1, q2, q3, q4, q5, q6):
             for bit in range(len(d)):
                 m.submodules += Instance("IDDR71B",
                     i_SCLK=sclk,
                     i_ECLK=eclk,
-                    i_RST=Const(0),
+                    i_RST=prst,
                     i_D=d[bit],
                     o_Q0=q0[bit], o_Q1=q1[bit], o_Q2=q2[bit], o_Q3=q3[bit],
                     o_Q4=q4[bit], o_Q5=q5[bit], o_Q6=q6[bit],
                 )
 
-        def get_oddr(sclk, d0, d1, q):
+        def get_oddr(sclk, prst, d0, d1, q):
             for bit in range(len(q)):
                 m.submodules += Instance("ODDRX1F",
                     i_SCLK=sclk,
-                    i_RST=Const(0),
+                    i_RST=prst,
                     i_D0=d0[bit], i_D1=d1[bit],
                     o_Q=q[bit]
                 )
 
-        def get_oddrx2(sclk, eclk, d0, d1, d2, d3, q):
+        def get_oddrx2(sclk, eclk, prst, 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_RST=prst,
                     i_D0=d0[bit], i_D1=d1[bit], i_D2=d2[bit], i_D3=d3[bit],
                     o_Q=q[bit]
                 )
 
-        def get_oddr71b(sclk, eclk, d0, d1, d2, d3, d4, d5, d6, q):
+        def get_oddr71b(sclk, eclk, prst, d0, d1, d2, d3, d4, d5, d6, q):
             for bit in range(len(q)):
                 m.submodules += Instance("ODDR71B",
                     i_SCLK=sclk,
                     i_ECLK=eclk,
-                    i_RST=Const(0),
+                    i_RST=prst,
                     i_D0=d0[bit], i_D1=d1[bit], i_D2=d2[bit], i_D3=d3[bit],
                     i_D4=d4[bit], i_D5=d5[bit], i_D6=d6[bit],
                     o_Q=q[bit]
@@ -535,23 +535,29 @@ class LatticeECP5Platform(TemplatedPlatform):
                 get_oereg(pin.o_clk, ~pin.oe, t)
         elif pin.xdr == 2:
             if "i" in pin.dir:
-                get_iddr(pin.i_clk, i, pin_i0, pin_i1)
+                get_iddr(pin.i_clk, pin.i_prst, i, pin_i0, pin_i1)
             if "o" in pin.dir:
-                get_oddr(pin.o_clk, pin_o0, pin_o1, o)
+                get_oddr(pin.o_clk, pin.o_prst, pin_o0, pin_o1, o)
             if pin.dir in ("oe", "io"):
                 get_oereg(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)
+                get_iddrx2(pin.i_clk, pin.i_fclk, pin.i_prst, 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)
+                get_oddrx2(pin.o_clk, pin.o_fclk, pin.o_prst,
+                            pin_o0, pin_o1, pin_o2, pin_o3, o)
             if pin.dir in ("oe", "io"):
                 get_oereg(pin.o_clk, ~pin.oe, t)
         elif pin.xdr == 7:
             if "i" in pin.dir:
-                get_iddr71b(pin.i_clk, pin.i_fclk, i, pin_i0, pin_i1, pin_i2, pin_i3, pin_i4, pin_i5, pin_i6)
+                get_iddr71b(pin.i_clk, pin.i_fclk, pin.o_prst, i,
+                            pin_i0, pin_i1, pin_i2, pin_i3,
+                            pin_i4, pin_i5, pin_i6)
             if "o" in pin.dir:
-                get_oddr71b(pin.o_clk, pin.o_fclk, pin_o0, pin_o1, pin_o2, pin_o3, pin_o4, pin_o5, pin_o6, o)
+                get_oddr71b(pin.o_clk, pin.o_fclk, pin.o_prst,
+                            pin_o0, pin_o1, pin_o2, pin_o3,
+                            pin_o4, pin_o5, pin_o6, o)
             if pin.dir in ("oe", "io"):
                 get_oereg(pin.o_clk, ~pin.oe, t)
         else: