Source code for artiq.coredevice.fastino

"""RTIO driver for the Fastino 32channel, 16 bit, 2.5 MS/s per channel,
streaming DAC.
from numpy import int32

from artiq.language.core import kernel, portable, delay
from artiq.coredevice.rtio import (rtio_output, rtio_output_wide,
from artiq.language.units import us
from artiq.language.types import TInt32, TList

[docs]class Fastino: """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 :meth:`set_hold`, the next frame will contain the update. For the DACs held, the update is triggered explicitly by setting the corresponding bit using :meth:`set_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 :meth:`set_dac`/:meth:`set_dac_mu` interface must be used. If non-zero, the :meth:`set_group`/:meth:`set_group_mu` interface must be used. :param channel: RTIO channel number :param core_device: Core device name (default: "core") :param log2_width: Width of DAC channel group (logarithm base 2). Value must match the corresponding value in the RTIO PHY (gateware). """ kernel_invariants = {"core", "channel", "width"} def __init__(self, dmgr, channel, core_device="core", log2_width=0): = channel << 8 self.core = dmgr.get(core_device) self.width = 1 << log2_width
[docs] @kernel def init(self): """Initialize the device. This clears reset, unsets DAC_CLR, enables AFE_PWR, clears error counters, then enables error counting """ self.set_cfg(reset=0, afe_power_down=0, dac_clr=0, clr_err=1) delay(1*us) self.set_cfg(reset=0, afe_power_down=0, dac_clr=0, clr_err=0) delay(1*us)
[docs] @kernel def write(self, addr, data): """Write data to a Fastino register. :param addr: Address to write to. :param data: Data to write. """ rtio_output( | addr, data)
[docs] @kernel def read(self, addr): """Read from Fastino register. TODO: untested :param addr: Address to read from. :return: The data read. """ rtio_output( | addr | 0x80) return rtio_input_data( >> 8)
[docs] @kernel def set_dac_mu(self, dac, data): """Write DAC data in machine units. :param dac: DAC channel to write to (0-31). :param data: DAC word to write, 16 bit unsigned integer, in machine units. """ self.write(dac, data)
[docs] @kernel def set_group_mu(self, dac: TInt32, data: TList(TInt32)): """Write a group of DAC channels in machine units. :param dac: First channel in DAC channel group (0-31). The `log2_width` LSBs must be zero. :param 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). """ if dac & (self.width - 1): raise ValueError("Group index LSBs must be zero") rtio_output_wide( | dac, data)
[docs] @portable def voltage_to_mu(self, voltage): """Convert SI Volts to DAC machine units. :param voltage: Voltage in SI Volts. :return: DAC data word in machine units, 16 bit integer. """ data = int32(round((0x8000/10.)*voltage)) + int32(0x8000) if data < 0 or data > 0xffff: raise ValueError("DAC voltage out of bounds") return data
[docs] @portable def voltage_group_to_mu(self, voltage, data): """Convert SI Volts to packed DAC channel group machine units. :param voltage: List of SI Volt voltages. :param data: List of DAC channel data pairs to write to. Half the length of `voltage`. """ for i in range(len(voltage)): v = self.voltage_to_mu(voltage[i]) if i & 1: v = data[i // 2] | (v << 16) data[i // 2] = int32(v)
[docs] @kernel def set_dac(self, dac, voltage): """Set DAC data to given voltage. :param dac: DAC channel (0-31). :param voltage: Desired output voltage. """ self.write(dac, self.voltage_to_mu(voltage))
[docs] @kernel def set_group(self, dac, voltage): """Set DAC group data to given voltage. :param dac: DAC channel (0-31). :param voltage: Desired output voltage. """ data = [int32(0)] * (len(voltage) // 2) self.voltage_group_to_mu(voltage, data) self.set_group_mu(dac, data)
[docs] @kernel def update(self, update): """Schedule channels for update. :param update: Bit mask of channels to update (32 bit). """ self.write(0x20, update)
[docs] @kernel def set_hold(self, hold): """Set channels to manual update. :param hold: Bit mask of channels to hold (32 bit). """ self.write(0x21, hold)
[docs] @kernel def set_cfg(self, reset=0, afe_power_down=0, dac_clr=0, clr_err=0): """Set configuration bits. :param reset: Reset SPI PLL and SPI clock domain. :param afe_power_down: Disable AFE power. :param dac_clr: Assert all 32 DAC_CLR signals setting all DACs to mid-scale (0 V). :param clr_err: Clear error counters and PLL reset indicator. This clears the sticky red error LED. Must be cleared to enable error counting. """ self.write(0x22, (reset << 0) | (afe_power_down << 1) | (dac_clr << 2) | (clr_err << 3))
[docs] @kernel def set_leds(self, leds): """Set the green user-defined LEDs :param leds: LED status, 8 bit integer each bit corresponding to one green LED. """ self.write(0x23, leds)