Core drivers reference

These drivers are for the core device and the peripherals closely integrated into it, which do not use the controller mechanism.

System drivers

artiq.coredevice.core module

exception artiq.coredevice.core.CompileError(diagnostic)[source]
class artiq.coredevice.core.Core(dmgr, host, ref_period, ref_multiplier=8, target='rv32g')[source]

Core device driver.

Parameters:
  • host – hostname or IP address of the core device.

  • ref_period – period of the reference clock for the RTIO subsystem. On platforms that use clock multiplication and SERDES-based PHYs, this is the period after multiplication. For example, with a RTIO core clocked at 125MHz and a SERDES multiplication factor of 8, the reference period is 1ns. The time machine unit is equal to this period.

  • ref_multiplier – ratio between the RTIO fine timestamp frequency and the RTIO coarse timestamp frequency (e.g. SERDES multiplication factor).

break_realtime()[source]

Set the time cursor after the current value of the hardware RTIO counter plus a margin of 125000 machine units.

If the time cursor is already after that position, this function does nothing.

get_rtio_counter_mu()[source]

Retrieve the current value of the hardware RTIO timeline counter.

As the timing of kernel code executed on the CPU is inherently non-deterministic, the return value is by necessity only a lower bound for the actual value of the hardware register at the instant when execution resumes in the caller.

For a more detailed description of these concepts, see ARTIQ Real-Time I/O Concepts.

get_rtio_destination_status(destination)[source]

Returns whether the specified RTIO destination is up. This is particularly useful in startup kernels to delay startup until certain DRTIO destinations are up.

mu_to_seconds(mu)[source]

Convert machine units (RTIO cycles) to seconds.

Parameters:

mu – cycle count to convert.

precompile(function, *args, **kwargs)[source]

Precompile a kernel and return a callable that executes it on the core device at a later time.

Arguments to the kernel are set at compilation time and passed to this function, as additional positional and keyword arguments. The returned callable accepts no arguments.

Precompiled kernels may use RPCs.

Object attributes at the beginning of a precompiled kernel execution have the values they had at precompilation time. If up-to-date values are required, use RPC to read them. Similarly, modified values are not written back, and explicit RPC should be used to modify host objects. Carefully review the source code of drivers calls used in precompiled kernels, as they may rely on host object attributes being transfered between kernel calls. Examples include code used to control DDS phase, and Urukul RF switch control via the CPLD register.

The return value of the callable is the return value of the kernel, if any.

The callable may be called several times.

reset()[source]

Clear RTIO FIFOs, release RTIO PHY reset, and set the time cursor at the current value of the hardware RTIO counter plus a margin of 125000 machine units.

seconds_to_mu(seconds)[source]

Convert seconds to the corresponding number of machine units (RTIO cycles).

Parameters:

seconds – time (in seconds) to convert.

wait_until_mu(cursor_mu)[source]

Block execution until the hardware RTIO counter reaches the given value (see get_rtio_counter_mu()).

If the hardware counter has already passed the given time, the function returns immediately.

artiq.coredevice.exceptions module

exception artiq.coredevice.exceptions.CacheError[source]

Raised when putting a value into a cache row would violate memory safety.

exception artiq.coredevice.exceptions.ClockFailure[source]

Raised when RTIO PLL has lost lock.

class artiq.coredevice.exceptions.CoreException(exceptions, exception_info, traceback, stack_pointers)[source]

Information about an exception raised or passed through the core device.

exception artiq.coredevice.exceptions.DMAError[source]

Raised when performing an invalid DMA operation.

exception artiq.coredevice.exceptions.I2CError[source]

Raised when a I2C transaction fails.

exception artiq.coredevice.exceptions.InternalError[source]

Raised when the runtime encounters an internal error condition.

exception artiq.coredevice.exceptions.RTIODestinationUnreachable[source]

Raised with a RTIO operation could not be completed due to a DRTIO link being down.

exception artiq.coredevice.exceptions.RTIOOverflow[source]

Raised when at least one event could not be registered into the RTIO input FIFO because it was full (CPU not reading fast enough).

This does not interrupt operations further than cancelling the current read attempt and discarding some events. Reading can be reattempted after the exception is caught, and events will be partially retrieved.

exception artiq.coredevice.exceptions.RTIOUnderflow[source]

Raised when the CPU or DMA core fails to submit a RTIO event early enough (with respect to the event’s timestamp).

The offending event is discarded and the RTIO core keeps operating.

exception artiq.coredevice.exceptions.SPIError[source]

Raised when a SPI transaction fails.

exception artiq.coredevice.exceptions.UnwrapNoneError[source]

Raised when unwrapping a none Option.

artiq.coredevice.dma module

Direct Memory Access (DMA) extension.

This feature allows storing pre-defined sequences of output RTIO events into the core device’s SDRAM, and playing them back at higher speeds than the CPU alone could achieve.

class artiq.coredevice.dma.CoreDMA(dmgr, core_device='core')[source]

Core device Direct Memory Access (DMA) driver.

Gives access to the DMA functionality of the core device.

erase(name)[source]

Removes the DMA trace with the given name from storage.

get_handle(name)[source]

Returns a handle to a previously recorded DMA trace. The returned handle is only valid until the next call to record() or erase().

playback(name)[source]

Replays a previously recorded DMA trace. This function blocks until the entire trace is submitted to the RTIO FIFOs.

playback_handle(handle)[source]

Replays a handle obtained with get_handle(). Using this function is much faster than playback() for replaying a set of traces repeatedly, but incurs the overhead of managing the handles onto the programmer.

record(name)[source]

Returns a context manager that will record a DMA trace called name. Any previously recorded trace with the same name is overwritten. The trace will persist across kernel switches.

class artiq.coredevice.dma.DMARecordContextManager[source]

Context manager returned by CoreDMA.record().

Upon entering, starts recording a DMA trace. All RTIO operations are redirected to a newly created DMA buffer after this call, and now is reset to zero.

Upon leaving, stops recording a DMA trace. All recorded RTIO operations are stored in a newly created trace, and now is restored to the value it had before the context manager was entered.

artiq.coredevice.cache module

class artiq.coredevice.cache.CoreCache(dmgr, core_device='core')[source]

Core device cache access

get(key)[source]

Extract a value from the core device cache. After a value is extracted, it cannot be replaced with another value using put() until all kernel functions finish executing; attempting to replace it will result in a artiq.coredevice.exceptions.CacheError.

If the cache does not contain any value associated with key, an empty list is returned.

The value is not copied, so mutating it will change what’s stored in the cache.

Parameters:

key (str) – cache key

Returns:

a list of 32-bit integers

put(key, value)[source]

Put a value into the core device cache. The value will persist until reboot.

To remove a value from the cache, call put() with an empty list.

Parameters:
  • key (str) – cache key

  • value (list) – a list of 32-bit integers

Digital I/O drivers

artiq.coredevice.ttl module

Drivers for TTL signals on RTIO.

TTL channels (including the clock generator) all support output event replacement. For example, pulses of “zero” length (e.g. TTLInOut.on() immediately followed by TTLInOut.off(), without a delay) are suppressed.

class artiq.coredevice.ttl.TTLClockGen(dmgr, channel, acc_width=24, core_device='core')[source]

RTIO TTL clock generator driver.

This should be used with TTL channels that have a clock generator built into the gateware (not compatible with regular TTL channels).

The time cursor is not modified by any function in this class.

Parameters:
  • channel – channel number

  • acc_width – accumulator width in bits

frequency_to_ftw(frequency)[source]

Returns the frequency tuning word corresponding to the given frequency.

ftw_to_frequency(ftw)[source]

Returns the frequency corresponding to the given frequency tuning word.

set(frequency)[source]

Like set_mu(), but using Hz.

set_mu(frequency)[source]

Set the frequency of the clock, in machine units, at the current position of the time cursor.

This also sets the phase, as the time of the first generated rising edge corresponds to the time of the call.

The clock generator contains a 24-bit phase accumulator operating on the RTIO clock. At each RTIO clock tick, the frequency tuning word is added to the phase accumulator. The most significant bit of the phase accumulator is connected to the TTL line. Setting the frequency tuning word has the additional effect of setting the phase accumulator to 0x800000.

Due to the way the clock generator operates, frequency tuning words that are not powers of two cause jitter of one RTIO clock cycle at the output.

stop()[source]

Stop the toggling of the clock and set the output level to 0.

class artiq.coredevice.ttl.TTLInOut(dmgr, channel, gate_latency_mu=None, core_device='core')[source]

RTIO TTL input/output driver.

In output mode, provides functions to set the logic level on the signal.

In input mode, provides functions to analyze the incoming signal, with real-time gating to prevent overflows.

RTIO TTLs supports zero-length transition suppression. For example, if two pulses are emitted back-to-back with no delay between them, they will be merged into a single pulse with a duration equal to the sum of the durations of the original pulses.

This should be used with bidirectional channels.

Note that the channel is in input mode by default. If you need to drive a signal, you must call output(). If the channel is in output mode most of the time in your setup, it is a good idea to call output() in the startup kernel.

There are three input APIs: gating, sampling and watching. When one API is active (e.g. the gate is open, or the input events have not been fully read out), another API must not be used simultaneously.

Parameters:

channel – channel number

count(up_to_timestamp_mu)[source]

Consume RTIO input events until the hardware timestamp counter has reached the specified timestamp and return the number of observed events.

This function does not interact with the timeline cursor.

See the gate_*() family of methods to select the input transitions that generate events, and timestamp_mu() to obtain the timestamp of the first event rather than an accumulated count.

Parameters:

up_to_timestamp_mu – The timestamp up to which execution is blocked, that is, up to which input events are guaranteed to be taken into account. (Events with later timestamps might still be registered if they are already available.)

Returns:

The number of events before the timeout elapsed (0 if none observed).

Examples

To count events on channel ttl_input, up to the current timeline position:

ttl_input.count(now_mu())

If other events are scheduled between the end of the input gate period and when the number of events is counted, using now_mu() as timeout consumes an unnecessary amount of timeline slack. In such cases, it can be beneficial to pass a more precise timestamp, for example:

gate_end_mu = ttl_input.gate_rising(100 * us)

# Schedule a long pulse sequence, represented here by a delay.
delay(10 * ms)

# Get number of rising edges. This will block until the end of
# the gate window, but does not wait for the long pulse sequence
# afterwards, thus (likely) completing with a large amount of
# slack left.
num_rising_edges = ttl_input.count(gate_end_mu)

The gate_*() family of methods return the cursor at the end of the window, allowing this to be expressed in a compact fashion:

ttl_input.count(ttl_input.gate_rising(100 * us))
gate_both(duration)[source]

Register both rising and falling edge events for the specified duration (in seconds).

The time cursor is advanced by the specified duration.

Returns:

The timeline cursor at the end of the gate window, for convenience when used with count()/timestamp_mu().

gate_both_mu(duration)[source]

Register both rising and falling edge events for the specified duration (in machine units).

The time cursor is advanced by the specified duration.

Returns:

The timeline cursor at the end of the gate window, for convenience when used with count()/timestamp_mu().

gate_falling(duration)[source]

Register falling edge events for the specified duration (in seconds).

The time cursor is advanced by the specified duration.

Returns:

The timeline cursor at the end of the gate window, for convenience when used with count()/timestamp_mu().

gate_falling_mu(duration)[source]

Register falling edge events for the specified duration (in machine units).

The time cursor is advanced by the specified duration.

Returns:

The timeline cursor at the end of the gate window, for convenience when used with count()/timestamp_mu().

gate_rising(duration)[source]

Register rising edge events for the specified duration (in seconds).

The time cursor is advanced by the specified duration.

Returns:

The timeline cursor at the end of the gate window, for convenience when used with count()/timestamp_mu().

gate_rising_mu(duration)[source]

Register rising edge events for the specified duration (in machine units).

The time cursor is advanced by the specified duration.

Returns:

The timeline cursor at the end of the gate window, for convenience when used with count()/timestamp_mu().

input()[source]

Set the direction to input at the current position of the time cursor.

There must be a delay of at least one RTIO clock cycle before any other command can be issued.

This method only configures the direction at the FPGA. When using buffered I/O interfaces, such as the Sinara TTL cards, the buffer direction must be configured separately in the hardware.

off()[source]

Set the output to a logic low state at the current position of the time cursor.

The channel must be in output mode.

The time cursor is not modified by this function.

on()[source]

Set the output to a logic high state at the current position of the time cursor.

The channel must be in output mode.

The time cursor is not modified by this function.

output()[source]

Set the direction to output at the current position of the time cursor.

There must be a delay of at least one RTIO clock cycle before any other command can be issued.

This method only configures the direction at the FPGA. When using buffered I/O interfaces, such as the Sinara TTL cards, the buffer direction must be configured separately in the hardware.

pulse(duration)[source]

Pulse the output high for the specified duration (in seconds).

The time cursor is advanced by the specified duration.

pulse_mu(duration)[source]

Pulse the output high for the specified duration (in machine units).

The time cursor is advanced by the specified duration.

sample_get()[source]

Returns the value of a sample previously obtained with sample_input().

Multiple samples may be queued (using multiple calls to sample_input()) into the RTIO FIFOs and subsequently read out using multiple calls to this function.

This function does not interact with the time cursor.

sample_get_nonrt()[source]

Convenience function that obtains the value of a sample at the position of the time cursor, breaks realtime, and returns the sample value.

sample_input()[source]

Instructs the RTIO core to read the value of the TTL input at the position of the time cursor.

The time cursor is not modified by this function.

timestamp_mu(up_to_timestamp_mu)[source]

Return the timestamp of the next RTIO input event, or -1 if the hardware timestamp counter reaches the given value before an event is received.

This function does not interact with the timeline cursor.

See the gate_*() family of methods to select the input transitions that generate events, and count() for usage examples.

Parameters:

up_to_timestamp_mu – The timestamp up to which execution is blocked, that is, up to which input events are guaranteed to be taken into account. (Events with later timestamps might still be registered if they are already available.)

Returns:

The timestamp (in machine units) of the first event received; -1 on timeout.

watch_done()[source]

Stop watching the input at the position of the time cursor.

Returns True if the input has not changed state while it was being watched.

The time cursor is not modified by this function. This function always makes the slack negative.

watch_stay_off()[source]

Like watch_stay_on(), but for low levels.

watch_stay_on()[source]

Checks that the input is at a high level at the position of the time cursor and keep checking until watch_done() is called.

Returns True if the input is high. A call to this function must always be followed by an eventual call to watch_done() (use e.g. a try/finally construct to ensure this).

The time cursor is not modified by this function.

class artiq.coredevice.ttl.TTLOut(dmgr, channel, core_device='core')[source]

RTIO TTL output driver.

This should be used with output-only channels.

Parameters:

channel – channel number

off()[source]

Set the output to a logic low state at the current position of the time cursor.

The time cursor is not modified by this function.

on()[source]

Set the output to a logic high state at the current position of the time cursor.

The time cursor is not modified by this function.

pulse(duration)[source]

Pulse the output high for the specified duration (in seconds).

The time cursor is advanced by the specified duration.

pulse_mu(duration)[source]

Pulse the output high for the specified duration (in machine units).

The time cursor is advanced by the specified duration.

artiq.coredevice.edge_counter module

Driver for RTIO-enabled TTL edge counter.

Like for the TTL input PHYs, sensitivity can be configured over RTIO (gate_rising(), etc.). In contrast to the former, however, the count is accumulated in gateware, and only a single input event is generated at the end of each gate period:

with parallel:
    doppler_cool()
    self.pmt_counter.gate_rising(1 * ms)

with parallel:
    readout()
    self.pmt_counter.gate_rising(100 * us)

print("Doppler cooling counts:", self.pmt_counter.fetch_count())
print("Readout counts:", self.pmt_counter.fetch_count())

For applications where the timestamps of the individual input events are not required, this has two advantages over TTLInOut.count() beyond raw throughput. First, it is easy to count events during multiple separate periods without blocking to read back counts in between, as illustrated in the above example. Secondly, as each count total only takes up a single input event, it is much easier to acquire counts on several channels in parallel without risking input FIFO overflows:

# Using the TTLInOut driver, pmt_1 input events are only processed
# after pmt_0 is done counting. To avoid RTIOOverflows, a round-robin
# scheme would have to be implemented manually.

with parallel:
    self.pmt_0.gate_rising(10 * ms)
    self.pmt_1.gate_rising(10 * ms)

counts_0 = self.pmt_0.count(now_mu()) # blocks
counts_1 = self.pmt_1.count(now_mu())

#

# Using gateware counters, only a single input event each is
# generated, greatly reducing the load on the input FIFOs:

with parallel:
    self.pmt_0_counter.gate_rising(10 * ms)
    self.pmt_1_counter.gate_rising(10 * ms)

counts_0 = self.pmt_0_counter.fetch_count() # blocks
counts_1 = self.pmt_1_counter.fetch_count()

See artiq.gateware.rtio.phy.edge_counter and artiq.gateware.eem.DIO.add_std() for the gateware components.

exception artiq.coredevice.edge_counter.CounterOverflow[source]

Raised when an edge counter value is read which indicates that the counter might have overflowed.

class artiq.coredevice.edge_counter.EdgeCounter(dmgr, channel, gateware_width=31, core_device='core')[source]

RTIO TTL edge counter driver driver.

Like for regular TTL inputs, timeline periods where the counter is sensitive to a chosen set of input transitions can be specified. Unlike the former, however, the specified edges do not create individual input events; rather, the total count can be requested as a single input event from the core (typically at the end of the gate window).

Parameters:
  • channel – The RTIO channel of the gateware phy.

  • gateware_width – The width of the gateware counter register, in bits. This is only used for overflow handling; to change the size, the gateware needs to be rebuilt.

fetch_count() numpy.int32[source]

Wait for and return count total from previously requested input event.

It is valid to trigger multiple gate periods without immediately reading back the count total; the results will be returned in order on subsequent fetch calls.

This function blocks until a result becomes available.

fetch_timestamped_count(timeout_mu=<Mock name='mock.int64()' id='140737286669456'>) -> (numpy.int64, numpy.int32)[source]

Wait for and return the timestamp and count total of a previously requested input event.

It is valid to trigger multiple gate periods without immediately reading back the count total; the results will be returned in order on subsequent fetch calls.

This function blocks until a result becomes available or the given timeout elapses.

Returns:

A tuple of timestamp (-1 if timeout elapsed) and counter value. (The timestamp is that of the requested input event – typically the gate closing time – and not that of any input edges.)

gate_both(duration)[source]

Count both rising and falling edges for the given duration, and request the total at the end.

The counter is reset at the beginning of the gate period. Use set_config() directly for more detailed control.

Parameters:

duration – The duration for which the gate is to stay open.

Returns:

The timestamp at the end of the gate period, in machine units.

gate_both_mu(duration_mu)[source]

See gate_both_mu().

gate_falling(duration)[source]

Count falling edges for the given duration and request the total at the end.

The counter is reset at the beginning of the gate period. Use set_config() directly for more detailed control.

Parameters:

duration – The duration for which the gate is to stay open.

Returns:

The timestamp at the end of the gate period, in machine units.

gate_falling_mu(duration_mu)[source]

See gate_falling().

gate_rising(duration)[source]

Count rising edges for the given duration and request the total at the end.

The counter is reset at the beginning of the gate period. Use set_config() directly for more detailed control.

Parameters:

duration – The duration for which the gate is to stay open.

Returns:

The timestamp at the end of the gate period, in machine units.

gate_rising_mu(duration_mu)[source]

See gate_rising().

set_config(count_rising: bool, count_falling: bool, send_count_event: bool, reset_to_zero: bool)[source]

Emit an RTIO event at the current timeline position to set the gateware configuration.

For most use cases, the gate_* wrappers will be more convenient.

Parameters:
  • count_rising – Whether to count rising signal edges.

  • count_falling – Whether to count falling signal edges.

  • send_count_event – If True, an input event with the current counter value is generated on the next clock cycle (once).

  • reset_to_zero – If True, the counter value is reset to zero on the next clock cycle (once).

artiq.coredevice.shiftreg module

class artiq.coredevice.shiftreg.ShiftReg(dmgr, clk, ser, latch, n=32, dt=9.999999999999999e-06, ser_in=None)[source]

Driver for shift registers/latch combos connected to TTLs

set(data)[source]

Sets the values of the latch outputs. This does not advance the timeline and the waveform is generated before now.

artiq.coredevice.spi2 module

Driver for generic SPI on RTIO.

This ARTIQ coredevice driver corresponds to the “new” MiSoC SPI core (v2).

Output event replacement is not supported and issuing commands at the same time is an error.

class artiq.coredevice.spi2.NRTSPIMaster(dmgr, busno=0, core_device='core')[source]

Core device non-realtime Serial Peripheral Interface (SPI) bus master. Owns one non-realtime SPI bus.

With this driver, SPI transactions and are performed by the CPU without involving RTIO.

Realtime and non-realtime buses are separate and defined at bitstream compilation time.

See SPIMaster for a description of the methods.

set_config_mu(flags=0, length=8, div=6, cs=1)[source]

Set the config register.

Note that the non-realtime SPI cores are usually clocked by the system clock and not the RTIO clock. In many cases, the SPI configuration is already set by the firmware and you do not need to call this method.

class artiq.coredevice.spi2.SPIMaster(dmgr, channel, div=0, length=0, core_device='core')[source]

Core device Serial Peripheral Interface (SPI) bus master.

Owns one SPI bus.

This ARTIQ coredevice driver corresponds to the “new” MiSoC SPI core (v2).

Transfer Sequence:

  • If necessary, set the config register (set_config() and set_config_mu()) to activate and configure the core and to set various transfer parameters like transfer length, clock divider, and chip selects.

  • write() to the data register. Writing starts the transfer.

  • If the transfer included submitting the SPI input data as an RTIO input event (SPI_INPUT set), then read() the data.

  • If SPI_END was not set, repeat the transfer sequence.

A transaction consists of one or more transfers. The chip select pattern is asserted for the entire length of the transaction. All but the last transfer are submitted with SPI_END cleared in the configuration register.

Parameters:
frequency_to_div(f)[source]

Convert a SPI clock frequency to the closest SPI clock divider.

read()[source]

Read SPI data submitted by the SPI core.

For bit alignment and bit ordering see set_config().

This method does not alter the timeline.

Returns:

SPI input data.

set_config(flags, length, freq, cs)[source]

Set the configuration register.

  • If SPI_CS_POLARITY is cleared (cs active low, the default), “cs 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 SPI_HALF_DUPLEX must to be set when reading data or when the slave drives the mosi signal at any point.

  • The first bit output on mosi is always the MSB/LSB (depending on SPI_LSB_FIRST) of the data written, independent of the length of the transfer. The last bit input from miso always ends up in the LSB/MSB (respectively) of the data read, independent of the length of the transfer.

  • cs is asserted at the beginning and deasserted at the end of the transaction.

  • cs handling is agnostic to whether it is one-hot or decoded somewhere downstream. If it is decoded, “cs 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.

  • Changes to the configuration register take effect on the start of the next transfer with the exception of SPI_OFFLINE which takes effect immediately.

  • The SPI core can only be written to when it is idle or waiting for the next transfer data. Writing (set_config(), set_config_mu() or write()) when the core is busy will result in an RTIO busy error being logged.

This method advances the timeline by one coarse RTIO clock cycle.

Configuration flags:

  • SPI_OFFLINE: all pins high-z (reset=1)

  • SPI_END: transfer in progress (reset=1)

  • SPI_INPUT: submit SPI read data as RTIO input event when transfer is complete (reset=0)

  • SPI_CS_POLARITY: active level of cs_n (reset=0)

  • SPI_CLK_POLARITY: idle level of clk (reset=0)

  • SPI_CLK_PHASE: first edge after cs assertion to sample data on (reset=0). In Motorola/Freescale SPI language (SPI_CLK_POLARITY, 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

  • SPI_LSB_FIRST: LSB is the first bit on the wire (reset=0)

  • SPI_HALF_DUPLEX: 3-wire SPI, in/out on mosi (reset=0)

Parameters:
  • flags – A bit map of SPI_* flags.

  • length – Number of bits to write during the next transfer. (reset=1)

  • freq – Desired SPI clock frequency. (reset=f_rtio/2)

  • cs – Bit pattern of chip selects to assert. Or number of the chip select to assert if cs is decoded downstream. (reset=0)

set_config_mu(flags, length, div, cs)[source]

Set the config register (in SPI bus machine units).

See also

set_config()

Parameters:
  • flags – A bit map of SPI_* flags.

  • length – Number of bits to write during the next transfer. (reset=1)

  • div – Counter load value to divide the RTIO clock by to generate the SPI clock. (minimum=2, reset=2) f_rtio_clk/f_spi == div. If div is odd, the setup phase of the SPI clock is one coarse RTIO clock cycle longer than the hold phase.

  • cs – Bit pattern of chip selects to assert. Or number of the chip select to assert if cs is decoded downstream. (reset=0)

update_xfer_duration_mu(div, length)[source]

Calculate and set the transfer duration.

This method updates the SPI transfer duration which is used in write() to advance the timeline.

Use this method (and avoid having to call set_config_mu()) when the divider and transfer length have been configured (using set_config() or set_config_mu()) by previous experiments and are known.

This method is portable and can also be called from e.g. __init__().

Warning

If this method is called while recording a DMA sequence, the playback of the sequence will not update the driver state. When required, update the driver state manually (by calling this method) after playing back a DMA sequence.

Parameters:
write(data)[source]

Write SPI data to shift register register and start transfer.

  • The data register and the shift register are 32 bits wide.

  • Data writes take one ref_period cycle.

  • A transaction consisting of a single transfer (SPI_END) takes xfer_duration_mu =(n + 1)*div cycles RTIO time where n is the number of bits and div is the SPI clock divider.

  • Transfers in a multi-transfer transaction take up to one SPI clock cycle less time depending on multiple parameters. Advanced users may rewind the timeline appropriately to achieve faster multi-transfer transactions.

  • The SPI core will be busy for the duration of the SPI transfer.

  • For bit alignment and bit ordering see set_config().

  • The SPI core can only be written to when it is idle or waiting for the next transfer data. Writing (set_config(), set_config_mu() or write()) when the core is busy will result in an RTIO busy error being logged.

This method advances the timeline by the duration of one single-transfer SPI transaction (xfer_duration_mu).

Parameters:

data – SPI output data to be written.

artiq.coredevice.i2c module

Non-realtime drivers for I2C chips on the core device.

class artiq.coredevice.i2c.I2CSwitch(dmgr, busno=0, address=232, core_device='core')[source]

Driver for the I2C bus switch.

PCA954X (or other) type detection is done by the CPU during I2C init.

I2C transactions not real-time, and are performed by the CPU without involving RTIO.

On the KC705, this chip is used for selecting the I2C buses on the two FMC connectors. HPC=1, LPC=2.

set(channel)[source]

Enable one channel.

Parameters:

channel – channel number (0-7)

unset()[source]

Disable output of the I2C switch.

class artiq.coredevice.i2c.PCF8574A(dmgr, busno=0, address=124, core_device='core')[source]

Driver for the PCF8574 I2C remote 8-bit I/O expander.

I2C transactions not real-time, and are performed by the CPU without involving RTIO.

get()[source]

Retrieve quasi-bidirectional pin input data.

Returns:

Pin data

set(data)[source]

Drive data on the quasi-bidirectional pins.

Parameters:

data – Pin data. High bits are weakly driven high (and thus inputs), low bits are strongly driven low.

class artiq.coredevice.i2c.TCA6424A(dmgr, busno=0, address=68, core_device='core')[source]

Driver for the TCA6424A I2C I/O expander.

I2C transactions not real-time, and are performed by the CPU without involving RTIO.

On the NIST QC2 hardware, this chip is used for switching the directions of TTL buffers.

set(outputs)[source]

Drive all pins of the chip to the levels given by the specified 24-bit word.

On the QC2 hardware, the LSB of the word determines the direction of TTL0 (on a given FMC card) and the MSB that of TTL23.

A bit set to 1 means the TTL is an output.

artiq.coredevice.i2c.i2c_poll(busno, busaddr)[source]

Poll I2C device at address.

Parameters:
  • busno – I2C bus number

  • busaddr – 8 bit I2C device address (LSB=0)

Returns:

True if the poll was ACKed

artiq.coredevice.i2c.i2c_read_byte(busno, busaddr)[source]

Read one byte from a device.

Parameters:
  • busno – I2C bus number

  • busaddr – 8 bit I2C device address (LSB=0)

Returns:

Byte read

artiq.coredevice.i2c.i2c_read_many(busno, busaddr, addr, data)[source]

Transfer multiple bytes from a device.

Parameters:
  • busno – I2c bus number

  • busaddr – 8 bit I2C device address (LSB=0)

  • addr – 8 bit data address

  • data – List of integers to be filled with the data read. One entry ber byte.

artiq.coredevice.i2c.i2c_write_byte(busno, busaddr, data, ack=True)[source]

Write one byte to a device.

Parameters:
  • busno – I2C bus number

  • busaddr – 8 bit I2C device address (LSB=0)

  • data – Data byte to be written

  • nack – Allow NACK

artiq.coredevice.i2c.i2c_write_many(busno, busaddr, addr, data, ack_last=True)[source]

Transfer multiple bytes to a device.

Parameters:
  • busno – I2c bus number

  • busaddr – 8 bit I2C device address (LSB=0)

  • addr – 8 bit data address

  • data – Data bytes to be written

  • ack_last – Expect I2C ACK of the last byte written. If False, the last byte may be NACKed (e.g. EEPROM full page writes).

RF generation drivers

artiq.coredevice.urukul module

class artiq.coredevice.urukul.CPLD(dmgr, spi_device, io_update_device=None, dds_reset_device=None, sync_device=None, sync_sel=0, clk_sel=0, clk_div=0, rf_sw=0, refclk=125000000.0, att=0, sync_div=None, core_device='core')[source]

Urukul CPLD SPI router and configuration interface.

Parameters:
  • spi_device – SPI bus device name

  • io_update_device – IO update RTIO TTLOut channel name

  • dds_reset_device – DDS reset RTIO TTLOut channel name

  • sync_device – AD9910 SYNC_IN RTIO TTLClockGen channel name

  • refclk – Reference clock (SMA, MMCX or on-board 100 MHz oscillator) frequency in Hz

  • clk_sel – Reference clock selection. For hardware revision >= 1.3 valid options are: 0 - internal 100MHz XO; 1 - front-panel SMA; 2 internal MMCX. For hardware revision <= v1.2 valid options are: 0 - either XO or MMCX dependent on component population; 1 SMA. Unsupported clocking options are silently ignored.

  • clk_div – Reference clock divider. Valid options are 0: variant dependent default (divide-by-4 for AD9910 and divide-by-1 for AD9912); 1: divide-by-1; 2: divide-by-2; 3: divide-by-4. On Urukul boards with CPLD gateware before v1.3.1 only the default (0, i.e. variant dependent divider) is valid.

  • sync_sel – SYNC (multi-chip synchronisation) signal source selection. 0 corresponds to SYNC_IN being supplied by the FPGA via the EEM connector. 1 corresponds to SYNC_OUT from DDS0 being distributed to the other chips.

  • rf_sw – Initial CPLD RF switch register setting (default: 0x0). Knowledge of this state is not transferred between experiments.

  • att – Initial attenuator setting shift register (default: 0x00000000). See also get_att_mu() which retrieves the hardware state without side effects. Knowledge of this state is not transferred between experiments.

  • sync_div – SYNC_IN generator divider. The ratio between the coarse RTIO frequency and the SYNC_IN generator frequency (default: 2 if sync_device was specified).

  • core_device – Core device name

If the clocking is incorrect (for example, setting clk_sel to the front panel SMA with no clock connected), then the init() method of the DDS channels can fail with the error message PLL lock timeout.

att_to_mu(att: float) numpy.int32[source]

Convert an attenuation setting in dB to machine units.

Parameters:

att – Attenuation setting in dB.

Returns:

Digital attenuation setting.

cfg_sw(channel: numpy.int32, on: bool)[source]

Configure the RF switches through the configuration register.

These values are logically OR-ed with the LVDS lines on EEM1.

Parameters:
  • channel – Channel index (0-3)

  • on – Switch value

cfg_switches(state: numpy.int32)[source]

Configure all four RF switches through the configuration register.

Parameters:

state – RF switch state as a 4 bit integer.

cfg_write(cfg: numpy.int32)[source]

Write to the configuration register.

See urukul_cfg() for possible flags.

Parameters:

cfg – 24 bit data to be written. Will be stored at cfg_reg.

get_att_mu() numpy.int32[source]

Return the digital step attenuator settings in machine units.

The result is stored and will be used in future calls of set_att_mu() and set_att().

Returns:

32 bit attenuator settings

get_channel_att(channel: numpy.int32) float[source]

Get digital step attenuator value for a channel in SI units.

Parameters:

channel – Attenuator channel (0-3).

Returns:

Attenuation setting in dB. Higher value is more attenuation. Minimum attenuation is 0*dB, maximum attenuation is 31.5*dB.

get_channel_att_mu(channel: numpy.int32) numpy.int32[source]

Get digital step attenuator value for a channel in machine units.

The result is stored and will be used in future calls of set_att_mu() and set_att().

See also

get_att_mu()

Parameters:

channel – Attenuator channel (0-3).

Returns:

8-bit digital attenuation setting: 255 minimum attenuation, 0 maximum attenuation (31.5 dB)

init(blind: bool = False)[source]

Initialize and detect Urukul.

Resets the DDS I/O interface and verifies correct CPLD gateware version. Does not pulse the DDS MASTER_RESET as that confuses the AD9910.

Parameters:

blind – Do not attempt to verify presence and compatibility.

io_rst()[source]

Pulse IO_RST

mu_to_att(att_mu: numpy.int32) float[source]

Convert a digital attenuation setting to dB.

Parameters:

att_mu – Digital attenuation setting.

Returns:

Attenuation setting in dB.

set_all_att_mu(att_reg: numpy.int32)[source]

Set all four digital step attenuators (in machine units).

See also

set_att_mu()

Parameters:

att_reg – Attenuator setting string (32 bit)

set_att(channel: numpy.int32, att: float)[source]

Set digital step attenuator in SI units.

This method will write the attenuator settings of all four channels.

See also

set_att_mu()

Parameters:
  • channel – Attenuator channel (0-3).

  • att – Attenuation setting in dB. Higher value is more attenuation. Minimum attenuation is 0*dB, maximum attenuation is 31.5*dB.

set_att_mu(channel: numpy.int32, att: numpy.int32)[source]

Set digital step attenuator in machine units.

This method will also write the attenuator settings of the three other channels. Use get_att_mu() to retrieve the hardware state set in previous experiments.

Parameters:
  • channel – Attenuator channel (0-3).

  • att – 8-bit digital attenuation setting: 255 minimum attenuation, 0 maximum attenuation (31.5 dB)

set_profile(profile: numpy.int32)[source]

Set the PROFILE pins.

The PROFILE pins are common to all four DDS channels.

Parameters:

profile – PROFILE pins in numeric representation (0-7).

set_sync_div(div: numpy.int32)[source]

Set the SYNC_IN AD9910 pulse generator frequency and align it to the current RTIO timestamp.

The SYNC_IN signal is derived from the coarse RTIO clock and the divider must be a power of two. Configure sync_sel == 0.

Parameters:

div – SYNC_IN frequency divider. Must be a power of two. Minimum division ratio is 2. Maximum division ratio is 16.

sta_read() numpy.int32[source]

Read the status register.

Use any of the following functions to extract values:

Returns:

The status register value.

artiq.coredevice.urukul.urukul_cfg(rf_sw, led, profile, io_update, mask_nu, clk_sel, sync_sel, rst, io_rst, clk_div)[source]

Build Urukul CPLD configuration register

artiq.coredevice.urukul.urukul_sta_ifc_mode(sta)[source]

Return the IFC_MODE status from Urukul status register value.

artiq.coredevice.urukul.urukul_sta_pll_lock(sta)[source]

Return the PLL_LOCK status from Urukul status register value.

artiq.coredevice.urukul.urukul_sta_proto_rev(sta)[source]

Return the PROTO_REV value from Urukul status register value.

artiq.coredevice.urukul.urukul_sta_rf_sw(sta)[source]

Return the RF switch status from Urukul status register value.

artiq.coredevice.urukul.urukul_sta_smp_err(sta)[source]

Return the SMP_ERR status from Urukul status register value.

artiq.coredevice.ad9910 module

class artiq.coredevice.ad9910.AD9910(dmgr, chip_select, cpld_device, sw_device=None, pll_n=40, pll_cp=7, pll_vco=5, sync_delay_seed=- 1, io_update_delay=0, pll_en=1)[source]

AD9910 DDS channel on Urukul.

This class supports a single DDS channel and exposes the DDS, the digital step attenuator, and the RF switch.

Parameters:
  • chip_select – Chip select configuration. On Urukul this is an encoded chip select and not “one-hot”: 3 to address multiple chips (as configured through CFG_MASK_NU), 4-7 for individual channels.

  • cpld_device – Name of the Urukul CPLD this device is on.

  • sw_device – Name of the RF switch device. The RF switch is a TTLOut channel available as the sw attribute of this instance.

  • pll_n – DDS PLL multiplier. The DDS sample clock is f_ref/clk_div*pll_n where f_ref is the reference frequency and clk_div is the reference clock divider (both set in the parent Urukul CPLD instance).

  • pll_en – PLL enable bit, set to 0 to bypass PLL (default: 1). Note that when bypassing the PLL the red front panel LED may remain on.

  • pll_cp – DDS PLL charge pump setting.

  • pll_vco – DDS PLL VCO range selection.

  • sync_delay_seed – SYNC_IN delay tuning starting value. To stabilize the SYNC_IN delay tuning, run tune_sync_delay() once and set this to the delay tap number returned (default: -1 to signal no synchronization and no tuning during init()). Can be a string of the form “eeprom_device:byte_offset” to read the value from a I2C EEPROM; in which case, io_update_delay must be set to the same string value.

  • io_update_delay – IO_UPDATE pulse alignment delay. To align IO_UPDATE to SYNC_CLK, run tune_io_update_delay() and set this to the delay tap number returned. Can be a string of the form “eeprom_device:byte_offset” to read the value from a I2C EEPROM; in which case, sync_delay_seed must be set to the same string value.

amplitude_to_asf(amplitude: float) numpy.int32[source]

Return 14-bit amplitude scale factor corresponding to given fractional amplitude.

amplitude_to_ram(amplitude: list(elt=float), ram: list(elt=numpy.int32))[source]

Convert amplitude values to RAM profile data.

To be used with RAM_DEST_ASF.

Parameters:
  • amplitude – List of amplitude values in units of full scale.

  • ram – List to write RAM data into. Suitable for write_ram().

asf_to_amplitude(asf: numpy.int32) float[source]

Return amplitude as a fraction of full scale corresponding to given amplitude scale factor.

cfg_sw(state: bool)[source]

Set CPLD CFG RF switch state. The RF switch is controlled by the logical or of the CPLD configuration shift register RF switch bit and the SW TTL line (if used).

Parameters:

state – CPLD CFG RF switch bit

clear_smp_err()[source]

Clear the SMP_ERR flag and enables SMP_ERR validity monitoring.

Violations of the SYNC_IN sample and hold margins will result in SMP_ERR being asserted. This then also activates the red LED on the respective Urukul channel.

Also modifies CFR2.

frequency_to_ftw(frequency: float) numpy.int32[source]

Return the 32-bit frequency tuning word corresponding to the given frequency.

frequency_to_ram(frequency: list(elt=float), ram: list(elt=numpy.int32))[source]

Convert frequency values to RAM profile data.

To be used with RAM_DEST_FTW.

Parameters:
  • frequency – List of frequency values in Hz.

  • ram – List to write RAM data into. Suitable for write_ram().

ftw_to_frequency(ftw: numpy.int32) float[source]

Return the frequency corresponding to the given frequency tuning word.

get(profile: numpy.int32 = 7)[source]

Get the frequency, phase, and amplitude.

See also

get_mu()

Parameters:

profile – Profile number to get (0-7, default: 7)

Returns:

A tuple (frequency, phase, amplitude)

get_amplitude() float[source]

Get the value stored to the AD9910’s amplitude scale factor (ASF) register.

Returns:

amplitude in units of full scale.

get_asf() numpy.int32[source]

Get the value stored to the AD9910’s amplitude scale factor (ASF) register.

Returns:

Amplitude scale factor

get_att() float[source]

Get digital step attenuator value in SI units.

Returns:

Attenuation in dB.

get_att_mu() numpy.int32[source]

Get digital step attenuator value in machine units.

Returns:

Attenuation setting, 8 bit digital.

get_frequency() float[source]

Get the value stored to the AD9910’s frequency tuning word (FTW) register.

Returns:

frequency in Hz.

get_ftw() numpy.int32[source]

Get the value stored to the AD9910’s frequency tuning word (FTW) register.

Returns:

Frequency tuning word

get_mu(profile: numpy.int32 = 7)[source]

Get the frequency tuning word, phase offset word, and amplitude scale factor.

See also

get()

Parameters:

profile – Profile number to get (0-7, default: 7)

Returns:

A tuple (ftw, pow, asf)

get_phase() float[source]

Get the value stored to the AD9910’s phase offset word (POW) register.

Returns:

phase offset in turns.

get_pow() numpy.int32[source]

Get the value stored to the AD9910’s phase offset word (POW) register.

Returns:

Phase offset word

init(blind: bool = False)[source]

Initialize and configure the DDS.

Sets up SPI mode, confirms chip presence, powers down unused blocks, configures the PLL, waits for PLL lock. Uses the IO_UPDATE signal multiple times.

Parameters:

blind – Do not read back DDS identity and do not wait for lock.

measure_io_update_alignment(delay_start: numpy.int64, delay_stop: numpy.int64) numpy.int32[source]

Use the digital ramp generator to locate the alignment between IO_UPDATE and SYNC_CLK.

The ramp generator is set up to a linear frequency ramp (dFTW/t_SYNC_CLK=1) and started at a coarse RTIO time stamp plus delay_start and stopped at a coarse RTIO time stamp plus delay_stop.

Parameters:
  • delay_start – Start IO_UPDATE delay in machine units.

  • delay_stop – Stop IO_UPDATE delay in machine units.

Returns:

Odd/even SYNC_CLK cycle indicator.

pow_to_turns(pow_: numpy.int32) float[source]

Return the phase in turns corresponding to a given phase offset word.

power_down(bits: numpy.int32 = 15)[source]

Power down DDS.

Parameters:

bits – Power down bits, see datasheet

read16(addr: numpy.int32) numpy.int32[source]

Read from 16 bit register.

Parameters:

addr – Register address

read32(addr: numpy.int32) numpy.int32[source]

Read from 32 bit register.

Parameters:

addr – Register address

read64(addr: numpy.int32) numpy.int64[source]

Read from 64 bit register.

Parameters:

addr – Register address

Returns:

64 bit integer register value

read_ram(data: list(elt=numpy.int32))[source]

Read data from RAM.

The profile to read from and the step, start, and end address need to be configured before and separately using set_profile_ram() and the parent CPLD set_profile.

Parameters:

data – List to be filled with data read from RAM.

set(frequency: float = 0.0, phase: float = 0.0, amplitude: float = 1.0, phase_mode: numpy.int32 = -1, ref_time_mu: numpy.int64 = <Mock name='mock.int64()' id='140737286669456'>, profile: numpy.int32 = 7, ram_destination: numpy.int32 = -1) float[source]

Set DDS data in SI units.

See also

set_mu()

Parameters:
  • frequency – Frequency in Hz

  • phase – Phase tuning word in turns

  • amplitude – Amplitude in units of full scale

  • phase_mode – Phase mode constant

  • ref_time_mu – Fiducial time stamp in machine units

  • profile – Single tone profile to affect.

  • ram_destination – RAM destination.

Returns:

Resulting phase offset in turns

set_amplitude(amplitude: float)[source]

Set the value stored to the AD9910’s amplitude scale factor (ASF) register.

Parameters:

amplitude – amplitude to be stored, in units of full scale.

set_asf(asf: numpy.int32)[source]

Set the value stored to the AD9910’s amplitude scale factor (ASF) register.

Parameters:

asf – Amplitude scale factor to be stored, range: 0 to 0x3fff.

set_att(att: float)[source]

Set digital step attenuator in SI units.

<