Joe,

Attached is a patch I hacked up a while ago that adds differential IOs to the SPIMaster. It is ugly, but hopefully transparent. I don't think I have tested this on hardware, and this will only work with a single chip-select line.

Instantiate it like:
phy = spi.SPIMaster(self.platform.request("spi", 0), differential=True)

where the pins are defined like:
("spi", 0,
        Subsignal("miso_p", Pins("LA00_CC_P")),
        Subsignal("miso_n", Pins("LA00_CC_N")),
        Subsignal("clk_p", Pins("LA08_P")),
        Subsignal("clk_n", Pins("LA08_N")),
        Subsignal("mosi_p", Pins("LA03_P")),
        Subsignal("mosi_n", Pins("LA03_N")),
        Subsignal("cs_n_n", Pins("LA02_P")),
        Subsignal("cs_n_p", Pins("LA02_N")),
        IOStandard("LVDS_25")
)

(Possibly turning on some termination, depending on your hardware).

Hope this helps move you in the right direction -
Chris

On 21/09/2017 19:43, Joe Britton via ARTIQ wrote:
This was discussed on IRC too...

https://irclog.whitequark.org/m-labs/2017-09-13
https://irclog.whitequark.org/m-labs/2017-09-15
https://irclog.whitequark.org/m-labs/2017-09-16
https://irclog.whitequark.org/m-labs/2017-09-17

The Q&A is much appreciated but we still don't have a solution that
works. It looks like rather greater knowledge of MIGEN is required.
M-Labs, please provide code so Arpit can contribute at the ARTIQ
Python level and steer clear of not-yet fully documented aspects of
Migen. AFACT this is needed for integration of any of the EEM modules
with VHDCI_carrier. Arpit  can't contribute to testing and driver
writing for Zotino if he remains stuck on this bump. -Joe

On Wed, Sep 13, 2017 at 1:35 PM, Joe Britton <[email protected]> wrote:
Thanks Arpit and Daniel for email responses. Catching up the mailing list...

The path Metlino/Sayma -> FMC -> VHDCI uses LVDS which are driven as
_p and _n pin pairs from the FPGA for each EEM IO line. Each EEM board
has an LVDS receiver that generates local single-ended IO.

If that is the case, you need to dig into Migen to figure out how to express the
subsignals with two pins using an LVDS standard (I haven’t done this myself, 
you might ask Robert
or Sebastien).
We've already figure out how to do get Migen to express subsignals
using a pair of pins. For example,

("clk", 0,
Subsignal("p", Pins("Y23")),
Subsignal("n", Pins("Y24")),
IOStandard("LVDS_25"), Misc("DIFF_TERM=TRUE")
)

Does Migen supports sub-subsignals or is a change needed to SPIMaster?
This is what the NIST_clock Migen subsignal definition looks like for
spi when driven single-ended by the FPGA.

("spi", 0,
Subsignal("clk", Pins("LPC:LA13_N")),
Subsignal("cs_n", Pins("LPC:LA14_N")),
Subsignal("mosi", Pins("LPC:LA17_CC_P")),
Subsignal("miso", Pins("LPC:LA17_CC_N")),
IOStandard("LVTTL")),


On Wed, Sep 13, 2017 at 11:21 AM, Joe Britton <[email protected]> wrote:
What's the right way to use ARTIQ to drive SPI devices across the VHDCI bus?
The SPIMaster interface appears to take only a single signal for each of
clk, cs_n, mosi and miso.

Has anybody yet interfaced with an SPI device using VHCDI? -Joe
_______________________________________________
ARTIQ mailing list
https://ssl.serverraum.org/lists/listinfo/artiq

diff --git a/artiq/gateware/spi.py b/artiq/gateware/spi.py
index 2daa390..a0743a4 100644
--- a/artiq/gateware/spi.py
+++ b/artiq/gateware/spi.py
@@ -1,10 +1,54 @@
 from itertools import product
 
 from migen import *
+from migen.genlib.io import DifferentialInput
 from misoc.interconnect import wishbone
 from misoc.cores.spi import SPIMachine
 
 
+class DifferentialTristate(TSTriple):
+    # HACK : This only works for single-bit signals
+    def get_tristate(self, pad, pad_n):
+        return Instance("IOBUFDS",
+                        i_I=self.i, o_O=self.o, i_T=self.oe,
+                        io_IO=pad, io_IOB=pad_n)
+
+
+class SingleEndedInterface(Module):
+    def __init__(self, pads):
+        self.mosi_t = TSTriple()
+        self.specials += self.mosi_t.get_tristate(pads.mosi)
+
+        self.clk_t = TSTriple()
+        self.specials += self.clk_t.get_tristate(pads.clk)
+
+        if hasattr(pads, "cs_n"):
+            self.cs_n_t = TSTriple(len(pads.cs_n))
+            self.specials += self.cs_n_t.get_tristate(pads.cs_n)
+
+        if hasattr(pads, "miso"):
+            self.miso = pads.miso
+
+
+class DifferentialInterface(Module):
+    def __init__(self, pads):
+        self.mosi_t = DifferentialTristate()
+        self.specials += self.mosi_t.get_tristate(pads.mosi_p, pads.mosi_n)
+
+        self.clk_t = DifferentialTristate()
+        self.specials += self.clk_t.get_tristate(pads.clk_p, pads.clk_n)
+
+        if hasattr(pads, "cs_n_p"):
+            # HACK : this only works when cs is a single-bit signal
+            self.cs_n_t = DifferentialTristate()
+            self.specials += self.cs_n_t.get_tristate(pads.cs_n_p, pads.cs_n_n)
+
+        if hasattr(pads, "miso_p"):
+            self.miso = Signal()
+            self.specials += DifferentialInput(pads.miso_p, pads.miso_n, 
self.miso)
+
+
+
 class SPIMaster(Module):
     """SPI Master.
 
@@ -104,11 +148,17 @@ class SPIMaster(Module):
     data (address 0):
         M write/read data (reset=0)
     """
-    def __init__(self, pads, bus=None):
+    def __init__(self, pads, bus=None, differential=False):
         if bus is None:
             bus = wishbone.Interface(data_width=32)
         self.bus = bus
 
+        if differential:
+            iface = DifferentialInterface(pads)
+        else:
+            iface = SingleEndedInterface(pads)
+        self.submodules.interface = iface
+
         ###
 
         # Wishbone
@@ -197,30 +247,24 @@ class SPIMaster(Module):
         ]
 
         # I/O
-        if hasattr(pads, "cs_n"):
-            cs_n_t = TSTriple(len(pads.cs_n))
-            self.specials += cs_n_t.get_tristate(pads.cs_n)
+        if hasattr(iface, "cs_n_t"):
             self.comb += [
-                cs_n_t.oe.eq(~config.offline),
-                cs_n_t.o.eq((cs & Replicate(spi.cs, len(cs))) ^
+                iface.cs_n_t.oe.eq(~config.offline),
+                iface.cs_n_t.o.eq((cs & Replicate(spi.cs, len(cs))) ^
                             Replicate(~config.cs_polarity, len(cs))),
             ]
 
-        clk_t = TSTriple()
-        self.specials += clk_t.get_tristate(pads.clk)
         self.comb += [
-            clk_t.oe.eq(~config.offline),
-            clk_t.o.eq((spi.cg.clk & spi.cs) ^ config.clk_polarity),
+            iface.clk_t.oe.eq(~config.offline),
+            iface.clk_t.o.eq((spi.cg.clk & spi.cs) ^ config.clk_polarity),
         ]
 
-        mosi_t = TSTriple()
-        self.specials += mosi_t.get_tristate(pads.mosi)
         self.comb += [
-            mosi_t.oe.eq(~config.offline & spi.cs &
+            iface.mosi_t.oe.eq(~config.offline & spi.cs &
                          (spi.oe | ~config.half_duplex)),
-            mosi_t.o.eq(spi.reg.o),
-            spi.reg.i.eq(Mux(config.half_duplex, mosi_t.i,
-                             getattr(pads, "miso", mosi_t.i))),
+            iface.mosi_t.o.eq(spi.reg.o),
+            spi.reg.i.eq(Mux(config.half_duplex, iface.mosi_t.i,
+                             getattr(iface, "miso", iface.mosi_t.i))),
         ]
 
 
_______________________________________________
ARTIQ mailing list
https://ssl.serverraum.org/lists/listinfo/artiq

Reply via email to