mirror of https://github.com/m-labs/artiq.git
integrate new AD9914 driver
moninj, analyzer, docs, examples, tests.
This commit is contained in:
parent
663d8e66ba
commit
3027951dd8
|
@ -32,6 +32,9 @@ ARTIQ-4
|
||||||
to the new bus.
|
to the new bus.
|
||||||
* The ``ad5360`` coredevice driver has been renamed to ``ad53xx`` and the API
|
* The ``ad5360`` coredevice driver has been renamed to ``ad53xx`` and the API
|
||||||
has changed to better support Zotino.
|
has changed to better support Zotino.
|
||||||
|
* ``artiq.coredevice.dds`` has been renamed to ``artiq.coredevice.ad9914`` and
|
||||||
|
simplified. DDS batch mode is no longer supported. The ``core_dds`` device
|
||||||
|
is no longer necessary.
|
||||||
|
|
||||||
|
|
||||||
ARTIQ-3
|
ARTIQ-3
|
||||||
|
|
|
@ -1,9 +1,3 @@
|
||||||
from artiq.coredevice import exceptions, dds, spi2
|
|
||||||
from artiq.coredevice.exceptions import (RTIOUnderflow, RTIOOverflow)
|
from artiq.coredevice.exceptions import (RTIOUnderflow, RTIOOverflow)
|
||||||
from artiq.coredevice.dds import (PHASE_MODE_CONTINUOUS, PHASE_MODE_ABSOLUTE,
|
|
||||||
PHASE_MODE_TRACKING)
|
|
||||||
|
|
||||||
__all__ = []
|
__all__ = ["RTIOUnderflow", "RTIOOverflow"]
|
||||||
__all__ += ["RTIOUnderflow", "RTIOOverflow"]
|
|
||||||
__all__ += ["PHASE_MODE_CONTINUOUS", "PHASE_MODE_ABSOLUTE",
|
|
||||||
"PHASE_MODE_TRACKING"]
|
|
||||||
|
|
|
@ -211,9 +211,8 @@ class TTLClockGenHandler:
|
||||||
|
|
||||||
|
|
||||||
class DDSHandler:
|
class DDSHandler:
|
||||||
def __init__(self, vcd_manager, dds_type, onehot_sel, sysclk):
|
def __init__(self, vcd_manager, onehot_sel, sysclk):
|
||||||
self.vcd_manager = vcd_manager
|
self.vcd_manager = vcd_manager
|
||||||
self.dds_type = dds_type
|
|
||||||
self.onehot_sel = onehot_sel
|
self.onehot_sel = onehot_sel
|
||||||
self.sysclk = sysclk
|
self.sysclk = sysclk
|
||||||
|
|
||||||
|
@ -227,7 +226,6 @@ class DDSHandler:
|
||||||
self.vcd_manager.get_channel(name + "/frequency", 64)
|
self.vcd_manager.get_channel(name + "/frequency", 64)
|
||||||
dds_channel["vcd_phase"] = \
|
dds_channel["vcd_phase"] = \
|
||||||
self.vcd_manager.get_channel(name + "/phase", 64)
|
self.vcd_manager.get_channel(name + "/phase", 64)
|
||||||
if self.dds_type == "DDSChannelAD9914":
|
|
||||||
dds_channel["ftw"] = [None, None]
|
dds_channel["ftw"] = [None, None]
|
||||||
dds_channel["pow"] = None
|
dds_channel["pow"] = None
|
||||||
self.dds_channels[dds_channel_nr] = dds_channel
|
self.dds_channels[dds_channel_nr] = dds_channel
|
||||||
|
@ -252,9 +250,9 @@ class DDSHandler:
|
||||||
self.selected_dds_channels = self._gpio_to_channels(message.data)
|
self.selected_dds_channels = self._gpio_to_channels(message.data)
|
||||||
for dds_channel_nr in self.selected_dds_channels:
|
for dds_channel_nr in self.selected_dds_channels:
|
||||||
dds_channel = self.dds_channels[dds_channel_nr]
|
dds_channel = self.dds_channels[dds_channel_nr]
|
||||||
if message.address == 0x2d:
|
if message.address == 0x11:
|
||||||
dds_channel["ftw"][0] = message.data
|
dds_channel["ftw"][0] = message.data
|
||||||
elif message.address == 0x2f:
|
elif message.address == 0x13:
|
||||||
dds_channel["ftw"][1] = message.data
|
dds_channel["ftw"][1] = message.data
|
||||||
elif message.address == 0x31:
|
elif message.address == 0x31:
|
||||||
dds_channel["pow"] = message.data
|
dds_channel["pow"] = message.data
|
||||||
|
@ -273,7 +271,6 @@ class DDSHandler:
|
||||||
logger.debug("DDS write @%d 0x%04x to 0x%02x, selected channels: %s",
|
logger.debug("DDS write @%d 0x%04x to 0x%02x, selected channels: %s",
|
||||||
message.timestamp, message.data, message.address,
|
message.timestamp, message.data, message.address,
|
||||||
self.selected_dds_channels)
|
self.selected_dds_channels)
|
||||||
if self.dds_type == "DDSChannelAD9914":
|
|
||||||
self._decode_ad9914_write(message)
|
self._decode_ad9914_write(message)
|
||||||
|
|
||||||
|
|
||||||
|
@ -444,16 +441,17 @@ def get_vcd_log_channels(log_channel, messages):
|
||||||
|
|
||||||
|
|
||||||
def get_single_device_argument(devices, module, cls, argument):
|
def get_single_device_argument(devices, module, cls, argument):
|
||||||
ref_period = None
|
found = None
|
||||||
for desc in devices.values():
|
for desc in devices.values():
|
||||||
if isinstance(desc, dict) and desc["type"] == "local":
|
if isinstance(desc, dict) and desc["type"] == "local":
|
||||||
if (desc["module"] == module
|
if (desc["module"] == module
|
||||||
and desc["class"] in cls):
|
and desc["class"] in cls):
|
||||||
if ref_period is None:
|
value = desc["arguments"][argument]
|
||||||
ref_period = desc["arguments"][argument]
|
if found is None:
|
||||||
else:
|
found = value
|
||||||
return None # more than one device found
|
elif value != found:
|
||||||
return ref_period
|
return None # more than one value/device found
|
||||||
|
return found
|
||||||
|
|
||||||
|
|
||||||
def get_ref_period(devices):
|
def get_ref_period(devices):
|
||||||
|
@ -462,8 +460,8 @@ def get_ref_period(devices):
|
||||||
|
|
||||||
|
|
||||||
def get_dds_sysclk(devices):
|
def get_dds_sysclk(devices):
|
||||||
return get_single_device_argument(devices, "artiq.coredevice.dds",
|
return get_single_device_argument(devices, "artiq.coredevice.ad9914",
|
||||||
("DDSGroupAD9914",), "sysclk")
|
("ad9914",), "sysclk")
|
||||||
|
|
||||||
|
|
||||||
def create_channel_handlers(vcd_manager, devices, ref_period,
|
def create_channel_handlers(vcd_manager, devices, ref_period,
|
||||||
|
@ -479,14 +477,12 @@ def create_channel_handlers(vcd_manager, devices, ref_period,
|
||||||
and desc["class"] == "TTLClockGen"):
|
and desc["class"] == "TTLClockGen"):
|
||||||
channel = desc["arguments"]["channel"]
|
channel = desc["arguments"]["channel"]
|
||||||
channel_handlers[channel] = TTLClockGenHandler(vcd_manager, name, ref_period)
|
channel_handlers[channel] = TTLClockGenHandler(vcd_manager, name, ref_period)
|
||||||
if (desc["module"] == "artiq.coredevice.dds"
|
if (desc["module"] == "artiq.coredevice.ad9914"
|
||||||
and desc["class"] in {"DDSChannelAD9914"}):
|
and desc["class"] == "AD9914"):
|
||||||
dds_bus_channel = desc["arguments"]["bus_channel"]
|
dds_bus_channel = desc["arguments"]["bus_channel"]
|
||||||
dds_channel = desc["arguments"]["channel"]
|
dds_channel = desc["arguments"]["channel"]
|
||||||
if dds_bus_channel in channel_handlers:
|
if dds_bus_channel in channel_handlers:
|
||||||
dds_handler = channel_handlers[dds_bus_channel]
|
dds_handler = channel_handlers[dds_bus_channel]
|
||||||
if dds_handler.dds_type != desc["class"]:
|
|
||||||
raise ValueError("All DDS channels must have the same type")
|
|
||||||
else:
|
else:
|
||||||
dds_handler = DDSHandler(vcd_manager, desc["class"],
|
dds_handler = DDSHandler(vcd_manager, desc["class"],
|
||||||
dds_onehot_sel, dds_sysclk)
|
dds_onehot_sel, dds_sysclk)
|
||||||
|
|
|
@ -1,401 +0,0 @@
|
||||||
"""
|
|
||||||
Drivers for direct digital synthesis (DDS) chips on RTIO.
|
|
||||||
|
|
||||||
Output event replacement is not supported and issuing commands at the same
|
|
||||||
time is an error.
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
from artiq.language.core import *
|
|
||||||
from artiq.language.types import *
|
|
||||||
from artiq.language.units import *
|
|
||||||
from artiq.coredevice.rtio import rtio_output
|
|
||||||
from artiq.coredevice.exceptions import DDSError
|
|
||||||
|
|
||||||
from numpy import int32, int64
|
|
||||||
|
|
||||||
|
|
||||||
_PHASE_MODE_DEFAULT = -1
|
|
||||||
PHASE_MODE_CONTINUOUS = 0
|
|
||||||
PHASE_MODE_ABSOLUTE = 1
|
|
||||||
PHASE_MODE_TRACKING = 2
|
|
||||||
|
|
||||||
|
|
||||||
class DDSParams:
|
|
||||||
def __init__(self):
|
|
||||||
self.bus_channel = 0
|
|
||||||
self.channel = 0
|
|
||||||
self.ftw = 0
|
|
||||||
self.pow = 0
|
|
||||||
self.phase_mode = 0
|
|
||||||
self.amplitude = 0
|
|
||||||
|
|
||||||
|
|
||||||
class BatchContextManager:
|
|
||||||
kernel_invariants = {"core", "core_dds", "params"}
|
|
||||||
|
|
||||||
def __init__(self, core_dds):
|
|
||||||
self.core_dds = core_dds
|
|
||||||
self.core = self.core_dds.core
|
|
||||||
self.active = False
|
|
||||||
self.params = [DDSParams() for _ in range(16)]
|
|
||||||
self.count = 0
|
|
||||||
self.ref_time = int64(0)
|
|
||||||
|
|
||||||
@kernel
|
|
||||||
def __enter__(self):
|
|
||||||
"""Starts a DDS command batch. All DDS commands are buffered
|
|
||||||
after this call, until ``batch_exit`` is called.
|
|
||||||
|
|
||||||
The time of execution of the DDS commands is the time cursor position
|
|
||||||
when the batch is entered."""
|
|
||||||
if self.active:
|
|
||||||
raise DDSError("DDS batch entered twice")
|
|
||||||
|
|
||||||
self.active = True
|
|
||||||
self.count = 0
|
|
||||||
self.ref_time = now_mu()
|
|
||||||
|
|
||||||
@kernel
|
|
||||||
def append(self, bus_channel, channel, ftw, pow, phase_mode, amplitude):
|
|
||||||
if self.count == len(self.params):
|
|
||||||
raise DDSError("Too many commands in DDS batch")
|
|
||||||
|
|
||||||
params = self.params[self.count]
|
|
||||||
params.bus_channel = bus_channel
|
|
||||||
params.channel = channel
|
|
||||||
params.ftw = ftw
|
|
||||||
params.pow = pow
|
|
||||||
params.phase_mode = phase_mode
|
|
||||||
params.amplitude = amplitude
|
|
||||||
self.count += 1
|
|
||||||
|
|
||||||
@kernel
|
|
||||||
def __exit__(self, type, value, traceback):
|
|
||||||
"""Ends a DDS command batch. All buffered DDS commands are issued
|
|
||||||
on the bus."""
|
|
||||||
if not self.active:
|
|
||||||
raise DDSError("DDS batch exited twice")
|
|
||||||
|
|
||||||
self.active = False
|
|
||||||
at_mu(self.ref_time - self.core_dds.batch_duration_mu())
|
|
||||||
for i in range(self.count):
|
|
||||||
param = self.params[i]
|
|
||||||
self.core_dds.program(self.ref_time,
|
|
||||||
param.bus_channel, param.channel, param.ftw,
|
|
||||||
param.pow, param.phase_mode, param.amplitude)
|
|
||||||
|
|
||||||
|
|
||||||
class DDSGroup:
|
|
||||||
"""Core device Direct Digital Synthesis (DDS) driver.
|
|
||||||
|
|
||||||
Gives access to the DDS functionality of the core device.
|
|
||||||
|
|
||||||
:param sysclk: DDS system frequency. The DDS system clock must be a
|
|
||||||
phase-locked multiple of the RTIO clock.
|
|
||||||
"""
|
|
||||||
|
|
||||||
kernel_invariants = {"core", "sysclk", "batch"}
|
|
||||||
|
|
||||||
def __init__(self, dmgr, sysclk, core_device="core"):
|
|
||||||
self.core = dmgr.get(core_device)
|
|
||||||
self.sysclk = sysclk
|
|
||||||
self.batch = BatchContextManager(self)
|
|
||||||
|
|
||||||
@kernel
|
|
||||||
def batch_duration_mu(self):
|
|
||||||
raise NotImplementedError
|
|
||||||
|
|
||||||
@kernel
|
|
||||||
def init(self, bus_channel, channel):
|
|
||||||
raise NotImplementedError
|
|
||||||
|
|
||||||
@kernel
|
|
||||||
def program(self, ref_time, bus_channel, channel, ftw, pow, phase_mode, amplitude):
|
|
||||||
raise NotImplementedError
|
|
||||||
|
|
||||||
@kernel
|
|
||||||
def set(self, bus_channel, channel, ftw, pow, phase_mode, amplitude):
|
|
||||||
if self.batch.active:
|
|
||||||
self.batch.append(bus_channel, channel, ftw, pow, phase_mode, amplitude)
|
|
||||||
else:
|
|
||||||
ref_time = now_mu()
|
|
||||||
at_mu(ref_time - self.program_duration_mu)
|
|
||||||
self.program(ref_time,
|
|
||||||
bus_channel, channel, ftw, pow, phase_mode, amplitude)
|
|
||||||
|
|
||||||
@portable(flags={"fast-math"})
|
|
||||||
def frequency_to_ftw(self, frequency):
|
|
||||||
"""Returns the frequency tuning word corresponding to the given
|
|
||||||
frequency.
|
|
||||||
"""
|
|
||||||
return round(float(int64(2)**32*frequency/self.sysclk))
|
|
||||||
|
|
||||||
@portable(flags={"fast-math"})
|
|
||||||
def ftw_to_frequency(self, ftw):
|
|
||||||
"""Returns the frequency corresponding to the given frequency tuning
|
|
||||||
word.
|
|
||||||
"""
|
|
||||||
return ftw*self.sysclk/int64(2)**32
|
|
||||||
|
|
||||||
@portable(flags={"fast-math"})
|
|
||||||
def turns_to_pow(self, turns):
|
|
||||||
"""Returns the phase offset word corresponding to the given phase
|
|
||||||
in turns."""
|
|
||||||
return round(float(turns*2**self.pow_width))
|
|
||||||
|
|
||||||
@portable(flags={"fast-math"})
|
|
||||||
def pow_to_turns(self, pow):
|
|
||||||
"""Returns the phase in turns corresponding to the given phase offset
|
|
||||||
word."""
|
|
||||||
return pow/2**self.pow_width
|
|
||||||
|
|
||||||
@portable(flags={"fast-math"})
|
|
||||||
def amplitude_to_asf(self, amplitude):
|
|
||||||
"""Returns amplitude scale factor corresponding to given amplitude."""
|
|
||||||
return round(float(amplitude*0x0fff))
|
|
||||||
|
|
||||||
@portable(flags={"fast-math"})
|
|
||||||
def asf_to_amplitude(self, asf):
|
|
||||||
"""Returns the amplitude corresponding to the given amplitude scale
|
|
||||||
factor."""
|
|
||||||
return asf/0x0fff
|
|
||||||
|
|
||||||
|
|
||||||
class DDSChannel:
|
|
||||||
"""Core device Direct Digital Synthesis (DDS) channel driver.
|
|
||||||
|
|
||||||
Controls one DDS channel managed directly by the core device's runtime.
|
|
||||||
|
|
||||||
This class should not be used directly, instead, use the chip-specific
|
|
||||||
drivers such as ``DDSChannelAD9914``.
|
|
||||||
|
|
||||||
The time cursor is not modified by any function in this class.
|
|
||||||
|
|
||||||
:param bus: name of the DDS bus device that this DDS is connected to.
|
|
||||||
:param channel: channel number of the DDS device to control.
|
|
||||||
"""
|
|
||||||
|
|
||||||
kernel_invariants = {
|
|
||||||
"core", "core_dds", "bus_channel", "channel",
|
|
||||||
}
|
|
||||||
|
|
||||||
def __init__(self, dmgr, bus_channel, channel, core_dds_device="core_dds"):
|
|
||||||
self.core_dds = dmgr.get(core_dds_device)
|
|
||||||
self.core = self.core_dds.core
|
|
||||||
self.bus_channel = bus_channel
|
|
||||||
self.channel = channel
|
|
||||||
self.phase_mode = PHASE_MODE_CONTINUOUS
|
|
||||||
|
|
||||||
@kernel
|
|
||||||
def init(self):
|
|
||||||
"""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.
|
|
||||||
|
|
||||||
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. 2ms provides a good
|
|
||||||
timing margin."""
|
|
||||||
self.core_dds.init(self.bus_channel, self.channel)
|
|
||||||
|
|
||||||
@kernel
|
|
||||||
def set_phase_mode(self, phase_mode):
|
|
||||||
"""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.
|
|
||||||
"""
|
|
||||||
self.phase_mode = phase_mode
|
|
||||||
|
|
||||||
@kernel
|
|
||||||
def set_mu(self, frequency, phase=0, phase_mode=_PHASE_MODE_DEFAULT,
|
|
||||||
amplitude=0x0fff):
|
|
||||||
"""Sets the DDS channel to the specified frequency and phase.
|
|
||||||
|
|
||||||
This uses machine units (FTW and POW). The frequency tuning word width
|
|
||||||
is 32, whereas the phase offset word width depends on the type of DDS
|
|
||||||
chip and can be retrieved via the ``pow_width`` attribute. The amplitude
|
|
||||||
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.
|
|
||||||
|
|
||||||
:param frequency: frequency to generate.
|
|
||||||
:param phase: adds an offset, in turns, to the phase.
|
|
||||||
:param phase_mode: if specified, overrides the default phase mode set
|
|
||||||
by ``set_phase_mode`` for this call.
|
|
||||||
"""
|
|
||||||
if phase_mode == _PHASE_MODE_DEFAULT:
|
|
||||||
phase_mode = self.phase_mode
|
|
||||||
self.core_dds.set(self.bus_channel, self.channel, frequency, phase, phase_mode, amplitude)
|
|
||||||
|
|
||||||
@kernel
|
|
||||||
def set(self, frequency, phase=0.0, phase_mode=_PHASE_MODE_DEFAULT,
|
|
||||||
amplitude=1.0):
|
|
||||||
"""Like ``set_mu``, but uses Hz and turns."""
|
|
||||||
self.set_mu(self.core_dds.frequency_to_ftw(frequency),
|
|
||||||
self.core_dds.turns_to_pow(phase), phase_mode,
|
|
||||||
self.core_dds.amplitude_to_asf(amplitude))
|
|
||||||
|
|
||||||
|
|
||||||
AD9914_REG_CFR1L = 0x01
|
|
||||||
AD9914_REG_CFR1H = 0x03
|
|
||||||
AD9914_REG_CFR2L = 0x05
|
|
||||||
AD9914_REG_CFR2H = 0x07
|
|
||||||
AD9914_REG_CFR3L = 0x09
|
|
||||||
AD9914_REG_CFR3H = 0x0b
|
|
||||||
AD9914_REG_CFR4L = 0x0d
|
|
||||||
AD9914_REG_CFR4H = 0x0f
|
|
||||||
AD9914_REG_FTWL = 0x2d
|
|
||||||
AD9914_REG_FTWH = 0x2f
|
|
||||||
AD9914_REG_POW = 0x31
|
|
||||||
AD9914_REG_ASF = 0x33
|
|
||||||
AD9914_REG_USR0 = 0x6d
|
|
||||||
AD9914_FUD = 0x80
|
|
||||||
AD9914_GPIO = 0x81
|
|
||||||
|
|
||||||
|
|
||||||
class DDSGroupAD9914(DDSGroup):
|
|
||||||
"""Driver for AD9914 DDS chips. See ``DDSGroup`` for a description
|
|
||||||
of the functionality."""
|
|
||||||
kernel_invariants = DDSGroup.kernel_invariants.union({
|
|
||||||
"pow_width", "rtio_period_mu", "sysclk_per_mu", "write_duration_mu", "dac_cal_duration_mu",
|
|
||||||
"init_duration_mu", "init_sync_duration_mu", "program_duration_mu",
|
|
||||||
"first_dds_bus_channel", "dds_channel_count", "continuous_phase_comp"
|
|
||||||
})
|
|
||||||
|
|
||||||
pow_width = 16
|
|
||||||
|
|
||||||
def __init__(self, *args, first_dds_bus_channel, dds_bus_count, dds_channel_count, **kwargs):
|
|
||||||
super().__init__(*args, **kwargs)
|
|
||||||
|
|
||||||
self.first_dds_bus_channel = first_dds_bus_channel
|
|
||||||
self.dds_bus_count = dds_bus_count
|
|
||||||
self.dds_channel_count = dds_channel_count
|
|
||||||
|
|
||||||
self.rtio_period_mu = int64(8)
|
|
||||||
self.sysclk_per_mu = int32(self.sysclk * self.core.ref_period)
|
|
||||||
|
|
||||||
self.write_duration_mu = 5 * self.rtio_period_mu
|
|
||||||
self.dac_cal_duration_mu = 147000 * self.rtio_period_mu
|
|
||||||
self.init_duration_mu = 8 * self.write_duration_mu + self.dac_cal_duration_mu
|
|
||||||
self.init_sync_duration_mu = 16 * self.write_duration_mu + 2 * self.dac_cal_duration_mu
|
|
||||||
self.program_duration_mu = 6 * self.write_duration_mu
|
|
||||||
|
|
||||||
self.continuous_phase_comp = [0] * (self.dds_bus_count * self.dds_channel_count)
|
|
||||||
|
|
||||||
@kernel
|
|
||||||
def batch_duration_mu(self):
|
|
||||||
return self.batch.count * (self.program_duration_mu +
|
|
||||||
self.write_duration_mu) # + FUD time
|
|
||||||
|
|
||||||
@kernel
|
|
||||||
def write(self, bus_channel, addr, data):
|
|
||||||
rtio_output(now_mu(), bus_channel, addr, data)
|
|
||||||
delay_mu(self.write_duration_mu)
|
|
||||||
|
|
||||||
@kernel
|
|
||||||
def init(self, bus_channel, channel):
|
|
||||||
delay_mu(-self.init_duration_mu)
|
|
||||||
self.write(bus_channel, AD9914_GPIO, (1 << channel) << 1);
|
|
||||||
|
|
||||||
self.write(bus_channel, AD9914_REG_CFR1H, 0x0000) # Enable cosine output
|
|
||||||
self.write(bus_channel, AD9914_REG_CFR2L, 0x8900) # Enable matched latency
|
|
||||||
self.write(bus_channel, AD9914_REG_CFR2H, 0x0080) # Enable profile mode
|
|
||||||
self.write(bus_channel, AD9914_REG_ASF, 0x0fff) # Set amplitude to maximum
|
|
||||||
self.write(bus_channel, AD9914_REG_CFR4H, 0x0105) # Enable DAC calibration
|
|
||||||
self.write(bus_channel, AD9914_FUD, 0)
|
|
||||||
delay_mu(self.dac_cal_duration_mu)
|
|
||||||
self.write(bus_channel, AD9914_REG_CFR4H, 0x0005) # Disable DAC calibration
|
|
||||||
self.write(bus_channel, AD9914_FUD, 0)
|
|
||||||
|
|
||||||
@kernel
|
|
||||||
def init_sync(self, bus_channel, channel, sync_delay):
|
|
||||||
delay_mu(-self.init_sync_duration_mu)
|
|
||||||
self.write(bus_channel, AD9914_GPIO, (1 << channel) << 1)
|
|
||||||
|
|
||||||
self.write(bus_channel, AD9914_REG_CFR4H, 0x0105) # Enable DAC calibration
|
|
||||||
self.write(bus_channel, AD9914_FUD, 0)
|
|
||||||
delay_mu(self.dac_cal_duration_mu)
|
|
||||||
self.write(bus_channel, AD9914_REG_CFR4H, 0x0005) # Disable DAC calibration
|
|
||||||
self.write(bus_channel, AD9914_FUD, 0)
|
|
||||||
self.write(bus_channel, AD9914_REG_CFR2L, 0x8b00) # Enable matched latency and sync_out
|
|
||||||
self.write(bus_channel, AD9914_FUD, 0)
|
|
||||||
# Set cal with sync and set sync_out and sync_in delay
|
|
||||||
self.write(bus_channel, AD9914_REG_USR0, 0x0840 | (sync_delay & 0x3f))
|
|
||||||
self.write(bus_channel, AD9914_FUD, 0)
|
|
||||||
self.write(bus_channel, AD9914_REG_CFR4H, 0x0105) # Enable DAC calibration
|
|
||||||
self.write(bus_channel, AD9914_FUD, 0)
|
|
||||||
delay_mu(self.dac_cal_duration_mu)
|
|
||||||
self.write(bus_channel, AD9914_REG_CFR4H, 0x0005) # Disable DAC calibration
|
|
||||||
self.write(bus_channel, AD9914_FUD, 0)
|
|
||||||
self.write(bus_channel, AD9914_REG_CFR1H, 0x0000) # Enable cosine output
|
|
||||||
self.write(bus_channel, AD9914_REG_CFR2H, 0x0080) # Enable profile mode
|
|
||||||
self.write(bus_channel, AD9914_REG_ASF, 0x0fff) # Set amplitude to maximum
|
|
||||||
self.write(bus_channel, AD9914_FUD, 0)
|
|
||||||
|
|
||||||
@kernel
|
|
||||||
def program(self, ref_time, bus_channel, channel, ftw, pow, phase_mode, amplitude):
|
|
||||||
self.write(bus_channel, AD9914_GPIO, (1 << channel) << 1)
|
|
||||||
|
|
||||||
self.write(bus_channel, AD9914_REG_FTWL, ftw & 0xffff)
|
|
||||||
self.write(bus_channel, AD9914_REG_FTWH, (ftw >> 16) & 0xffff)
|
|
||||||
|
|
||||||
# We need the RTIO fine timestamp clock to be phase-locked
|
|
||||||
# to DDS SYSCLK, and divided by an integer self.sysclk_per_mu.
|
|
||||||
dds_bus_index = bus_channel - self.first_dds_bus_channel
|
|
||||||
phase_comp_index = dds_bus_index * self.dds_channel_count + channel
|
|
||||||
if phase_mode == PHASE_MODE_CONTINUOUS:
|
|
||||||
# Do not clear phase accumulator on FUD
|
|
||||||
# Disable autoclear phase accumulator and enables OSK.
|
|
||||||
self.write(bus_channel, AD9914_REG_CFR1L, 0x0108)
|
|
||||||
pow += self.continuous_phase_comp[phase_comp_index]
|
|
||||||
else:
|
|
||||||
# Clear phase accumulator on FUD
|
|
||||||
# Enable autoclear phase accumulator and enables OSK.
|
|
||||||
self.write(bus_channel, AD9914_REG_CFR1L, 0x2108)
|
|
||||||
fud_time = now_mu() + 2 * self.write_duration_mu
|
|
||||||
pow -= int32((ref_time - fud_time) * self.sysclk_per_mu * ftw >> (32 - self.pow_width))
|
|
||||||
if phase_mode == PHASE_MODE_TRACKING:
|
|
||||||
pow += int32(ref_time * self.sysclk_per_mu * ftw >> (32 - self.pow_width))
|
|
||||||
self.continuous_phase_comp[phase_comp_index] = pow
|
|
||||||
|
|
||||||
self.write(bus_channel, AD9914_REG_POW, pow)
|
|
||||||
self.write(bus_channel, AD9914_REG_ASF, amplitude)
|
|
||||||
self.write(bus_channel, AD9914_FUD, 0)
|
|
||||||
|
|
||||||
|
|
||||||
class DDSChannelAD9914(DDSChannel):
|
|
||||||
"""Driver for AD9914 DDS chips. See ``DDSChannel`` for a description
|
|
||||||
of the functionality."""
|
|
||||||
@kernel
|
|
||||||
def init_sync(self, sync_delay=0):
|
|
||||||
"""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.
|
|
||||||
|
|
||||||
:param 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.
|
|
||||||
"""
|
|
||||||
self.core_dds.init_sync(self.bus_channel, self.channel, sync_delay)
|
|
|
@ -102,13 +102,6 @@ class DMAError(Exception):
|
||||||
artiq_builtin = True
|
artiq_builtin = True
|
||||||
|
|
||||||
|
|
||||||
class DDSError(Exception):
|
|
||||||
"""Raised when attempting to start a DDS batch while already in a batch,
|
|
||||||
when too many commands are batched, and when DDS channel settings are
|
|
||||||
incorrect.
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
class WatchdogExpired(Exception):
|
class WatchdogExpired(Exception):
|
||||||
"""Raised when a watchdog expires."""
|
"""Raised when a watchdog expires."""
|
||||||
|
|
||||||
|
|
|
@ -215,13 +215,11 @@ def setup_from_ddb(ddb):
|
||||||
force_out = v["class"] == "TTLOut"
|
force_out = v["class"] == "TTLOut"
|
||||||
widget = _WidgetDesc(k, comment, _TTLWidget, (channel, force_out, k))
|
widget = _WidgetDesc(k, comment, _TTLWidget, (channel, force_out, k))
|
||||||
description.add(widget)
|
description.add(widget)
|
||||||
elif (v["module"] == "artiq.coredevice.dds"
|
elif (v["module"] == "artiq.coredevice.ad9914"
|
||||||
and v["class"] == "DDSGroupAD9914"):
|
and v["class"] == "AD9914"):
|
||||||
dds_sysclk = v["arguments"]["sysclk"]
|
|
||||||
elif (v["module"] == "artiq.coredevice.dds"
|
|
||||||
and v["class"] == "DDSChannelAD9914"):
|
|
||||||
bus_channel = v["arguments"]["bus_channel"]
|
bus_channel = v["arguments"]["bus_channel"]
|
||||||
channel = v["arguments"]["channel"]
|
channel = v["arguments"]["channel"]
|
||||||
|
dds_sysclk = v["arguments"]["sysclk"]
|
||||||
widget = _WidgetDesc(k, comment, _DDSWidget, (bus_channel, channel, k))
|
widget = _WidgetDesc(k, comment, _DDSWidget, (bus_channel, channel, k))
|
||||||
description.add(widget)
|
description.add(widget)
|
||||||
elif ( (v["module"] == "artiq.coredevice.ad53xx" and v["class"] == "AD53XX")
|
elif ( (v["module"] == "artiq.coredevice.ad53xx" and v["class"] == "AD53XX")
|
||||||
|
|
|
@ -27,17 +27,6 @@ device_db = {
|
||||||
"module": "artiq.coredevice.dma",
|
"module": "artiq.coredevice.dma",
|
||||||
"class": "CoreDMA"
|
"class": "CoreDMA"
|
||||||
},
|
},
|
||||||
"core_dds": {
|
|
||||||
"type": "local",
|
|
||||||
"module": "artiq.coredevice.dds",
|
|
||||||
"class": "DDSGroupAD9914",
|
|
||||||
"arguments": {
|
|
||||||
"sysclk": 3e9,
|
|
||||||
"first_dds_bus_channel": 39,
|
|
||||||
"dds_bus_count": 2,
|
|
||||||
"dds_channel_count": 3
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
"i2c_switch": {
|
"i2c_switch": {
|
||||||
"type": "local",
|
"type": "local",
|
||||||
|
@ -333,22 +322,22 @@ device_db = {
|
||||||
# AD9914 DDS
|
# AD9914 DDS
|
||||||
"dds0": {
|
"dds0": {
|
||||||
"type": "local",
|
"type": "local",
|
||||||
"module": "artiq.coredevice.dds",
|
"module": "artiq.coredevice.ad9914",
|
||||||
"class": "DDSChannelAD9914",
|
"class": "AD9914",
|
||||||
"arguments": {"bus_channel": 39, "channel": 0},
|
"arguments": {"sysclk": 3e9, "bus_channel": 39, "channel": 0},
|
||||||
"comment": "Comments work in DDS panel as well"
|
"comment": "Comments work in DDS panel as well"
|
||||||
},
|
},
|
||||||
"dds1": {
|
"dds1": {
|
||||||
"type": "local",
|
"type": "local",
|
||||||
"module": "artiq.coredevice.dds",
|
"module": "artiq.coredevice.ad9914",
|
||||||
"class": "DDSChannelAD9914",
|
"class": "AD9914",
|
||||||
"arguments": {"bus_channel": 39, "channel": 1}
|
"arguments": {"sysclk": 3e9, "bus_channel": 39, "channel": 1}
|
||||||
},
|
},
|
||||||
"dds2": {
|
"dds2": {
|
||||||
"type": "local",
|
"type": "local",
|
||||||
"module": "artiq.coredevice.dds",
|
"module": "artiq.coredevice.ad9914",
|
||||||
"class": "DDSChannelAD9914",
|
"class": "AD9914",
|
||||||
"arguments": {"bus_channel": 39, "channel": 2}
|
"arguments": {"sysclk": 3e9, "bus_channel": 39, "channel": 2}
|
||||||
},
|
},
|
||||||
|
|
||||||
# Aliases
|
# Aliases
|
||||||
|
|
|
@ -14,8 +14,8 @@ class DDSSetter(EnvExperiment):
|
||||||
for k, v in sorted(device_db.items(), key=itemgetter(0)):
|
for k, v in sorted(device_db.items(), key=itemgetter(0)):
|
||||||
if (isinstance(v, dict)
|
if (isinstance(v, dict)
|
||||||
and v["type"] == "local"
|
and v["type"] == "local"
|
||||||
and v["module"] == "artiq.coredevice.dds"
|
and v["module"] == "artiq.coredevice.ad9914"
|
||||||
and v["class"] in {"DDSChannelAD9914"}):
|
and v["class"] == "AD9914"):
|
||||||
self.dds[k] = {
|
self.dds[k] = {
|
||||||
"driver": self.get_device(k),
|
"driver": self.get_device(k),
|
||||||
"frequency": self.get_argument(
|
"frequency": self.get_argument(
|
||||||
|
|
|
@ -6,7 +6,6 @@ class DDSTest(EnvExperiment):
|
||||||
|
|
||||||
def build(self):
|
def build(self):
|
||||||
self.setattr_device("core")
|
self.setattr_device("core")
|
||||||
self.setattr_device("core_dds")
|
|
||||||
self.setattr_device("dds0")
|
self.setattr_device("dds0")
|
||||||
self.setattr_device("dds1")
|
self.setattr_device("dds1")
|
||||||
self.setattr_device("dds2")
|
self.setattr_device("dds2")
|
||||||
|
@ -19,8 +18,8 @@ class DDSTest(EnvExperiment):
|
||||||
def run(self):
|
def run(self):
|
||||||
self.core.reset()
|
self.core.reset()
|
||||||
delay(200*us)
|
delay(200*us)
|
||||||
with self.core_dds.batch:
|
|
||||||
self.dds1.set(120*MHz)
|
self.dds1.set(120*MHz)
|
||||||
|
delay(10*us)
|
||||||
self.dds2.set(200*MHz)
|
self.dds2.set(200*MHz)
|
||||||
delay(1*us)
|
delay(1*us)
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,6 @@ class PhotonHistogram(EnvExperiment):
|
||||||
|
|
||||||
def build(self):
|
def build(self):
|
||||||
self.setattr_device("core")
|
self.setattr_device("core")
|
||||||
self.setattr_device("core_dds")
|
|
||||||
self.setattr_device("bd_dds")
|
self.setattr_device("bd_dds")
|
||||||
self.setattr_device("bd_sw")
|
self.setattr_device("bd_sw")
|
||||||
self.setattr_device("bdd_dds")
|
self.setattr_device("bdd_dds")
|
||||||
|
@ -22,8 +21,9 @@ class PhotonHistogram(EnvExperiment):
|
||||||
|
|
||||||
@kernel
|
@kernel
|
||||||
def program_cooling(self):
|
def program_cooling(self):
|
||||||
with self.core_dds.batch:
|
delay_mu(-self.bd_dds.set_duration_mu)
|
||||||
self.bd_dds.set(200*MHz)
|
self.bd_dds.set(200*MHz)
|
||||||
|
delay_mu(self.bd_dds.set_duration_mu)
|
||||||
self.bdd_dds.set(300*MHz)
|
self.bdd_dds.set(300*MHz)
|
||||||
|
|
||||||
@kernel
|
@kernel
|
||||||
|
|
|
@ -28,17 +28,6 @@ device_db = {
|
||||||
"module": "artiq.coredevice.dma",
|
"module": "artiq.coredevice.dma",
|
||||||
"class": "CoreDMA"
|
"class": "CoreDMA"
|
||||||
},
|
},
|
||||||
"core_dds": {
|
|
||||||
"type": "local",
|
|
||||||
"module": "artiq.coredevice.dds",
|
|
||||||
"class": "DDSGroupAD9914",
|
|
||||||
"arguments": {
|
|
||||||
"sysclk": 3e9,
|
|
||||||
"first_dds_bus_channel": 39,
|
|
||||||
"dds_bus_count": 2,
|
|
||||||
"dds_channel_count": 3
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
"i2c_switch": {
|
"i2c_switch": {
|
||||||
"type": "local",
|
"type": "local",
|
||||||
|
@ -334,22 +323,22 @@ device_db = {
|
||||||
# AD9914 DDS
|
# AD9914 DDS
|
||||||
"dds0": {
|
"dds0": {
|
||||||
"type": "local",
|
"type": "local",
|
||||||
"module": "artiq.coredevice.dds",
|
"module": "artiq.coredevice.ad9914",
|
||||||
"class": "DDSChannelAD9914",
|
"class": "AD9914",
|
||||||
"arguments": {"bus_channel": 39, "channel": 0},
|
"arguments": {"sysclk": 3e9, "bus_channel": 39, "channel": 0},
|
||||||
"comment": "Comments work in DDS panel as well"
|
"comment": "Comments work in DDS panel as well"
|
||||||
},
|
},
|
||||||
"dds1": {
|
"dds1": {
|
||||||
"type": "local",
|
"type": "local",
|
||||||
"module": "artiq.coredevice.dds",
|
"module": "artiq.coredevice.ad9914",
|
||||||
"class": "DDSChannelAD9914",
|
"class": "AD9914",
|
||||||
"arguments": {"bus_channel": 39, "channel": 1}
|
"arguments": {"sysclk": 3e9, "bus_channel": 39, "channel": 1}
|
||||||
},
|
},
|
||||||
"dds2": {
|
"dds2": {
|
||||||
"type": "local",
|
"type": "local",
|
||||||
"module": "artiq.coredevice.dds",
|
"module": "artiq.coredevice.ad9914",
|
||||||
"class": "DDSChannelAD9914",
|
"class": "AD9914",
|
||||||
"arguments": {"bus_channel": 39, "channel": 2}
|
"arguments": {"sysclk": 3e9, "bus_channel": 39, "channel": 2}
|
||||||
},
|
},
|
||||||
|
|
||||||
# Aliases
|
# Aliases
|
||||||
|
|
|
@ -4,8 +4,8 @@ from artiq.gateware import ad9_dds
|
||||||
from artiq.gateware.rtio.phy.wishbone import RT2WB
|
from artiq.gateware.rtio.phy.wishbone import RT2WB
|
||||||
|
|
||||||
|
|
||||||
class _AD9_DDS(Module):
|
class AD9914(Module):
|
||||||
def __init__(self, ftw_base, pads, nchannels, onehot=False, **kwargs):
|
def __init__(self, pads, nchannels, onehot=False, **kwargs):
|
||||||
self.submodules._ll = ClockDomainsRenamer("rio_phy")(
|
self.submodules._ll = ClockDomainsRenamer("rio_phy")(
|
||||||
ad9_dds.AD9_DDS(pads, **kwargs))
|
ad9_dds.AD9_DDS(pads, **kwargs))
|
||||||
self.submodules._rt2wb = RT2WB(len(pads.a)+1, self._ll.bus)
|
self.submodules._rt2wb = RT2WB(len(pads.a)+1, self._ll.bus)
|
||||||
|
@ -38,13 +38,13 @@ class _AD9_DDS(Module):
|
||||||
if len(pads.d) == 8:
|
if len(pads.d) == 8:
|
||||||
self.sync.rio_phy += \
|
self.sync.rio_phy += \
|
||||||
If(selected(c), [
|
If(selected(c), [
|
||||||
If(current_address == ftw_base+i,
|
If(current_address == 0x11+i,
|
||||||
ftw[i*8:(i+1)*8].eq(current_data))
|
ftw[i*8:(i+1)*8].eq(current_data))
|
||||||
for i in range(4)])
|
for i in range(4)])
|
||||||
elif len(pads.d) == 16:
|
elif len(pads.d) == 16:
|
||||||
self.sync.rio_phy += \
|
self.sync.rio_phy += \
|
||||||
If(selected(c), [
|
If(selected(c), [
|
||||||
If(current_address == ftw_base+2*i,
|
If(current_address == 0x11+2*i,
|
||||||
ftw[i*16:(i+1)*16].eq(current_data))
|
ftw[i*16:(i+1)*16].eq(current_data))
|
||||||
for i in range(2)])
|
for i in range(2)])
|
||||||
else:
|
else:
|
||||||
|
@ -54,8 +54,3 @@ class _AD9_DDS(Module):
|
||||||
self.sync.rio_phy += If(current_address == 2**len(pads.a), [
|
self.sync.rio_phy += If(current_address == 2**len(pads.a), [
|
||||||
If(selected(c), probe.eq(ftw))
|
If(selected(c), probe.eq(ftw))
|
||||||
for c, (probe, ftw) in enumerate(zip(self.probes, ftws))])
|
for c, (probe, ftw) in enumerate(zip(self.probes, ftws))])
|
||||||
|
|
||||||
|
|
||||||
class AD9914(_AD9_DDS):
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
_AD9_DDS.__init__(self, 0x2d, *args, **kwargs)
|
|
||||||
|
|
|
@ -127,7 +127,6 @@ class PulseRate(EnvExperiment):
|
||||||
class PulseRateDDS(EnvExperiment):
|
class PulseRateDDS(EnvExperiment):
|
||||||
def build(self):
|
def build(self):
|
||||||
self.setattr_device("core")
|
self.setattr_device("core")
|
||||||
self.setattr_device("core_dds")
|
|
||||||
self.setattr_device("dds0")
|
self.setattr_device("dds0")
|
||||||
self.setattr_device("dds1")
|
self.setattr_device("dds1")
|
||||||
|
|
||||||
|
@ -135,13 +134,14 @@ class PulseRateDDS(EnvExperiment):
|
||||||
def run(self):
|
def run(self):
|
||||||
self.core.reset()
|
self.core.reset()
|
||||||
dt = self.core.seconds_to_mu(5*us)
|
dt = self.core.seconds_to_mu(5*us)
|
||||||
freq = self.core_dds.frequency_to_ftw(100*MHz)
|
freq = self.dds0.frequency_to_ftw(100*MHz)
|
||||||
while True:
|
while True:
|
||||||
delay(10*ms)
|
delay(10*ms)
|
||||||
for i in range(1250):
|
for i in range(1250):
|
||||||
try:
|
try:
|
||||||
with self.core_dds.batch:
|
delay_mu(-self.dds0.set_duration_mu)
|
||||||
self.dds0.set_mu(freq)
|
self.dds0.set_mu(freq)
|
||||||
|
delay_mu(self.dds0.set_duration_mu)
|
||||||
self.dds1.set_mu(freq)
|
self.dds1.set_mu(freq)
|
||||||
delay_mu(dt)
|
delay_mu(dt)
|
||||||
except RTIOUnderflow:
|
except RTIOUnderflow:
|
||||||
|
|
|
@ -75,16 +75,22 @@ RF generation drivers
|
||||||
.. automodule:: artiq.coredevice.urukul
|
.. automodule:: artiq.coredevice.urukul
|
||||||
:members:
|
:members:
|
||||||
|
|
||||||
|
:mod:`artiq.coredevice.ad9910` module
|
||||||
|
+++++++++++++++++++++++++++++++++++++
|
||||||
|
|
||||||
|
.. automodule:: artiq.coredevice.ad9910
|
||||||
|
:members:
|
||||||
|
|
||||||
:mod:`artiq.coredevice.ad9912` module
|
:mod:`artiq.coredevice.ad9912` module
|
||||||
+++++++++++++++++++++++++++++++++++++
|
+++++++++++++++++++++++++++++++++++++
|
||||||
|
|
||||||
.. automodule:: artiq.coredevice.ad9912
|
.. automodule:: artiq.coredevice.ad9912
|
||||||
:members:
|
:members:
|
||||||
|
|
||||||
:mod:`artiq.coredevice.ad9910` module
|
:mod:`artiq.coredevice.ad9914` module
|
||||||
+++++++++++++++++++++++++++++++++++++
|
++++++++++++++++++++++++++++++++++
|
||||||
|
|
||||||
.. automodule:: artiq.coredevice.ad9910
|
.. automodule:: artiq.coredevice.ad9914
|
||||||
:members:
|
:members:
|
||||||
|
|
||||||
:mod:`artiq.coredevice.spline` module
|
:mod:`artiq.coredevice.spline` module
|
||||||
|
@ -99,12 +105,6 @@ RF generation drivers
|
||||||
.. automodule:: artiq.coredevice.sawg
|
.. automodule:: artiq.coredevice.sawg
|
||||||
:members:
|
:members:
|
||||||
|
|
||||||
:mod:`artiq.coredevice.dds` module
|
|
||||||
++++++++++++++++++++++++++++++++++
|
|
||||||
|
|
||||||
.. automodule:: artiq.coredevice.dds
|
|
||||||
:members:
|
|
||||||
|
|
||||||
|
|
||||||
DAC/ADC drivers
|
DAC/ADC drivers
|
||||||
---------------
|
---------------
|
||||||
|
|
|
@ -76,7 +76,7 @@ The sequence is exactly equivalent to::
|
||||||
|
|
||||||
ttl.pulse(2*us)
|
ttl.pulse(2*us)
|
||||||
|
|
||||||
The :meth:`artiq.coredevice.ttl.TTLOut.pulse` method advances the timeline cursor (using ``delay()``) while other methods such as :meth:`artiq.coredevice.ttl.TTLOut.on`, :meth:`artiq.coredevice.ttl.TTLOut.off`, :meth:`artiq.coredevice.dds._DDSGeneric.set`. The latter are called *zero-duration* methods.
|
The :meth:`artiq.coredevice.ttl.TTLOut.pulse` method advances the timeline cursor (using ``delay()``) while other methods such as :meth:`artiq.coredevice.ttl.TTLOut.on`, :meth:`artiq.coredevice.ttl.TTLOut.off`, :meth:`artiq.coredevice.ad9914.set`. The latter are called *zero-duration* methods.
|
||||||
|
|
||||||
Underflow exceptions
|
Underflow exceptions
|
||||||
--------------------
|
--------------------
|
||||||
|
|
Loading…
Reference in New Issue