Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: m-labs/artiq
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: 19442efdaeb7
Choose a base ref
...
head repository: m-labs/artiq
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: f9d878119a2f
Choose a head ref
  • 4 commits
  • 5 files changed
  • 1 contributor

Commits on Jul 5, 2015

  1. Verified

    This commit was signed with the committer’s verified signature.
    makenowjust Hiroya Fujinami
    Copy the full SHA
    2eeaa3b View commit details
  2. Verified

    This commit was signed with the committer’s verified signature. The key has expired.
    chris-huxtable Chris Huxtable
    Copy the full SHA
    58c0150 View commit details
  3. pdq2/mediator: fix arm

    sbourdeauducq committed Jul 5, 2015

    Verified

    This commit was signed with the committer’s verified signature. The key has expired.
    chris-huxtable Chris Huxtable
    Copy the full SHA
    2bc8286 View commit details
  4. pxi6733: add mediator

    sbourdeauducq committed Jul 5, 2015

    Verified

    This commit was signed with the committer’s verified signature. The key has expired.
    chris-huxtable Chris Huxtable
    Copy the full SHA
    f9d8781 View commit details
Showing with 193 additions and 4 deletions.
  1. +4 −0 artiq/coredevice/ttl.py
  2. +2 −1 artiq/devices/pdq2/mediator.py
  3. +4 −3 artiq/devices/pxi6733/driver.py
  4. +182 −0 artiq/devices/pxi6733/mediator.py
  5. +1 −0 artiq/gateware/rtio/phy/ttl_simple.py
4 changes: 4 additions & 0 deletions artiq/coredevice/ttl.py
Original file line number Diff line number Diff line change
@@ -253,6 +253,10 @@ def set_mu(self, frequency):
accumulator is connected to the TTL line. Setting the frequency tuning
word has the additional effect of setting the phase accumulator to
0x800000.
Due to the way the clock generator operates, frequency tuning words
that are not powers of two cause jitter of one RTIO clock cycle at the
output.
"""
syscall("ttl_clock_set", now_mu(), self.channel, frequency)
self.previous_timestamp = now_mu()
3 changes: 2 additions & 1 deletion artiq/devices/pdq2/mediator.py
Original file line number Diff line number Diff line change
@@ -75,7 +75,7 @@ def __init__(self, pdq, frame_number):
self.pdq = pdq
self.frame_number = frame_number
self.segments = []
self.segment_count = 0
self.segment_count = 0 # == len(self.segments), used in kernel

self.invalidated = False

@@ -184,6 +184,7 @@ def arm(self):
raise ArmError
for frame in self.frames:
frame._arm()
self.armed = True

full_program = [f._get_program() for f in self.frames]
for n, pdq2 in enumerate(self.pdq2s):
7 changes: 4 additions & 3 deletions artiq/devices/pxi6733/driver.py
Original file line number Diff line number Diff line change
@@ -25,11 +25,11 @@ class DAQmx:

def __init__(self, channels, clock):
"""
:param channels: List of channels as a string or bytes(), following
:param channels: List of channels as a string, following
the physical channels lists and ranges NI-DAQmx syntax.
Example: Dev1/ao0, Dev1/ao1:ao3
:param clock: Clock source terminal as a string or bytes(), following
:param clock: Clock source terminal as a string, following
NI-DAQmx terminal names syntax.
Example: PFI5
@@ -80,7 +80,8 @@ def load_sample_values(self, sampling_freq, values):
not yet completed.
:param sampling_freq: The sampling frequency in samples per second.
:param values: A numpy array of sample values to load in the device.
:param values: A numpy array of sample values (in volts) to load in
the device.
"""

self.clear_pending_task()
182 changes: 182 additions & 0 deletions artiq/devices/pxi6733/mediator.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
import numpy as np

from artiq.language.core import *
from artiq.language.db import *
from artiq.language.units import *
from artiq.wavesynth.compute_samples import Synthesizer


class SegmentSequenceError(Exception):
"""Raised when attempting to play back a named segment which is not the
next in the sequence."""
pass


class InvalidatedError(Exception):
"""Raised when attemting to use a frame or segment that has been
invalidated (due to disarming the DAQmx)."""
pass


class ArmError(Exception):
"""Raised when attempting to arm an already armed DAQmx, to modify the
program of an armed DAQmx, or to play a segment on a disarmed DAQmx."""
pass


def _ceil_div(a, b):
return (a + b - 1)//b


def _compute_duration_mu(nsamples, ftw, acc_width):
# This returns the precise duration so that the clock can be stopped
# exactly at the next rising edge (RTLink commands take precedence over
# toggling from the accumulator).
# If segments are played continuously, replacement of the stop command
# will keep the clock running. If the FTW is not a power of two, note that
# the accumulator is reset at that time, which causes jitter and frequency
# inaccuracy.
# Formally:
# duration *ftw >= nsamples*2**acc_width
# (duration - 1)*ftw < nsamples*2**acc_width
return _ceil_div(nsamples*2**acc_width, ftw)


class _Segment:
def __init__(self, frame, segment_number):
self.frame = frame
self.segment_number = segment_number

self.lines = []

# for @kernel
self.core = frame.daqmx.core

def add_line(self, duration, channel_data):
if self.frame.invalidated:
raise InvalidatedError
if self.frame.daqmx.armed:
raise ArmError
self.lines.append((duration, channel_data))

@kernel
def advance(self):
if self.frame.invalidated:
raise InvalidatedError
if not self.frame.daqmx.armed:
raise ArmError
# If the frame is currently being played, check that we are next.
if (self.frame.daqmx.next_segment >= 0
and self.frame.daqmx.next_segment != self.segment_number):
raise SegmentSequenceError
self.frame.advance()


class _Frame:
def __init__(self, daqmx):
self.daqmx = daqmx
self.segments = []
self.segment_count = 0 # == len(self.segments), used in kernel

self.invalidated = False

# for @kernel
self.core = self.daqmx.core

def create_segment(self, name=None):
if self.invalidated:
raise InvalidatedError
if self.daqmx.armed:
raise ArmError
segment = _Segment(self, self.segment_count)
if name is not None:
if hasattr(self, name):
raise NameError("Segment name already exists")
setattr(self, name, segment)
self.segments.append(segment)
self.segment_count += 1
return segment

def _arm(self):
self.segment_delays = [
_compute_duration_mu(s.get_sample_count(),
self.daqmx.sample_rate,
self.daqmx.clock.acc_width)
for s in self.segments]

def _invalidate(self):
self.invalidated = True

def _get_samples(self):
program = [
{
"dac_divider": 1,
"duration": duration,
"channel_data": channel_data,
} for duration, channel_data in segment.lines
for segment in self.segments]
synth = Synthesizer(self.daqmx.channel_count, program)
synth.select(0)
# not setting any trigger flag in the program causes the whole
# waveform to be computed here for all segments.
# slicing the segments is done by stopping the clock.
return synth.trigger()

@kernel
def advance(self):
if self.invalidated:
raise InvalidatedError
if not self.daqmx.armed:
raise ArmError

self.daqmx.clock.set(self.daqmx.sample_rate)
delay_mu(self.segment_delays[self.daqmx.next_segment])
self.daqmx.next_segment += 1
self.daqmx.clock.stop()

# test for end of frame
if self.daqmx.next_segment == self.segment_count:
self.daqmx.next_segment = -1


class CompoundDAQmx(AutoDB):
class DBKeys:
core = Device()
daqmx_device = Argument()
clock_device = Argument()
channel_count = Argument()
sample_rate = Argument()
sample_rate_in_mu = Argument(False)

def build(self):
self.daqmx = self.dbh.get_device(self.daqmx_device)
self.clock = self.dbh.get_device(self.clock_device)

if not self.sample_rate_in_mu:
self.sample_rate = self.clock.frequency_to_ftw(sample_rate)

self.frame = None
self.next_segment = -1
self.armed = False

def disarm(self):
if self.frame is not None:
self.frame._invalidate()
self.frame = None
self.armed = False

def arm(self):
if self.armed:
raise ArmError
if self.frame is not None:
self.frame._arm()
self.daqmx.load_sample_values(
self.clock.ftw_to_frequency(self.sample_rate),
np.array(self.frame._get_samples()))
self.armed = True

def create_frame(self):
if self.armed:
raise ArmError
self.frame = _Frame(self)
return self.frame
1 change: 1 addition & 0 deletions artiq/gateware/rtio/phy/ttl_simple.py
Original file line number Diff line number Diff line change
@@ -89,6 +89,7 @@ def __init__(self, pad, ftw_width=24):
self.sync.rio += If(self.rtlink.o.stb, ftw.eq(self.rtlink.o.data))
self.sync.rio_phy += [
acc.eq(acc + ftw),
# rtlink takes precedence over regular acc increments
If(self.rtlink.o.stb,
If(self.rtlink.o.data != 0,
# known phase on frequency write: at rising edge