From 4fa823b62a5e6dfd8ae7a67a356eaa210a0ea0bf Mon Sep 17 00:00:00 2001 From: Sebastien Bourdeauducq Date: Mon, 23 Oct 2017 15:04:01 +0800 Subject: [PATCH] gateware: add support for SPI-over-LVDS --- artiq/gateware/rtio/phy/spi.py | 4 +- artiq/gateware/spi.py | 76 ++++++++++++++++++++++++---------- 2 files changed, 56 insertions(+), 24 deletions(-) diff --git a/artiq/gateware/rtio/phy/spi.py b/artiq/gateware/rtio/phy/spi.py index 1882eb700..25d0d7703 100644 --- a/artiq/gateware/rtio/phy/spi.py +++ b/artiq/gateware/rtio/phy/spi.py @@ -5,9 +5,9 @@ from artiq.gateware.rtio.phy.wishbone import RT2WB class SPIMaster(Module): - def __init__(self, pads, **kwargs): + def __init__(self, pads, pads_n=None, **kwargs): self.submodules._ll = ClockDomainsRenamer("rio_phy")( - SPIMasterWB(pads, **kwargs)) + SPIMasterWB(pads, pads_n, **kwargs)) self.submodules._rt2wb = RT2WB(2, self._ll.bus) self.rtlink = self._rt2wb.rtlink self.probes = [] diff --git a/artiq/gateware/spi.py b/artiq/gateware/spi.py index 2daa3907c..182a934f5 100644 --- a/artiq/gateware/spi.py +++ b/artiq/gateware/spi.py @@ -104,7 +104,7 @@ class SPIMaster(Module): data (address 0): M write/read data (reset=0) """ - def __init__(self, pads, bus=None): + def __init__(self, pads, pads_n=None, bus=None): if bus is None: bus = wishbone.Interface(data_width=32) self.bus = bus @@ -197,31 +197,63 @@ 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) + mosi_oe = Signal() + clk = Signal() + self.comb += [ + mosi_oe.eq( + ~config.offline & spi.cs & + (spi.oe | ~config.half_duplex)), + clk.eq((spi.cg.clk & spi.cs) ^ config.clk_polarity) + ] + + if pads_n is None: + if hasattr(pads, "cs_n"): + cs_n_t = TSTriple(len(pads.cs_n)) + self.specials += cs_n_t.get_tristate(pads.cs_n) + self.comb += [ + cs_n_t.oe.eq(~config.offline), + 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 += [ - cs_n_t.oe.eq(~config.offline), - cs_n_t.o.eq((cs & Replicate(spi.cs, len(cs))) ^ - Replicate(~config.cs_polarity, len(cs))), + clk_t.oe.eq(~config.offline), + clk_t.o.eq(clk), ] - 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), - ] + mosi_t = TSTriple() + self.specials += mosi_t.get_tristate(pads.mosi) + self.comb += [ + mosi_t.oe.eq(mosi_oe), + mosi_t.o.eq(spi.reg.o), + spi.reg.i.eq(Mux(config.half_duplex, mosi_t.i, + getattr(pads, "miso", mosi_t.i))), + ] + else: + if hasattr(pads, "cs_n"): + for i in range(len(pads.cs_n)): + self.specials += Instance("IOBUFDS", + i_I=(cs[i] & spi.cs) ^ ~config.cs_polarity, + i_T=config.offline, + io_IO=pads.cs_n[i], io_IOB=pads_n.cs_n[i]) - mosi_t = TSTriple() - self.specials += mosi_t.get_tristate(pads.mosi) - self.comb += [ - 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))), - ] + self.specials += Instance("IOBUFDS", + i_I=clk, i_T=config.offline, + io_IO=pads.clk, io_IOB=pads_n.clk) + + mosi = Signal() + self.specials += Instance("IOBUFDS", + o_O=mosi, i_I=spi.reg.o, i_T=~mosi_oe, + io_IO=pads.mosi, io_IOB=pads_n.mosi) + if hasattr(pads, "miso"): + miso = Signal() + self.specials += Instance("IBUFDS", + o_O=miso, i_I=pads.miso, i_IB=pads_n.miso) + else: + miso = mosi + self.comb += spi.reg.i.eq(Mux(config.half_duplex, mosi, miso)) SPI_DATA_ADDR, SPI_XFER_ADDR, SPI_CONFIG_ADDR = range(3)