integrate new AD9914 driver

moninj, analyzer, docs, examples, tests.
This commit is contained in:
Sebastien Bourdeauducq 2018-05-13 23:29:35 +08:00
parent 663d8e66ba
commit 3027951dd8
15 changed files with 70 additions and 515 deletions

View File

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

View File

@ -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"]

View File

@ -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)

View File

@ -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)

View File

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

View File

@ -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")

View File

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

View File

@ -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(

View File

@ -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)

View File

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

View File

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

View File

@ -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)

View File

@ -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:

View File

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

View File

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