2014-07-11 00:13:37 +08:00
|
|
|
from artiq.language.core import *
|
2015-01-12 18:51:23 +08:00
|
|
|
from artiq.language.db import *
|
2014-05-31 00:20:13 +08:00
|
|
|
from artiq.language.units import *
|
|
|
|
|
2014-09-05 12:03:22 +08:00
|
|
|
|
2014-11-21 04:32:56 +08:00
|
|
|
PHASE_MODE_DEFAULT = -1
|
2015-05-08 14:44:39 +08:00
|
|
|
# keep in sync with dds.h
|
2014-11-21 04:32:56 +08:00
|
|
|
PHASE_MODE_CONTINUOUS = 0
|
|
|
|
PHASE_MODE_ABSOLUTE = 1
|
|
|
|
PHASE_MODE_TRACKING = 2
|
|
|
|
|
|
|
|
|
2015-05-09 14:47:40 +08:00
|
|
|
class _BatchContextManager:
|
|
|
|
def __init__(self, dds_bus):
|
|
|
|
self.dds_bus = dds_bus
|
|
|
|
|
|
|
|
@kernel
|
|
|
|
def __enter__(self):
|
|
|
|
self.dds_bus.batch_enter()
|
|
|
|
|
|
|
|
@kernel
|
|
|
|
def __exit__(self, type, value, traceback):
|
|
|
|
self.dds_bus.batch_exit()
|
|
|
|
|
|
|
|
|
2015-05-08 22:17:06 +08:00
|
|
|
class DDSBus(AutoDB):
|
|
|
|
"""Core device Direct Digital Synthesis (DDS) bus batching driver.
|
|
|
|
|
|
|
|
Manages batching of DDS commands on a DDS shared bus."""
|
|
|
|
class DBKeys:
|
|
|
|
core = Device()
|
|
|
|
|
2015-05-09 14:47:40 +08:00
|
|
|
def build(self):
|
|
|
|
self.batch = _BatchContextManager(self)
|
|
|
|
|
2015-05-08 22:17:06 +08:00
|
|
|
@kernel
|
|
|
|
def batch_enter(self):
|
|
|
|
"""Starts a DDS command batch. All DDS commands are buffered
|
|
|
|
after this call, until ``batch_exit`` is called."""
|
|
|
|
syscall("dds_batch_enter", time_to_cycles(now()))
|
|
|
|
|
|
|
|
@kernel
|
|
|
|
def batch_exit(self):
|
|
|
|
"""Ends a DDS command batch. All buffered DDS commands are issued
|
|
|
|
on the bus, and FUD is pulsed at the time the batch started."""
|
|
|
|
syscall("dds_batch_exit")
|
|
|
|
|
|
|
|
|
2015-01-12 18:51:23 +08:00
|
|
|
class DDS(AutoDB):
|
2014-09-30 17:38:52 +08:00
|
|
|
"""Core device Direct Digital Synthesis (DDS) driver.
|
|
|
|
|
2015-05-08 22:17:06 +08:00
|
|
|
Controls one DDS channel managed directly by the core device's runtime.
|
2014-09-30 17:38:52 +08:00
|
|
|
|
2015-06-20 05:30:17 +08:00
|
|
|
:param sysclk: DDS system frequency.
|
2015-05-08 14:44:39 +08:00
|
|
|
:param channel: channel number of the DDS device to control.
|
2014-09-30 17:38:52 +08:00
|
|
|
"""
|
2015-01-12 18:51:23 +08:00
|
|
|
class DBKeys:
|
2015-03-08 18:37:53 +08:00
|
|
|
core = Device()
|
2015-06-20 05:30:17 +08:00
|
|
|
sysclk = Argument()
|
2015-05-08 14:44:39 +08:00
|
|
|
channel = Argument()
|
2014-05-31 00:20:13 +08:00
|
|
|
|
2014-09-05 12:03:22 +08:00
|
|
|
def build(self):
|
2015-05-08 14:44:39 +08:00
|
|
|
self.phase_mode = PHASE_MODE_CONTINUOUS
|
2014-05-31 00:20:13 +08:00
|
|
|
|
2014-10-13 17:05:35 +08:00
|
|
|
@portable
|
|
|
|
def frequency_to_ftw(self, frequency):
|
|
|
|
"""Returns the frequency tuning word corresponding to the given
|
|
|
|
frequency.
|
|
|
|
"""
|
2015-06-20 05:30:17 +08:00
|
|
|
return round(2**32*frequency/self.sysclk)
|
2014-10-13 17:05:35 +08:00
|
|
|
|
|
|
|
@portable
|
|
|
|
def ftw_to_frequency(self, ftw):
|
|
|
|
"""Returns the frequency corresponding to the given frequency tuning
|
|
|
|
word.
|
|
|
|
"""
|
2015-06-20 05:30:17 +08:00
|
|
|
return ftw*self.sysclk/2**32
|
2014-10-13 17:05:35 +08:00
|
|
|
|
2015-05-08 14:44:39 +08:00
|
|
|
@kernel
|
|
|
|
def init(self):
|
2015-05-09 17:11:34 +08:00
|
|
|
"""Resets and initializes the DDS channel.
|
|
|
|
|
|
|
|
The runtime does this for all channels upon core device startup."""
|
2015-05-08 14:44:39 +08:00
|
|
|
syscall("dds_init", time_to_cycles(now()), self.channel)
|
|
|
|
|
2014-09-05 12:03:22 +08:00
|
|
|
@kernel
|
2014-11-21 04:32:56 +08:00
|
|
|
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
|
2015-05-08 14:44:39 +08:00
|
|
|
def set(self, frequency, phase_mode=PHASE_MODE_DEFAULT, phase_offset=0):
|
|
|
|
"""Sets the DDS channel to the specified frequency and phase.
|
2014-11-21 04:32:56 +08:00
|
|
|
|
|
|
|
:param frequency: frequency to generate.
|
|
|
|
:param phase_mode: if specified, overrides the default phase mode set
|
|
|
|
by ``set_phase_mode`` for this call.
|
|
|
|
:param phase_offset: adds an offset, in turns, to the phase.
|
2014-09-30 17:38:52 +08:00
|
|
|
"""
|
2015-05-08 14:44:39 +08:00
|
|
|
if phase_mode == PHASE_MODE_DEFAULT:
|
|
|
|
phase_mode = self.phase_mode
|
2014-09-09 22:00:51 +08:00
|
|
|
|
2015-05-08 14:44:39 +08:00
|
|
|
syscall("dds_set", time_to_cycles(now()), self.channel,
|
|
|
|
self.frequency_to_ftw(frequency), round(phase_offset*2**14),
|
2015-06-21 08:42:39 +08:00
|
|
|
phase_mode)
|