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: 27d1a5ffb9cd
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: caab81974a33
Choose a head ref
  • 3 commits
  • 3 files changed
  • 1 contributor

Commits on Oct 16, 2014

  1. Copy the full SHA
    3c49d74 View commit details
  2. Copy the full SHA
    111bd30 View commit details
  3. Copy the full SHA
    caab819 View commit details
Showing with 213 additions and 26 deletions.
  1. +131 −0 artiq/devices/pdq2.py
  2. +53 −0 artiq/devices/rtio_core.py
  3. +29 −26 examples/transport.py
131 changes: 131 additions & 0 deletions artiq/devices/pdq2.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
from artiq.language.core import *
from artiq.language.units import *
from artiq.devices import rtio_core


# FIXME: check those numbers
frame_setup = 20*ns
trigger_duration = 100*ns
frame_wait = 100*ns
sample_period = 10*us


class SegmentSequenceError(Exception):
pass


class FrameActiveError(Exception):
pass


class FrameCloseError(Exception):
pass


class _Segment:
def __init__(self, frame, sn, duration, host_data):
self.core = frame.core
self.frame = frame
self.sn = sn
self.duration = duration
self.host_data = host_data

@kernel
def advance(self):
if self.frame.pdq.current_frame != self.frame.fn:
raise FrameActiveError
if self.frame.pdq.next_sn != self.sn:
raise SegmentSequenceError
self.frame.pdq.next_sn += 1

t = time_to_cycles(now())
self.frame.pdq.trigger.on(t)
self.frame.pdq.trigger.off(t + time_to_cycles(trigger_duration))
delay(self.duration)


class _Frame:
def __init__(self, core):
self.core = core
self.segment_count = 0
self.closed = False

def append(self, name, t, u, trigger=False):
if self.closed:
raise FrameCloseError
sn = self.segment_count
duration = (t[-1] - t[0])*sample_period
segment = _Segment(self, sn, duration, (t, u, trigger))
setattr(self, name, segment)
self.segment_count += 1

def close(self):
if self.closed:
raise FrameCloseError
self.closed = True

@kernel
def begin(self):
if self.pdq.current_frame >= 0:
raise FrameActiveError
self.pdq.current_frame = self.fn
self.pdq.next_sn = 0

t = (time_to_cycles(now())
- time_to_cycles(frame_setup + trigger_duration + frame_wait))
self.pdq.frame0.set_value(t, self.fn & 1)
self.pdq.frame1.set_value(t, (self.fn & 2) >> 1)
self.pdq.frame2.set_value(t, (self.fn & 4) >> 2)
t += time_to_cycles(frame_setup)
self.pdq.trigger.on(t)
self.pdq.trigger.off(t + time_to_cycles(trigger_duration))

@kernel
def finish(self):
if self.pdq.current_frame != self.fn:
raise FrameActiveError
if self.pdq.next_sn != self.segment_count:
raise FrameActiveError
self.pdq.current_frame = -1
self.pdq.next_sn = -1

def _prepare(self, pdq, fn):
if not self.closed:
raise FrameCloseError
self.pdq = pdq
self.fn = fn

def _invalidate(self):
del self.pdq
del self.fn


class CompoundPDQ2(AutoContext):
parameters = "ids rtio_trigger rtio_frame"

def build(self):
self.trigger = rtio_core.LLRTIOOut(self, channel=self.rtio_trigger)
self.frame0 = rtio_core.LLRTIOOut(self, channel=self.rtio_frame[0])
self.frame1 = rtio_core.LLRTIOOut(self, channel=self.rtio_frame[1])
self.frame2 = rtio_core.LLRTIOOut(self, channel=self.rtio_frame[2])

self.frames = []
self.current_frame = -1
self.next_sn = -1

kernel_attr = "current_frame next_sn"

def create_frame(self):
return _Frame(self.core)

def prepare(self, *frames):
# prevent previous frames and their segments from
# being (incorrectly) used again
for frame in self.frames:
frame._invalidate()

self.frames = list(frames)
for fn, frame in enumerate(frames):
frame._prepare(self, fn)

# TODO: upload to PDQ2 devices
53 changes: 53 additions & 0 deletions artiq/devices/rtio_core.py
Original file line number Diff line number Diff line change
@@ -2,6 +2,59 @@
from artiq.devices.runtime_exceptions import RTIOSequenceError


class LLRTIOOut(AutoContext):
"""Low-level RTIO output driver.
Allows setting RTIO outputs at arbitrary times, without time unit
conversion and without zero-length transition suppression.
This is meant to be used mostly in drivers; consider using
``RTIOOut`` instead.
"""
parameters = "channel"

def build(self):
self.previous_timestamp = int64(0) # in RTIO cycles
self._set_oe()

kernel_attr = "previous_timestamp"

@kernel
def _set_oe(self):
syscall("rtio_oe", self.channel, 1)

@kernel
def set_value(self, t, value):
"""Sets the value of the RTIO channel.
:param t: timestamp in RTIO cycles (64-bit integer).
:param value: value to set at the output.
"""
if t <= self.previous_timestamp:
raise RTIOSequenceError
syscall("rtio_set", t, self.channel, value)
self.previous_timestamp = t

@kernel
def on(self, t):
"""Turns the RTIO channel on.
:param t: timestamp in RTIO cycles (64-bit integer).
"""
self.set_value(t, 1)

@kernel
def off(self, t):
"""Turns the RTIO channel off.
:param t: timestamp in RTIO cycles (64-bit integer).
"""
self.set_value(t, 0)

class _RTIOBase(AutoContext):
parameters = "channel"

55 changes: 29 additions & 26 deletions examples/transport.py
Original file line number Diff line number Diff line change
@@ -5,35 +5,37 @@


class Transport(AutoContext):
parameters = ("bd pmt repeats nbins "
"electrodes transport_data wait_at_stop speed"
parameters = (
"bd pmt repeats nbins "
"electrodes transport_data wait_at_stop speed"
)

def prepare(self, stop):
t = self.data["t"][:stop]*self.speed
u = self.data["u"][:stop]
# start a new frame, selects possible frame id based on rtio_frame
# assignments from coredev
self.transport = self.electrodes.open_frame()
t = self.transport_data["t"][:stop]*self.speed
u = self.transport_data["u"][:stop]
# start a new frame
self.tf = self.electrodes.create_frame()
# interpolates t and u and appends the (t, u) segment to the frame
# adds wait-for-trigger to the first line/spline knot
# will also apply offset and gain calibration data
# stores duration and the fact that this segment needs to be triggered
# both (duration and segment triggering flag) to be retrieved during
# kernel compilation, see transport()
self.transport.append(t, u, trigger=True)
self.tf.append("to_stop",
t, u, trigger=True)
# append the reverse transport (from stop to 0)
# both durations are the same in this case
self.transport.append(t[-1] - t[::-1], u[::-1], trigger=True,
name="from_stop")
# closes the frame with a wait line before jumping back into the jump table
# so that frame signal can be set before the jump
self.tf.append("from_stop",
t[-1] - t[::-1], u[::-1], trigger=True)
# closes the frame with a wait line before jumping back into
# the jump table so that frame signal can be set before the jump
# also mark the frame as closed and prevent further append()ing
self.transport.close()
# packs all in-use frames, distributes them to the sub-devices in
# CompoundPDQ2 and uploads them
self.tf.close()
# user must pass all frames that are going to be used next
# selects possible frame id based on rtio_frame assignments from coredev
# distributes frames to the sub-devices in CompoundPDQ2 and uploads them
# uploading is ARM_DIS, writing, ARM_EN
self.electrodes.prepare()
self.electrodes.prepare(self.tf)

@kernel
def cool(self):
@@ -50,20 +52,19 @@ def transport(self):
# (it would be nice if this could be made zero-duration/not advancing the
# timeline by smart scheduling of this frame-select + trigger + minimum wait
# sequence)
self.transport.begin()
self.tf.begin()
# triggers pdqs to start transport frame segment
# plays the transport waveform from 0 to stop
# delay()s the core by the duration of the waveform segment
self.transport.advance()
self.tf.to_stop.advance()
# leaves the ion in the dark at the transport endpoint
delay(self.wait_at_stop)
# transport back (again: trigger, delay())
# can explicitly reference segment name
# yet segments can only be advance()ed in order
self.transport.advance(name="from_stop")
# segments can only be advance()ed in order
self.tf.from_stop.advance()
# ensures all segments have been advanced() through, must leave pdq
# in a state where the next frame can begin()
self.transport.finish()
self.tf.finish()

@kernel
def detect(self):
@@ -94,8 +95,8 @@ def repeat(self):
self.histogram.append(hist[i])

def scan(self, stops):
self.histogram = []
for s in stops:
self.histogram = []
# non-kernel, calculate waveforms, build frames
# could also be rpc'ed from repeat()
self.prepare(s)
@@ -115,10 +116,12 @@ def scan(self, stops):

with corecom_serial.CoreCom() as com:
coredev = core.Core(com)
exp = PhotonHistogram(
exp = Transport(
core=coredev,
bd=dds_core.DDS(core=coredev, dds_sysclk=1*GHz,
reg_channel=0, rtio_channel=1),
reg_channel=0, rtio_switch=1),
bdd=dds_core.DDS(core=coredev, dds_sysclk=1*GHz,
reg_channel=1, rtio_switch=2),
pmt=rtio_core.RTIOIn(core=coredev, channel=0),
# a compound pdq device that wraps multiple usb devices (looked up
# by usb "serial number"/id) into one
@@ -132,5 +135,5 @@ def scan(self, stops):
nbins=100
)
# scan transport endpoint
stop = range(0, len(exp.transport_data["t"]), 10)
stop = range(10, len(exp.transport_data["t"]), 10)
exp.scan(stop)