Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: m-labs/artiq
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: fb929c8599f2^
Choose a base ref
...
head repository: m-labs/artiq
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: e7146cc99942
Choose a head ref
  • 2 commits
  • 3 files changed
  • 1 contributor

Commits on Feb 26, 2016

  1. gateware/spi: stubs

    jordens committed Feb 26, 2016
    Copy the full SHA
    fb929c8 View commit details
  2. gateware.spi: design sketch

    jordens committed Feb 26, 2016
    Copy the full SHA
    e7146cc View commit details
Showing with 140 additions and 4 deletions.
  1. +13 −0 artiq/gateware/rtio/phy/spi.py
  2. +109 −0 artiq/gateware/spi.py
  3. +18 −4 artiq/gateware/targets/pipistrello.py
13 changes: 13 additions & 0 deletions artiq/gateware/rtio/phy/spi.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
from migen import *

from artiq.gateware.spi import SPIMaster as SPIMasterWB
from artiq.gateware.rtio.phy.wishbone import RT2WB


class SPIMaster(Module):
def __init__(self, pads, onehot=False, **kwargs):
self.submodules._ll = ClockDomainsRenamer("rio")(
SPIMasterWB(pads, **kwargs))
self.submodules._rt2wb = RT2WB(2, self._ll.bus)
self.rtlink = self._rt2wb.rtlink
self.probes = []
109 changes: 109 additions & 0 deletions artiq/gateware/spi.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
from migen import *
from migen.genlib.fsm import *
from migen.genlib.misc import WaitTimer
from misoc.interconnect import wishbone


class SPIMaster(Module):
"""SPI Master.
Notes:
* If there is a miso wire in pads, the input and output are done with
two signals (a.k.a. 4-wire SPI), else mosi is used for both output
and input (a.k.a. 3-wire SPI).
* Every transfer consists of a 0-32 bit write followed by a 0-32
bit read.
* cs_n is always asserted at the beginning and deasserted
at the end of the tranfer.
* cs_n handling is agnostic to whether it is one-hot or decoded
somewhere downstream. If it is decoded, "cs_n all deasserted"
should be handled accordingly (no slave selected).
If it is one-hot, asserting multiple slaves should only be attempted
if miso is either not connected between slaves or open collector.
* If config.cs_polarity == 0 (cs active low, the default),
"cs_n all deasserted" means "all cs_n bits high".
* The first bit output on mosi is always the MSB/LSB (depending on
config.lsb_first) of the data register, independent of
xfer.write_len. The last bit input from miso always ends up in
the LSB/MSB (respectively) of the data register, independent of
read_len.
* For 4-wire SPI only the sum of read_len and write_len matters. The
behavior is the same no matter how the transfer length is divided
between the two. For 3-wire SPI, the direction of mosi/miso is
switched from output to input after write_len cycles, at the
"output" clk edge corresponding to bit write_len + 1 of the transfer.
* Data output on mosi in 4-wire SPI during the read cycles is
undefined. Data in the data register outside the
least/most (depending on config.lsb_first) significant read_len
bits is undefined.
* The transfer is complete when the wishbone transaction is ack-ed.
* Input data from the last transaction can be read from the data
register at any time.
Transaction Sequence:
* if desired, write the xfer register to change lengths and cs_n.
* write the data register (also for zero-length writes),
writing triggers the transfer and the transfer is complete when the
write is complete.
* if desired, read the data register
Register address and bit map:
config (address 0):
1 offline: all pins high-z (reset=1)
1 cs_polarity: active level of chip select (reset=0)
1 clk_polarity: idle level for clk (reset=0)
1 clk_phase: first edge after cs assertion to sample data on (reset=0)
(0, 0): idle low, output on falling, input on rising
(0, 1): idle low, output on rising, input on falling
(1, 0): idle high, output on rising, input on falling
(1, 1): idle high, output on falling, input on rising
1 lsb_first: LSB is the first bit on the wire (reset=0)
11 undefined
16 speed: divider from this module's clock to the SPI clk
(minimum=2, reset=4)
clk pulses are asymmetric if speed is odd, favoring longer setup
over hold times
xfer (address 1):
16 cs: active high bit mask of chip selects to assert
6 write_len: 0-32 bits
2 undefined
6 read_len: 0-32 bits
2 undefined
data (address 2):
32 write/read data
"""
def __init__(self, pads, bus=None):
if bus is None:
bus = wishbone.Interface(data_width=32)
self.bus = bus

###


def _test_gen(bus):
yield from bus.write(0, 0 | (5 << 16))
yield
yield from bus.write(1, 1 | (24 << 16) | (16 << 24))
yield
yield from bus.write(2, 0x12345678)
yield
r = (yield from bus.read(2))
print(r)
yield


class _TestPads:
def __init__(self):
self.cs_n = Signal(3)
self.clk = Signal()
self.mosi = Signal()
self.miso = Signal()


if __name__ == "__main__":
pads = _TestPads()
dut = SPIMaster(pads)
run_simulation(dut, _test_gen(dut.bus), vcd_name="spi_master.vcd")
22 changes: 18 additions & 4 deletions artiq/gateware/targets/pipistrello.py
Original file line number Diff line number Diff line change
@@ -19,7 +19,7 @@

from artiq.gateware.soc import AMPSoC
from artiq.gateware import rtio, nist_qc1
from artiq.gateware.rtio.phy import ttl_simple, ttl_serdes_spartan6, dds
from artiq.gateware.rtio.phy import ttl_simple, ttl_serdes_spartan6, dds, spi
from artiq import __artiq_dir__ as artiq_dir
from artiq import __version__ as artiq_version

@@ -152,12 +152,12 @@ def __init__(self, cpu_type="or1k", **kwargs):
rtio_channels.append(rtio.Channel.from_phy(phy, ififo_depth=512,
ofifo_depth=4))

# ttl2 can run on a 8x serdes if xtrig is not used
for i in range(15):
# the last five ttls are used for SPI and a ClockGen
for i in range(11):
if i in (0, 1):
phy = ttl_serdes_spartan6.Output_4X(platform.request("ttl", i),
self.rtio_crg.rtiox4_stb)
elif i in (2,):
elif i in (2,): # ttl2 can run on a 8x serdes if xtrig is not used
phy = ttl_serdes_spartan6.Output_8X(platform.request("ttl", i),
self.rtio_crg.rtiox8_stb)
else:
@@ -192,6 +192,20 @@ def __init__(self, cpu_type="or1k", **kwargs):
ofifo_depth=512,
ififo_depth=4))

spi_pins = Record([("cs_n", 1), ("clk", 1), ("mosi", 1), ("miso", 1)])
# cs_n can be multiple bits wide, one-hot
# absence of miso indicates bidirectional mosi
self.comb += [
platform.request("ttl", 11).eq(spi_pins.cs_n),
platform.request("ttl", 12).eq(spi_pins.clk),
platform.request("ttl", 13).eq(spi_pins.mosi),
spi_pins.miso.eq(platform.request("ttl", 14)),
]
phy = spi.SPIMaster(spi_pins)
self.submodules += phy
rtio_channels.append(rtio.Channel.from_phy(
phy, ofifo_depth=4, ififo_depth=4))

self.config["RTIO_LOG_CHANNEL"] = len(rtio_channels)
rtio_channels.append(rtio.LogChannel())