forked from M-Labs/artiq
1
0
Fork 0

remove old spi RTIO Phy

This commit is contained in:
Robert Jördens 2018-02-23 17:31:19 +01:00
parent 1329e1a23e
commit cc70578f1f
7 changed files with 2 additions and 697 deletions

View File

@ -1,4 +1,4 @@
from artiq.coredevice import exceptions, dds, spi, spi2 from artiq.coredevice import exceptions, dds, spi2
from artiq.coredevice.exceptions import (RTIOUnderflow, RTIOOverflow) from artiq.coredevice.exceptions import (RTIOUnderflow, RTIOOverflow)
from artiq.coredevice.dds import (PHASE_MODE_CONTINUOUS, PHASE_MODE_ABSOLUTE, from artiq.coredevice.dds import (PHASE_MODE_CONTINUOUS, PHASE_MODE_ABSOLUTE,
PHASE_MODE_TRACKING) PHASE_MODE_TRACKING)

View File

@ -492,11 +492,6 @@ def create_channel_handlers(vcd_manager, devices, ref_period,
dds_onehot_sel, dds_sysclk) dds_onehot_sel, dds_sysclk)
channel_handlers[dds_bus_channel] = dds_handler channel_handlers[dds_bus_channel] = dds_handler
dds_handler.add_dds_channel(name, dds_channel) dds_handler.add_dds_channel(name, dds_channel)
if (desc["module"] == "artiq.coredevice.spi" and
desc["class"] == "SPIMaster"):
channel = desc["arguments"]["channel"]
channel_handlers[channel] = SPIMasterHandler(
vcd_manager, name)
if (desc["module"] == "artiq.coredevice.spi2" and if (desc["module"] == "artiq.coredevice.spi2" and
desc["class"] == "SPIMaster"): desc["class"] == "SPIMaster"):
channel = desc["arguments"]["channel"] channel = desc["arguments"]["channel"]

View File

@ -1,286 +0,0 @@
"""
Driver for generic SPI on RTIO.
This ARTIQ coredevice driver corresponds to the legacy MiSoC SPI core (v1).
Output event replacement is not supported and issuing commands at the same
time is an error.
"""
import numpy
from artiq.language.core import syscall, kernel, portable, now_mu, delay_mu
from artiq.language.types import TInt32, TNone
from artiq.language.units import MHz
from artiq.coredevice.rtio import rtio_output, rtio_input_data
__all__ = [
"SPI_DATA_ADDR", "SPI_XFER_ADDR", "SPI_CONFIG_ADDR",
"SPI_OFFLINE", "SPI_ACTIVE", "SPI_PENDING",
"SPI_CS_POLARITY", "SPI_CLK_POLARITY", "SPI_CLK_PHASE",
"SPI_LSB_FIRST", "SPI_HALF_DUPLEX",
"SPIMaster"
]
SPI_DATA_ADDR, SPI_XFER_ADDR, SPI_CONFIG_ADDR = range(3)
(
SPI_OFFLINE,
SPI_ACTIVE,
SPI_PENDING,
SPI_CS_POLARITY,
SPI_CLK_POLARITY,
SPI_CLK_PHASE,
SPI_LSB_FIRST,
SPI_HALF_DUPLEX,
) = (1 << i for i in range(8))
SPI_RT2WB_READ = 1 << 2
class SPIMaster:
"""Core device Serial Peripheral Interface (SPI) bus master.
Owns one SPI bus.
**Transfer Sequence**:
* If desired, write the ``config`` register (:meth:`set_config`)
to configure and activate the core.
* If desired, write the ``xfer`` register (:meth:`set_xfer`)
to set ``cs_n``, ``write_length``, and ``read_length``.
* :meth:`write` to the ``data`` register (also for transfers with
zero bits to be written). Writing starts the transfer.
* If desired, :meth:`read_sync` (or :meth:`read_async` followed by a
:meth:`input_async` later) the ``data`` register corresponding to
the last completed transfer.
* If desired, :meth:`set_xfer` for the next transfer.
* If desired, :meth:`write` ``data`` queuing the next
(possibly chained) transfer.
**Notes**:
* In order to chain a transfer onto an in-flight transfer without
deasserting ``cs`` in between, the second :meth:`write` needs to
happen strictly later than ``2*ref_period_mu`` (two coarse RTIO
cycles) but strictly earlier than ``xfer_period_mu + write_period_mu``
after the first. Note that :meth:`write` already applies a delay of
``xfer_period_mu + write_period_mu``.
* A full transfer takes ``write_period_mu + xfer_period_mu``.
* Chained transfers can happen every ``xfer_period_mu``.
* Read data is available every ``xfer_period_mu`` starting
a bit after xfer_period_mu (depending on ``clk_phase``).
* As a consequence, in order to chain transfers together, new data must
be written before the pending transfer's read data becomes available.
:param channel: RTIO channel number of the SPI bus to control.
"""
kernel_invariants = {"core", "ref_period_mu", "channel"}
def __init__(self, dmgr, channel, core_device="core"):
self.core = dmgr.get(core_device)
self.ref_period_mu = self.core.seconds_to_mu(
self.core.coarse_ref_period)
assert self.ref_period_mu == self.core.ref_multiplier
self.channel = channel
self.write_period_mu = numpy.int64(0)
self.read_period_mu = numpy.int64(0)
self.xfer_period_mu = numpy.int64(0)
@portable
def frequency_to_div(self, f):
return int(1/(f*self.core.mu_to_seconds(self.ref_period_mu))) + 1
@kernel
def set_config(self, flags=0, write_freq=20*MHz, read_freq=20*MHz):
"""Set the configuration register.
* If ``config.cs_polarity`` == 0 (``cs`` active low, the default),
"``cs_n`` all deasserted" means "all ``cs_n`` bits high".
* ``cs_n`` is not mandatory in the pads supplied to the gateware core.
Framing and chip selection can also be handled independently
through other means, e.g. ``TTLOut``.
* If there is a ``miso`` wire in the pads supplied in the gateware,
input and output may be two signals ("4-wire SPI"),
otherwise ``mosi`` must be used for both output and input
("3-wire SPI") and ``config.half_duplex`` must to be set
when reading data is desired or when the slave drives the
``mosi`` signal at any point.
* The first bit output on ``mosi`` is always the MSB/LSB (depending
on ``config.lsb_first``) of the ``data`` register, independent of
``xfer.write_length``. The last bit input from ``miso`` always ends
up in the LSB/MSB (respectively) of the ``data`` register,
independent of ``xfer.read_length``.
* Writes to the ``config`` register take effect immediately.
**Configuration flags**:
* :const:`SPI_OFFLINE`: all pins high-z (reset=1)
* :const:`SPI_ACTIVE`: transfer in progress (read-only)
* :const:`SPI_PENDING`: transfer pending in intermediate buffer
(read-only)
* :const:`SPI_CS_POLARITY`: active level of ``cs_n`` (reset=0)
* :const:`SPI_CLK_POLARITY`: idle level of ``clk`` (reset=0)
* :const:`SPI_CLK_PHASE`: first edge after ``cs`` assertion to sample
data on (reset=0). In Motorola/Freescale SPI language
(:const:`SPI_CLK_POLARITY`, :const:`SPI_CLK_PHASE`) == (CPOL, CPHA):
- (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
* :const:`SPI_LSB_FIRST`: LSB is the first bit on the wire (reset=0)
* :const:`SPI_HALF_DUPLEX`: 3-wire SPI, in/out on ``mosi`` (reset=0)
This method advances the timeline by the duration of the
RTIO-to-Wishbone bus transaction (three RTIO clock cycles).
:param flags: A bit map of `SPI_*` flags.
:param write_freq: Desired SPI clock frequency during write bits.
:param read_freq: Desired SPI clock frequency during read bits.
"""
self.set_config_mu(flags, self.frequency_to_div(write_freq),
self.frequency_to_div(read_freq))
@kernel
def set_config_mu(self, flags=0, write_div=6, read_div=6):
"""Set the ``config`` register (in SPI bus machine units).
.. seealso:: :meth:`set_config`
:param write_div: Counter load value to divide the RTIO
clock by to generate the SPI write clk. (minimum=2, reset=2)
``f_rtio_clk/f_spi_write == write_div``. If ``write_div`` is odd,
the setup phase of the SPI clock is biased to longer lengths
by one RTIO clock cycle.
:param read_div: Ditto for the read clock.
"""
if write_div > 257 or write_div < 2 or read_div > 257 or read_div < 2:
raise ValueError('Divider values out of range')
rtio_output(now_mu(), self.channel, SPI_CONFIG_ADDR, flags |
((write_div - 2) << 16) | ((read_div - 2) << 24))
self.write_period_mu = int(write_div*self.ref_period_mu)
self.read_period_mu = int(read_div*self.ref_period_mu)
delay_mu(3*self.ref_period_mu)
@kernel
def set_xfer(self, chip_select=0, write_length=0, read_length=0):
"""Set the ``xfer`` register.
* Every transfer consists of a write of ``write_length`` bits
immediately followed by a read of ``read_length`` bits.
* ``cs_n`` is asserted at the beginning and deasserted at the end
of the transfer if there is no other transfer pending.
* ``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, or correctly multiplexed externally.
* For 4-wire SPI only the sum of ``read_length`` and ``write_length``
matters. The behavior is the same (except for clock speeds) no matter
how the total transfer length is divided between the two. For
3-wire SPI, the direction of ``mosi`` is switched from output to
input after ``write_length`` bits.
* Data output on ``mosi`` in 4-wire SPI during the read cycles is what
is found in the data register at the time.
Data in the ``data`` register outside the least/most (depending
on ``config.lsb_first``) significant ``read_length`` bits is what is
seen on ``miso`` (or ``mosi`` if ``config.half_duplex``)
during the write cycles.
* Writes to ``xfer`` are synchronized to the start of the next
(possibly chained) transfer.
This method advances the timeline by the duration of the
RTIO-to-Wishbone bus transaction (three RTIO clock cycles).
:param chip_select: Bit mask of chip selects to assert. Or number of
the chip select to assert if ``cs`` is decoded downstream.
(reset=0)
:param write_length: Number of bits to write during the next transfer.
(reset=0)
:param read_length: Number of bits to read during the next transfer.
(reset=0)
"""
rtio_output(now_mu(), self.channel, SPI_XFER_ADDR,
chip_select | (write_length << 16) | (read_length << 24))
self.xfer_period_mu = int(write_length*self.write_period_mu +
read_length*self.read_period_mu)
delay_mu(3*self.ref_period_mu)
@kernel
def write(self, data=0):
"""Write data to data register.
* The ``data`` register and the shift register are 32 bits wide.
If there are no writes to the register, ``miso`` data reappears on
``mosi`` after 32 cycles.
* A wishbone data register write is acknowledged when the
transfer has been written to the intermediate buffer.
It will be started when there are no other transactions being
executed, either beginning a new SPI transfer of chained
to an in-flight transfer.
* Writes take three ``ref_period`` cycles unless another
chained transfer is pending and the transfer being
executed is not complete.
* The SPI ``data`` register is double-buffered: Once a transfer has
started, new write data can be written, queuing a new transfer.
Transfers submitted this way are chained and executed without
deasserting ``cs`` in between. Once a transfer completes,
the previous transfer's read data is available in the
``data`` register.
* For bit alignment and bit ordering see :meth:`set_config`.
This method advances the timeline by the duration of the SPI transfer.
If a transfer is to be chained, the timeline needs to be rewound.
"""
rtio_output(now_mu(), self.channel, SPI_DATA_ADDR, data)
delay_mu(self.xfer_period_mu + self.write_period_mu)
@kernel
def read_async(self):
"""Trigger an asynchronous read from the ``data`` register.
For bit alignment and bit ordering see :meth:`set_config`.
Reads always finish in two cycles.
Every data register read triggered by a :meth:`read_async`
must be matched by a :meth:`input_async` to retrieve the data.
This method advances the timeline by the duration of the
RTIO-to-Wishbone bus transaction (three RTIO clock cycles).
"""
rtio_output(now_mu(), self.channel, SPI_DATA_ADDR | SPI_RT2WB_READ, 0)
delay_mu(3*self.ref_period_mu)
@kernel
def input_async(self):
"""Retrieves data read asynchronously from the ``data`` register.
:meth:`input_async` must match a preeeding :meth:`read_async`.
"""
return rtio_input_data(self.channel)
@kernel
def read_sync(self):
"""Read the ``data`` register synchronously.
This is a shortcut for :meth:`read_async` followed by
:meth:`input_async`.
"""
self.read_async()
return self.input_async()
@kernel
def _get_xfer_sync(self):
rtio_output(now_mu(), self.channel, SPI_XFER_ADDR | SPI_RT2WB_READ, 0)
return rtio_input_data(self.channel)
@kernel
def _get_config_sync(self):
rtio_output(now_mu(), self.channel, SPI_CONFIG_ADDR | SPI_RT2WB_READ,
0)
return rtio_input_data(self.channel)

View File

@ -1,13 +0,0 @@
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, pads_n=None, **kwargs):
self.submodules._ll = ClockDomainsRenamer("rio_phy")(
SPIMasterWB(pads, pads_n, **kwargs))
self.submodules._rt2wb = RT2WB(2, self._ll.bus)
self.rtlink = self._rt2wb.rtlink
self.probes = []

View File

@ -1,385 +0,0 @@
from itertools import product
from migen import *
from misoc.interconnect import wishbone
from misoc.cores.spi import SPIMachine
class SPIMaster(Module):
"""SPI Master.
Notes:
* M = 32 is the data width (width of the data register,
maximum write bits, maximum read bits)
* Every transfer consists of a write_length 0-M bit write followed
by a read_length 0-M bit read.
* cs_n is asserted at the beginning and deasserted at the end of the
transfer if there is no other transfer pending.
* 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,
or correctly multiplexed externally.
* If config.cs_polarity == 0 (cs active low, the default),
"cs_n all deasserted" means "all cs_n bits high".
* cs is not mandatory in pads. Framing and chip selection can also
be handled independently through other means.
* If there is a miso wire in pads, the input and output can be done
with two signals (a.k.a. 4-wire SPI), else mosi must be used for
both output and input (a.k.a. 3-wire SPI) and config.half_duplex
must to be set when reading data is desired.
* For 4-wire SPI only the sum of read_length and write_length matters.
The behavior is the same no matter how the total 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
"shift_out" clk edge corresponding to bit write_length + 1 of the
transfer.
* 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.
* Data output on mosi in 4-wire SPI during the read cycles is what
is found in the data register at the time.
Data in the data register outside the least/most (depending
on config.lsb_first) significant read_length bits is what is
seen on miso during the write cycles.
* The SPI data register is double-buffered: Once a transfer has
started, new write data can be written, queuing a new transfer.
Transfers submitted this way are chained and executed without
deasserting cs. Once a transfer completes, the previous transfer's
read data is available in the data register.
* Writes to the config register take effect immediately. Writes to xfer
and data are synchronized to the start of a transfer.
* A wishbone data register write is ack-ed when the transfer has
been written to the intermediate buffer. It will be started when
there are no other transactions being executed, either starting
a new SPI transfer of chained to an in-flight transfer.
Writes take two cycles unless the write is to the data register
and another chained transfer is pending and the transfer being
executed is not complete. Reads always finish in two cycles.
Transaction Sequence:
* If desired, write the config register to set up the core.
* 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 when the transfer is accepted to
the inermediate buffer, the write is ack-ed.
* If desired, read the data register corresponding to the last
completed transfer.
* If desired, change xfer register for the next transfer.
* If desired, write data queuing the next (possibly chained) transfer.
Register address and bit map:
config (address 2):
1 offline: all pins high-z (reset=1)
1 active: cs/transfer active (read-only)
1 pending: transfer pending in intermediate buffer (read-only)
1 cs_polarity: active level of chip select (reset=0)
1 clk_polarity: idle level of clk (reset=0)
1 clk_phase: first edge after cs assertion to sample data on (reset=0)
(clk_polarity, clk_phase) == (CPOL, CPHA) in Freescale language.
(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
There is never a clk edge during a cs edge.
1 lsb_first: LSB is the first bit on the wire (reset=0)
1 half_duplex: 3-wire SPI, in/out on mosi (reset=0)
8 undefined
8 div_write: counter load value to divide this module's clock
to generate the SPI write clk (reset=0)
f_clk/f_spi_write == div_write + 2
8 div_read: ditto for the read clock
xfer (address 1):
16 cs: active high bit mask of chip selects to assert (reset=0)
6 write_len: 0-M bits (reset=0)
2 undefined
6 read_len: 0-M bits (reset=0)
2 undefined
data (address 0):
M write/read data (reset=0)
"""
def __init__(self, pads, pads_n=None, bus=None):
if bus is None:
bus = wishbone.Interface(data_width=32)
self.bus = bus
###
# Wishbone
config = Record([
("offline", 1),
("active", 1),
("pending", 1),
("cs_polarity", 1),
("clk_polarity", 1),
("clk_phase", 1),
("lsb_first", 1),
("half_duplex", 1),
("padding", 8),
("div_write", 8),
("div_read", 8),
])
config.offline.reset = 1
assert len(config) <= len(bus.dat_w)
xfer = Record([
("cs", 16),
("write_length", 6),
("padding0", 2),
("read_length", 6),
("padding1", 2),
])
assert len(xfer) <= len(bus.dat_w)
self.submodules.spi = spi = SPIMachine(
data_width=len(bus.dat_w) + 1,
clock_width=len(config.div_read),
bits_width=len(xfer.read_length))
pending = Signal()
cs = Signal.like(xfer.cs)
data_read = Signal.like(spi.reg.data)
data_write = Signal.like(spi.reg.data)
self.comb += [
spi.start.eq(pending & (~spi.cs | spi.done)),
spi.clk_phase.eq(config.clk_phase),
spi.reg.lsb.eq(config.lsb_first),
spi.div_write.eq(config.div_write),
spi.div_read.eq(config.div_read),
]
self.sync += [
If(spi.done,
data_read.eq(
Mux(spi.reg.lsb, spi.reg.data[1:], spi.reg.data[:-1])),
),
If(spi.start,
cs.eq(xfer.cs),
spi.bits.n_write.eq(xfer.write_length),
spi.bits.n_read.eq(xfer.read_length),
If(spi.reg.lsb,
spi.reg.data[:-1].eq(data_write),
).Else(
spi.reg.data[1:].eq(data_write),
),
pending.eq(0),
),
# wb.ack a transaction if any of the following:
# a) reading,
# b) writing to non-data register
# c) writing to data register and no pending transfer
# d) writing to data register and pending and swapping buffers
bus.ack.eq(bus.cyc & bus.stb &
(~bus.we | (bus.adr != 0) | ~pending | spi.done)),
If(bus.cyc & bus.stb,
bus.dat_r.eq(
Array([data_read, xfer.raw_bits(), config.raw_bits()
])[bus.adr]),
),
If(bus.ack,
bus.ack.eq(0),
If(bus.we,
Array([data_write, xfer.raw_bits(), config.raw_bits()
])[bus.adr].eq(bus.dat_w),
If(bus.adr == 0, # data register
pending.eq(1),
),
),
),
config.active.eq(spi.cs),
config.pending.eq(pending),
]
# I/O
mosi_oe = Signal()
clk = Signal()
self.comb += [
mosi_oe.eq(
~config.offline & spi.cs &
(spi.oe | ~config.half_duplex)),
]
self.sync += [
If(spi.cg.ce & spi.cg.edge,
clk.eq((~spi.cg.clk & spi.cs_next) ^ 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 += [
clk_t.oe.eq(~config.offline),
clk_t.o.eq(clk),
]
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("OBUFTDS",
i_I=(cs[i] & spi.cs) ^ ~config.cs_polarity,
i_T=config.offline,
o_O=pads.cs_n[i], o_OB=pads_n.cs_n[i])
self.specials += Instance("OBUFTDS",
i_I=clk, i_T=config.offline,
o_O=pads.clk, o_OB=pads_n.clk)
mosi = Signal()
self.specials += Instance("IOBUFDS_INTERMDISABLE",
p_DIFF_TERM="TRUE",
p_IBUF_LOW_PWR="FALSE",
p_USE_IBUFDISABLE="TRUE",
i_IBUFDISABLE=config.offline | mosi_oe,
i_INTERMDISABLE=config.offline | mosi_oe,
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_INTERMDISABLE",
p_DIFF_TERM="TRUE",
p_IBUF_LOW_PWR="FALSE",
p_USE_IBUFDISABLE="TRUE",
i_IBUFDISABLE=config.offline,
i_INTERMDISABLE=config.offline,
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)
(
SPI_OFFLINE,
SPI_ACTIVE,
SPI_PENDING,
SPI_CS_POLARITY,
SPI_CLK_POLARITY,
SPI_CLK_PHASE,
SPI_LSB_FIRST,
SPI_HALF_DUPLEX,
) = (1 << i for i in range(8))
def SPI_DIV_WRITE(i):
return i << 16
def SPI_DIV_READ(i):
return i << 24
def SPI_CS(i):
return i << 0
def SPI_WRITE_LENGTH(i):
return i << 16
def SPI_READ_LENGTH(i):
return i << 24
def _test_xfer(bus, cs, wlen, rlen, wdata):
yield from bus.write(SPI_XFER_ADDR, SPI_CS(cs) |
SPI_WRITE_LENGTH(wlen) | SPI_READ_LENGTH(rlen))
yield from bus.write(SPI_DATA_ADDR, wdata)
yield
def _test_read(bus, sync=SPI_ACTIVE | SPI_PENDING):
while (yield from bus.read(SPI_CONFIG_ADDR)) & sync:
pass
return (yield from bus.read(SPI_DATA_ADDR))
def _test_gen(bus):
yield from bus.write(SPI_CONFIG_ADDR,
0*SPI_CLK_PHASE | 0*SPI_LSB_FIRST |
1*SPI_HALF_DUPLEX |
SPI_DIV_WRITE(3) | SPI_DIV_READ(5))
yield from _test_xfer(bus, 0b01, 4, 0, 0x90000000)
print(hex((yield from _test_read(bus))))
yield from _test_xfer(bus, 0b10, 0, 4, 0x90000000)
print(hex((yield from _test_read(bus))))
yield from _test_xfer(bus, 0b11, 4, 4, 0x81000000)
print(hex((yield from _test_read(bus))))
yield from _test_xfer(bus, 0b01, 8, 32, 0x87654321)
yield from _test_xfer(bus, 0b01, 0, 32, 0x12345678)
print(hex((yield from _test_read(bus, SPI_PENDING))))
print(hex((yield from _test_read(bus, SPI_ACTIVE))))
return
for cpol, cpha, lsb, clk in product(
(0, 1), (0, 1), (0, 1), (0, 1)):
yield from bus.write(SPI_CONFIG_ADDR,
cpol*SPI_CLK_POLARITY | cpha*SPI_CLK_PHASE |
lsb*SPI_LSB_FIRST | SPI_DIV_WRITE(clk) |
SPI_DIV_READ(clk))
for wlen, rlen, wdata in product((0, 8, 32), (0, 8, 32),
(0, 0xffffffff, 0xdeadbeef)):
rdata = (yield from _test_xfer(bus, 0b1, wlen, rlen, wdata, True))
len = (wlen + rlen) % 32
mask = (1 << len) - 1
if lsb:
shift = (wlen + rlen) % 32
else:
shift = 0
a = (wdata >> wshift) & wmask
b = (rdata >> rshift) & rmask
if a != b:
print("ERROR", end=" ")
print(cpol, cpha, lsb, clk, wlen, rlen,
hex(wdata), hex(rdata), hex(a), hex(b))
class _TestPads:
def __init__(self):
self.cs_n = Signal(2)
self.clk = Signal()
self.mosi = Signal()
self.miso = Signal()
class _TestTristate(Module):
def __init__(self, t):
oe = Signal()
self.comb += [
t.target.eq(t.o),
oe.eq(t.oe),
t.i.eq(t.o),
]
if __name__ == "__main__":
from migen.fhdl.specials import Tristate
pads = _TestPads()
dut = SPIMaster(pads)
dut.comb += pads.miso.eq(pads.mosi)
# from migen.fhdl.verilog import convert
# print(convert(dut))
Tristate.lower = _TestTristate
run_simulation(dut, _test_gen(dut.bus), vcd_name="spi_master.vcd")

View File

@ -27,12 +27,6 @@ These drivers are for the core device and the peripherals closely integrated int
.. automodule:: artiq.coredevice.dma .. automodule:: artiq.coredevice.dma
:members: :members:
:mod:`artiq.coredevice.spi` module
----------------------------------
.. automodule:: artiq.coredevice.spi
:members:
:mod:`artiq.coredevice.spi2` module :mod:`artiq.coredevice.spi2` module
----------------------------------- -----------------------------------

View File

@ -76,7 +76,7 @@ The sequence is exactly equivalent to::
ttl.pulse(2*us) ttl.pulse(2*us)
The :meth:`artiq.coredevice.ttl.TTLOut.pulse` method advances the timeline cursor (using ``delay()``) while other methods such as :meth:`artiq.coredevice.ttl.TTLOut.on`, :meth:`artiq.coredevice.ttl.TTLOut.off`, :meth:`artiq.coredevice.dds._DDSGeneric.set`, or the ``set_*()`` methods of :class:`artiq.coredevice.spi.SPIMaster` do not. The latter are called *zero-duration* methods. The :meth:`artiq.coredevice.ttl.TTLOut.pulse` method advances the timeline cursor (using ``delay()``) while other methods such as :meth:`artiq.coredevice.ttl.TTLOut.on`, :meth:`artiq.coredevice.ttl.TTLOut.off`, :meth:`artiq.coredevice.dds._DDSGeneric.set`. The latter are called *zero-duration* methods.
Underflow exceptions Underflow exceptions
-------------------- --------------------