From cc70578f1fdc2f222c3c814fb3e03be85af640e4 Mon Sep 17 00:00:00 2001 From: Robert Jordens Date: Fri, 23 Feb 2018 17:31:19 +0100 Subject: [PATCH] remove old spi RTIO Phy --- artiq/coredevice/__init__.py | 2 +- artiq/coredevice/comm_analyzer.py | 5 - artiq/coredevice/spi.py | 286 ------------------- artiq/gateware/rtio/phy/spi.py | 13 - artiq/gateware/spi.py | 385 -------------------------- doc/manual/core_drivers_reference.rst | 6 - doc/manual/rtio.rst | 2 +- 7 files changed, 2 insertions(+), 697 deletions(-) delete mode 100644 artiq/coredevice/spi.py delete mode 100644 artiq/gateware/rtio/phy/spi.py delete mode 100644 artiq/gateware/spi.py diff --git a/artiq/coredevice/__init__.py b/artiq/coredevice/__init__.py index 2d0e7d27a..fbec3d901 100644 --- a/artiq/coredevice/__init__.py +++ b/artiq/coredevice/__init__.py @@ -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.dds import (PHASE_MODE_CONTINUOUS, PHASE_MODE_ABSOLUTE, PHASE_MODE_TRACKING) diff --git a/artiq/coredevice/comm_analyzer.py b/artiq/coredevice/comm_analyzer.py index d5be1fde2..1da425730 100644 --- a/artiq/coredevice/comm_analyzer.py +++ b/artiq/coredevice/comm_analyzer.py @@ -492,11 +492,6 @@ def create_channel_handlers(vcd_manager, devices, ref_period, dds_onehot_sel, dds_sysclk) channel_handlers[dds_bus_channel] = dds_handler 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 desc["class"] == "SPIMaster"): channel = desc["arguments"]["channel"] diff --git a/artiq/coredevice/spi.py b/artiq/coredevice/spi.py deleted file mode 100644 index b10d9dd0d..000000000 --- a/artiq/coredevice/spi.py +++ /dev/null @@ -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) diff --git a/artiq/gateware/rtio/phy/spi.py b/artiq/gateware/rtio/phy/spi.py deleted file mode 100644 index 25d0d7703..000000000 --- a/artiq/gateware/rtio/phy/spi.py +++ /dev/null @@ -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 = [] diff --git a/artiq/gateware/spi.py b/artiq/gateware/spi.py deleted file mode 100644 index 3ed4a2159..000000000 --- a/artiq/gateware/spi.py +++ /dev/null @@ -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") diff --git a/doc/manual/core_drivers_reference.rst b/doc/manual/core_drivers_reference.rst index a77244da2..e55d6a79f 100644 --- a/doc/manual/core_drivers_reference.rst +++ b/doc/manual/core_drivers_reference.rst @@ -27,12 +27,6 @@ These drivers are for the core device and the peripherals closely integrated int .. automodule:: artiq.coredevice.dma :members: -:mod:`artiq.coredevice.spi` module ----------------------------------- - -.. automodule:: artiq.coredevice.spi - :members: - :mod:`artiq.coredevice.spi2` module ----------------------------------- diff --git a/doc/manual/rtio.rst b/doc/manual/rtio.rst index f62c3b57d..33c03c10f 100644 --- a/doc/manual/rtio.rst +++ b/doc/manual/rtio.rst @@ -76,7 +76,7 @@ The sequence is exactly equivalent to:: 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 --------------------