coredevice.dds: reimplement fully in ARTIQ Python.

This commit also drops AD9858 support from software.
This commit is contained in:
whitequark 2016-11-21 11:55:44 +00:00
parent 55ea68da7f
commit f4b7666768
11 changed files with 276 additions and 479 deletions

View File

@ -182,10 +182,7 @@ class DDSHandler:
self.vcd_manager.get_channel("dds/" + name + "/frequency", 64) self.vcd_manager.get_channel("dds/" + name + "/frequency", 64)
dds_channel["vcd_phase"] = \ dds_channel["vcd_phase"] = \
self.vcd_manager.get_channel("dds/" + name + "/phase", 64) self.vcd_manager.get_channel("dds/" + name + "/phase", 64)
if self.dds_type == "AD9858": if self.dds_type == "DDSChannelAD9914":
dds_channel["ftw"] = [None, None, None, None]
dds_channel["pow"] = [None, None]
elif self.dds_type == "AD9914":
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
@ -205,26 +202,6 @@ class DDSHandler:
else: else:
return {gpio} return {gpio}
def _decode_ad9858_write(self, message):
if message.address == 0x41:
self.selected_dds_channels = self._gpio_to_channels(message.data)
for dds_channel_nr in self.selected_dds_channels:
dds_channel = self.dds_channels[dds_channel_nr]
if message.address in range(0x0a, 0x0e):
dds_channel["ftw"][message.address - 0x0a] = message.data
elif message.address in range(0x0e, 0x10):
dds_channel["pow"][message.address - 0x0e] = message.data
elif message.address == 0x40: # FUD
if None not in dds_channel["ftw"]:
ftw = sum(x << i*8
for i, x in enumerate(dds_channel["ftw"]))
frequency = ftw*self.sysclk/2**32
dds_channel["vcd_frequency"].set_value_double(frequency)
if None not in dds_channel["pow"]:
pow = dds_channel["pow"][0] | (dds_channel["pow"][1] & 0x3f) << 8
phase = pow/2**14
dds_channel["vcd_phase"].set_value_double(phase)
def _decode_ad9914_write(self, message): def _decode_ad9914_write(self, message):
if message.address == 0x81: if message.address == 0x81:
self.selected_dds_channels = self._gpio_to_channels(message.data) self.selected_dds_channels = self._gpio_to_channels(message.data)
@ -251,9 +228,7 @@ 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 == "AD9858": if self.dds_type == "DDSChannelAD9914":
self._decode_ad9858_write(message)
elif self.dds_type == "AD9914":
self._decode_ad9914_write(message) self._decode_ad9914_write(message)
@ -312,7 +287,7 @@ def get_single_device_argument(devices, module, cls, argument):
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"] == cls): and desc["class"] in cls):
if ref_period is None: if ref_period is None:
ref_period = desc["arguments"][argument] ref_period = desc["arguments"][argument]
else: else:
@ -322,12 +297,12 @@ def get_single_device_argument(devices, module, cls, argument):
def get_ref_period(devices): def get_ref_period(devices):
return get_single_device_argument(devices, "artiq.coredevice.core", return get_single_device_argument(devices, "artiq.coredevice.core",
"Core", "ref_period") ("Core",), "ref_period")
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.dds",
"CoreDDS", "sysclk") ("DDSGroupAD9914",), "sysclk")
def create_channel_handlers(vcd_manager, devices, ref_period, def create_channel_handlers(vcd_manager, devices, ref_period,
@ -344,7 +319,7 @@ def create_channel_handlers(vcd_manager, devices, ref_period,
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.dds"
and desc["class"] in {"AD9858", "AD9914"}): and desc["class"] in {"DDSChannelAD9914"}):
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:

View File

@ -1,56 +1,84 @@
from artiq.language.core import * from artiq.language.core import *
from artiq.language.types import * from artiq.language.types import *
from artiq.language.units import * from artiq.language.units import *
from numpy import int64 from artiq.coredevice.rtio import rtio_output
from artiq.coredevice.exceptions import DDSError
from numpy import int32, int64
_PHASE_MODE_DEFAULT = -1 _PHASE_MODE_DEFAULT = -1
# keep in sync with dds.h
PHASE_MODE_CONTINUOUS = 0 PHASE_MODE_CONTINUOUS = 0
PHASE_MODE_ABSOLUTE = 1 PHASE_MODE_ABSOLUTE = 1
PHASE_MODE_TRACKING = 2 PHASE_MODE_TRACKING = 2
@syscall(flags={"nowrite"}) class DDSParams:
def dds_init(time_mu: TInt64, bus_channel: TInt32, channel: TInt32) -> TNone: def __init__(self):
raise NotImplementedError("syscall not simulated") self.bus_channel = 0
self.channel = 0
@syscall(flags={"nowrite"}) self.ftw = 0
def dds_init_sync(time_mu: TInt64, bus_channel: TInt32, self.pow = 0
channel: TInt32, sync_delay: TInt32) -> TNone: self.phase_mode = 0
raise NotImplementedError("syscall not simulated") self.amplitude = 0
@syscall(flags={"nowrite"})
def dds_set(time_mu: TInt64, bus_channel: TInt32, channel: TInt32, ftw: TInt32,
pow: TInt32, phase_mode: TInt32, amplitude: TInt32) -> TNone:
raise NotImplementedError("syscall not simulated")
@syscall(flags={"nowrite"})
def dds_batch_enter(time_mu: TInt64) -> TNone:
raise NotImplementedError("syscall not simulated")
@syscall(flags={"nowrite"})
def dds_batch_exit() -> TNone:
raise NotImplementedError("syscall not simulated")
class _BatchContextManager: class BatchContextManager:
kernel_invariants = {"core", "core_dds"} kernel_invariants = {"core", "core_dds", "params"}
def __init__(self, core_dds): def __init__(self, core_dds):
self.core_dds = core_dds self.core_dds = core_dds
self.core = self.core_dds.core 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 @kernel
def __enter__(self): def __enter__(self):
self.core_dds.dds_batch_enter() """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 @kernel
def __exit__(self, type, value, traceback): def __exit__(self, type, value, traceback):
self.core_dds.dds_batch_exit() """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 CoreDDS: class DDSGroup:
"""Core device Direct Digital Synthesis (DDS) driver. """Core device Direct Digital Synthesis (DDS) driver.
Gives access to the DDS functionality of the core device. Gives access to the DDS functionality of the core device.
@ -64,31 +92,75 @@ class CoreDDS:
def __init__(self, dmgr, sysclk, core_device="core"): def __init__(self, dmgr, sysclk, core_device="core"):
self.core = dmgr.get(core_device) self.core = dmgr.get(core_device)
self.sysclk = sysclk self.sysclk = sysclk
self.batch = _BatchContextManager(self) self.batch = BatchContextManager(self)
@kernel @kernel
def dds_batch_enter(self): def batch_duration_mu(self):
"""Starts a DDS command batch. All DDS commands are buffered raise NotImplementedError
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."""
dds_batch_enter(now_mu())
@kernel @kernel
def dds_batch_exit(self): def init(self, bus_channel, channel):
"""Ends a DDS command batch. All buffered DDS commands are issued raise NotImplementedError
on the bus."""
dds_batch_exit() @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 _DDSGeneric: class DDSChannel:
"""Core device Direct Digital Synthesis (DDS) channel driver. """Core device Direct Digital Synthesis (DDS) channel driver.
Controls one DDS channel managed directly by the core device's runtime. Controls one DDS channel managed directly by the core device's runtime.
This class should not be used directly, instead, use the chip-specific This class should not be used directly, instead, use the chip-specific
drivers such as ``AD9858`` and ``AD9914``. drivers such as ``DDSChannelAD9914``.
The time cursor is not modified by any function in this class. The time cursor is not modified by any function in this class.
@ -97,7 +169,7 @@ class _DDSGeneric:
""" """
kernel_invariants = { kernel_invariants = {
"core", "core_dds", "bus_channel", "channel", "pow_width" "core", "core_dds", "bus_channel", "channel",
} }
def __init__(self, dmgr, bus_channel, channel, core_dds_device="core_dds"): def __init__(self, dmgr, bus_channel, channel, core_dds_device="core_dds"):
@ -107,43 +179,6 @@ class _DDSGeneric:
self.channel = channel self.channel = channel
self.phase_mode = PHASE_MODE_CONTINUOUS self.phase_mode = PHASE_MODE_CONTINUOUS
@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.core_dds.sysclk))
@portable(flags=["fast-math"])
def ftw_to_frequency(self, ftw):
"""Returns the frequency corresponding to the given frequency tuning
word.
"""
return ftw*self.core_dds.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
@kernel @kernel
def init(self): def init(self):
"""Resets and initializes the DDS channel. """Resets and initializes the DDS channel.
@ -155,7 +190,7 @@ class _DDSGeneric:
initializing multiple DDS channels is to call this function initializing multiple DDS channels is to call this function
sequentially with a delay between the calls. 2ms provides a good sequentially with a delay between the calls. 2ms provides a good
timing margin.""" timing margin."""
dds_init(now_mu(), self.bus_channel, self.channel) self.core_dds.init(self.bus_channel, self.channel)
@kernel @kernel
def set_phase_mode(self, phase_mode): def set_phase_mode(self, phase_mode):
@ -197,29 +232,147 @@ class _DDSGeneric:
""" """
if phase_mode == _PHASE_MODE_DEFAULT: if phase_mode == _PHASE_MODE_DEFAULT:
phase_mode = self.phase_mode phase_mode = self.phase_mode
dds_set(now_mu(), self.bus_channel, self.channel, self.core_dds.set(self.bus_channel, self.channel, frequency, phase, phase_mode, amplitude)
frequency, phase, phase_mode, amplitude)
@kernel @kernel
def set(self, frequency, phase=0.0, phase_mode=_PHASE_MODE_DEFAULT, def set(self, frequency, phase=0.0, phase_mode=_PHASE_MODE_DEFAULT,
amplitude=1.0): amplitude=1.0):
"""Like ``set_mu``, but uses Hz and turns.""" """Like ``set_mu``, but uses Hz and turns."""
self.set_mu(self.frequency_to_ftw(frequency), self.set_mu(self.core_dds.frequency_to_ftw(frequency),
self.turns_to_pow(phase), phase_mode, self.core_dds.turns_to_pow(phase), phase_mode,
self.amplitude_to_asf(amplitude)) self.core_dds.amplitude_to_asf(amplitude))
class AD9858(_DDSGeneric): AD9914_REG_CFR1L = 0x01
"""Driver for AD9858 DDS chips. See ``_DDSGeneric`` for a description 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.""" of the functionality."""
pow_width = 14 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"
})
class AD9914(_DDSGeneric):
"""Driver for AD9914 DDS chips. See ``_DDSGeneric`` for a description
of the functionality."""
pow_width = 16 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 @kernel
def init_sync(self, sync_delay=0): def init_sync(self, sync_delay=0):
"""Resets and initializes the DDS channel as well as configures """Resets and initializes the DDS channel as well as configures
@ -237,4 +390,4 @@ class AD9914(_DDSGeneric):
:param sync_delay: integer from 0 to 0x3f that sets the value of :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. SYNC_OUT (bits 3-5) and SYNC_IN (bits 0-2) delay ADJ bits.
""" """
dds_init_sync(now_mu(), self.bus_channel, self.channel, sync_delay) self.core_dds.init_sync(self.bus_channel, self.channel, sync_delay)

View File

@ -125,7 +125,6 @@ class DDSError(Exception):
when too many commands are batched, and when DDS channel settings are when too many commands are batched, and when DDS channel settings are
incorrect. incorrect.
""" """
artiq_builtin = True
class I2CError(Exception): class I2CError(Exception):
"""Raised with a I2C transaction fails.""" """Raised with a I2C transaction fails."""

View File

@ -219,10 +219,10 @@ class _DeviceManager:
self.ttl_widgets[k] = widget self.ttl_widgets[k] = widget
self.ttl_cb() self.ttl_cb()
if (v["module"] == "artiq.coredevice.dds" if (v["module"] == "artiq.coredevice.dds"
and v["class"] == "CoreDDS"): and v["class"] == "DDSGroupAD9914"):
self.dds_sysclk = v["arguments"]["sysclk"] self.dds_sysclk = v["arguments"]["sysclk"]
if (v["module"] == "artiq.coredevice.dds" if (v["module"] == "artiq.coredevice.dds"
and v["class"] in {"AD9858", "AD9914"}): and v["class"] in {"DDSChannelAD9914"}):
bus_channel = v["arguments"]["bus_channel"] bus_channel = v["arguments"]["bus_channel"]
channel = v["arguments"]["channel"] channel = v["arguments"]["channel"]
widget = _DDSWidget( widget = _DDSWidget(

View File

@ -23,8 +23,13 @@
"core_dds": { "core_dds": {
"type": "local", "type": "local",
"module": "artiq.coredevice.dds", "module": "artiq.coredevice.dds",
"class": "CoreDDS", "class": "DDSGroupAD9914",
"arguments": {"sysclk": 3e9} "arguments": {
"sysclk": 3e9,
"first_dds_bus_channel": 26,
"dds_bus_count": 2,
"dds_channel_count": 3
}
}, },
"i2c_switch": { "i2c_switch": {
@ -136,20 +141,20 @@
"dds0": { "dds0": {
"type": "local", "type": "local",
"module": "artiq.coredevice.dds", "module": "artiq.coredevice.dds",
"class": "AD9914", "class": "DDSChannelAD9914",
"arguments": {"bus_channel": 26, "channel": 0}, "arguments": {"bus_channel": 26, "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.dds",
"class": "AD9914", "class": "DDSChannelAD9914",
"arguments": {"bus_channel": 26, "channel": 1} "arguments": {"bus_channel": 26, "channel": 1}
}, },
"dds2": { "dds2": {
"type": "local", "type": "local",
"module": "artiq.coredevice.dds", "module": "artiq.coredevice.dds",
"class": "AD9914", "class": "DDSChannelAD9914",
"arguments": {"bus_channel": 26, "channel": 2} "arguments": {"bus_channel": 26, "channel": 2}
}, },

View File

@ -15,7 +15,7 @@ class DDSSetter(EnvExperiment):
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.dds"
and v["class"] in {"AD9858", "AD9914"}): and v["class"] in {"DDSChannelAD9914"}):
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

@ -144,7 +144,6 @@ class _NIST_Ions(MiniSoC, AMPSoC):
self.csr_devices.append("rtio_crg") self.csr_devices.append("rtio_crg")
self.submodules.rtio = rtio.RTIO(rtio_channels) self.submodules.rtio = rtio.RTIO(rtio_channels)
self.register_kernel_cpu_csrdevice("rtio") self.register_kernel_cpu_csrdevice("rtio")
self.config["RTIO_FINE_TS_WIDTH"] = self.rtio.fine_ts_width
self.submodules.rtio_moninj = rtio.MonInj(rtio_channels) self.submodules.rtio_moninj = rtio.MonInj(rtio_channels)
self.csr_devices.append("rtio_moninj") self.csr_devices.append("rtio_moninj")

View File

@ -225,7 +225,6 @@ trce -v 12 -fastpaths -tsi {build_name}.tsi -o {build_name}.twr {build_name}.ncd
# RTIO logic # RTIO logic
self.submodules.rtio = rtio.RTIO(rtio_channels) self.submodules.rtio = rtio.RTIO(rtio_channels)
self.register_kernel_cpu_csrdevice("rtio") self.register_kernel_cpu_csrdevice("rtio")
self.config["RTIO_FINE_TS_WIDTH"] = self.rtio.fine_ts_width
self.config["DDS_RTIO_CLK_RATIO"] = 8 >> self.rtio.fine_ts_width self.config["DDS_RTIO_CLK_RATIO"] = 8 >> self.rtio.fine_ts_width
self.submodules.rtio_moninj = rtio.MonInj(rtio_channels) self.submodules.rtio_moninj = rtio.MonInj(rtio_channels)
self.csr_devices.append("rtio_moninj") self.csr_devices.append("rtio_moninj")

View File

@ -4,7 +4,7 @@ include $(MISOC_DIRECTORY)/software/common.mak
PYTHON ?= python3.5 PYTHON ?= python3.5
OBJECTS := flash_storage.o main.o OBJECTS := flash_storage.o main.o
OBJECTS_KSUPPORT := ksupport_glue.o artiq_personality.o rtio.o dds.o i2c.o OBJECTS_KSUPPORT := ksupport_glue.o artiq_personality.o rtio.o i2c.o
RUSTOUT_DIRECTORY := cargo/or1k-unknown-none/debug RUSTOUT_DIRECTORY := cargo/or1k-unknown-none/debug
CORE_IO_COMMIT := d40c593f42fafbac1ff3d827f6df96338b5b7d8b CORE_IO_COMMIT := d40c593f42fafbac1ff3d827f6df96338b5b7d8b

View File

@ -1,263 +0,0 @@
#include <generated/csr.h>
#if ((defined CONFIG_RTIO_DDS_COUNT) && (CONFIG_RTIO_DDS_COUNT > 0))
#include <stdio.h>
#include "artiq_personality.h"
#include "rtio.h"
#include "dds.h"
#define DURATION_WRITE (5 << CONFIG_RTIO_FINE_TS_WIDTH)
#if defined CONFIG_DDS_AD9858
/* Assume 8-bit bus */
#define DURATION_INIT (7*DURATION_WRITE) /* not counting FUD */
#define DURATION_PROGRAM (8*DURATION_WRITE) /* not counting FUD */
#elif defined CONFIG_DDS_AD9914
/* Assume 16-bit bus */
/* DAC calibration takes max. 1ms as per datasheet */
#define DURATION_DAC_CAL (147000 << CONFIG_RTIO_FINE_TS_WIDTH)
/* not counting final FUD */
#define DURATION_INIT (8*DURATION_WRITE + DURATION_DAC_CAL)
#define DURATION_INIT_SYNC (16*DURATION_WRITE + 2*DURATION_DAC_CAL)
#define DURATION_PROGRAM (6*DURATION_WRITE) /* not counting FUD */
#else
#error Unknown DDS configuration
#endif
#define DDS_WRITE(addr, data) do { \
rtio_output(now, bus_channel, addr, data); \
now += DURATION_WRITE; \
} while(0)
void dds_init(long long int timestamp, int bus_channel, int channel)
{
long long int now;
now = timestamp - DURATION_INIT;
#ifdef CONFIG_DDS_ONEHOT_SEL
channel = 1 << channel;
#endif
channel <<= 1;
DDS_WRITE(DDS_GPIO, channel);
#ifndef CONFIG_DDS_AD9914
/*
* Resetting a AD9914 intermittently crashes it. It does not produce any
* output until power-cycled.
* Increasing the reset pulse length and the delay until the first write
* to 300ns do not solve the problem.
* The chips seem fine without a reset.
*/
DDS_WRITE(DDS_GPIO, channel | 1); /* reset */
DDS_WRITE(DDS_GPIO, channel);
#endif
#ifdef CONFIG_DDS_AD9858
/*
* 2GHz divider disable
* SYNCLK disable
* Mixer power-down
* Phase detect power down
*/
DDS_WRITE(DDS_CFR0, 0x78);
DDS_WRITE(DDS_CFR1, 0x00);
DDS_WRITE(DDS_CFR2, 0x00);
DDS_WRITE(DDS_CFR3, 0x00);
DDS_WRITE(DDS_FUD, 0);
#endif
#ifdef CONFIG_DDS_AD9914
DDS_WRITE(DDS_CFR1H, 0x0000); /* Enable cosine output */
DDS_WRITE(DDS_CFR2L, 0x8900); /* Enable matched latency */
DDS_WRITE(DDS_CFR2H, 0x0080); /* Enable profile mode */
DDS_WRITE(DDS_ASF, 0x0fff); /* Set amplitude to maximum */
DDS_WRITE(DDS_CFR4H, 0x0105); /* Enable DAC calibration */
DDS_WRITE(DDS_FUD, 0);
now += DURATION_DAC_CAL;
DDS_WRITE(DDS_CFR4H, 0x0005); /* Disable DAC calibration */
DDS_WRITE(DDS_FUD, 0);
#endif
}
void dds_init_sync(long long int timestamp, int bus_channel, int channel, int sync_delay)
{
#ifdef CONFIG_DDS_AD9914
long long int now;
now = timestamp - DURATION_INIT_SYNC;
#ifdef CONFIG_DDS_ONEHOT_SEL
channel = 1 << channel;
#endif
channel <<= 1;
DDS_WRITE(DDS_GPIO, channel);
DDS_WRITE(DDS_CFR4H, 0x0105); /* Enable DAC calibration */
DDS_WRITE(DDS_FUD, 0);
now += DURATION_DAC_CAL;
DDS_WRITE(DDS_CFR4H, 0x0005); /* Disable DAC calibration */
DDS_WRITE(DDS_FUD, 0);
DDS_WRITE(DDS_CFR2L, 0x8b00); /* Enable matched latency and sync_out*/
DDS_WRITE(DDS_FUD, 0);
/* Set cal with sync and set sync_out and sync_in delay */
DDS_WRITE(DDS_USR0, 0x0840 | (sync_delay & 0x3f));
DDS_WRITE(DDS_FUD, 0);
DDS_WRITE(DDS_CFR4H, 0x0105); /* Enable DAC calibration */
DDS_WRITE(DDS_FUD, 0);
now += DURATION_DAC_CAL;
DDS_WRITE(DDS_CFR4H, 0x0005); /* Disable DAC calibration */
DDS_WRITE(DDS_FUD, 0);
DDS_WRITE(DDS_CFR1H, 0x0000); /* Enable cosine output */
DDS_WRITE(DDS_CFR2H, 0x0080); /* Enable profile mode */
DDS_WRITE(DDS_ASF, 0x0fff); /* Set amplitude to maximum */
DDS_WRITE(DDS_FUD, 0);
#endif
}
/* Compensation to keep phase continuity when switching from absolute or tracking
* to continuous phase mode. */
static unsigned int continuous_phase_comp[CONFIG_RTIO_DDS_COUNT][CONFIG_DDS_CHANNELS_PER_BUS];
static void dds_set_one(long long int now, long long int ref_time,
int bus_channel, int channel,
unsigned int ftw, unsigned int pow, int phase_mode, unsigned int amplitude)
{
unsigned int channel_enc;
if((channel < 0) || (channel >= CONFIG_DDS_CHANNELS_PER_BUS))
artiq_raise_from_c("DDSError", "Attempted to set invalid DDS channel", 0, 0, 0);
if((bus_channel < CONFIG_RTIO_FIRST_DDS_CHANNEL)
|| (bus_channel >= (CONFIG_RTIO_FIRST_DDS_CHANNEL+CONFIG_RTIO_DDS_COUNT)))
artiq_raise_from_c("DDSError", "Attempted to use invalid DDS bus", 0, 0, 0);
#ifdef CONFIG_DDS_ONEHOT_SEL
channel_enc = 1 << channel;
#else
channel_enc = channel;
#endif
DDS_WRITE(DDS_GPIO, channel_enc << 1);
#ifdef CONFIG_DDS_AD9858
DDS_WRITE(DDS_FTW0, ftw & 0xff);
DDS_WRITE(DDS_FTW1, (ftw >> 8) & 0xff);
DDS_WRITE(DDS_FTW2, (ftw >> 16) & 0xff);
DDS_WRITE(DDS_FTW3, (ftw >> 24) & 0xff);
#endif
#ifdef CONFIG_DDS_AD9914
DDS_WRITE(DDS_FTWL, ftw & 0xffff);
DDS_WRITE(DDS_FTWH, (ftw >> 16) & 0xffff);
#endif
/* We need the RTIO fine timestamp clock to be phase-locked
* to DDS SYSCLK, and divided by an integer CONFIG_DDS_RTIO_CLK_RATIO.
*/
if(phase_mode == PHASE_MODE_CONTINUOUS) {
/* Do not clear phase accumulator on FUD */
#ifdef CONFIG_DDS_AD9858
DDS_WRITE(DDS_CFR2, 0x00);
#endif
#ifdef CONFIG_DDS_AD9914
/* Disable autoclear phase accumulator and enables OSK. */
DDS_WRITE(DDS_CFR1L, 0x0108);
#endif
pow += continuous_phase_comp[bus_channel-CONFIG_RTIO_FIRST_DDS_CHANNEL][channel];
} else {
long long int fud_time;
/* Clear phase accumulator on FUD */
#ifdef CONFIG_DDS_AD9858
DDS_WRITE(DDS_CFR2, 0x40);
#endif
#ifdef CONFIG_DDS_AD9914
/* Enable autoclear phase accumulator and enables OSK. */
DDS_WRITE(DDS_CFR1L, 0x2108);
#endif
fud_time = now + 2*DURATION_WRITE;
pow -= (ref_time - fud_time)*CONFIG_DDS_RTIO_CLK_RATIO*ftw >> (32-DDS_POW_WIDTH);
if(phase_mode == PHASE_MODE_TRACKING)
pow += ref_time*CONFIG_DDS_RTIO_CLK_RATIO*ftw >> (32-DDS_POW_WIDTH);
continuous_phase_comp[bus_channel-CONFIG_RTIO_FIRST_DDS_CHANNEL][channel] = pow;
}
#ifdef CONFIG_DDS_AD9858
DDS_WRITE(DDS_POW0, pow & 0xff);
DDS_WRITE(DDS_POW1, (pow >> 8) & 0x3f);
#endif
#ifdef CONFIG_DDS_AD9914
DDS_WRITE(DDS_POW, pow);
#endif
#ifdef CONFIG_DDS_AD9914
DDS_WRITE(DDS_ASF, amplitude);
#endif
DDS_WRITE(DDS_FUD, 0);
}
struct dds_set_params {
int bus_channel;
int channel;
unsigned int ftw;
unsigned int pow;
int phase_mode;
unsigned int amplitude;
};
static int batch_mode;
static int batch_count;
static long long int batch_ref_time;
static struct dds_set_params batch[DDS_MAX_BATCH];
void dds_batch_enter(long long int timestamp)
{
if(batch_mode)
artiq_raise_from_c("DDSError", "DDS batch entered twice", 0, 0, 0);
batch_mode = 1;
batch_count = 0;
batch_ref_time = timestamp;
}
void dds_batch_exit(void)
{
long long int now;
int i;
if(!batch_mode)
artiq_raise_from_c("DDSError", "DDS batch exited twice", 0, 0, 0);
batch_mode = 0;
/* + FUD time */
now = batch_ref_time - batch_count*(DURATION_PROGRAM + DURATION_WRITE);
for(i=0;i<batch_count;i++) {
dds_set_one(now, batch_ref_time,
batch[i].bus_channel, batch[i].channel,
batch[i].ftw, batch[i].pow, batch[i].phase_mode,
batch[i].amplitude);
now += DURATION_PROGRAM + DURATION_WRITE;
}
}
void dds_set(long long int timestamp, int bus_channel, int channel,
unsigned int ftw, unsigned int pow, int phase_mode, unsigned int amplitude)
{
if(batch_mode) {
if(batch_count >= DDS_MAX_BATCH)
artiq_raise_from_c("DDSError", "Too many commands in DDS batch", 0, 0, 0);
/* timestamp parameter ignored (determined by batch) */
batch[batch_count].bus_channel = bus_channel;
batch[batch_count].channel = channel;
batch[batch_count].ftw = ftw;
batch[batch_count].pow = pow;
batch[batch_count].phase_mode = phase_mode;
batch[batch_count].amplitude = amplitude;
batch_count++;
} else {
dds_set_one(timestamp - DURATION_PROGRAM, timestamp,
bus_channel, channel,
ftw, pow, phase_mode, amplitude);
}
}
#endif /* CONFIG_RTIO_DDS_COUNT */

View File

@ -1,70 +0,0 @@
#ifndef __DDS_H
#define __DDS_H
#include <hw/common.h>
#include <generated/csr.h>
#include <generated/mem.h>
#if ((defined CONFIG_RTIO_DDS_COUNT) && (CONFIG_RTIO_DDS_COUNT > 0))
/* Maximum number of commands in a batch */
#define DDS_MAX_BATCH 16
#ifdef CONFIG_DDS_AD9858
#define DDS_CFR0 0x00
#define DDS_CFR1 0x01
#define DDS_CFR2 0x02
#define DDS_CFR3 0x03
#define DDS_FTW0 0x0a
#define DDS_FTW1 0x0b
#define DDS_FTW2 0x0c
#define DDS_FTW3 0x0d
#define DDS_POW0 0x0e
#define DDS_POW1 0x0f
#define DDS_FUD 0x40
#define DDS_GPIO 0x41
#endif
#ifdef CONFIG_DDS_AD9914
#define DDS_CFR1L 0x01
#define DDS_CFR1H 0x03
#define DDS_CFR2L 0x05
#define DDS_CFR2H 0x07
#define DDS_CFR3L 0x09
#define DDS_CFR3H 0x0b
#define DDS_CFR4L 0x0d
#define DDS_CFR4H 0x0f
#define DDS_FTWL 0x2d
#define DDS_FTWH 0x2f
#define DDS_POW 0x31
#define DDS_ASF 0x33
#define DDS_USR0 0x6d
#define DDS_FUD 0x80
#define DDS_GPIO 0x81
#endif
#ifdef CONFIG_DDS_AD9858
#define DDS_POW_WIDTH 14
#endif
#ifdef CONFIG_DDS_AD9914
#define DDS_POW_WIDTH 16
#endif
enum {
PHASE_MODE_CONTINUOUS = 0,
PHASE_MODE_ABSOLUTE = 1,
PHASE_MODE_TRACKING = 2
};
void dds_init(long long int timestamp, int bus_channel, int channel);
void dds_init_sync(long long int timestamp, int bus_channel, int channel,
int sync_delay);
void dds_batch_enter(long long int timestamp);
void dds_batch_exit(void);
void dds_set(long long int timestamp, int bus_channel, int channel,
unsigned int ftw, unsigned int pow, int phase_mode, unsigned int amplitude);
#endif /* CONFIG_RTIO_DDS_COUNT */
#endif /* __DDS_H */