forked from M-Labs/artiq
expose machine units to user
This commit is contained in:
parent
944bfafefa
commit
9d6287a6a3
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
||||
|
|
|
@ -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))
|
||||
"""
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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]),
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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": {
|
||||
|
|
Loading…
Reference in New Issue