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: baf7b0dcf2f5
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: 0456169558e9
Choose a head ref
  • 3 commits
  • 13 files changed
  • 1 contributor

Commits on Mar 1, 2016

  1. Copy the full SHA
    f30dc4b View commit details
  2. Copy the full SHA
    2cc1dfa View commit details
  3. Copy the full SHA
    0456169 View commit details
7 changes: 0 additions & 7 deletions artiq/coredevice/rt2wb.py

This file was deleted.

5 changes: 5 additions & 0 deletions artiq/coredevice/rtio.py
Original file line number Diff line number Diff line change
@@ -11,3 +11,8 @@ def rtio_output(time_mu: TInt64, channel: TInt32, addr: TInt32, data: TInt32
@syscall
def rtio_input_timestamp(timeout_mu: TInt64, channel: TInt32) -> TInt64:
raise NotImplementedError("syscall not simulated")


@syscall
def rtio_input_data(channel: TInt32) -> TInt32:
raise NotImplementedError("syscall not simulated")
187 changes: 168 additions & 19 deletions artiq/coredevice/spi.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
from artiq.language.core import (kernel, seconds_to_mu, now_mu,
delay_mu, int)
from artiq.language.units import MHz
from artiq.coredevice.rtio import rtio_output as rt2wb_output
from artiq.coredevice.rt2wb import rt2wb_input
from artiq.coredevice.rtio import rtio_output, rtio_input_data


SPI_DATA_ADDR, SPI_XFER_ADDR, SPI_CONFIG_ADDR = range(3)
@@ -22,9 +21,25 @@

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.
:param ref_period: clock period of the SPI core.
:param channel: channel number of the SPI bus to control.
:param channel: RTIO channel number of the SPI bus to control.
"""
def __init__(self, dmgr, ref_period, channel):
self.core = dmgr.get("core")
@@ -44,54 +59,188 @@ def __init__(self, dmgr, ref_period, channel):

@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.
"""
write_div = round(1/(write_freq*self.ref_period))
read_div = round(1/(read_freq*self.ref_period))
self.set_config_mu(flags, write_div, read_div)

@kernel
def set_config_mu(self, flags=0, write_div=6, read_div=6):
rt2wb_output(now_mu(), self.channel, SPI_CONFIG_ADDR, flags |
((write_div - 2) << 16) | ((read_div - 2) << 24))
"""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.
"""
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):
rt2wb_output(now_mu(), self.channel, SPI_XFER_ADDR,
chip_select | (write_length << 16) | (read_length << 24))
"""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):
rt2wb_output(now_mu(), self.channel, SPI_DATA_ADDR, data)
"""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.
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, data)
delay_mu(3*self.ref_period_mu)

@kernel
def read_async(self):
# every read_async() must be matched by an input_async()
rt2wb_output(now_mu(), self.channel, SPI_DATA_ADDR | SPI_RT2WB_READ, 0)
"""Trigger an asynchronous read from the data register.
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):
# matches the preeeding read_async()
return rt2wb_input(self.channel)
"""Retrieves data written asynchronously.
:meth:`input_async` must match a preeeding :meth:`read_async`.
"""
return rtio_input_data(self.channel)

@kernel
def read_sync(self):
rt2wb_output(now_mu(), self.channel, SPI_DATA_ADDR | SPI_RT2WB_READ, 0)
return rt2wb_input(self.channel)
"""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):
rt2wb_output(now_mu(), self.channel, SPI_XFER_ADDR | SPI_RT2WB_READ, 0)
return rt2wb_input(self.channel)
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):
rt2wb_output(now_mu(), self.channel, SPI_CONFIG_ADDR | SPI_RT2WB_READ,
0)
return rt2wb_input(self.channel)
rtio_output(now_mu(), self.channel, SPI_CONFIG_ADDR | SPI_RT2WB_READ,
0)
return rtio_input_data(self.channel)
8 changes: 4 additions & 4 deletions artiq/gateware/targets/kc705.py
Original file line number Diff line number Diff line change
@@ -256,6 +256,10 @@ def __init__(self, cpu_type="or1k", **kwargs):
rtio_channels.append(rtio.Channel.from_phy(phy))
self.config["RTIO_REGULAR_TTL_COUNT"] = len(rtio_channels)

phy = ttl_simple.ClockGen(platform.request("la32_p"))
self.submodules += phy
rtio_channels.append(rtio.Channel.from_phy(phy))

phy = spi.SPIMaster(ams101_dac)
self.submodules += phy
self.config["RTIO_FIRST_SPI_CHANNEL"] = len(rtio_channels)
@@ -268,10 +272,6 @@ def __init__(self, cpu_type="or1k", **kwargs):
rtio_channels.append(rtio.Channel.from_phy(
phy, ofifo_depth=128, ififo_depth=128))

phy = ttl_simple.ClockGen(platform.request("la32_p"))
self.submodules += phy
rtio_channels.append(rtio.Channel.from_phy(phy))

self.config["RTIO_DDS_CHANNEL"] = len(rtio_channels)
self.config["DDS_CHANNEL_COUNT"] = 11
self.config["DDS_AD9914"] = True
2 changes: 1 addition & 1 deletion artiq/runtime/Makefile
Original file line number Diff line number Diff line change
@@ -7,7 +7,7 @@ OBJECTS := isr.o clock.o rtiocrg.o flash_storage.o mailbox.o \
session.o log.o analyzer.o moninj.o net_server.o bridge_ctl.o \
ksupport_data.o kloader.o test_mode.o main.o
OBJECTS_KSUPPORT := ksupport.o artiq_personality.o mailbox.o \
bridge.o rtio.o rt2wb.o dds.o
bridge.o rtio.o dds.o

CFLAGS += -I$(LIBALLOC_DIRECTORY) \
-I$(MISOC_DIRECTORY)/software/include/dyld \
4 changes: 1 addition & 3 deletions artiq/runtime/ksupport.c
Original file line number Diff line number Diff line change
@@ -15,7 +15,6 @@
#include "artiq_personality.h"
#include "dds.h"
#include "rtio.h"
#include "rt2wb.h"

double round(double x);

@@ -111,14 +110,13 @@ static const struct symbol runtime_exports[] = {
{"rtio_log", &rtio_log},
{"rtio_output", &rtio_output},
{"rtio_input_timestamp", &rtio_input_timestamp},
{"rtio_input_data", &rtio_input_data},

{"dds_init", &dds_init},
{"dds_batch_enter", &dds_batch_enter},
{"dds_batch_exit", &dds_batch_exit},
{"dds_set", &dds_set},

{"rt2wb_input", &rt2wb_input},

{"cache_get", &cache_get},
{"cache_put", &cache_put},

26 changes: 0 additions & 26 deletions artiq/runtime/rt2wb.c

This file was deleted.

9 changes: 0 additions & 9 deletions artiq/runtime/rt2wb.h

This file was deleted.

21 changes: 21 additions & 0 deletions artiq/runtime/rtio.c
Original file line number Diff line number Diff line change
@@ -91,6 +91,27 @@ long long int rtio_input_timestamp(long long int timeout, int channel)
}


unsigned int rtio_input_data(int channel)
{
unsigned int data;
int status;

rtio_chan_sel_write(channel);
while((status = rtio_i_status_read())) {
if(status & RTIO_I_STATUS_OVERFLOW) {
rtio_i_overflow_reset_write(1);
artiq_raise_from_c("RTIOOverflow",
"RTIO input overflow on channel {0}",
channel, 0, 0);
}
}

data = rtio_i_data_read();
rtio_i_re_write(1);
return data;
}


void rtio_log_va(long long int timestamp, const char *fmt, va_list args)
{
// This executes on the kernel CPU's stack, which is specifically designed
11 changes: 11 additions & 0 deletions artiq/runtime/rtio.h
Original file line number Diff line number Diff line change
@@ -19,6 +19,17 @@ void rtio_log(long long int timestamp, const char *format, ...);
void rtio_log_va(long long int timestamp, const char *format, va_list args);
void rtio_output(long long int timestamp, int channel, unsigned int address,
unsigned int data);

/*
* Waits at least until timeout and returns the timestamp of the first
* input event on the chanel, -1 if there was no event.
*/
long long int rtio_input_timestamp(long long int timeout, int channel);

/*
* Assumes that there is or will be an event in the channel and returns only
* its data.
*/
unsigned int rtio_input_data(int channel);

#endif /* __RTIO_H */
Loading