Core real-time drivers

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, analyzer_proxy=None, analyze_at_run_end=False, ref_multiplier=8, target='rv32g', satellite_cpu_targets={}, report_invariants=False)[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 1 ns. The machine time unit (mu) 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).

  • analyzer_proxy – name of the core device analyzer proxy to trigger (optional).

  • analyze_at_run_end – automatically trigger the core device analyzer proxy after the Experiment’s run stage finishes.

  • report_invariants – report variables which are not changed inside kernels and are thus candidates for inclusion in kernel_invariants

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.

close()[source]

Disconnect core device and close sockets.

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 available.

mu_to_seconds(mu)[source]

Convert machine units (fine 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 and subkernels.

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 transferred 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 (fine RTIO cycles).

Parameters:

seconds – time (in seconds) to convert.

trigger_analyzer_proxy()[source]

Causes the core analyzer proxy to retrieve a dump from the device, and distribute it to all connected clients (typically dashboards).

Returns only after the dump has been retrieved from the device.

Raises IOError if no analyzer proxy has been configured, or if the analyzer proxy fails. In the latter case, more details would be available in the proxy log.

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 when 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.SubkernelError[source]

Raised when an operation regarding a subkernel is invalid or cannot be completed.

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 offloads the overhead of managing the handles onto the programmer.

record(name, enable_ddma=False)[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.

In DRTIO context, distributed DMA can be toggled with enable_ddma. Enabling it allows running DMA on satellites, rather than sending all events from the master.

Keeping it disabled it may improve performance in some scenarios, e.g. when there are many small satellite buffers.

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 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.

A delay of at least one RTIO clock cycle is necessary 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.

A delay of at least one RTIO clock cycle is necessary 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 results in negative slack.

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.

As 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 RTIO 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 the sources of 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=-1)[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.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 results in collision errors.

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.

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; 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. (minimum=2, reset=2)

  • 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 are 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 are 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 are 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_selSYNC (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_divSYNC_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().

See also get_channel_att_mu().

Returns:

32-bit attenuator settings

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

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

See also get_channel_att_mu().

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:

divSYNC_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_seedSYNC_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_delayIO_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 AD9910.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. See also CPLD.get_channel_att.

Returns:

Attenuation in dB.

get_att_mu() numpy.int32[source]

Get digital step attenuator value in machine units. See also CPLD.get_channel_att.

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 AD9910.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 = -1, profile: numpy.int32 = 7, ram_destination: numpy.int32 = -1) float[source]

Set DDS data in SI units.

See also AD9910.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.

This method will write the attenuator settings of all four channels. See also CPLD.get_channel_att.

Parameters:

att – Attenuation in dB.

set_att_mu(att: numpy.int32)[source]

Set digital step attenuator in machine units.

This method will write the attenuator settings of all four channels. See also CPLD.get_channel_att.

Parameters:

att – Attenuation setting, 8-bit digital.

set_cfr1(power_down: numpy.int32 = 0, phase_autoclear: numpy.int32 = 0, drg_load_lrr: numpy.int32 = 0, drg_autoclear: numpy.int32 = 0, phase_clear: numpy.int32 = 0, internal_profile: numpy.int32 = 0, ram_destination: numpy.int32 = 0, ram_enable: numpy.int32 = 0, manual_osk_external: numpy.int32 = 0, osk_enable: numpy.int32 = 0, select_auto_osk: numpy.int32 = 0)[source]

Set CFR1. See the AD9910 datasheet for parameter meanings and sizes.

This method does not pulse IO_UPDATE.

Parameters:
  • power_down – Power down bits.

  • phase_autoclear – Autoclear phase accumulator.

  • phase_clear – Asynchronous, static reset of the phase accumulator.

  • drg_load_lrr – Load digital ramp generator LRR.

  • drg_autoclear – Autoclear digital ramp generator.

  • internal_profile – Internal profile control.

  • ram_destination – RAM destination (RAM_DEST_FTW, RAM_DEST_POW, RAM_DEST_ASF, RAM_DEST_POWASF).

  • ram_enable – RAM mode enable.

  • manual_osk_external – Enable OSK pin control in manual OSK mode.

  • osk_enable – Enable OSK mode.

  • select_auto_osk – Select manual or automatic OSK mode.

set_cfr2(asf_profile_enable: numpy.int32 = 1, drg_enable: numpy.int32 = 0, effective_ftw: numpy.int32 = 1, sync_validation_disable: numpy.int32 = 0, matched_latency_enable: numpy.int32 = 0)[source]

Set CFR2. See the AD9910 datasheet for parameter meanings and sizes.

This method does not pulse IO_UPDATE.

Parameters:
  • asf_profile_enable – Enable amplitude scale from single tone profiles.

  • drg_enable – Digital ramp enable.

  • effective_ftw – Read effective FTW.

  • sync_validation_disable – Disable the SYNC_SMP_ERR pin indicating (active high) detection of a synchronization pulse sampling error.

  • matched_latency_enable

    Simultaneous application of amplitude, phase, and frequency changes to the DDS arrive at the output

    • matched_latency_enable = 0: in the order listed

    • matched_latency_enable = 1: simultaneously.

set_frequency(frequency: float)[source]

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

Parameters:

frequency – frequency to be stored, in Hz.

set_ftw(ftw: numpy.int32)[source]

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

Parameters:

ftw – Frequency tuning word to be stored, range: 0 to 0xffffffff.

set_mu(ftw: numpy.int32 = 0, pow_: numpy.int32 = 0, asf: numpy.int32 = 16383, phase_mode: numpy.int32 = -1, ref_time_mu: numpy.int64 = -1, profile: numpy.int32 = 7, ram_destination: numpy.int32 = -1) numpy.int32[source]

Set DDS data in machine units.

This uses machine units (FTW, POW, ASF). The frequency tuning word width is 32, the phase offset word width is 16, and the amplitude scale factor width is 14.

After the SPI transfer, the shared IO update pin is pulsed to activate the data.

Parameters:
  • ftw – Frequency tuning word: 32-bit.

  • pow – Phase tuning word: 16-bit unsigned.

  • asf – Amplitude scale factor: 14-bit unsigned.

  • phase_mode – If specified, overrides the default phase mode set by set_phase_mode() for this call.

  • ref_time_mu – Fiducial time used to compute absolute or tracking phase updates. In machine units as obtained by now_mu().

  • profile – Single tone profile number to set (0-7, default: 7). Ineffective if ram_destination is specified.

  • ram_destination – RAM destination (RAM_DEST_FTW, RAM_DEST_POW, RAM_DEST_ASF, RAM_DEST_POWASF). If specified, write free DDS parameters to the ASF/FTW/POW registers instead of to the single tone profile register (default behaviour, see profile).

Returns:

Resulting phase offset word after application of phase tracking offset. When using PHASE_MODE_CONTINUOUS in subsequent calls, use this value as the “current” phase.

set_phase(turns: float)[source]

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

Parameters:

turns – phase offset to be stored, in turns.

set_phase_mode(phase_mode: numpy.int32)[source]

Set the default phase mode for future calls to set() and set_mu(). Supported phase modes are:

  • PHASE_MODE_CONTINUOUS: the phase accumulator is unchanged when changing frequency or phase. The DDS phase is the sum of the phase accumulator and the phase offset. The only discontinuous changes in the DDS output phase come from changes to the phase offset. This mode is also knows as “relative phase mode”. \(\phi(t) = q(t^\prime) + p + (t - t^\prime) f\)

  • PHASE_MODE_ABSOLUTE: the phase accumulator is reset when changing frequency or phase. Thus, the phase of the DDS at the time of the change is equal to the specified phase offset. \(\phi(t) = p + (t - t^\prime) f\)

  • PHASE_MODE_TRACKING: when changing frequency or phase, the phase accumulator is cleared and the phase offset is offset by the value the phase accumulator would have if the DDS had been running at the specified frequency since a given fiducial time stamp. This is functionally equivalent to PHASE_MODE_ABSOLUTE. The only difference is the fiducial time stamp. This mode is also known as “coherent phase mode”. The default fiducial time stamp is 0. \(\phi(t) = p + (t - T) f\)

Where:

  • \(\phi(t)\): the DDS output phase

  • \(q(t) = \phi(t) - p\): DDS internal phase accumulator

  • \(p\): phase offset

  • \(f\): frequency

  • \(t^\prime\): time stamp of setting \(p\), \(f\)

  • \(T\): fiducial time stamp

  • \(t\): running time

Warning

This setting may become inconsistent when used as part of a DMA recording. When using DMA, it is recommended to specify the phase mode explicitly when calling set() or set_mu().

set_pow(pow_: numpy.int32)[source]

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

Parameters:

pow – Phase offset word to be stored, range: 0 to 0xffff.

set_profile_ram(start: numpy.int32, end: numpy.int32, step: numpy.int32 = 1, profile: numpy.int32 = 0, nodwell_high: numpy.int32 = 0, zero_crossing: numpy.int32 = 0, mode: numpy.int32 = 1)[source]

Set the RAM profile settings. See also AD9910 datasheet.

Parameters:
  • start – Profile start address in RAM (10-bit).

  • end – Profile end address in RAM, inclusive (10-bit).

  • step – Profile time step, counted in DDS sample clock cycles, typically 4 ns (16-bit, default: 1)

  • profile – Profile index (0 to 7) (default: 0).

  • nodwell_high – No-dwell high bit (default: 0, see AD9910 documentation).

  • zero_crossing – Zero crossing bit (default: 0, see AD9910 documentation).

  • mode – Profile RAM mode (RAM_MODE_DIRECTSWITCH, RAM_MODE_RAMPUP, RAM_MODE_BIDIR_RAMP, RAM_MODE_CONT_BIDIR_RAMP, or RAM_MODE_CONT_RAMPUP, default: RAM_MODE_RAMPUP)

set_sync(in_delay: numpy.int32, window: numpy.int32, en_sync_gen: numpy.int32 = 0)[source]

Set the relevant parameters in the multi device synchronization register. See the AD9910 datasheet for details. The SYNC clock generator preset value is set to zero, and the SYNC_OUT generator is disabled by default.

Parameters:
  • in_delaySYNC_IN delay tap (0-31) in steps of ~75ps

  • window – Symmetric SYNC_IN validation window (0-15) in steps of ~75ps for both hold and setup margin.

  • en_sync_gen – Whether to enable the DDS-internal sync generator (SYNC_OUT, cf. sync_sel == 1). Should be left off for the normal use case, where the SYNC clock is supplied by the core device.

tune_io_update_delay() numpy.int32[source]

Find a stable IO_UPDATE delay alignment.

Scan through increasing IO_UPDATE delays until a delay is found that lets IO_UPDATE be registered in the next SYNC_CLK cycle. Return a IO_UPDATE delay that is as far away from that SYNC_CLK edge as possible.

This method assumes that the IO_UPDATE TTLOut device has one machine unit resolution (SERDES).

This method and tune_sync_delay() can be run in any order.

Returns:

Stable IO_UPDATE delay to be passed to the constructor AD9910 via the device database.

tune_sync_delay(search_seed: numpy.int32 = 15)[source]

Find a stable SYNC_IN delay.

This method first locates a valid SYNC_IN delay at zero validation window size (setup/hold margin) by scanning around search_seed. It then looks for similar valid delays at successively larger validation window sizes until none can be found. It then decreases the validation window a bit to provide some slack and stability and returns the optimal values.

This method and tune_io_update_delay() can be run in any order.

Parameters:

search_seed – Start value for valid SYNC_IN delay search. Defaults to 15 (half range).

Returns:

Tuple of optimal delay and window size.

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

Convert phase and amplitude values to RAM profile data.

To be used with RAM_DEST_POWASF.

Parameters:
  • turns – List of phase values in turns.

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

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

turns_to_pow(turns: float) numpy.int32[source]

Return the 16-bit phase offset word corresponding to the given phase in turns.

turns_to_ram(turns: list(elt=float), ram: list(elt=numpy.int32))[source]

Convert phase values to RAM profile data.

To be used with RAM_DEST_POW.

Parameters:
  • turns – List of phase values in turns.

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

write16(addr: numpy.int32, data: numpy.int32)[source]

Write to 16-bit register.

Parameters:
  • addr – Register address

  • data – Data to be written

write32(addr: numpy.int32, data: numpy.int32)[source]

Write to 32-bit register.

Parameters:
  • addr – Register address

  • data – Data to be written

write64(addr: numpy.int32, data_high: numpy.int32, data_low: numpy.int32)[source]

Write to 64-bit register.

Parameters:
  • addr – Register address

  • data_high – High (MSB) 32 data bits

  • data_low – Low (LSB) 32 data bits

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

Write data to RAM.

The profile to write to and the step, start, and end address need to be configured in advance and separately using set_profile_ram() and the parent CPLD set_profile().

Parameters:

data – Data to be written to RAM.

artiq.coredevice.ad9912 module

class artiq.coredevice.ad9912.AD9912(dmgr, chip_select, cpld_device, sw_device=None, pll_n=10, pll_en=1)[source]

AD9912 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”.

  • 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.

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

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

Returns the 48-bit frequency tuning word corresponding to the given frequency.

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

Returns the frequency corresponding to the given frequency tuning word.

get()[source]

Get the frequency and phase.

See also AD9912.get_mu().

Returns:

A tuple (frequency, phase).

get_att() float[source]

Get digital step attenuator value in SI units.

See also get_channel_att().

Returns:

Attenuation in dB.

get_att_mu() numpy.int32[source]

Get digital step attenuator value in machine units.

See also get_channel_att_mu().

Returns:

Attenuation setting, 8-bit digital.

get_mu()[source]

Get the frequency tuning word and phase offset word.

See also AD9912.get().

Returns:

A tuple (FTW, POW).

init()[source]

Initialize and configure the DDS.

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

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

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

Parameters:

pow – Phase offset word.

Returns:

Phase in turns.

read(addr: numpy.int32, length: numpy.int32) numpy.int32[source]

Variable length read from a register. Up to 4 bytes.

Parameters:
  • addr – Register address

  • length – Length in bytes (1-4)

Returns:

Data read

set(frequency: float, phase: float = 0.0)[source]

Set profile 0 data in SI units.

See also AD9912.set_mu().

Parameters:
  • frequency – Frequency in Hz

  • phase – Phase tuning word in turns

set_att(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().

Parameters:

att – Attenuation in dB. Higher values mean more attenuation.

set_att_mu(att: numpy.int32)[source]

Set digital step attenuator in machine units.

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

See also set_att_mu().

Parameters:

att – Attenuation setting, 8-bit digital.

set_mu(ftw: numpy.int64, pow_: numpy.int32 = 0)[source]

Set profile 0 data in machine units.

After the SPI transfer, the shared IO update pin is pulsed to activate the data.

Parameters:
  • ftw – Frequency tuning word: 48-bit unsigned.

  • pow – Phase tuning word: 16-bit unsigned.

turns_to_pow(phase: float) numpy.int32[source]

Returns the 16-bit phase offset word corresponding to the given phase.

write(addr: numpy.int32, data: numpy.int32, length: numpy.int32)[source]

Variable length write to a register. Up to 4 bytes.

Parameters:
  • addr – Register address

  • data – Data to be written: int32

  • length – Length in bytes (1-4)

artiq.coredevice.ad9914 module

Driver for the AD9914 DDS (with parallel bus) on RTIO.

class artiq.coredevice.ad9914.AD9914(dmgr, sysclk, bus_channel, channel, core_device='core')[source]

Driver for one AD9914 DDS channel.

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

Output event replacement is not supported and issuing commands at the same time results in collision errors.

Parameters:
  • sysclk – DDS system frequency. The DDS system clock must be a phase-locked multiple of the RTIO clock.

  • bus_channel – RTIO channel number of the DDS bus.

  • channel – channel number (on the bus) of the DDS device to control.

amplitude_to_asf(amplitude)[source]

Returns 12-bit amplitude scale factor corresponding to given amplitude.

asf_to_amplitude(asf)[source]

Returns the amplitude corresponding to the given amplitude scale factor.

exit_x()[source]

Exits extended-resolution mode.

frequency_to_ftw(frequency)[source]

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

frequency_to_xftw(frequency)[source]

Returns the 63-bit frequency tuning word corresponding to the given frequency (extended resolution mode).

ftw_to_frequency(ftw)[source]

Returns the frequency corresponding to the given frequency tuning word.

init()[source]

Resets and initializes the DDS channel.

This needs to be done for each DDS channel before it can be used, and it is recommended to use the startup kernel for this purpose.

init_sync(sync_delay)[source]

Resets and initializes the DDS channel as well as configures the AD9914 DDS for synchronisation. The synchronisation procedure follows the steps outlined in the AN-1254 application note.

This needs to be done for each DDS channel before it can be used, and it is recommended to use the startup kernel for this.

This function cannot be used in a batch; the correct way of initializing multiple DDS channels is to call this function sequentially with a delay between the calls. 10ms provides a good timing margin.

Parameters:

sync_delay – integer from 0 to 0x3f that sets the value of SYNC_OUT (bits 3-5) and SYNC_IN (bits 0-2) delay ADJ bits.

pow_to_turns(pow)[source]

Returns the phase in turns corresponding to the given phase offset word.

set(frequency, phase=0.0, phase_mode=-1, amplitude=1.0)[source]

Like set_mu(), but uses Hz and turns.

set_mu(ftw, pow=0, phase_mode=-1, asf=4095, ref_time_mu=-1)[source]

Sets the DDS channel to the specified frequency and phase.

This uses machine units (FTW and POW). The frequency tuning word width is 32, the phase offset word width is 16, and the amplitude scale factor width is 12.

The “frequency update” pulse is sent to the DDS with a fixed latency with respect to the current position of the time cursor.

Parameters:
  • ftw – frequency to generate.

  • pow – adds an offset to the phase.

  • phase_mode – if specified, overrides the default phase mode set by set_phase_mode() for this call.

  • ref_time_mu – reference time used to compute phase. Specifying this makes it easier to have a well-defined phase relationship between DDSes on the same bus that are updated at a similar time.

Returns:

Resulting phase offset word after application of phase tracking offset. When using PHASE_MODE_CONTINUOUS in subsequent calls, use this value as the “current” phase.

set_phase_mode(phase_mode)[source]

Sets the phase mode of the DDS channel. Supported phase modes are:

  • PHASE_MODE_CONTINUOUS: the phase accumulator is unchanged when switching frequencies. The DDS phase is the sum of the phase accumulator and the phase offset. The only discrete jumps in the DDS output phase come from changes to the phase offset.

  • PHASE_MODE_ABSOLUTE: the phase accumulator is reset when switching frequencies. Thus, the phase of the DDS at the time of the frequency change is equal to the phase offset.

  • PHASE_MODE_TRACKING: when switching frequencies, the phase accumulator is set to the value it would have if the DDS had been running at the specified frequency since the start of the experiment.

Warning

This setting may become inconsistent when used as part of a DMA recording. When using DMA, it is recommended to specify the phase mode explicitly when calling set() or set_mu().

set_x(frequency, amplitude=1.0)[source]

Like set_x_mu(), but uses Hz and turns.

Note that the precision of float is less than the precision of the extended frequency tuning word.

set_x_mu(xftw, amplitude=4095)[source]

Set the DDS frequency and amplitude with an extended-resolution (63-bit) frequency tuning word.

Phase control is not implemented in this mode; the phase offset can assume any value.

After this function has been called, exit extended-resolution mode before calling functions that use standard-resolution mode.

turns_to_pow(turns)[source]

Returns the 16-bit phase offset word corresponding to the given phase in turns.

xftw_to_frequency(xftw)[source]

Returns the frequency corresponding to the given frequency tuning word (extended resolution mode).

artiq.coredevice.mirny module

RTIO driver for Mirny (4-channel GHz PLLs)

class artiq.coredevice.mirny.Mirny(dmgr, spi_device, refclk=100000000.0, clk_sel='XO', core_device='core')[source]

Mirny PLL-based RF generator.

Parameters:
  • spi_device – SPI bus device

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

  • clk_sel – Reference clock selection. Valid options are: “XO” - onboard crystal oscillator; “SMA” - front-panel SMA connector; “MMCX” - internal MMCX connector. Passing an integer writes it as clk_sel in the CPLD’s register 1. The effect depends on the hardware revision.

  • core_device – Core device name (default: “core”)

att_to_mu(att)[source]

Convert an attenuation setting in dB to machine units.

Parameters:

att – Attenuation setting in dB.

Returns:

Digital attenuation setting.

init(blind=False)[source]

Initialize and detect Mirny.

Select the clock source based the board’s hardware revision. Raise ValueError if the board’s hardware revision is not supported.

Parameters:

blind – Verify presence and protocol compatibility. Raise ValueError on failure.

read_reg(addr)[source]

Read a register.

set_att(channel, att)[source]

Set digital step attenuator in SI units.

This method will write the attenuator settings of the selected channel.

See also Mirny.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, att)[source]

Set digital step attenuator in machine units.

Parameters:

att – Attenuation setting, 8-bit digital.

write_ext(addr, length, data, ext_div=4)[source]

Perform SPI write to a prefixed address.

write_reg(addr, data)[source]

Write a register.

artiq.coredevice.almazny module

class artiq.coredevice.almazny.AlmaznyChannel(dmgr, host_mirny, channel)[source]

Driver for one Almazny channel.

Almazny is a mezzanine for the Quad PLL RF source Mirny that exposes and controls the frequency-doubled outputs. This driver requires Almazny hardware revision v1.2 or later and Mirny CPLD gateware v0.3 or later. Use AlmaznyLegacy for Almazny hardware v1.1 and earlier.

Parameters:
  • host_mirny – Mirny CPLD device name

  • channel – channel index (0-3)

set(att, enable, led=False)[source]

Set attenuation, RF switch, and LED state (SI units).

Parameters:
  • att – attenuator setting in dB (0-31.5)

  • enable – RF switch state (bool)

  • led – LED state (bool)

set_mu(mu)[source]

Set channel state (machine units).

Parameters:

mu – channel state in machine units.

to_mu(att, enable, led)[source]

Convert an attenuation in dB, RF switch state and LED state to machine units.

Parameters:
  • att – attenuator setting in dB (0-31.5)

  • enable – RF switch state (bool)

  • led – LED state (bool)

Returns:

channel setting in machine units

class artiq.coredevice.almazny.AlmaznyLegacy(dmgr, host_mirny)[source]

Almazny (High-frequency mezzanine board for Mirny)

This applies to Almazny hardware v1.1 and earlier. Use AlmaznyChannel for Almazny v1.2 and later.

Parameters:

host_mirnyMirny device Almazny is connected to

att_to_mu(att)[source]

Convert an attenuator setting in dB to machine units.

Parameters:

att – attenuator setting in dB [0-31.5]

Returns:

attenuator setting in machine units

mu_to_att(att_mu)[source]

Convert a digital attenuator setting to dB.

Parameters:

att_mu – attenuator setting in machine units

Returns:

attenuator setting in dB

output_toggle(oe)[source]

Toggles output on all shift registers on or off.

Parameters:

oe – toggle output enable (bool)

set_att(channel, att, rf_switch=True)[source]

Sets attenuators on chosen shift register (channel).

Parameters:
  • channel – index of the register [0-3]

  • att – attenuation setting in dBm [0-31.5]

  • rf_switch – rf switch (bool)

set_att_mu(channel, att_mu, rf_switch=True)[source]

Sets attenuators on chosen shift register (channel).

Parameters:
  • channel – index of the register [0-3]

  • att_mu – attenuation setting in machine units [0-63]

  • rf_switch – rf switch (bool)

artiq.coredevice.adf5356 module

RTIO driver for the Analog Devices ADF[45]35[56] family of GHz PLLs on Mirny-style prefixed SPI buses.

class artiq.coredevice.adf5356.ADF5356(dmgr, cpld_device, sw_device, channel, ref_doubler=False, ref_divider=False, core='core')[source]

Analog Devices AD[45]35[56] family of GHz PLLs.

Parameters:
  • cpld_device – Mirny CPLD device name

  • sw_device – Mirny RF switch device name

  • channel – Mirny RF channel index

  • ref_doubler – enable/disable reference clock doubler

  • ref_divider – enable/disable reference clock divide-by-2

  • core_device – Core device name (default: “core”)

disable_output()[source]

Disable output A of the PLL chip.

enable_output()[source]

Enable output A of the PLL chip. This is the default after init.

f_pfd() numpy.int64[source]

Return the PFD frequency for the cached set of registers.

f_vco() numpy.int64[source]

Return the VCO frequency for the cached set of registers.

info()[source]

Return a summary of high-level parameters as a dict.

init(blind=False)[source]

Initialize and configure the PLL.

Parameters:

blind – Do not attempt to verify presence.

output_divider() numpy.int32[source]

Return the value of the output A divider.

output_power_mu()[source]

Return the power level at output A of the PLL chip in machine units.

pll_frac1() numpy.int32[source]

Return the main fractional value (FRAC1) for the cached set of registers.

pll_frac2() numpy.int32[source]

Return the auxiliary fractional value (FRAC2) for the cached set of registers.

pll_mod2() numpy.int32[source]

Return the auxiliary modulus value (MOD2) for the cached set of registers.

pll_n() numpy.int32[source]

Return the PLL integer value (INT) for the cached set of registers.

read_muxout()[source]

Read the state of the MUXOUT line.

By default, this is configured to be the digital lock detection.

ref_counter() numpy.int32[source]

Return the reference counter value (R) for the cached set of registers.

set_att(att)[source]

Set digital step attenuator in SI units.

This method will write the attenuator settings of the channel.

See also Mirny.set_att.

Parameters:

att – Attenuation in dB.

set_att_mu(att)[source]

Set digital step attenuator in machine units.

Parameters:

att – Attenuation setting, 8-bit digital.

set_frequency(f)[source]

Output given frequency on output A.

Parameters:

f – 53.125 MHz <= f <= 6800 MHz

set_output_power_mu(n)[source]

Set the power level at output A of the PLL chip in machine units.

This driver defaults to n = 3 at init.

Parameters:

n – output power setting, 0, 1, 2, or 3 (see ADF5356 datasheet, fig. 44).

sync()[source]

Write all registers to the device. Attempts to lock the PLL.

artiq.coredevice.adf5356.calculate_pll(f_vco: numpy.int64, f_pfd: numpy.int64)[source]

Calculate fractional-N PLL parameters such that

f_vco = f_pfd * (n + (frac1 + frac2/mod2) / mod1)

where

mod1 = 2**24 and mod2 <= 2**28

Parameters:
  • f_vco – target VCO frequency

  • f_pfd – PFD frequency

Returns:

(n, frac1, (frac2_msb, frac2_lsb), (mod2_msb, mod2_lsb))

artiq.coredevice.phaser module

class artiq.coredevice.phaser.Miqro(channel)[source]

Miqro pulse generator.

A Miqro instance represents one RF output. The DSP components are fully contained in the Phaser gateware. The output is generated by with the following data flow:

Oscillators

  • There are n_osc = 16 oscillators with oscillator IDs 0n_osc-1.

  • Each oscillator outputs one tone at any given time

    • I/Q (quadrature, a.k.a. complex) 2x16-bit signed data at tau = 4 ns sample intervals, 250 MS/s, Nyquist 125 MHz, bandwidth 200 MHz (from f = -100..+100 MHz, taking into account the interpolation anti-aliasing filters in subsequent interpolators),

    • 32-bit frequency (f) resolution (~ 1/16 Hz),

    • 16-bit unsigned amplitude (a) resolution

    • 16-bit phase offset (p) resolution

  • The output phase p' of each oscillator at time t (boot/reset/initialization of the device at t=0) is then p' = f*t + p (mod 1 turn) where f and p are the (currently active) profile frequency and phase offset.

Note

The terms “phase coherent” and “phase tracking” are defined to refer to this choice of oscillator output phase p'. Note that the phase offset p is not relative to (on top of previous phase/profiles/oscillator history). It is “absolute” in the sense that frequency f and phase offset p fully determine oscillator output phase p' at time t. This is unlike typical DDS behavior.

  • Frequency, phase, and amplitude of each oscillator are configurable by selecting one of n_profiles = 32 profiles 0n_profile-1. This selection is fast and can be done for each pulse. The phase coherence defined above is guaranteed for each profile individually.

  • Note: one profile per oscillator (usually profile index 0) should be reserved for the NOP (no operation, identity) profile, usually with zero amplitude.

  • Data for each profile for each oscillator can be configured individually. Storing profile data should be considered “expensive”.

Note

To refer to an operation as “expensive” does not mean it is impossible, merely that it may take a significant amount of time and resources to execute, such that it may be impractical when used often or during fast pulse sequences. They are intended for use in calibration and initialization.

Summation

  • The oscillator outputs are added together (wrapping addition).

  • The user must ensure that the sum of oscillators outputs does not exceed the data range. In general that means that the sum of the amplitudes must not exceed one.

Shaper

  • The summed complex output stream is then multiplied with a the complex-valued output of a triggerable shaper.

  • Triggering the shaper corresponds to passing a pulse from all oscillators to the RF output.

  • Selected profiles become active simultaneously (on the same output sample) when triggering the shaper with the first shaper output sample.

  • The shaper reads (replays) window samples from a memory of size n_window = 1 << 10.

  • The window memory can be segmented by choosing different start indices to support different windows.

  • Each window memory segment starts with a header determining segment length and interpolation parameters.

  • The window samples are interpolated by a factor (rate change) between 1 and r = 1 << 12.

  • The interpolation order is constant, linear, quadratic, or cubic. This corresponds to interpolation modes from rectangular window (1st order CIC) or zero order hold) to Parzen window (4th order CIC or cubic spline).

  • This results in support for single shot pulse lengths (envelope support) between tau and a bit more than r * n_window * tau = (1 << 12 + 10) tau ~ 17 ms.

  • Windows can be configured to be head-less and/or tail-less, meaning, they do not feed zero-amplitude samples into the shaper before and after each window respectively. This is used to implement pulses with arbitrary length or CW output.

Overall properties

  • The DAC may upconvert the signal by applying a frequency offset f1 with phase p1.

  • In the Upconverter Phaser variant, the analog quadrature upconverter applies another frequency of f2 and phase p2.

  • The resulting phase of the signal from one oscillator at the SMA output is (f + f1 + f2)*t + p + s(t - t0) + p1 + p2 (mod 1 turn) where s(t - t0) is the phase of the interpolated shaper output, and t0 is the trigger time (fiducial of the shaper). Unsurprisingly the frequency is the derivative of the phase.

  • Group delays between pulse parameter updates are matched across oscillators, shapers, and channels.

  • The minimum time to change profiles and phase offsets is ~128 ns (estimate, TBC). This is the minimum pulse interval. The sustained pulse rate of the RTIO PHY/Fastlink is one pulse per Fastlink frame (may be increased, TBC).

encode(window, profiles, data)[source]

Encode window and profile selection.

Parameters:
  • window – Window start address (0 to 0x3ff)

  • profiles – List of profile indices for the oscillators. Maximum length 16. Unused oscillators will be set to profile 0.

  • data – List of integers to store the encoded data words into. Unused entries will remain untouched. Must contain at least three lements if all oscillators are used and should be initialized to zeros.

Returns:

Number of words from data used.

pulse(window, profiles)[source]

Emit a pulse

This encodes the window and profiles (see encode()) and emits them (see pulse_mu()).

Parameters:
  • window – Window start address (0 to 0x3ff)

  • profiles – List of profile indices for the oscillators. Maximum length 16. Unused oscillators will select profile 0.

pulse_mu(data)[source]

Emit a pulse (encoded)

The pulse fiducial timing resolution is 4 ns.

Parameters:

data – List of up to 3 words containing an encoded MIQRO pulse as returned by encode().

reset()[source]

Establish no-output profiles and no-output window and execute them.

This establishes the first profile (index 0) on all oscillators as zero amplitude, creates a trivial window (one sample with zero amplitude, minimal interpolation), and executes a corresponding pulse.

set_profile(oscillator, profile, frequency, amplitude, phase=0.0)[source]

Store an oscillator profile.

Parameters:
  • oscillator – Oscillator index (0 to 15)

  • profile – Profile index (0 to 31)

  • frequency – Frequency in Hz (passband -100 to 100 MHz). Interpreted in the Nyquist sense, i.e. aliased.

  • amplitude – Amplitude in units of full scale (0. to 1.)

  • phase – Phase in turns. See Miqro for a definition of phase in this context.

Returns:

The quantized 32-bit frequency tuning word

set_profile_mu(oscillator, profile, ftw, asf, pow_=0)[source]

Store an oscillator profile (machine units).

Parameters:
  • oscillator – Oscillator index (0 to 15)

  • profile – Profile index (0 to 31)

  • ftw – Frequency tuning word (32-bit signed integer on a 250 MHz clock)

  • asf – Amplitude scale factor (16-bit unsigned integer)

  • pow – Phase offset word (16-bit integer)

set_window(start, iq, period=4e-09, order=3, head=1, tail=1)[source]

Store a window segment.

Parameters:
  • start – Window start address (0 to 0x3ff)

  • iq – List of IQ window samples. Each window sample is a pair of two float numbers -1 to 1, one for each I and Q in units of full scale. The maximum window length is 0x3fe. The user must ensure that this window does not overlap with other windows in the memory.

  • period – Desired window sample period in SI units (4*ns to (4 << 12)*ns).

  • order – Interpolation order from 0 (corresponding to constant/zero-order-hold/1st order CIC interpolation) to 3 (corresponding to cubic/Parzen/4th order CIC interpolation)

  • head – Update the interpolator settings and clear its state at the start of the window. This also implies starting the envelope from zero.

  • tail – Feed zeros into the interpolator after the window samples. In the absence of further pulses this will return the output envelope to zero with the chosen interpolation.

Returns:

Actual sample period in SI units

set_window_mu(start, iq, rate=1, shift=0, order=3, head=1, tail=1)[source]

Store a window segment (machine units).

Parameters:
  • start – Window start address (0 to 0x3ff)

  • iq – List of IQ window samples. Each window sample is an integer containing the signed I part in the 16 LSB and the signed Q part in the 16 MSB. The maximum window length is 0x3fe. The user must ensure that this window does not overlap with other windows in the memory.

  • rate – Interpolation rate change (1 to 1 << 12)

  • shift – Interpolator amplitude gain compensation in powers of 2 (0 to 63)

  • order – Interpolation order from 0 (corresponding to constant/rectangular window/zero-order-hold/1st order CIC interpolation) to 3 (corresponding to cubic/Parzen window/4th order CIC interpolation)

  • head – Update the interpolator settings and clear its state at the start of the window. This also implies starting the envelope from zero.

  • tail – Feed zeros into the interpolator after the window samples. In the absence of further pulses this will return the output envelope to zero with the chosen interpolation.

Returns:

Next available window memory address after this segment.

class artiq.coredevice.phaser.Phaser(dmgr, channel_base, miso_delay=1, tune_fifo_offset=True, clk_sel=0, sync_dly=0, dac=None, trf0=None, trf1=None, gw_rev=1, core_device='core')[source]

Phaser 4-channel, 16-bit, 1 GS/s DAC coredevice driver.

Phaser contains a 4-channel, 1 GS/s DAC chip with integrated upconversion, quadrature modulation compensation and interpolation features.

The coredevice RTIO PHY and the Phaser gateware come in different modes that have different features. Phaser mode and coredevice PHY mode are both selected at their respective gateware compile-time and need to match.

Phaser gateware

Coredevice PHY

Features per PhaserChannel

Base <= v0.5

Base

Base (5 PhaserOscillator)

Base >= v0.6

Base

Base + Servo

Miqro >= v0.6

Miqro

Miqro

The coredevice driver (this class and PhaserChannel) exposes the superset of all functionality regardless of the Coredevice RTIO PHY or Phaser gateware modes. This is to evade type unification limitations. Features absent in Coredevice PHY/Phaser gateware will not work and should not be accessed.

Base mode

The coredevice produces 2 IQ (in-phase and quadrature) data streams with 25 MS/s and 14 bits per quadrature. Each data stream supports 5 independent numerically controlled IQ oscillators (NCOs, DDSs with 32-bit frequency, 16-bit phase, 15-bit amplitude, and phase accumulator clear functionality) added together. See PhaserChannel and PhaserOscillator.

Together with a data clock, framing marker, a checksum and metadata for register access the streams are sent in groups of 8 samples over 1.5 Gb/s FastLink via a single EEM connector from coredevice to Phaser.

On Phaser in the FPGA the data streams are buffered and interpolated from 25 MS/s to 500 MS/s 16-bit followed by a 500 MS/s digital upconverter with adjustable frequency and phase. The interpolation passband is 20 MHz wide, passband ripple is less than 1e-3 amplitude, stopband attenuation is better than 75 dB at offsets > 15 MHz and better than 90 dB at offsets > 30 MHz.

The four 16-bit 500 MS/s DAC data streams are sent via a 32-bit parallel LVDS bus operating at 1 Gb/s per pin pair and processed in the DAC (Texas Instruments DAC34H84). On the DAC 2x interpolation, sinx/x compensation, quadrature modulator compensation, fine and coarse mixing as well as group delay capabilities are available. If desired, these features my be configured via the dac dictionary.

The latency/group delay from the RTIO events setting PhaserOscillator or PhaserChannel DUC parameters all the way to the DAC outputs is deterministic. This enables deterministic absolute phase with respect to other RTIO input and output events (see get_next_frame_mu()).

Miqro mode

See Miqro. Here the DAC operates in 4x interpolation.

Analog flow

The four analog DAC outputs are passed through anti-aliasing filters.

In the baseband variant, the even/in-phase DAC channels feed 31.5 dB range attenuators and are available on the front panel. The odd outputs are available at MMCX connectors on board.

In the upconverter variant, each IQ output pair feeds one quadrature upconverter (Texas Instruments TRF372017) with integrated PLL/VCO. This digitally configured analog quadrature upconverter supports offset tuning for carrier and sideband suppression. The output from the upconverter passes through the 31.5 dB range step attenuator and is available at the front panel.

The DAC, the analog quadrature upconverters and the attenuators are configured through a shared SPI bus that is accessed and controlled via FPGA registers.

Servo

Each phaser output channel features a servo to control the RF output amplitude using feedback from an ADC. The servo consists of a first order IIR (infinite impulse response) filter fed by the ADC and a multiplier that scales the I and Q datastreams from the DUC by the IIR output. The IIR state is updated at the 3.788 MHz ADC sampling rate.

Each channel IIR features 4 profiles, each consisting of the [b0, b1, a1] filter coefficients as well as an output offset. The coefficients and offset can be set for each profile individually and the profiles each have their own y0, y1 output registers (the x0, x1 inputs are shared). To avoid transient effects, care should be taken to not update the coefficents in the currently selected profile.

The servo can be en- or disabled for each channel. When disabled, the servo output multiplier is simply bypassed and the datastream reaches the DAC unscaled.

The IIR output can be put on hold for each channel. In hold mode, the filter still ingests samples and updates its input x0 and x1 registers, but does not update the y0, y1 output registers.

After power-up the servo is disabled, in profile 0, with coefficients [0, 0, 0] and hold is enabled. If older gateware without ther servo is loaded onto the Phaser FPGA, the device simply behaves as if the servo is disabled and none of the servo functions have any effect.

Note

Various register settings of the DAC and the quadrature upconverters are available to be modified through the dac, trf0, trf1 dictionaries. These can be set through the device database (device_db.py). The settings are frozen during instantiation of the class and applied during init(). See the dac34H84 and trf372017 source for details.

Note

To establish deterministic latency between RTIO time base and DAC output, the DAC FIFO read pointer value (fifo_offset) must be fixed. If tune_fifo_offset = True (the default) a value with maximum margin is determined automatically by dac_tune_fifo_offset each time init() is called. This value should be used for the fifo_offset key of the dac settings of Phaser in device_db.py and automatic tuning should be disabled by tune_fifo_offset = False`.

Parameters:
  • channel – Base RTIO channel number

  • core_device – Core device name (default: “core”)

  • miso_delay – Fastlink MISO signal delay to account for cable and buffer round trip. Tuning this might be automated later.

  • tune_fifo_offset – Tune the DAC FIFO read pointer offset (default=True)

  • clk_sel – Select the external SMA clock input (1 or 0)

  • sync_dly – SYNC delay with respect to ISTR.

  • dac – DAC34H84 DAC settings as a dictionary.

  • trf0 – Channel 0 TRF372017 quadrature upconverter settings as a dictionary.

  • trf1 – Channel 1 TRF372017 quadrature upconverter settings as a dictionary.

Attributes:

  • channel: List of two instances of PhaserChannel

    To access oscillators, digital upconverters, PLL/VCO analog quadrature upconverters and attenuators.

clear_dac_alarms()[source]

Clear DAC alarm flags.

dac_iotest(pattern) numpy.int32[source]

Performs a DAC IO test according to the datasheet.

Parameters:

pattern – List of four int32s containing the pattern

Returns:

Bit error mask (16-bit)

dac_read(addr, div=34) numpy.int32[source]

Read from a DAC register.

Parameters:
  • addr – Register address to read from

  • div – SPI clock divider. Needs to be at least 250 (1 µs SPI clock) to read the temperature register.

dac_sync()[source]

Trigger DAC synchronisation for both output channels.

The DAC sif_sync is de-asserted, then asserted. The synchronisation is triggered on assertion.

By default, the fine-mixer (NCO) and QMC are synchronised. This includes applying the latest register settings.

The synchronisation sources may be configured through the syncsel_x fields in the dac configuration dictionary (see Phaser).

Note

Synchronising the NCO clears the phase-accumulator.

dac_tune_fifo_offset()[source]

Scan through fifo_offset and configure midpoint setting.

Returns:

Optimal fifo_offset setting with maximum margin to write pointer.

dac_write(addr, data)[source]

Write 16 bits to a DAC register.

Parameters:
  • addr – Register address

  • data – Register data to write

duc_stb()[source]

Strobe the DUC configuration register update.

Transfer staging to active registers. This affects both DUC channels.

get_crc_err()[source]

Get the frame CRC error counter.

Returns:

The number of frames with CRC mismatches sind the reset of the device. Overflows at 256.

get_dac_alarms()[source]

Read the DAC alarm flags.

Returns:

DAC alarm flags (see datasheet for bit meaning)

get_dac_temperature() numpy.int32[source]

Read the DAC die temperature.

Returns:

DAC temperature in degree Celsius

get_next_frame_mu()[source]

Return the timestamp of the frame strictly after now_mu().

Register updates (DUC, DAC, TRF, etc.) scheduled at this timestamp and multiples of self.t_frame later will have deterministic latency to output.

get_sta()[source]

Get the status register value.

Bit flags are:

  • PHASER_STA_DAC_ALARM: DAC alarm pin

  • PHASER_STA_TRF0_LD: Quadrature upconverter 0 lock detect

  • PHASER_STA_TRF1_LD: Quadrature upconverter 1 lock detect

  • PHASER_STA_TERM0: ADC channel 0 termination indicator

  • PHASER_STA_TERM1: ADC channel 1 termination indicator

  • PHASER_STA_SPI_IDLE: SPI machine is idle and data registers can be read/written

Returns:

Status register

init(debug=False)[source]

Initialize the board.

Verifies board and chip presence, resets components, performs communication and configuration tests and establishes initial conditions.

measure_frame_timestamp()[source]

Measure the timestamp of an arbitrary frame and store it in self.frame_tstamp.

To be used as reference for aligning updates to the FastLink frames. See get_next_frame_mu().

read32(addr) numpy.int32[source]

Read 32 bits from a sequence of FPGA registers.

read8(addr) numpy.int32[source]

Read from FPGA register.

Parameters:

addr – Address to read from (7-bit)

Returns:

Data read (8-bit)

set_cfg(clk_sel=0, dac_resetb=1, dac_sleep=0, dac_txena=1, trf0_ps=0, trf1_ps=0, att0_rstn=1, att1_rstn=1)[source]

Set the configuration register.

Each flag is a single bit (0 or 1).

Parameters:
  • clk_sel – Select the external SMA clock input

  • dac_resetb – Active low DAC reset pin

  • dac_sleep – DAC sleep pin

  • dac_txena – Enable DAC transmission pin

  • trf0_ps – Quadrature upconverter 0 power save

  • trf1_ps – Quadrature upconverter 1 power save

  • att0_rstn – Active low attenuator 0 reset

  • att1_rstn – Active low attenuator 1 reset

set_dac_cmix(fs_8_step)[source]

Set the DAC coarse mixer frequency for both channels.

Use of the coarse mixer requires the DAC mixer to be enabled. The mixer can be configured via the dac configuration dictionary (see Phaser).

The selected coarse mixer frequency becomes active without explicit synchronisation.

Parameters:

fs_8_step – coarse mixer frequency shift in 125 MHz steps. This should be an integer between -3 and 4 (inclusive).

set_fan(duty)[source]

Set the fan duty cycle.

Parameters:

duty – Duty cycle (0. to 1.)

set_fan_mu(pwm)[source]

Set the fan duty cycle in machine units.

Parameters:

pwm – Duty cycle in machine units (8-bit)

set_leds(leds)[source]

Set the front panel LEDs.

Parameters:

leds – LED settings (6-bit)

set_sync_dly(dly)[source]

Set SYNC delay.

Parameters:

dly – DAC SYNC delay setting (0 to 7)

spi_cfg(select, div, end, clk_phase=0, clk_polarity=0, half_duplex=0, lsb_first=0, offline=0, length=8)[source]

Set the SPI machine configuration

Parameters:
  • select – Chip selects to assert (DAC, TRF0, TRF1, ATT0, ATT1)

  • div – SPI clock divider relative to 250 MHz fabric clock

  • end – Whether to end the SPI transaction and deassert chip select

  • clk_phase – SPI clock phase (sample on first or second edge)

  • clk_polarity – SPI clock polarity (idle low or high)

  • half_duplex – Read MISO data from MOSI wire

  • lsb_first – Transfer the least significant bit first

  • offline – Put the SPI interfaces offline and don’t drive voltages

  • length – SPI transfer length (1 to 8 bits)

spi_read()[source]

Read from the SPI input data register.

spi_write(data)[source]

Write 8 bits into the SPI data register and start/continue the transaction.

write16(addr, data: numpy.int32)[source]

Write 16 bits to a sequence of FPGA registers.

write32(addr, data: numpy.int32)[source]

Write 32 bits to a sequence of FPGA registers.

write8(addr, data)[source]

Write data to FPGA register.

Parameters:
  • addr – Address to write to (7-bit)

  • data – Data to write (8-bit)

class artiq.coredevice.phaser.PhaserChannel(phaser, index, trf)[source]

Phaser channel IQ pair.

A Phaser channel contains:

  • multiple PhaserOscillator (in the coredevice phy),

  • an interpolation chain and digital upconverter (DUC) on Phaser,

  • a Miqro instance on Phaser,

  • several channel-specific settings in the DAC:

    • quadrature modulation compensation QMC

    • numerically controlled oscillator NCO or coarse mixer CMIX,

  • the analog quadrature upconverter (in the Phaser-Upconverter hardware variant), and

  • a digitally controlled step attenuator.

Attributes:

Note

The amplitude sum of the oscillators must be less than one to avoid clipping or overflow. If any of the DDS or DUC frequencies are non-zero, it is not sufficient to ensure that the sum in each quadrature is within range.

Note

The interpolation filter on Phaser has an intrinsic sinc-like overshoot in its step response. That overshoot is a direct consequence of its near-brick-wall frequency response. For large and wide-band changes in oscillator parameters, the overshoot can lead to clipping or overflow after the interpolation. Either band-limit any changes in the oscillator parameters or back off the amplitude sufficiently. Miqro is not affected by this, but both the oscillators and Miqro can be affected by intrinsic overshoot of the interpolator on the DAC.

cal_trf_vco()[source]

Start calibration of the upconverter (hardware variant) VCO.

TRF outputs should be disabled during VCO calibration.

en_trf_out(rf=1, lo=0)[source]

Enable the rf/lo outputs of the upconverter (hardware variant).

Parameters:
  • rf – 1 to enable RF output, 0 to disable

  • lo – 1 to enable LO output, 0 to disable

get_att_mu() numpy.int32[source]

Read current attenuation.

The current attenuation value is read without side effects.

Returns:

Current attenuation in machine units

get_dac_data() numpy.int32[source]

Get a sample of the current DAC data.

The data is split accross multiple registers and thus the data is only valid if constant.

Returns:

DAC data as 32-bit IQ. I/DACA/DACC in the 16 LSB, Q/DACB/DACD in the 16 MSB

set_att(att)[source]

Set channel attenuation in SI units.

Parameters:

att – Attenuation in dB

set_att_mu(data)[source]

Set channel attenuation.

Parameters:

data – Attenuator data in machine units (8-bit)

set_dac_test(data: numpy.int32)[source]

Set the DAC test data.

Parameters:

data – 32-bit IQ test data, I/DACA/DACC in the 16 LSB, Q/DACB/DACD in the 16 MSB

set_duc_cfg(clr=0, clr_once=0, select=0)[source]

Set the digital upconverter (DUC) and interpolator configuration.

Parameters:
  • clr – Keep the phase accumulator cleared (persistent)

  • clr_once – Clear the phase accumulator for one cycle

  • select – Select the data to send to the DAC (0: DUC data, 1: test data, other values: reserved)

set_duc_frequency(frequency)[source]

Set the DUC frequency in SI units.

Parameters:

frequency – DUC frequency in Hz (passband from -200 MHz to 200 MHz, wrapping around at +- 250 MHz)

set_duc_frequency_mu(ftw)[source]

Set the DUC frequency.

Parameters:

ftw – DUC frequency tuning word (32-bit)

set_duc_phase(phase)[source]

Set the DUC phase in SI units.

Parameters:

phase – DUC phase in turns

set_duc_phase_mu(pow)[source]

Set the DUC phase offset.

Parameters:

pow – DUC phase offset word (16-bit)

set_iir(profile, kp, ki=0.0, g=0.0, x_offset=0.0, y_offset=0.0)[source]

Set servo profile IIR coefficients.

Avoid setting the IIR parameters of the currently active profile.

Gains are given in units of output full per scale per input full scale.

Note

Due to inherent constraints of the fixed point datatypes and IIR filters, the x_offset (setpoint) resolution depends on the selected gains. Low ki gains will lead to a low x_offset resolution.

The transfer function is (up to time discretization and coefficient quantization errors):

\[H(s) = k_p + \frac{k_i}{s + \frac{k_i}{g}}\]
Where:
  • \(s = \sigma + i\omega\) is the complex frequency

  • \(k_p\) is the proportional gain

  • \(k_i\) is the integrator gain

  • \(g\) is the integrator gain limit

Parameters:
  • profile – Profile number (0-3)

  • kp – Proportional gain. This is usually negative (closed loop, positive ADC voltage, positive setpoint). When 0, this implements a pure I controller.

  • ki – Integrator gain (rad/s). Equivalent to the gain at 1 Hz. When 0 (the default) this implements a pure P controller. Same sign as kp.

  • g – Integrator gain limit (1). When 0 (the default) the integrator gain limit is infinite. Same sign as ki.

  • x_offset – IIR input offset. Used as the negative setpoint when stabilizing to a desired input setpoint. Will be converted to an equivalent output offset and added to y_offset.

  • y_offset – IIR output offset.

set_iir_mu(profile, b0, b1, a1, offset)[source]

Load a servo profile consiting of the three filter coefficients and an output offset.

Avoid setting the IIR parameters of the currently active profile.

The recurrence relation is (all data signed and MSB aligned):

\[a_0 y_n = a_1 y_{n - 1} + b_0 x_n + b_1 x_{n - 1} + o\]

Where:

  • \(y_n\) and \(y_{n-1}\) are the current and previous filter outputs, clipped to \([0, 1[\).

  • \(x_n\) and \(x_{n-1}\) are the current and previous filter inputs in \([-1, 1[\).

  • \(o\) is the offset

  • \(a_0\) is the normalization factor \(2^{14}\)

  • \(a_1\) is the feedback gain

  • \(b_0\) and \(b_1\) are the feedforward gains for the two delays

See also PhaserChannel.set_iir().

Parameters:
  • profile – Profile to set (0 to 3)

  • b0 – b0 filter coefficient (16-bit signed)

  • b1 – b1 filter coefficient (16-bit signed)

  • a1 – a1 filter coefficient (16-bit signed)

  • offset – Output offset (16-bit signed)

set_nco_frequency(frequency)[source]

Set the NCO frequency in SI units.

This method stages the new NCO frequency, but does not apply it.

Use of the DAC-NCO requires the DAC mixer and NCO to be enabled. These can be configured via the dac configuration dictionary (see Phaser).

Parameters:

frequency – NCO frequency in Hz (passband from -400 MHz to 400 MHz, wrapping around at +- 500 MHz)

set_nco_frequency_mu(ftw)[source]

Set the NCO frequency.

This method stages the new NCO frequency, but does not apply it.

Use of the DAC-NCO requires the DAC mixer and NCO to be enabled. These can be configured via the dac configuration dictionary (see Phaser).

Parameters:

ftw – NCO frequency tuning word (32-bit)

set_nco_phase(phase)[source]

Set the NCO phase in SI units.

By default, the new NCO phase applies on completion of the SPI transfer. This also causes a staged NCO frequency to be applied. Different triggers for applying NCO settings may be configured through the syncsel_mixerxx fields in the dac configuration dictionary (see Phaser).

Use of the DAC-NCO requires the DAC mixer and NCO to be enabled. These can be configured via the dac configuration dictionary.

Parameters:

phase – NCO phase in turns

set_nco_phase_mu(pow)[source]

Set the NCO phase offset.

By default, the new NCO phase applies on completion of the SPI transfer. This also causes a staged NCO frequency to be applied. Different triggers for applying NCO settings may be configured through the syncsel_mixerxx fields in the dac configuration dictionary (see Phaser).

Use of the DAC-NCO requires the DAC mixer and NCO to be enabled. These can be configured via the dac configuration dictionary.

Parameters:

pow – NCO phase offset word (16-bit)

set_servo(profile=0, enable=0, hold=0)[source]

Set the servo configuration.

Parameters:
  • enable – 1 to enable servo, 0 to disable servo (default). If disabled, the servo is bypassed and hold is enforced since the control loop is broken.

  • hold – 1 to hold the servo IIR filter output constant, 0 for normal operation.

  • profile – Profile index to select for channel. (0 to 3)

trf_read(addr, cnt_mux_sel=0) numpy.int32[source]

Quadrature upconverter register read.

Parameters:
  • addr – Register address to read (0 to 7)

  • cnt_mux_sel – Report VCO counter min or max frequency

Returns:

Register data (32-bit)

trf_write(data, readback=False)[source]

Write 32 bits to quadrature upconverter register.

Parameters:
  • data – Register data (32-bit) containing encoded address

  • readback – Whether to return the read back MISO data

class artiq.coredevice.phaser.PhaserOscillator(channel, index)[source]

Phaser IQ channel oscillator (NCO/DDS).

Note

Latencies between oscillators within a channel and between oscillator parameters (amplitude and phase/frequency) are deterministic (with respect to the 25 MS/s sample clock) but not matched.

set_amplitude_phase(amplitude, phase=0.0, clr=0)[source]

Set Phaser MultiDDS amplitude and phase.

Parameters:
  • amplitude – Amplitude in units of full scale

  • phase – Phase in turns

  • clr – Clear the phase accumulator (persistent)

set_amplitude_phase_mu(asf=32767, pow=0, clr=0)[source]

Set Phaser MultiDDS amplitude, phase offset and accumulator clear.

Parameters:
  • asf – Amplitude (15-bit)

  • pow – Phase offset word (16-bit)

  • clr – Clear the phase accumulator (persistent)

set_frequency(frequency)[source]

Set Phaser MultiDDS frequency.

Parameters:

frequency – Frequency in Hz (passband from -10 MHz to 10 MHz, wrapping around at +- 12.5 MHz)

set_frequency_mu(ftw)[source]

Set Phaser MultiDDS frequency tuning word.

Parameters:

ftw – Frequency tuning word (32-bit)

DAC/ADC drivers

artiq.coredevice.ad53xx module

RTIO driver for the Analog Devices AD53[67][0123] family of multi-channel Digital to Analog Converters.

Output event replacement is not supported and issuing commands at the same time results in a collision error.

class artiq.coredevice.ad53xx.AD53xx(dmgr, spi_device, ldac_device=None, clr_device=None, chip_select=1, div_write=4, div_read=16, vref=5.0, offset_dacs=8192, core='core')[source]

Analog devices AD53[67][0123] family of multi-channel Digital to Analog Converters.

Parameters:
  • spi_device – SPI bus device name

  • ldac_device – LDAC RTIO TTLOut channel name (optional)

  • clr_device – CLR RTIO TTLOut channel name (optional)

  • chip_select – Value to drive on SPI chip select lines during transactions (default: 1)

  • div_write – SPI clock divider for write operations (default: 4, 50MHz max SPI clock with {t_high, t_low} >=8ns)

  • div_read – SPI clock divider for read operations (default: 16, not optimized for speed; datasheet says t22: 25ns min SCLK edge to SDO valid, and suggests the SPI speed for reads should be <=20 MHz)

  • vref – DAC reference voltage (default: 5.)

  • offset_dacs – Initial register value for the two offset DACs (default: 8192). Device dependent and must be set correctly for correct voltage-to-mu conversions. Knowledge of this state is not transferred between experiments.

  • core_device – Core device name (default: “core”)

calibrate(channel, vzs, vfs)[source]

Two-point calibration of a DAC channel.

Programs the offset and gain register to trim out DAC errors. Does not take effect until LDAC is pulsed (see load()).

Calibration consists of measuring the DAC output voltage for a channel with the DAC set to zero-scale (0x0000) and full-scale (0xffff).

Note that only negative offsets and full-scale errors (DAC gain too high) can be calibrated in this fashion.

Parameters:
  • channel – The number of the calibrated channel

  • vzs – Measured voltage with the DAC set to zero-scale (0x0000)

  • vfs – Measured voltage with the DAC set to full-scale (0xffff)

init(blind=False)[source]

Configures the SPI bus, drives LDAC and CLR high, programmes the offset DACs, and enables overtemperature shutdown.

This method must be called before any other method at start-up or if the SPI bus has been accessed by another device.

Parameters:

blind – If True, do not attempt to read back control register or check for overtemperature.

load()[source]

Pulse the LDAC line.

Note that there is a <= 1.5us “BUSY” period (t10) after writing to a DAC input/gain/offset register. All DAC registers may be programmed normally during the busy period, however LDACs during the busy period cause the DAC output to change after the BUSY period has completed, instead of the usual immediate update on LDAC behaviour.

This method advances the timeline by two RTIO clock periods.

read_reg(channel=0, op=1024)[source]

Read a DAC register.

This method advances the timeline by the duration of two SPI transfers plus two RTIO coarse cycles plus 270 ns and consumes all slack.

Parameters:
  • channel – Channel number to read from (default: 0)

  • op – Operation to perform, one of AD53XX_READ_X1A, AD53XX_READ_X1B, AD53XX_READ_OFFSET, AD53XX_READ_GAIN etc. (default: AD53XX_READ_X1A).

Returns:

The 16-bit register value

set_dac(voltages, channels=[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39])[source]

Program multiple DAC channels and pulse LDAC to update the DAC outputs.

This method does not advance the timeline; write events are scheduled in the past. The DACs will synchronously start changing their output levels now.

If no LDAC device was defined, the LDAC pulse is skipped.

Parameters:
  • voltages – list of voltages to program the DAC channels to

  • channels – list of DAC channels to program. If not specified, we program the DAC channels sequentially, starting at 0.

set_dac_mu(values, channels=[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39])[source]

Program multiple DAC channels and pulse LDAC to update the DAC outputs.

This method does not advance the timeline; write events are scheduled in the past. The DACs will synchronously start changing their output levels now.

If no LDAC device was defined, the LDAC pulse is skipped.

See load().

Parameters:
  • values – list of DAC values to program

  • channels – list of DAC channels to program. If not specified, we program the DAC channels sequentially, starting at 0.

voltage_to_mu(voltage)[source]

Returns the 16-bit DAC register value required to produce a given output voltage, assuming offset and gain errors have been trimmed out.

The 16-bit register value may also be used with 14-bit DACs. The additional bits are disregarded by 14-bit DACs.

Parameters:

voltage – Voltage in SI units. Valid voltages are: [-2*vref, + 2*vref - 1 LSB] + voltage offset.

Returns:

The 16-bit DAC register value

write_dac(channel, voltage)[source]

Program the DAC output voltage for a channel.

The DAC output is not updated until LDAC is pulsed (see load()). This method advances the timeline by the duration of one SPI transfer.

write_dac_mu(channel, value)[source]

Program the DAC input register for a channel.

The DAC output is not updated until LDAC is pulsed (see load()). This method advances the timeline by the duration of one SPI transfer.

write_gain_mu(channel, gain=65535)[source]

Program the gain register for a DAC channel.

The DAC output is not updated until LDAC is pulsed (see load()). This method advances the timeline by the duration of one SPI transfer.

Parameters:

gain – 16-bit gain register value (default: 0xffff)

write_offset(channel, voltage)[source]

Program the DAC offset voltage for a channel.

An offset of +V can be used to trim out a DAC offset error of -V. The DAC output is not updated until LDAC is pulsed (see load()). This method advances the timeline by the duration of one SPI transfer.

Parameters:

voltage – the offset voltage

write_offset_dacs_mu(value)[source]

Program the OFS0 and OFS1 offset DAC registers.

Writes to the offset DACs take effect immediately without requiring a LDAC. This method advances the timeline by the duration of two SPI transfers.

Parameters:

value – Value to set both offset DAC registers to

write_offset_mu(channel, offset=32768)[source]

Program the offset register for a DAC channel.

The DAC output is not updated until LDAC is pulsed (see load()). This method advances the timeline by the duration of one SPI transfer.

Parameters:

offset – 16-bit offset register value (default: 0x8000)

artiq.coredevice.ad53xx.ad53xx_cmd_read_ch(channel, op)[source]

Returns the word that must be written to the DAC to read a given DAC channel register.

Parameters:
  • channel – DAC channel to read (8 bits)

  • op – The channel register to read, one of AD53XX_READ_X1A, AD53XX_READ_X1B, AD53XX_READ_OFFSET, AD53XX_READ_GAIN etc.

Returns:

The 24-bit word to be written to the DAC to initiate read

artiq.coredevice.ad53xx.ad53xx_cmd_write_ch(channel, value, op)[source]

Returns the word that must be written to the DAC to set a DAC channel register to a given value.

Parameters:
  • channel – DAC channel to write to (8 bits)

  • value – 16-bit value to write to the register

  • op – The channel register to write to, one of AD53XX_CMD_DATA, AD53XX_CMD_OFFSET or AD53XX_CMD_GAIN.

Returns:

The 24-bit word to be written to the DAC

artiq.coredevice.ad53xx.voltage_to_mu(voltage, offset_dacs=8192, vref=5.0)[source]

Returns the 16-bit DAC register value required to produce a given output voltage, assuming offset and gain errors have been trimmed out.

The 16-bit register value may also be used with 14-bit DACs. The additional bits are disregarded by 14-bit DACs.

Also used to return offset register value required to produce a given voltage when the DAC register is set to mid-scale. An offset of V can be used to trim out a DAC offset error of -V.

Parameters:
  • voltage – Voltage in SI units. Valid voltages are: [-2*vref, + 2*vref - 1 LSB] + voltage offset.

  • offset_dacs – Register value for the two offset DACs (default: 0x2000)

  • vref – DAC reference voltage (default: 5.)

Returns:

The 16-bit DAC register value

artiq.coredevice.zotino module

RTIO driver for the Zotino 32-channel, 16-bit 1MSPS DAC.

Output event replacement is not supported and issuing commands at the same time results in a collision error.

class artiq.coredevice.zotino.Zotino(dmgr, spi_device, ldac_device=None, clr_device=None, div_write=4, div_read=16, vref=5.0, core='core')[source]

Zotino 32-channel, 16-bit 1MSPS DAC.

Controls the AD5372 DAC and the 8 user LEDs via a shared SPI interface.

Parameters:
  • spi_device – SPI bus device name

  • ldac_device – LDAC RTIO TTLOut channel name.

  • clr_device – CLR RTIO TTLOut channel name.

  • div_write – SPI clock divider for write operations (default: 4, 50MHz max SPI clock)

  • div_read – SPI clock divider for read operations (default: 16, not optimized for speed; datasheet says t22: 25ns min SCLK edge to SDO valid, and suggests the SPI speed for reads should be <=20 MHz)

  • vref – DAC reference voltage (default: 5.)

  • core_device – Core device name (default: “core”)

set_leds(leds)[source]

Sets the states of the 8 user LEDs.

Parameters:

leds – 8-bit word with LED state

artiq.coredevice.sampler module

class artiq.coredevice.sampler.Sampler(dmgr, spi_adc_device, spi_pgia_device, cnv_device, div=8, gains=0, hw_rev='v2.2', core_device='core')[source]

Sampler ADC.

Controls the LTC2320-16 8-channel 16-bit ADC with SPI interface and the switchable gain instrumentation amplifiers.

Parameters:
  • spi_adc_device – ADC SPI bus device name

  • spi_pgia_device – PGIA SPI bus device name

  • cnv_device – CNV RTIO TTLOut channel name

  • div – SPI clock divider (default: 8)

  • gains – Initial value for PGIA gains shift register (default: 0x0000). Knowledge of this state is not transferred between experiments.

  • hw_rev – Sampler’s hardware revision string (default ‘v2.2’)

  • core_device – Core device name

get_gains_mu()[source]

Read the PGIA gain settings of all channels.

Returns:

The PGIA gain settings in machine units.

init()[source]

Initialize the device.

Sets up SPI channels.

sample(data)[source]

Acquire a set of samples.

See also Sampler.sample_mu().

Parameters:

data – List of floating point data samples to fill.

sample_mu(data)[source]

Acquire a set of samples.

Perform a conversion and transfer the samples.

This assumes that the input FIFO of the ADC SPI RTIO channel is deep enough to buffer the samples (half the length of data deep). If it is not, there will be RTIO input overflows.

Parameters:

data – List of data samples to fill. Must have even length. Samples are always read from the last channel (channel 7) down. The data list will always be filled with the last item holding to the sample from channel 7.

set_gain_mu(channel, gain)[source]

Set instrumentation amplifier gain of a channel.

The four gain settings (0, 1, 2, 3) corresponds to gains of (1, 10, 100, 1000) respectively.

Parameters:
  • channel – Channel index

  • gain – Gain setting

artiq.coredevice.sampler.adc_mu_to_volt(data, gain=0, corrected_fs=True)[source]

Convert ADC data in machine units to volts.

Parameters:
  • data – 16-bit signed ADC word

  • gain – PGIA gain setting (0: 1, …, 3: 1000)

  • corrected_fs – use corrected ADC FS reference. Should be True for Sampler revisions after v2.1. False for v2.1 and earlier.

Returns:

Voltage in volts

artiq.coredevice.novogorny module

class artiq.coredevice.novogorny.Novogorny(dmgr, spi_device, cnv_device, div=8, gains=0, core_device='core')[source]

Novogorny ADC.

Controls the LTC2335-16 8 channel ADC with SPI interface and the switchable gain instrumentation amplifiers using a shift register.

Parameters:
  • spi_device – SPI bus device name

  • cnv_device – CNV RTIO TTLOut channel name

  • div – SPI clock divider (default: 8)

  • gains – Initial value for PGIA gains shift register (default: 0x0000). Knowledge of this state is not transferred between experiments.

  • core_device – Core device name

burst_mu(data, dt_mu, ctrl=0)[source]

Acquire a burst of samples.

If the burst is too long and the sample rate too high, there will be :exc:RTIOOverflow exceptions.

High sample rates lead to gain errors since the impedance between the instrumentation amplifier and the ADC is high.

Parameters:
  • data – List of data values to write result packets into. In machine units.

  • dt – Sample interval in machine units.

  • ctrl – ADC control word to write during each result packet transfer.

configure(data)[source]

Set up the ADC sequencer.

Parameters:

data – List of 8-bit control words to write into the sequencer table.

sample(next_ctrl=0)[source]

Acquire a sample. See also Novogorny.sample_mu().

Parameters:

next_ctrl – ADC control word for the next sample

Returns:

The ADC result packet (volts)

sample_mu(next_ctrl=0)[source]

Acquire a sample:

Perform a conversion and transfer the sample.

Parameters:

next_ctrl – ADC control word for the next sample

Returns:

The ADC result packet (machine units)

set_gain_mu(channel, gain)[source]

Set instrumentation amplifier gain of a channel.

The four gain settings (0, 1, 2, 3) corresponds to gains of (1, 10, 100, 1000) respectively.

Parameters:
  • channel – Channel index

  • gain – Gain setting

artiq.coredevice.novogorny.adc_channel(data)[source]

Return the channel index from a result packet.

artiq.coredevice.novogorny.adc_ctrl(channel=1, softspan=7, valid=1)[source]

Build a LTC2335-16 control word.

artiq.coredevice.novogorny.adc_data(data)[source]

Return the ADC value from a result packet.

artiq.coredevice.novogorny.adc_softspan(data)[source]

Return the softspan configuration index from a result packet.

artiq.coredevice.novogorny.adc_value(data, v_ref=5.0)[source]

Convert a ADC result packet to SI units (volts).

artiq.coredevice.fastino module

RTIO driver for the Fastino 32-channel, 16-bit, 2.5 MS/s per channel streaming DAC.

class artiq.coredevice.fastino.Fastino(dmgr, channel, core_device='core', log2_width=0)[source]

Fastino 32-channel, 16-bit, 2.5 MS/s per channel streaming DAC

The RTIO PHY supports staging DAC data before transmitting them by writing to the DAC RTIO addresses, if a channel is not “held” by setting its bit using set_hold(), the next frame will contain the update. For the DACs held, the update is triggered explicitly by setting the corresponding bit using update(). Update is self-clearing. This enables atomic DAC updates synchronized to a frame edge.

The log2_width=0 RTIO layout uses one DAC channel per RTIO address and a dense RTIO address space. The RTIO words are narrow (32-bit) and few-channel updates are efficient. There is the least amount of DAC state tracking in kernels, at the cost of more DMA and RTIO data. The setting here and in the RTIO PHY (gateware) must match.

Other log2_width (up to log2_width=5) settings pack multiple (in powers of two) DAC channels into one group and into one RTIO write. The RTIO data width increases accordingly. The log2_width LSBs of the RTIO address for a DAC channel write must be zero and the address space is sparse. For log2_width=5 the RTIO data is 512-bit wide.

If log2_width is zero, the set_dac()/set_dac_mu() interface must be used. If non-zero, the set_group()/set_group_mu() interface must be used.

Parameters:
  • channel – RTIO channel number

  • core_device – Core device name (default: “core”)

  • log2_width – Width of DAC channel group (logarithm base 2). Value must match the corresponding value in the RTIO PHY (gateware).

apply_cic(channel_mask)[source]

Apply the staged interpolator configuration on the specified channels.

Each Fastino channel starting with gateware v0.2 includes a fourth order (cubic) CIC interpolator with variable rate change and variable output gain compensation (see stage_cic()).

Fastino gateware before v0.2 does not include the interpolators and the methods affecting the CICs should not be used.

Channels using non-unity interpolation rate should have continous DAC updates enabled (see set_continuous()) unless their output is supposed to be constant.

This method resets and settles the affected interpolators. There will be no output updates for the next order = 3 input samples. Affected channels will only accept one input sample per input sample period. This method synchronizes the input sample period to the current frame on the affected channels.

If application of new interpolator settings results in a change of the overall gain, there will be a corresponding output step.

init()[source]

Initialize the device.

  • disables RESET, DAC_CLR, enables AFE_PWR

  • clears error counters, enables error counting

  • turns LEDs off

  • clears hold and continuous on all channels

  • clear and resets interpolators to unit rate change on all channels

It does not change set channel voltages and does not reset the PLLs or clock domains.

Warning

On Fastino gateware before v0.2 this may lead to 0 voltage being emitted transiently.

read(addr)[source]

Read from Fastino register.

TODO: untested

Parameters:

addr – Address to read from.

Returns:

The data read.

set_cfg(reset=0, afe_power_down=0, dac_clr=0, clr_err=0)[source]

Set configuration bits.

Parameters:
  • reset – Reset SPI PLL and SPI clock domain.

  • afe_power_down – Disable AFE power.

  • dac_clr – Assert all 32 DAC_CLR signals setting all DACs to mid-scale (0 V).

  • clr_err – Clear error counters and PLL reset indicator. This clears the sticky red error LED. Must be cleared to enable error counting.

set_continuous(channel_mask)[source]

Enable continuous DAC updates on channels regardless of new data being submitted.

set_dac(dac, voltage)[source]

Set DAC data to given voltage.

Parameters:
  • dac – DAC channel (0-31).

  • voltage – Desired output voltage.

set_dac_mu(dac, data)[source]

Write DAC data in machine units.

Parameters:
  • dac – DAC channel to write to (0-31).

  • data – DAC word to write, 16-bit unsigned integer, in machine units.

set_group(dac, voltage)[source]

Set DAC group data to given voltage.

Parameters:
  • dac – DAC channel (0-31).

  • voltage – Desired output voltage.

set_group_mu(dac: numpy.int32, data: list(elt=numpy.int32))[source]

Write a group of DAC channels in machine units.

Parameters:
  • dac – First channel in DAC channel group (0-31). The log2_width LSBs must be zero.

  • data – List of DAC data pairs (2x16-bit unsigned) to write, in machine units. Data exceeding group size is ignored. If the list length is less than group size, the remaining DAC channels within the group are cleared to 0 (machine units).

set_hold(hold)[source]

Set channels to manual update.

Parameters:

hold – Bit mask of channels to hold (32-bit).

set_leds(leds)[source]

Set the green user-defined LEDs.

Parameters:

leds – LED status, 8-bit integer each bit corresponding to one green LED.

stage_cic(rate) numpy.int32[source]

Compute and stage interpolator configuration.

This method approximates the desired interpolation rate using a 10-bit floating point representation (6-bit mantissa, 4-bit exponent) and then determines an optimal interpolation gain compensation exponent to avoid clipping. Gains for rates that are powers of two are accurately compensated. Other rates lead to overall less than unity gain (but more than 0.5 gain).

The overall gain including gain compensation is actual_rate ** order / 2 ** ceil(log2(actual_rate ** order)) where order = 3.

Returns the actual interpolation rate.

stage_cic_mu(rate_mantissa, rate_exponent, gain_exponent)[source]

Stage machine unit CIC interpolator configuration.

update(update)[source]

Schedule channels for update.

Parameters:

update – Bit mask of channels to update (32-bit).

voltage_group_to_mu(voltage, data)[source]

Convert SI volts to packed DAC channel group machine units.

Parameters:
  • voltage – List of SI volt voltages.

  • data – List of DAC channel data pairs to write to. Half the length of voltage.

voltage_to_mu(voltage)[source]

Convert SI volts to DAC machine units.

Parameters:

voltage – Voltage in SI volts.

Returns:

DAC data word in machine units, 16-bit integer.

write(addr, data)[source]

Write data to a Fastino register.

Parameters:
  • addr – Address to write to.

  • data – Data to write.

artiq.coredevice.shuttler module

class artiq.coredevice.shuttler.ADC(dmgr, spi_device, core_device='core')[source]

Shuttler AFE ADC (AD4115) driver.

Parameters:
  • spi_device – SPI bus device name.

  • core_device – Core device name.

calibrate(volts, trigger, config, samples=[-5.0, 0.0, 5.0])[source]

Calibrate the Shuttler waveform generator using the ADC on the AFE.

Finds the average slope rate and average offset by samples, and compensates by writing the pre-DAC gain and offset registers in the configuration registers.

Note

If the pre-calibration slope rate is less than 1, the calibration procedure will introduce a pre-DAC gain compensation. However, this may saturate the pre-DAC voltage code (see Config notes). Shuttler cannot cover the entire +/- 10 V range in this case. See also Config.set_gain() and Config.set_offset().

Parameters:
  • volts – A list of all 16 cubic DC-bias splines. (See DCBias)

  • trigger – The Shuttler spline coefficient update trigger.

  • config – The Shuttler Core configuration registers.

  • samples – A list of sample voltages for calibration. There must be at least 2 samples to perform slope rate calculation.

power_down()[source]

Place the ADC in power-down mode.

The ADC must be reset before returning to other modes.

Note

The AD4115 datasheet suggests placing the ADC in standby mode before power-down. This is to prevent accidental entry into the power-down mode. See also standby() and power_up().

power_up()[source]

Exit the ADC power-down mode.

The ADC should be in power-down mode before calling this method.

See also power_down().

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

Read from 16-bit register.

Parameters:

addr – Register address.

Returns:

Read-back register content.

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

Read from 24-bit register.

Parameters:

addr – Register address.

Returns:

Read-back register content.

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

Read from 8-bit register.

Parameters:

addr – Register address.

Returns:

Read-back register content.

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

Sample a Shuttler channel on the AFE.

Performs a single conversion using profile 0 and setup 0 on the selected channel. The sample is then recovered and converted to volts.

Parameters:

channel – Shuttler channel to be sampled.

Returns:

Voltage sample in volts.

read_id() numpy.int32[source]

Read the product ID of the ADC.

The expected return value is 0x38DX, the 4 LSbs are don’t cares.

Returns:

The read-back product ID.

reset()[source]

AD4115 reset procedure.

Performs a write operation of 96 serial clock cycles with DIN held at high. This resets the entire device, including the register contents.

Note

The datasheet only requires 64 cycles, but reasserting CS_n right after the transfer appears to interrupt the start-up sequence.

single_conversion()[source]

Place the ADC in single conversion mode.

The ADC returns to standby mode after the conversion is complete.

standby()[source]

Place the ADC in standby mode and disable power down the clock.

The ADC can be returned to single conversion mode by calling single_conversion().

write16(addr: numpy.int32, data: numpy.int32)[source]

Write to 16-bit register.

Parameters:
  • addr – Register address.

  • data – Data to be written.

write24(addr: numpy.int32, data: numpy.int32)[source]

Write to 24-bit register.

Parameters:
  • addr – Register address.

  • data – Data to be written.

write8(addr: numpy.int32, data: numpy.int32)[source]

Write to 8-bit register.

Parameters:
  • addr – Register address.

  • data – Data to be written.

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

Shuttler configuration registers interface.

The configuration registers control waveform phase auto-clear, pre-DAC gain and offset values for calibration with ADC on the Shuttler AFE card.

To find the calibrated DAC code, the Shuttler Core first multiplies the output data with pre-DAC gain, then adds the offset.

Note

The DAC code is capped at 0x7fff and 0x8000.

Parameters:
  • channel – RTIO channel number of this interface.

  • core_device – Core device name.

get_gain(channel)[source]

Return the pre-DAC gain value of a Shuttler Core channel.

Parameters:

channel – The Shuttler Core channel.

Returns:

Pre-DAC gain value. See set_gain().

get_offset(channel)[source]

Return the pre-DAC offset value of a Shuttler Core channel.

Parameters:

channel – The Shuttler Core channel.

Returns:

Pre-DAC offset value. See set_offset().

set_clr(clr)[source]

Set/Unset waveform phase clear bits.

Each bit corresponds to a Shuttler waveform generator core. Setting a clear bit forces the Shuttler Core to clear the phase accumulator on waveform trigger (See Trigger for the trigger method). Otherwise, the phase accumulator increments from its original value.

Parameters:

clr – Waveform phase clear bits. The MSB corresponds to Channel 15, LSB corresponds to Channel 0.

set_gain(channel, gain)[source]

Set the 16-bits pre-DAC gain register of a Shuttler Core channel.

The gain parameter represents the decimal portion of the gain factor. The MSB represents 0.5 and the sign bit. Hence, the valid total gain value (1 +/- 0.gain) ranges from 0.5 to 1.5 - LSB.

Parameters:
  • channel – Shuttler Core channel to be configured.

  • gain – Shuttler Core channel gain.

set_offset(channel, offset)[source]

Set the 16-bits pre-DAC offset register of a Shuttler Core channel.

See also shuttler_volt_to_mu().

Parameters:
  • channel – Shuttler Core channel to be configured.

  • offset – Shuttler Core channel offset.

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

Shuttler Core cubic DC-bias spline.

A Shuttler channel can generate a waveform w(t) that is the sum of a cubic spline a(t) and a sinusoid modulated in amplitude by a cubic spline b(t) and in phase/frequency by a quadratic spline c(t), where

\[w(t) = a(t) + b(t) * cos(c(t))\]

and t corresponds to time in seconds. This class controls the cubic spline a(t), in which

\[a(t) = p_0 + p_1t + \frac{p_2t^2}{2} + \frac{p_3t^3}{6}\]

and a(t) is measured in volts.

Parameters:
  • channel – RTIO channel number of this DC-bias spline interface.

  • core_device – Core device name.

set_waveform(a0: numpy.int32, a1: numpy.int32, a2: numpy.int64, a3: numpy.int64)[source]

Set the DC-bias spline waveform.

Given a(t) as defined in DCBias, the coefficients should be configured by the following formulae:

\[ \begin{align}\begin{aligned}T &= 8*10^{-9}\\a_0 &= p_0\\a_1 &= p_1T + \frac{p_2T^2}{2} + \frac{p_3T^3}{6}\\a_2 &= p_2T^2 + p_3T^3\\a_3 &= p_3T^3\end{aligned}\end{align} \]

\(a_0\), \(a_1\), \(a_2\) and \(a_3\) are 16, 32, 48 and 48 bits in width respectively. See shuttler_volt_to_mu() for machine unit conversion.

Note

The waveform is not updated to the Shuttler Core until triggered. See Trigger for the update triggering mechanism.

Parameters:
  • a0 – The \(a_0\) coefficient in machine unit.

  • a1 – The \(a_1\) coefficient in machine unit.

  • a2 – The \(a_2\) coefficient in machine unit.

  • a3 – The \(a_3\) coefficient in machine unit.

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

Shuttler Core DDS spline.

A Shuttler channel can generate a waveform w(t) that is the sum of a cubic spline a(t) and a sinusoid modulated in amplitude by a cubic spline b(t) and in phase/frequency by a quadratic spline c(t), where

\[w(t) = a(t) + b(t) * cos(c(t))\]

and t corresponds to time in seconds. This class controls the cubic spline b(t) and quadratic spline c(t), in which

\[ \begin{align}\begin{aligned}b(t) &= g * (q_0 + q_1t + \frac{q_2t^2}{2} + \frac{q_3t^3}{6})\\c(t) &= r_0 + r_1t + \frac{r_2t^2}{2}\end{aligned}\end{align} \]

b(t) is in volts, c(t) is in number of turns. Note that b(t) contributes to a constant gain of \(g=1.64676\).

Parameters:
  • channel – RTIO channel number of this DC-bias spline interface.

  • core_device – Core device name.

set_waveform(b0: numpy.int32, b1: numpy.int32, b2: numpy.int64, b3: numpy.int64, c0: numpy.int32, c1: numpy.int32, c2: numpy.int32)[source]

Set the DDS spline waveform.

Given b(t) and c(t) as defined in DDS, the coefficients should be configured by the following formulae.

\[ \begin{align}\begin{aligned}T &= 8*10^{-9}\\b_0 &= q_0\\b_1 &= q_1T + \frac{q_2T^2}{2} + \frac{q_3T^3}{6}\\b_2 &= q_2T^2 + q_3T^3\\b_3 &= q_3T^3\\c_0 &= r_0\\c_1 &= r_1T + \frac{r_2T^2}{2}\\c_2 &= r_2T^2\end{aligned}\end{align} \]

\(b_0\), \(b_1\), \(b_2\) and \(b_3\) are 16, 32, 48 and 48 bits in width respectively. See shuttler_volt_to_mu() for machine unit conversion. \(c_0\), \(c_1\) and \(c_2\) are 16, 32 and 32 bits in width respectively.

Note: The waveform is not updated to the Shuttler Core until triggered. See Trigger for the update triggering mechanism.

Parameters:
  • b0 – The \(b_0\) coefficient in machine units.

  • b1 – The \(b_1\) coefficient in machine units.

  • b2 – The \(b_2\) coefficient in machine units.

  • b3 – The \(b_3\) coefficient in machine units.

  • c0 – The \(c_0\) coefficient in machine units.

  • c1 – The \(c_1\) coefficient in machine units.

  • c2 – The \(c_2\) coefficient in machine units.

class artiq.coredevice.shuttler.Relay(dmgr, spi_device, core_device='core')[source]

Shuttler AFE relay switches.

This class controls the AFE relay switches and the LEDs. Switch the relay on to enable AFE output; off to disable the output. The LEDs indicate the relay status.

Note

The relay does not disable ADC measurements. Voltage of any channels can still be read by the ADC even after switching off the relays.

Parameters:
  • spi_device – SPI bus device name.

  • core_device – Core device name.

enable(en: numpy.int32)[source]

Enable/disable relay switches of corresponding channels.

Each bit corresponds to the relay switch of a channel. Asserting a bit turns on the corresponding relay switch; deasserting the same bit turns off the switch instead.

Parameters:

en – Switch enable bits. The MSB corresponds to Channel 15, LSB corresponds to Channel 0.

init()[source]

Initialize SPI device.

Configures the SPI bus to 16 bits, write-only, simultaneous relay switches and LED control.

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

Shuttler Core spline coefficients update trigger.

Parameters:
  • channel – RTIO channel number of the trigger interface.

  • core_device – Core device name.

trigger(trig_out)[source]

Triggers coefficient update of (a) Shuttler Core channel(s).

Each bit corresponds to a Shuttler waveform generator core. Setting trig_out bits commits the pending coefficient update (from set_waveform in DCBias and DDS) to the Shuttler Core synchronously.

Parameters:

trig_out – Coefficient update trigger bits. The MSB corresponds to Channel 15, LSB corresponds to Channel 0.

artiq.coredevice.shuttler.shuttler_volt_to_mu(volt)[source]

Return the equivalent DAC code. Valid input range is from -10 to 10 - LSB.

Miscellaneous

artiq.coredevice.suservo module

class artiq.coredevice.suservo.Channel(dmgr, channel, servo_device)[source]

Sampler-Urukul Servo channel

Parameters:
  • channel – RTIO channel number

  • servo_device – Name of the parent SUServo device

dds_offset_to_mu(offset)[source]

Convert IIR offset (negative setpoint) from units of full scale to machine units (see set_dds_mu(), set_dds_offset_mu()).

For positive ADC voltages as setpoints, this should be negative. Due to rounding and representation as two’s complement, offset=1 can not be represented while offset=-1 can.

get_profile_mu(profile, data)[source]

Retrieve profile data.

Profile data is returned in the data argument in machine units packed as: [ftw >> 16, b1, pow, adc | (delay << 8), offset, a1, ftw & 0xffff, b0].

See also

The individual fields are described in set_iir_mu() and set_dds_mu().

This method advances the timeline by 32 µs and consumes all slack.

Parameters:
  • profile – Profile number (0-31)

  • data – List of 8 integers to write the profile data into

get_y(profile)[source]

Get a profile’s IIR state (filter output, Y0).

The IIR state is also know as the “integrator”, or the DDS amplitude scale factor. It is 17 bits wide and unsigned.

This method does not advance the timeline but consumes all slack.

If reading servo state through this method collides with the servo writing that same data, the data can become invalid. To ensure consistent and valid data, stop the servo before using this method.

Parameters:

profile – Profile number (0-31)

Returns:

IIR filter output in Y0 units of full scale

get_y_mu(profile)[source]

Get a profile’s IIR state (filter output, Y0) in machine units.

The IIR state is also know as the “integrator”, or the DDS amplitude scale factor. It is 17 bits wide and unsigned.

This method does not advance the timeline but consumes all slack.

If reading servo state through this method collides with the servo writing that same data, the data can become invalid. To ensure consistent and valid data, stop the servo before using this method.

Parameters:

profile – Profile number (0-31)

Returns:

17-bit unsigned Y0

set(en_out, en_iir=0, profile=0)[source]

Operate channel.

This method does not advance the timeline. Output RF switch setting takes effect immediately and is independent of any other activity (profile settings, other channels). The RF switch behaves like artiq.coredevice.ttl.TTLOut. RTIO event replacement is supported. IIR updates take place once the RF switch has been enabled for the configured delay and the profile setting has been stable. Profile changes take between one and two servo cycles to reach the DDS.

Parameters:
  • en_out – RF switch enable

  • en_iir – IIR updates enable

  • profile – Active profile (0-31)

set_dds(profile, frequency, offset, phase=0.0)[source]

Set profile DDS coefficients.

This method advances the timeline by four servo memory accesses. Profile parameter changes are not synchronized. Activate a different profile or stop the servo to ensure synchronous changes.

Parameters:
  • profile – Profile number (0-31)

  • frequency – DDS frequency in Hz

  • offset – IIR offset (negative setpoint) in units of full scale, see dds_offset_to_mu()

  • phase – DDS phase in turns

set_dds_mu(profile, ftw, offs, pow_=0)[source]

Set profile DDS coefficients in machine units.

See also Channel.set_dds().

Parameters:
  • profile – Profile number (0-31)

  • ftw – Frequency tuning word (32-bit unsigned)

  • offs – IIR offset (17-bit signed)

  • pow – Phase offset word (16-bit)

set_dds_offset(profile, offset)[source]

Set only IIR offset in DDS coefficient profile.

See set_dds() for setting the complete DDS profile.

Parameters:
  • profile – Profile number (0-31)

  • offset – IIR offset (negative setpoint) in units of full scale

set_dds_offset_mu(profile, offs)[source]

Set only IIR offset in DDS coefficient profile.

See set_dds_mu() for setting the complete DDS profile.

Parameters:
  • profile – Profile number (0-31)

  • offs – IIR offset (17-bit signed)

set_iir(profile, adc, kp, ki=0.0, g=0.0, delay=0.0)[source]

Set profile IIR coefficients.

This method advances the timeline by four servo memory accesses. Profile parameter changes are not synchronized. Activate a different profile or stop the servo to ensure synchronous changes.

Gains are given in units of output full per scale per input full scale.

The transfer function is (up to time discretization and coefficient quantization errors):

\[H(s) = k_p + \frac{k_i}{s + \frac{k_i}{g}}\]
Where:
  • \(s = \sigma + i\omega\) is the complex frequency

  • \(k_p\) is the proportional gain

  • \(k_i\) is the integrator gain

  • \(g\) is the integrator gain limit

Parameters:
  • profile – Profile number (0-31)

  • adc – ADC channel to take IIR input from (0-7)

  • kp – Proportional gain (1). This is usually negative (closed loop, positive ADC voltage, positive setpoint). When 0, this implements a pure I controller.

  • ki – Integrator gain (rad/s). When 0 (the default) this implements a pure P controller. Same sign as kp.

  • g – Integrator gain limit (1). When 0 (the default) the integrator gain limit is infinite. Same sign as ki.

  • delay – Delay (in seconds, 0-300 µs) before allowing IIR updates after invoking set(). This is rounded to the nearest number of servo cycles (~1.2 µs). Since the RF switch (set()) can be opened at any time relative to the servo cycle, the first DDS update that carries updated IIR data will occur approximately between delay + 1 cycle and delay + 2 cycles after set().

set_iir_mu(profile, adc, a1, b0, b1, dly=0)[source]

Set profile IIR coefficients in machine units.

The recurrence relation is (all data signed and MSB aligned):

\[a_0 y_n = a_1 y_{n - 1} + b_0 (x_n + o)/2 + b_1 (x_{n - 1} + o)/2\]

Where:

  • \(y_n\) and \(y_{n-1}\) are the current and previous filter outputs, clipped to \([0, 1[\).

  • \(x_n\) and \(x_{n-1}\) are the current and previous filter inputs in \([-1, 1[\).

  • \(o\) is the offset

  • \(a_0\) is the normalization factor \(2^{11}\)

  • \(a_1\) is the feedback gain

  • \(b_0\) and \(b_1\) are the feedforward gains for the two delays

See also Channel.set_iir().

Parameters:
  • profile – Profile number (0-31)

  • adc – ADC channel to take IIR input from (0-7)

  • a1 – 18-bit signed A1 coefficient (Y1 coefficient, feedback, integrator gain)

  • b0 – 18-bit signed B0 coefficient (recent, X0 coefficient, feed forward, proportional gain)

  • b1 – 18-bit signed B1 coefficient (old, X1 coefficient, feed forward, proportional gain)

  • dly – IIR update suppression time. In units of IIR cycles (~1.2 µs, 0-255).

set_y(profile, y)[source]

Set a profile’s IIR state (filter output, Y0).

The IIR state is also know as the “integrator”, or the DDS amplitude scale factor. It is 17 bits wide and unsigned.

This method must not be used when the servo could be writing to the same location. Either deactivate the profile, or deactivate IIR updates, or disable servo iterations.

This method advances the timeline by one servo memory access.

Parameters:
  • profile – Profile number (0-31)

  • y – IIR state in units of full scale

set_y_mu(profile, y)[source]

Set a profile’s IIR state (filter output, Y0) in machine units.

The IIR state is also know as the “integrator”, or the DDS amplitude scale factor. It is 17 bits wide and unsigned.

This method must not be used when the servo could be writing to the same location. Either deactivate the profile, or deactivate IIR updates, or disable servo iterations.

This method advances the timeline by one servo memory access.

Parameters:
  • profile – Profile number (0-31)

  • y – 17-bit unsigned Y0

class artiq.coredevice.suservo.SUServo(dmgr, channel, pgia_device, cpld_devices, dds_devices, gains=0, sampler_hw_rev='v2.2', core_device='core')[source]

Sampler-Urukul Servo parent and configuration device.

Sampler-Urukul Servo is a integrated device controlling one 8-channel ADC (Sampler) and two 4-channel DDS (Urukuls) with a DSP engine connecting the ADC data and the DDS output amplitudes to enable feedback. SU Servo can for example be used to implement intensity stabilization of laser beams with an amplifier and AOM driven by Urukul and a photodetector connected to Sampler.

Additionally SU Servo supports multiple preconfigured profiles per channel and features like automatic integrator hold.

Notes

  • See the SU Servo variant of the Kasli target for an example of how to connect the gateware and the devices. Sampler and each Urukul need two EEM connections.

  • Ensure that both Urukuls are AD9910 variants and have the on-board dip switches set to 1100 (first two on, last two off).

  • Refer to the Sampler and Urukul documentation and the SU Servo example device database for runtime configuration of the devices (PLLs, gains, clock routing etc.)

Parameters:
  • channel – RTIO channel number

  • pgia_device – Name of the Sampler PGIA gain setting SPI bus

  • cpld_devices – Names of the Urukul CPLD SPI buses

  • dds_devices – Names of the AD9910 devices

  • gains – Initial value for PGIA gains shift register (default: 0x0000). Knowledge of this state is not transferred between experiments.

  • sampler_hw_rev – Sampler’s revision string

  • core_device – Core device name

get_adc(channel)[source]

Get the latest ADC reading (IIR filter input X0).

This method does not advance the timeline but consumes all slack.

If reading servo state through this method collides with the servo writing that same data, the data can become invalid. To ensure consistent and valid data, stop the servo before using this method.

The PGIA gain setting must be known prior to using this method, either by setting the gain (set_pgia_mu()) or by supplying it (gains or via the constructor/device database).

Parameters:

adc – ADC channel number (0-7)

Returns:

ADC voltage

get_adc_mu(adc)[source]

Get the latest ADC reading (IIR filter input X0) in machine units.

This method does not advance the timeline but consumes all slack.

If reading servo state through this method collides with the servo writing that same data, the data can become invalid. To ensure consistent and valid data, stop the servo before using this method.

Parameters:

adc – ADC channel number (0-7)

Returns:

17-bit signed X0

get_status()[source]

Get current SU Servo status.

This method does not advance the timeline but consumes all slack.

The done bit indicates that a SU Servo cycle has completed. It is pulsed for one RTIO cycle every SU Servo cycle and asserted continuously when the servo is not enabled and the pipeline has drained (the last DDS update is done).

This method returns and clears the clip indicator for all channels. An asserted clip indicator corresponds to the servo having encountered an input signal on an active channel that would have resulted in the IIR state exceeding the output range.

Returns:

Status. Bit 0: enabled, bit 1: done, bits 8-15: channel clip indicators.

init()[source]

Initialize the servo, Sampler and both Urukuls.

Leaves the servo disabled (see set_config()), resets and configures all DDS.

Urukul initialization is performed blindly as there is no readback from the DDS or the CPLDs.

This method does not alter the profile configuration memory or the channel controls.

read(addr)[source]

Read from servo memory.

This method does not advance the timeline but consumes all slack.

Parameters:

addr – Memory location address.

set_config(enable)[source]

Set SU Servo configuration.

This method advances the timeline by one servo memory access. It does not support RTIO event replacement.

Parameters:

enable (int) – Enable servo operation. Enabling starts servo iterations beginning with the ADC sampling stage. The first DDS update will happen about two servo cycles (~2.3 µs) after enabling the servo. The delay is deterministic. This also provides a mean for synchronization of servo updates to other RTIO activity. Disabling takes up to two servo cycles (~2.3 µs) to clear the processing pipeline.

set_pgia_mu(channel, gain)[source]

Set instrumentation amplifier gain of a ADC channel.

The four gain settings (0, 1, 2, 3) corresponds to gains of (1, 10, 100, 1000) respectively.

Parameters:
  • channel – Channel index

  • gain – Gain setting

write(addr, value)[source]

Write to servo memory.

This method advances the timeline by one coarse RTIO cycle.

Parameters:
  • addr – Memory location address.

  • value – Data to be written.

artiq.coredevice.suservo.adc_mu_to_volts(x, gain, corrected_fs=True)[source]

Convert servo ADC data from machine units to volts.

artiq.coredevice.suservo.y_mu_to_full_scale(y)[source]

Convert servo Y data from machine units to units of full scale.

artiq.coredevice.grabber module

class artiq.coredevice.grabber.Grabber(dmgr, channel_base, res_width=12, count_shift=0, core_device='core')[source]

Driver for the Grabber camera interface.

gate_roi(mask)[source]

Defines which ROI engines produce input events.

At the end of each video frame, the output from each ROI engine that has been enabled by the mask is enqueued into the RTIO input FIFO.

This function sets the mask at the current position of the RTIO time cursor.

Setting the mask using this function is atomic; in other words, if the system is in the middle of processing a frame and the mask is changed, the processing will complete using the value of the mask that it started with.

Parameters:

mask – bitmask enabling or disabling each ROI engine.

gate_roi_pulse(mask, dt)[source]

Sets a temporary mask for the specified duration (in seconds), before disabling all ROI engines.

input_mu(data, timeout_mu=-1)[source]

Retrieves the accumulated values for one frame from the ROI engines. Blocks until values are available or timeout is reached.

The input list must be a list of integers of the same length as there are enabled ROI engines. This method replaces the elements of the input list with the outputs of the enabled ROI engines, sorted by number.

If the number of elements in the list does not match the number of ROI engines that produced output, an exception will be raised during this call or the next.

If the timeout is reached before data is available, the exception GrabberTimeoutException is raised.

Parameters:

timeout_mu – Timestamp at which a timeout will occur. Set to -1 (default) to disable timeout.

setup_roi(n, x0, y0, x1, y1)[source]

Defines the coordinates of a ROI.

The coordinates are set around the current position of the RTIO time cursor.

The user must keep the ROI engine disabled for the duration of more than one video frame after calling this function, as the output generated for that video frame is undefined.

Advances the timeline by 4 coarse RTIO cycles.

exception artiq.coredevice.grabber.GrabberTimeoutException[source]

Raised when a timeout occurs while attempting to read Grabber RTIO input events.

exception artiq.coredevice.grabber.OutOfSyncException[source]

Raised when an incorrect number of ROI engine outputs has been retrieved from the RTIO input FIFO.