From 9d6287a6a362b63dd880d8d7705a94c9f323bde1 Mon Sep 17 00:00:00 2001 From: Sebastien Bourdeauducq Date: Wed, 1 Jul 2015 22:22:53 +0200 Subject: [PATCH] expose machine units to user --- artiq/coredevice/core.py | 7 +- artiq/coredevice/dds.py | 30 +++++-- artiq/coredevice/ttl.py | 132 +++++++++++++++--------------- artiq/devices/pdq2/mediator.py | 36 ++++---- artiq/language/core.py | 84 +++++++++---------- artiq/sim/devices.py | 10 ++- artiq/sim/time.py | 18 ++-- artiq/test/full_stack.py | 15 ++-- artiq/test/transforms.py | 4 +- artiq/transforms/interleave.py | 7 +- artiq/transforms/lower_time.py | 18 ++-- artiq/transforms/quantize_time.py | 47 ++++------- artiq/transforms/tools.py | 7 +- examples/master/ddb.pyon | 13 ++- 14 files changed, 219 insertions(+), 209 deletions(-) diff --git a/artiq/coredevice/core.py b/artiq/coredevice/core.py index 7f7549115..6923b8ce9 100644 --- a/artiq/coredevice/core.py +++ b/artiq/coredevice/core.py @@ -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) diff --git a/artiq/coredevice/dds.py b/artiq/coredevice/dds.py index f016b4cc0..09c4cb78b 100644 --- a/artiq/coredevice/dds.py +++ b/artiq/coredevice/dds.py @@ -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) diff --git a/artiq/coredevice/ttl.py b/artiq/coredevice/ttl.py index 359b9b590..9a363b83d 100644 --- a/artiq/coredevice/ttl.py +++ b/artiq/coredevice/ttl.py @@ -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) diff --git a/artiq/devices/pdq2/mediator.py b/artiq/devices/pdq2/mediator.py index 63d3d7b72..f3c536219 100644 --- a/artiq/devices/pdq2/mediator.py +++ b/artiq/devices/pdq2/mediator.py @@ -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 diff --git a/artiq/language/core.py b/artiq/language/core.py index 424fd07cc..340ea2374 100644 --- a/artiq/language/core.py +++ b/artiq/language/core.py @@ -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): diff --git a/artiq/sim/devices.py b/artiq/sim/devices.py index c5900a405..25fc934f5 100644 --- a/artiq/sim/devices.py +++ b/artiq/sim/devices.py @@ -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 diff --git a/artiq/sim/time.py b/artiq/sim/time.py index 3ae01a073..72cd3313f 100644 --- a/artiq/sim/time.py +++ b/artiq/sim/time.py @@ -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" diff --git a/artiq/test/full_stack.py b/artiq/test/full_stack.py index a75a73fcd..ad819827c 100644 --- a/artiq/test/full_stack.py +++ b/artiq/test/full_stack.py @@ -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) diff --git a/artiq/test/transforms.py b/artiq/test/transforms.py index 31d77bd63..75af1b2dc 100644 --- a/artiq/test/transforms.py +++ b/artiq/test/transforms.py @@ -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)) """ diff --git a/artiq/transforms/interleave.py b/artiq/transforms/interleave.py index 0a1b794a9..8aa9df838 100644 --- a/artiq/transforms/interleave.py +++ b/artiq/transforms/interleave.py @@ -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) diff --git a/artiq/transforms/lower_time.py b/artiq/transforms/lower_time.py index c2c779b84..5b3df0245 100644 --- a/artiq/transforms/lower_time.py +++ b/artiq/transforms/lower_time.py @@ -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]), diff --git a/artiq/transforms/quantize_time.py b/artiq/transforms/quantize_time.py index 835dcd780..aad75069a 100644 --- a/artiq/transforms/quantize_time.py +++ b/artiq/transforms/quantize_time.py @@ -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, - self.visit(node.args[0])) + 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) diff --git a/artiq/transforms/tools.py b/artiq/transforms/tools.py index 1b68fd0f3..97d596d2b 100644 --- a/artiq/transforms/tools.py +++ b/artiq/transforms/tools.py @@ -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" } diff --git a/examples/master/ddb.pyon b/examples/master/ddb.pyon index ff6772fcb..81903fca5 100644 --- a/examples/master/ddb.pyon +++ b/examples/master/ddb.pyon @@ -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": {