expose machine units to user

This commit is contained in:
Sebastien Bourdeauducq 2015-07-01 22:22:53 +02:00
parent 944bfafefa
commit 9d6287a6a3
14 changed files with 219 additions and 209 deletions

View File

@ -121,10 +121,9 @@ class Core(AutoDB):
self.first_run = False
@kernel
def get_rtio_time(self):
return cycles_to_time(syscall("rtio_get_counter"))
def get_rtio_counter_mu(self):
return syscall("rtio_get_counter")
@kernel
def break_realtime(self):
t = syscall("rtio_get_counter") + 125000
at(cycles_to_time(t))
at_mu(syscall("rtio_get_counter") + 125000)

View File

@ -37,7 +37,7 @@ class DDSBus(AutoDB):
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()))
syscall("dds_batch_enter", now_mu())
@kernel
def batch_exit(self):
@ -76,12 +76,24 @@ class DDS(AutoDB):
"""
return ftw*self.sysclk/2**32
@portable
def turns_to_pow(self, turns):
"""Returns the phase offset word corresponding to the given phase
in turns."""
return round(turns*2**14)
@portable
def pow_to_turns(self, pow):
"""Returns the phase in turns corresponding to the given phase offset
word."""
return pow/2**14
@kernel
def init(self):
"""Resets and initializes the DDS channel.
The runtime does this for all channels upon core device startup."""
syscall("dds_init", time_to_cycles(now()), self.channel)
syscall("dds_init", now_mu(), self.channel)
@kernel
def set_phase_mode(self, phase_mode):
@ -104,9 +116,11 @@ class DDS(AutoDB):
self.phase_mode = phase_mode
@kernel
def set(self, frequency, phase=0, phase_mode=_PHASE_MODE_DEFAULT):
def set_mu(self, frequency, phase=0, phase_mode=_PHASE_MODE_DEFAULT):
"""Sets the DDS channel to the specified frequency and phase.
This uses machine units (FTW and POW).
: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
@ -114,7 +128,11 @@ class DDS(AutoDB):
"""
if phase_mode == _PHASE_MODE_DEFAULT:
phase_mode = self.phase_mode
syscall("dds_set", now_mu(), self.channel,
frequency, round(phase*2**14), phase_mode)
syscall("dds_set", time_to_cycles(now()), self.channel,
self.frequency_to_ftw(frequency), round(phase*2**14),
phase_mode)
@kernel
def set(self, frequency, phase=0, phase_mode=_PHASE_MODE_DEFAULT):
"""Like ``set_mu``, but uses Hz and turns."""
self.set_mu(self.frequency_to_ftw(frequency),
self.turns_to_pow(phase), phase_mode)

View File

@ -2,47 +2,6 @@ from artiq.language.core import *
from artiq.language.db import *
class LLTTLOut(AutoDB):
"""Low-level RTIO TTL output driver.
Allows setting RTIO TTL outputs at arbitrary times, without time
unit conversion.
This is meant to be used mostly in drivers; consider using
``TTLOut`` instead.
This should be used with output-only channels.
"""
class DBKeys:
core = Device()
channel = Argument()
@kernel
def set_o(self, t, value):
"""Sets the output value of the RTIO channel.
:param t: timestamp in RTIO cycles (64-bit integer).
:param value: value to set at the output.
"""
syscall("ttl_set_o", t, self.channel, value)
@kernel
def on(self, t):
"""Turns the RTIO channel on.
:param t: timestamp in RTIO cycles (64-bit integer).
"""
self.set_o(t, True)
@kernel
def off(self, t):
"""Turns the RTIO channel off.
:param t: timestamp in RTIO cycles (64-bit integer).
"""
self.set_o(t, False)
class TTLOut(AutoDB):
"""RTIO TTL output driver.
@ -61,9 +20,9 @@ class TTLOut(AutoDB):
self.o_previous_timestamp = int64(0)
@kernel
def _set_o(self, o):
syscall("ttl_set_o", time_to_cycles(now()), self.channel, o)
self.o_previous_timestamp = time_to_cycles(now())
def set_o(self, o):
syscall("ttl_set_o", now_mu(), self.channel, o)
self.o_previous_timestamp = now_mu()
@kernel
def sync(self):
@ -74,16 +33,25 @@ class TTLOut(AutoDB):
@kernel
def on(self):
"""Sets the output to a logic high state."""
self._set_o(True)
self.set_o(True)
@kernel
def off(self):
"""Sets the output to a logic low state."""
self._set_o(False)
self.set_o(False)
@kernel
def pulse_mu(self, duration):
"""Pulses the output high for the specified duration
(in machine units)."""
self.on()
delay_mu(duration)
self.off()
@kernel
def pulse(self, duration):
"""Pulses the output high for the specified duration."""
"""Pulses the output high for the specified duration
(in seconds)."""
self.on()
delay(duration)
self.off()
@ -117,21 +85,21 @@ class TTLInOut(AutoDB):
self.i_previous_timestamp = int64(0)
@kernel
def _set_oe(self, oe):
syscall("ttl_set_oe", time_to_cycles(now()), self.channel, oe)
def set_oe(self, oe):
syscall("ttl_set_oe", now_mu(), self.channel, oe)
@kernel
def output(self):
self._set_oe(True)
self.set_oe(True)
@kernel
def input(self):
self._set_oe(False)
self.set_oe(False)
@kernel
def _set_o(self, o):
syscall("ttl_set_o", time_to_cycles(now()), self.channel, o)
self.o_previous_timestamp = time_to_cycles(now())
def set_o(self, o):
syscall("ttl_set_o", now_mu(), self.channel, o)
self.o_previous_timestamp = now_mu()
@kernel
def sync(self):
@ -142,43 +110,78 @@ class TTLInOut(AutoDB):
@kernel
def on(self):
"""Sets the output to a logic high state."""
self._set_o(True)
self.set_o(True)
@kernel
def off(self):
"""Sets the output to a logic low state."""
self._set_o(False)
self.set_o(False)
@kernel
def pulse_mu(self, duration):
"""Pulses the output high for the specified duration
(in machine units)."""
self.on()
delay_mu(duration)
self.off()
@kernel
def pulse(self, duration):
"""Pulses the output high for the specified duration."""
"""Pulses the output high for the specified duration
(in seconds)."""
self.on()
delay(duration)
self.off()
@kernel
def _set_sensitivity(self, value):
syscall("ttl_set_sensitivity", time_to_cycles(now()), self.channel, value)
self.i_previous_timestamp = time_to_cycles(now())
syscall("ttl_set_sensitivity", now_mu(), self.channel, value)
self.i_previous_timestamp = now_mu()
@kernel
def gate_rising_mu(self, duration):
"""Register rising edge events for the specified duration
(in machine units)."""
self._set_sensitivity(1)
delay_mu(duration)
self._set_sensitivity(0)
@kernel
def gate_falling_mu(self, duration):
"""Register falling edge events for the specified duration
(in machine units)."""
self._set_sensitivity(2)
delay_mu(duration)
self._set_sensitivity(0)
@kernel
def gate_both_mu(self, duration):
"""Register both rising and falling edge events for the specified
duration (in machine units)."""
self._set_sensitivity(3)
delay_mu(duration)
self._set_sensitivity(0)
@kernel
def gate_rising(self, duration):
"""Register rising edge events for the specified duration."""
"""Register rising edge events for the specified duration
(in seconds)."""
self._set_sensitivity(1)
delay(duration)
self._set_sensitivity(0)
@kernel
def gate_falling(self, duration):
"""Register falling edge events for the specified duration."""
"""Register falling edge events for the specified duration
(in seconds)."""
self._set_sensitivity(2)
delay(duration)
self._set_sensitivity(0)
@kernel
def gate_both(self, duration):
def gate_both_mu(self, duration):
"""Register both rising and falling edge events for the specified
duration."""
duration (in seconds)."""
self._set_sensitivity(3)
delay(duration)
self._set_sensitivity(0)
@ -200,5 +203,4 @@ class TTLInOut(AutoDB):
If the gate is permanently closed, returns a negative value.
"""
return cycles_to_time(syscall("ttl_get", self.channel,
self.i_previous_timestamp))
return syscall("ttl_get", self.channel, self.i_previous_timestamp)

View File

@ -99,7 +99,7 @@ class _Frame:
def _arm(self):
self.segment_delays = [
time_to_cycles(s.get_duration()*delay_margin_factor, self.core)
seconds_to_mu(s.get_duration()*delay_margin_factor, self.core)
for s in self.segments]
def _invalidate(self):
@ -125,7 +125,8 @@ class _Frame:
if not self.pdq.armed:
raise ArmError
t = time_to_cycles(now()) - time_to_cycles(trigger_duration/2)
call_t = now_mu()
trigger_start_t = call_t - seconds_to_mu(trigger_duration/2)
if self.pdq.current_frame >= 0:
# PDQ is in the middle of a frame. Check it is us.
@ -136,15 +137,16 @@ class _Frame:
# to play our first segment.
self.pdq.current_frame = self.frame_number
self.pdq.next_segment = 0
t2 = t - time_to_cycles(frame_setup)
self.pdq.frame0.set_value(t2, self.frame_number & 1)
self.pdq.frame1.set_value(t2, (self.frame_number & 2) >> 1)
self.pdq.frame2.set_value(t2, (self.frame_number & 4) >> 2)
at_mu(trigger_start_t - seconds_to_mu(frame_setup))
self.pdq.frame0.set_o(bool(self.frame_number & 1))
self.pdq.frame1.set_o(bool((self.frame_number & 2) >> 1))
self.pdq.frame2.set_o(bool((self.frame_number & 4) >> 2))
self.pdq.trigger.on(t)
self.pdq.trigger.off(t + time_to_cycles(trigger_duration))
at_mu(trigger_start_t)
self.pdq.trigger.pulse(trigger_duration)
delay(cycles_to_time(self.segment_delays[self.pdq.next_segment]))
at_mu(call_t)
delay_mu(self.segment_delays[self.pdq.next_segment])
self.pdq.next_segment += 1
# test for end of frame
@ -157,19 +159,15 @@ class CompoundPDQ2(AutoDB):
class DBKeys:
core = Device()
pdq2_devices = Argument()
rtio_trigger = Argument()
rtio_frame = Argument()
trigger_device = Argument()
frame_devices = Argument()
def build(self):
self.pdq2s = [self.dbh.get_device(d) for d in self.pdq2_devices]
self.trigger = ttl.LLTTLOut(
core=self.core, channel=self.rtio_trigger)
self.frame0 = ttl.LLTTLOut(
core=self.core, channel=self.rtio_frame[0])
self.frame1 = ttl.LLTTLOut(
core=self.core, channel=self.rtio_frame[1])
self.frame2 = ttl.LLTTLOut(
core=self.core, channel=self.rtio_frame[2])
self.trigger = self.dbh.get_device(self.trigger_device)
self.frame0 = self.dbh.get_device(self.frame_devices[0])
self.frame1 = self.dbh.get_device(self.frame_devices[1])
self.frame2 = self.dbh.get_device(self.frame_devices[2])
self.frames = []
self.current_frame = -1

View File

@ -28,7 +28,6 @@ class int64(int):
True
>>> a + b
6
"""
pass
@ -62,7 +61,6 @@ def round64(x):
This function is equivalent to ``int64(round(x))`` but, when targeting
static compilation, prevents overflow when the rounded value is too large
to fit in a 32-bit integer.
"""
return int64(round(x))
@ -88,7 +86,6 @@ def kernel(arg):
The decorator takes an optional parameter that defaults to ``core`` and
specifies the name of the attribute to use as core device driver.
"""
if isinstance(arg, str):
def real_decorator(k_function):
@ -117,7 +114,6 @@ def portable(f):
host will be executed on the host (no compilation and execution on the
core device). A decorated function called from a kernel will be executed
on the core device (no RPC).
"""
f.k_function_info = _KernelFunctionInfo(core_name="", k_function=f)
return f
@ -131,9 +127,10 @@ class _DummyTimeManager:
enter_sequential = _not_implemented
enter_parallel = _not_implemented
exit = _not_implemented
take_time_mu = _not_implemented
get_time_mu = _not_implemented
set_time_mu = _not_implemented
take_time = _not_implemented
get_time = _not_implemented
set_time = _not_implemented
_time_manager = _DummyTimeManager()
@ -143,7 +140,6 @@ def set_time_manager(time_manager):
directly inside the Python interpreter. The time manager responds to the
entering and leaving of parallel/sequential blocks, delays, etc. and
provides a time-stamped logging facility for events.
"""
global _time_manager
_time_manager = time_manager
@ -160,7 +156,6 @@ _syscall_manager = _DummySyscallManager()
def set_syscall_manager(syscall_manager):
"""Set the system call manager used for simulating the core device's
runtime in the Python interpreter.
"""
global _syscall_manager
_syscall_manager = syscall_manager
@ -168,7 +163,8 @@ def set_syscall_manager(syscall_manager):
# global namespace for kernels
kernel_globals = ("sequential", "parallel",
"delay", "now", "at", "time_to_cycles", "cycles_to_time",
"delay_mu", "now_mu", "at_mu", "delay",
"seconds_to_mu", "mu_to_seconds",
"syscall", "watchdog")
@ -190,7 +186,6 @@ class _Parallel:
The execution time of a parallel block is the execution time of its longest
statement. A parallel block may contain sequential blocks, which themselves
may contain parallel blocks, etc.
"""
def __enter__(self):
_time_manager.enter_parallel()
@ -200,50 +195,49 @@ class _Parallel:
parallel = _Parallel()
def delay(duration):
"""Increases the RTIO time by the given amount.
def delay_mu(duration):
"""Increases the RTIO time by the given amount (in machine units)."""
_time_manager.take_time_mu(duration)
"""
def now_mu():
"""Retrieves the current RTIO time, in machine units."""
return _time_manager.get_time_mu()
def at_mu(time):
"""Sets the RTIO time to the specified absolute value, in machine units."""
_time_manager.set_time_mu(time)
def delay(duration):
"""Increases the RTIO time by the given amount (in seconds)."""
_time_manager.take_time(duration)
def now():
"""Retrieves the current RTIO time, in seconds.
def seconds_to_mu(seconds, core=None):
"""Converts seconds to the corresponding number of machine units
(RTIO cycles).
"""
return _time_manager.get_time()
def at(time):
"""Sets the RTIO time to the specified absolute value.
"""
_time_manager.set_time(time)
def time_to_cycles(time, core=None):
"""Converts time to the corresponding number of RTIO cycles.
:param time: Time (in seconds) to convert.
:param core: Core device for which to perform the conversion. Specify only
when running in the interpreter (not in kernel).
"""
if core is None:
raise ValueError("Core device must be specified for time conversion")
return round64(time//core.ref_period)
def cycles_to_time(cycles, core=None):
"""Converts RTIO cycles to the corresponding time.
:param time: Cycle count to convert.
:param core: Core device for which to perform the conversion. Specify only
:param seconds: time (in seconds) to convert.
:param core: core device for which to perform the conversion. Specify only
when running in the interpreter (not in kernel).
"""
if core is None:
raise ValueError("Core device must be specified for time conversion")
return cycles*core.ref_period
return round64(seconds//core.ref_period)
def mu_to_seconds(mu, core=None):
"""Converts machine units (RTIO cycles) to seconds.
:param mu: cycle count to convert.
:param core: core device for which to perform the conversion. Specify only
when running in the interpreter (not in kernel).
"""
if core is None:
raise ValueError("Core device must be specified for time conversion")
return mu*core.ref_period
def syscall(*args):

View File

@ -7,13 +7,15 @@ from artiq.sim import time
class Core(AutoDB):
_level = 0
def build(self):
self.ref_period = 1
self._level = 0
def run(self, k_function, k_args, k_kwargs):
Core._level += 1
self._level += 1
r = k_function(*k_args, **k_kwargs)
Core._level -= 1
if Core._level == 0:
self._level -= 1
if self._level == 0:
print(time.manager.format_timeline())
return r

View File

@ -30,37 +30,39 @@ class Manager:
self.timeline = []
def enter_sequential(self):
new_context = SequentialTimeContext(self.get_time())
new_context = SequentialTimeContext(self.get_time_mu())
self.stack.append(new_context)
def enter_parallel(self):
new_context = ParallelTimeContext(self.get_time())
new_context = ParallelTimeContext(self.get_time_mu())
self.stack.append(new_context)
def exit(self):
old_context = self.stack.pop()
self.take_time(old_context.block_duration)
def take_time(self, duration):
def take_time_mu(self, duration):
self.stack[-1].take_time(duration)
def get_time(self):
def get_time_mu(self):
return self.stack[-1].current_time
def set_time(self, t):
dt = t - self.get_time()
def set_time_mu(self, t):
dt = t - self.get_time_mu()
if dt < 0*s:
raise ValueError("Attempted to go back in time")
self.take_time(dt)
take_time = take_time_mu
def event(self, description):
self.timeline.append((self.get_time(), description))
self.timeline.append((self.get_time_mu(), description))
def format_timeline(self):
r = ""
prev_time = 0*s
for time, description in sorted(self.timeline, key=itemgetter(0)):
r += "@{:10} (+{:10}) ".format(str(time), str(time-prev_time))
r += "@{:.9f} (+{:.9f}) ".format(time, time-prev_time)
for item in description:
r += "{:16}".format(str(item))
r += "\n"

View File

@ -74,17 +74,20 @@ class _PulseLogger(AutoDB):
self.first_timestamp = t
self.output_list.append((self.name, t-self.first_timestamp, l, f))
def int_usec(self, mu):
return round(mu_to_seconds(mu, self.core)*1000000)
def on(self, t, f):
self._append(t, True, f)
self._append(self.int_usec(t), True, f)
def off(self, t):
self._append(t, False, 0)
self._append(self.int_usec(t), False, 0)
@kernel
def pulse(self, f, duration):
self.on(round(now()*1000000000), f)
self.on(now_mu(), f)
delay(duration)
self.off(round(now()*1000000000))
self.off(now_mu())
class _Pulses(AutoDB):
@ -288,9 +291,9 @@ class _RTIOSequenceError(AutoDB):
@kernel
def run(self):
t = now()
t = now_mu()
self.o.pulse(25*us)
at(t)
at_mu(t)
self.o.pulse(25*us)

View File

@ -10,7 +10,7 @@ optimize_in = """
def run():
dds_sysclk = Fraction(1000000000, 1)
n = time_to_cycles((1.2345 * Fraction(1, 1000000000)))
n = seconds_to_mu((1.2345 * Fraction(1, 1000000000)))
with sequential:
frequency = 345 * Fraction(1000000, 1)
frequency_to_ftw_return = int((((2 ** 32) * frequency) / dds_sysclk))
@ -19,7 +19,7 @@ def run():
ftw2 = ftw
ftw_to_frequency_return = ((ftw2 * dds_sysclk) / (2 ** 32))
f = ftw_to_frequency_return
phi = ((1000 * cycles_to_time(n)) * f)
phi = ((1000 * mu_to_seconds(n)) * f)
do_something(int(phi))
"""

View File

@ -28,13 +28,14 @@ def _get_duration(stmt):
return -1
elif isinstance(stmt, ast.Call):
name = stmt.func.id
if name == "delay":
assert(name != "delay")
if name == "delay_mu":
try:
da = eval_constant(stmt.args[0])
except NotConstant:
da = -1
return da
elif name == "at":
elif name == "at_mu":
return -1
else:
return 0
@ -69,7 +70,7 @@ def _interleave_timelines(timelines):
ref_stmt = stmt.stmt
delay_stmt = ast.copy_location(
ast.Expr(ast.Call(
func=ast.Name("delay", ast.Load()),
func=ast.Name("delay_mu", ast.Load()),
args=[value_to_ast(dt)],
keywords=[], starargs=[], kwargs=[])),
ref_stmt)

View File

@ -1,15 +1,15 @@
"""
This transform implements time management functions (delay/now/at)
This transform implements time management functions (delay_mu/now_mu/at_mu)
using an accumulator 'now' and simple replacement rules:
delay(t) -> now += t
now() -> now
at(t) -> now = t
delay_mu(t) -> now += t
now_mu() -> now
at_mu(t) -> now = t
Time parameters must be quantized to integers before running this transform.
The function delay(), that uses seconds, must be lowered to delay_mu() before
invoking this transform.
The accumulator is initialized to an int64 value at the beginning of the
output function.
"""
import ast
@ -17,7 +17,7 @@ import ast
class _TimeLowerer(ast.NodeTransformer):
def visit_Call(self, node):
if node.func.id == "now":
if node.func.id == "now_mu":
return ast.copy_location(ast.Name("now", ast.Load()), node)
else:
self.generic_visit(node)
@ -27,13 +27,13 @@ class _TimeLowerer(ast.NodeTransformer):
r = node
if isinstance(node.value, ast.Call):
funcname = node.value.func.id
if funcname == "delay":
if funcname == "delay_mu":
r = ast.copy_location(
ast.AugAssign(target=ast.Name("now", ast.Store()),
op=ast.Add(),
value=node.value.args[0]),
node)
elif funcname == "at":
elif funcname == "at_mu":
r = ast.copy_location(
ast.Assign(targets=[ast.Name("now", ast.Store())],
value=node.value.args[0]),

View File

@ -1,12 +1,12 @@
"""
This transform turns calls to delay/now/at that use non-integer time
expressed in seconds into calls that use int64 time expressed in multiples of
ref_period.
This transform turns calls to delay() that use non-integer time
expressed in seconds into calls to delay_mu() that use int64 time
expressed in multiples of ref_period.
It does so by inserting multiplication/division/rounding operations around
those calls.
The time_to_cycles and cycles_to_time core language functions are also
The seconds_to_mu and mu_to_seconds core language functions are also
implemented here, as well as watchdog to syscall conversion.
"""
@ -15,14 +15,7 @@ import ast
from artiq.transforms.tools import value_to_ast
def _call_now(node):
return ast.copy_location(
ast.Call(func=ast.Name("now", ast.Load()),
args=[], keywords=[], starargs=[], kwargs=[]),
node)
def _time_to_cycles(ref_period, node):
def _seconds_to_mu(ref_period, node):
divided = ast.copy_location(
ast.BinOp(left=node,
op=ast.Div(),
@ -35,7 +28,7 @@ def _time_to_cycles(ref_period, node):
divided)
def _cycles_to_time(ref_period, node):
def _mu_to_seconds(ref_period, node):
return ast.copy_location(
ast.BinOp(left=node,
op=ast.Mult(),
@ -50,29 +43,22 @@ class _TimeQuantizer(ast.NodeTransformer):
def visit_Call(self, node):
funcname = node.func.id
if funcname == "now":
return _cycles_to_time(self.ref_period, _call_now(node))
elif funcname == "delay" or funcname == "at":
if funcname == "delay":
node.func.id = "delay_mu"
if (isinstance(node.args[0], ast.Call)
and node.args[0].func.id == "cycles_to_time"):
and node.args[0].func.id == "mu_to_seconds"):
# optimize:
# delay/at(cycles_to_time(x)) -> delay/at(x)
# delay(mu_to_seconds(x)) -> delay_mu(x)
node.args[0] = self.visit(node.args[0].args[0])
else:
node.args[0] = _time_to_cycles(self.ref_period,
node.args[0] = _seconds_to_mu(self.ref_period,
self.visit(node.args[0]))
return node
elif funcname == "time_to_cycles":
if (isinstance(node.args[0], ast.Call)
and node.args[0].func.id == "now"):
# optimize:
# time_to_cycles(now()) -> now()
return _call_now(node)
else:
return _time_to_cycles(self.ref_period,
elif funcname == "seconds_to_mu":
return _seconds_to_mu(self.ref_period,
self.visit(node.args[0]))
elif funcname == "cycles_to_time":
return _cycles_to_time(self.ref_period,
elif funcname == "mu_to_seconds":
return _mu_to_seconds(self.ref_period,
self.visit(node.args[0]))
else:
self.generic_visit(node)
@ -123,6 +109,5 @@ class _TimeQuantizer(ast.NodeTransformer):
return node
def quantize_time(func_def, ref_period):
_TimeQuantizer(ref_period).visit(func_def)

View File

@ -6,8 +6,9 @@ from artiq.language import units
embeddable_funcs = (
core_language.delay, core_language.at, core_language.now,
core_language.time_to_cycles, core_language.cycles_to_time,
core_language.delay_mu, core_language.at_mu, core_language.now_mu,
core_language.delay,
core_language.seconds_to_mu, core_language.mu_to_seconds,
core_language.syscall, core_language.watchdog,
range, bool, int, float, round, len,
core_language.int64, core_language.round64,
@ -92,7 +93,7 @@ def eval_constant(node):
_replaceable_funcs = {
"bool", "int", "float", "round",
"int64", "round64", "Fraction",
"time_to_cycles", "cycles_to_time"
"seconds_to_mu", "mu_to_seconds"
}

View File

@ -43,6 +43,12 @@
"class": "TTLOut",
"arguments": {"channel": 4}
},
"ttl3": {
"type": "local",
"module": "artiq.coredevice.ttl",
"class": "TTLOut",
"arguments": {"channel": 5}
},
"led": {
"type": "local",
"module": "artiq.coredevice.ttl",
@ -113,10 +119,9 @@
"class": "CompoundPDQ2",
"arguments": {
"pdq2_devices": ["qc_q1_0", "qc_q1_1", "qc_q1_2", "qc_q1_3"],
"rtio_trigger": 7,
"rtio_frame": [2, 3, 4]
},
"comment": "Uses ttl 0, 1, 2 and 5"
"trigger_device": "ttl3",
"frame_devices": ["ttl0", "ttl1", "ttl2"]
}
},
"lda": {