vendor.fpga.lattice_ice40: implement SDR and DDR I/O buffers.
authorwhitequark <cz@m-labs.hk>
Mon, 3 Jun 2019 07:43:02 +0000 (07:43 +0000)
committerwhitequark <cz@m-labs.hk>
Mon, 3 Jun 2019 07:43:31 +0000 (07:43 +0000)
nmigen/vendor/fpga/lattice_ice40.py

index 0583451421d3f3c1803d43bbcbfb2db45981a751..778e9c76621e9e946c0849f23a4784344b889735 100644 (file)
@@ -108,53 +108,102 @@ class LatticeICE40Platform(TemplatedPlatform):
         """
     ]
 
-    def _get_io_buffer(self, port, extras, fn):
+    def _get_dff(self, clk, d, q):
+        return Instance("$dff",
+            p_CLK_POLARITY=0,
+            p_WIDTH=len(d),
+            i_CLK=clk,
+            i_D=d,
+            o_Q=q)
+
+    def _get_io_buffer(self, pin, port, extras):
         m = Module()
+
+        if "i" in pin.dir and pin.xdr == 2:
+            i0_ff = Signal.like(pin.i0, name="{}_ff".format(pin.i0.name))
+            i1_ff = Signal.like(pin.i1, name="{}_ff".format(pin.i1.name))
+            m.submodules += self._get_dff(pin.i_clk, i0_ff, pin.i0)
+            m.submodules += self._get_dff(pin.i_clk, i1_ff, pin.i1)
+        if "o" in pin.dir and pin.xdr == 2:
+            o1_ff = Signal.like(pin.o1, name="{}_ff".format(pin.o1.name))
+            m.submodules += self._get_dff(pin.o_clk, pin.o1, o1_ff)
+
         for bit in range(len(port)):
-            m.submodules += Instance("SB_IO",
+            io_args = [
                 ("io", "PACKAGE_PIN", port[bit]),
-                *fn(bit),
-                *(("p", key, value) for key, value in extras.items()))
+                *(("p", key, value) for key, value in extras.items()),
+            ]
+
+            if "i" not in pin.dir:
+                i_type =     0b00 # PIN_NO_INPUT aka PIN_INPUT_REGISTERED
+            elif pin.xdr == 0:
+                i_type =     0b01 # PIN_INPUT
+            elif pin.xdr > 0:
+                i_type =     0b00 # PIN_INPUT_REGISTERED
+            if "o" not in pin.dir:
+                o_type = 0b0000   # PIN_NO_OUTPUT
+            elif pin.xdr == 0 and pin.dir == "o":
+                o_type = 0b0110   # PIN_OUTPUT
+            elif pin.xdr == 0:
+                o_type = 0b1010   # PIN_OUTPUT_TRISTATE
+            elif pin.xdr == 1 and pin.dir == "o":
+                o_type = 0b0101   # PIN_OUTPUT_REGISTERED
+            elif pin.xdr == 1:
+                o_type = 0b1101   # PIN_OUTPUT_REGISTERED_ENABLE_REGISTERED
+            elif pin.xdr == 2 and pin.dir == "o":
+                o_type = 0b0100   # PIN_OUTPUT_DDR
+            elif pin.xdr == 2:
+                o_type = 0b1100   # PIN_OUTPUT_DDR_ENABLE_REGISTERED
+            io_args.append(("p", "PIN_TYPE", (o_type << 2) | i_type))
+
+            if hasattr(pin, "i_clk"):
+                io_args.append(("i", "INPUT_CLK",  pin.i_clk))
+            if hasattr(pin, "o_clk"):
+                io_args.append(("i", "OUTPUT_CLK", pin.o_clk))
+
+            if "i" in pin.dir:
+                if pin.xdr < 2:
+                    io_args.append(("o", "D_IN_0",  pin.i[bit]))
+                if pin.xdr == 2:
+                    # Re-register both inputs before they enter fabric. This increases hold time
+                    # to an entire cycle, and adds one cycle of latency.
+                    io_args.append(("o", "D_IN_0",  i0_ff))
+                    io_args.append(("o", "D_IN_1",  i1_ff))
+            if "o" in pin.dir:
+                if pin.xdr < 2:
+                    io_args.append(("i", "D_OUT_0", pin.o[bit]))
+                if pin.xdr == 2:
+                    # Re-register negedge output after it leaves fabric. This increases setup time
+                    # to an entire cycle, and doesn't add latency.
+                    io_args.append(("i", "D_OUT_0", pin.o0[bit]))
+                    io_args.append(("i", "D_OUT_1", o1_ff))
+
+            if pin.dir in ("oe", "io"):
+                io_args.append(("i", "OUTPUT_ENABLE", pin.oe))
+
+            m.submodules += Instance("SB_IO", *io_args)
+
         return m
 
     def get_input(self, pin, port, extras):
         self._check_feature("single-ended input", pin, extras,
-                            valid_xdrs=(0,), valid_extras=True)
-        return self._get_io_buffer(port, extras, lambda bit: [
-            # PIN_NO_OUTPUT|PIN_INPUT
-            ("p", "PIN_TYPE",       0b0000_01),
-            ("o", "D_IN_0",         pin.i[bit]),
-        ])
+                            valid_xdrs=(0, 1, 2), valid_extras=True)
+        return self._get_io_buffer(pin, port, extras)
 
     def get_output(self, pin, port, extras):
         self._check_feature("single-ended output", pin, extras,
-                            valid_xdrs=(0,), valid_extras=True)
-        return self._get_io_buffer(port, extras, lambda bit: [
-            # PIN_OUTPUT|PIN_INPUT_REGISTERED
-            ("p", "PIN_TYPE",       0b0110_00),
-            ("i", "D_OUT_0",        pin.o[bit]),
-        ])
+                            valid_xdrs=(0, 1, 2), valid_extras=True)
+        return self._get_io_buffer(pin, port, extras)
 
     def get_tristate(self, pin, port, extras):
         self._check_feature("single-ended tristate", pin, extras,
-                            valid_xdrs=(0,), valid_extras=True)
-        return self._get_io_buffer(port, extras, lambda bit: [
-            # PIN_OUTPUT_TRISTATE|PIN_INPUT_REGISTERED
-            ("p", "PIN_TYPE",       0b1010_00),
-            ("i", "D_OUT_0",        pin.o[bit]),
-            ("i", "OUTPUT_ENABLE",  pin.oe),
-        ])
+                            valid_xdrs=(0, 1, 2), valid_extras=True)
+        return self._get_io_buffer(pin, port, extras)
 
     def get_input_output(self, pin, port, extras):
         self._check_feature("single-ended input/output", pin, extras,
-                            valid_xdrs=(0,), valid_extras=True)
-        return self._get_io_buffer(port, extras, lambda bit: [
-            # PIN_OUTPUT_TRISTATE|PIN_INPUT
-            ("p", "PIN_TYPE",       0b1010_01),
-            ("o", "D_IN_0",         pin.i[bit]),
-            ("i", "D_OUT_0",        pin.o[bit]),
-            ("i", "OUTPUT_ENABLE",  pin.oe),
-        ])
+                            valid_xdrs=(0, 1, 2), valid_extras=True)
+        return self._get_io_buffer(pin, port, extras)
 
 
 class IceStormProgrammerMixin: