mirror of https://github.com/m-labs/artiq.git
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
|
self.first_run = False
|
||||||
|
|
||||||
@kernel
|
@kernel
|
||||||
def get_rtio_time(self):
|
def get_rtio_counter_mu(self):
|
||||||
return cycles_to_time(syscall("rtio_get_counter"))
|
return syscall("rtio_get_counter")
|
||||||
|
|
||||||
@kernel
|
@kernel
|
||||||
def break_realtime(self):
|
def break_realtime(self):
|
||||||
t = syscall("rtio_get_counter") + 125000
|
at_mu(syscall("rtio_get_counter") + 125000)
|
||||||
at(cycles_to_time(t))
|
|
||||||
|
|
|
@ -37,7 +37,7 @@ class DDSBus(AutoDB):
|
||||||
def batch_enter(self):
|
def batch_enter(self):
|
||||||
"""Starts a DDS command batch. All DDS commands are buffered
|
"""Starts a DDS command batch. All DDS commands are buffered
|
||||||
after this call, until ``batch_exit`` is called."""
|
after this call, until ``batch_exit`` is called."""
|
||||||
syscall("dds_batch_enter", time_to_cycles(now()))
|
syscall("dds_batch_enter", now_mu())
|
||||||
|
|
||||||
@kernel
|
@kernel
|
||||||
def batch_exit(self):
|
def batch_exit(self):
|
||||||
|
@ -76,12 +76,24 @@ class DDS(AutoDB):
|
||||||
"""
|
"""
|
||||||
return ftw*self.sysclk/2**32
|
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
|
@kernel
|
||||||
def init(self):
|
def init(self):
|
||||||
"""Resets and initializes the DDS channel.
|
"""Resets and initializes the DDS channel.
|
||||||
|
|
||||||
The runtime does this for all channels upon core device startup."""
|
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
|
@kernel
|
||||||
def set_phase_mode(self, phase_mode):
|
def set_phase_mode(self, phase_mode):
|
||||||
|
@ -104,9 +116,11 @@ class DDS(AutoDB):
|
||||||
self.phase_mode = phase_mode
|
self.phase_mode = phase_mode
|
||||||
|
|
||||||
@kernel
|
@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.
|
"""Sets the DDS channel to the specified frequency and phase.
|
||||||
|
|
||||||
|
This uses machine units (FTW and POW).
|
||||||
|
|
||||||
:param frequency: frequency to generate.
|
:param frequency: frequency to generate.
|
||||||
:param phase: adds an offset, in turns, to the phase.
|
:param phase: adds an offset, in turns, to the phase.
|
||||||
:param phase_mode: if specified, overrides the default phase mode set
|
:param phase_mode: if specified, overrides the default phase mode set
|
||||||
|
@ -114,7 +128,11 @@ class DDS(AutoDB):
|
||||||
"""
|
"""
|
||||||
if phase_mode == _PHASE_MODE_DEFAULT:
|
if phase_mode == _PHASE_MODE_DEFAULT:
|
||||||
phase_mode = self.phase_mode
|
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,
|
@kernel
|
||||||
self.frequency_to_ftw(frequency), round(phase*2**14),
|
def set(self, frequency, phase=0, phase_mode=_PHASE_MODE_DEFAULT):
|
||||||
phase_mode)
|
"""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 *
|
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):
|
class TTLOut(AutoDB):
|
||||||
"""RTIO TTL output driver.
|
"""RTIO TTL output driver.
|
||||||
|
|
||||||
|
@ -61,9 +20,9 @@ class TTLOut(AutoDB):
|
||||||
self.o_previous_timestamp = int64(0)
|
self.o_previous_timestamp = int64(0)
|
||||||
|
|
||||||
@kernel
|
@kernel
|
||||||
def _set_o(self, o):
|
def set_o(self, o):
|
||||||
syscall("ttl_set_o", time_to_cycles(now()), self.channel, o)
|
syscall("ttl_set_o", now_mu(), self.channel, o)
|
||||||
self.o_previous_timestamp = time_to_cycles(now())
|
self.o_previous_timestamp = now_mu()
|
||||||
|
|
||||||
@kernel
|
@kernel
|
||||||
def sync(self):
|
def sync(self):
|
||||||
|
@ -74,16 +33,25 @@ class TTLOut(AutoDB):
|
||||||
@kernel
|
@kernel
|
||||||
def on(self):
|
def on(self):
|
||||||
"""Sets the output to a logic high state."""
|
"""Sets the output to a logic high state."""
|
||||||
self._set_o(True)
|
self.set_o(True)
|
||||||
|
|
||||||
@kernel
|
@kernel
|
||||||
def off(self):
|
def off(self):
|
||||||
"""Sets the output to a logic low state."""
|
"""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
|
@kernel
|
||||||
def pulse(self, duration):
|
def pulse(self, duration):
|
||||||
"""Pulses the output high for the specified duration."""
|
"""Pulses the output high for the specified duration
|
||||||
|
(in seconds)."""
|
||||||
self.on()
|
self.on()
|
||||||
delay(duration)
|
delay(duration)
|
||||||
self.off()
|
self.off()
|
||||||
|
@ -117,21 +85,21 @@ class TTLInOut(AutoDB):
|
||||||
self.i_previous_timestamp = int64(0)
|
self.i_previous_timestamp = int64(0)
|
||||||
|
|
||||||
@kernel
|
@kernel
|
||||||
def _set_oe(self, oe):
|
def set_oe(self, oe):
|
||||||
syscall("ttl_set_oe", time_to_cycles(now()), self.channel, oe)
|
syscall("ttl_set_oe", now_mu(), self.channel, oe)
|
||||||
|
|
||||||
@kernel
|
@kernel
|
||||||
def output(self):
|
def output(self):
|
||||||
self._set_oe(True)
|
self.set_oe(True)
|
||||||
|
|
||||||
@kernel
|
@kernel
|
||||||
def input(self):
|
def input(self):
|
||||||
self._set_oe(False)
|
self.set_oe(False)
|
||||||
|
|
||||||
@kernel
|
@kernel
|
||||||
def _set_o(self, o):
|
def set_o(self, o):
|
||||||
syscall("ttl_set_o", time_to_cycles(now()), self.channel, o)
|
syscall("ttl_set_o", now_mu(), self.channel, o)
|
||||||
self.o_previous_timestamp = time_to_cycles(now())
|
self.o_previous_timestamp = now_mu()
|
||||||
|
|
||||||
@kernel
|
@kernel
|
||||||
def sync(self):
|
def sync(self):
|
||||||
|
@ -142,43 +110,78 @@ class TTLInOut(AutoDB):
|
||||||
@kernel
|
@kernel
|
||||||
def on(self):
|
def on(self):
|
||||||
"""Sets the output to a logic high state."""
|
"""Sets the output to a logic high state."""
|
||||||
self._set_o(True)
|
self.set_o(True)
|
||||||
|
|
||||||
@kernel
|
@kernel
|
||||||
def off(self):
|
def off(self):
|
||||||
"""Sets the output to a logic low state."""
|
"""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
|
@kernel
|
||||||
def pulse(self, duration):
|
def pulse(self, duration):
|
||||||
"""Pulses the output high for the specified duration."""
|
"""Pulses the output high for the specified duration
|
||||||
|
(in seconds)."""
|
||||||
self.on()
|
self.on()
|
||||||
delay(duration)
|
delay(duration)
|
||||||
self.off()
|
self.off()
|
||||||
|
|
||||||
@kernel
|
@kernel
|
||||||
def _set_sensitivity(self, value):
|
def _set_sensitivity(self, value):
|
||||||
syscall("ttl_set_sensitivity", time_to_cycles(now()), self.channel, value)
|
syscall("ttl_set_sensitivity", now_mu(), self.channel, value)
|
||||||
self.i_previous_timestamp = time_to_cycles(now())
|
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
|
@kernel
|
||||||
def gate_rising(self, duration):
|
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)
|
self._set_sensitivity(1)
|
||||||
delay(duration)
|
delay(duration)
|
||||||
self._set_sensitivity(0)
|
self._set_sensitivity(0)
|
||||||
|
|
||||||
@kernel
|
@kernel
|
||||||
def gate_falling(self, duration):
|
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)
|
self._set_sensitivity(2)
|
||||||
delay(duration)
|
delay(duration)
|
||||||
self._set_sensitivity(0)
|
self._set_sensitivity(0)
|
||||||
|
|
||||||
@kernel
|
@kernel
|
||||||
def gate_both(self, duration):
|
def gate_both_mu(self, duration):
|
||||||
"""Register both rising and falling edge events for the specified
|
"""Register both rising and falling edge events for the specified
|
||||||
duration."""
|
duration (in seconds)."""
|
||||||
self._set_sensitivity(3)
|
self._set_sensitivity(3)
|
||||||
delay(duration)
|
delay(duration)
|
||||||
self._set_sensitivity(0)
|
self._set_sensitivity(0)
|
||||||
|
@ -200,5 +203,4 @@ class TTLInOut(AutoDB):
|
||||||
|
|
||||||
If the gate is permanently closed, returns a negative value.
|
If the gate is permanently closed, returns a negative value.
|
||||||
"""
|
"""
|
||||||
return cycles_to_time(syscall("ttl_get", self.channel,
|
return syscall("ttl_get", self.channel, self.i_previous_timestamp)
|
||||||
self.i_previous_timestamp))
|
|
||||||
|
|
|
@ -99,7 +99,7 @@ class _Frame:
|
||||||
|
|
||||||
def _arm(self):
|
def _arm(self):
|
||||||
self.segment_delays = [
|
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]
|
for s in self.segments]
|
||||||
|
|
||||||
def _invalidate(self):
|
def _invalidate(self):
|
||||||
|
@ -125,7 +125,8 @@ class _Frame:
|
||||||
if not self.pdq.armed:
|
if not self.pdq.armed:
|
||||||
raise ArmError
|
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:
|
if self.pdq.current_frame >= 0:
|
||||||
# PDQ is in the middle of a frame. Check it is us.
|
# PDQ is in the middle of a frame. Check it is us.
|
||||||
|
@ -136,15 +137,16 @@ class _Frame:
|
||||||
# to play our first segment.
|
# to play our first segment.
|
||||||
self.pdq.current_frame = self.frame_number
|
self.pdq.current_frame = self.frame_number
|
||||||
self.pdq.next_segment = 0
|
self.pdq.next_segment = 0
|
||||||
t2 = t - time_to_cycles(frame_setup)
|
at_mu(trigger_start_t - seconds_to_mu(frame_setup))
|
||||||
self.pdq.frame0.set_value(t2, self.frame_number & 1)
|
self.pdq.frame0.set_o(bool(self.frame_number & 1))
|
||||||
self.pdq.frame1.set_value(t2, (self.frame_number & 2) >> 1)
|
self.pdq.frame1.set_o(bool((self.frame_number & 2) >> 1))
|
||||||
self.pdq.frame2.set_value(t2, (self.frame_number & 4) >> 2)
|
self.pdq.frame2.set_o(bool((self.frame_number & 4) >> 2))
|
||||||
|
|
||||||
self.pdq.trigger.on(t)
|
at_mu(trigger_start_t)
|
||||||
self.pdq.trigger.off(t + time_to_cycles(trigger_duration))
|
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
|
self.pdq.next_segment += 1
|
||||||
|
|
||||||
# test for end of frame
|
# test for end of frame
|
||||||
|
@ -157,19 +159,15 @@ class CompoundPDQ2(AutoDB):
|
||||||
class DBKeys:
|
class DBKeys:
|
||||||
core = Device()
|
core = Device()
|
||||||
pdq2_devices = Argument()
|
pdq2_devices = Argument()
|
||||||
rtio_trigger = Argument()
|
trigger_device = Argument()
|
||||||
rtio_frame = Argument()
|
frame_devices = Argument()
|
||||||
|
|
||||||
def build(self):
|
def build(self):
|
||||||
self.pdq2s = [self.dbh.get_device(d) for d in self.pdq2_devices]
|
self.pdq2s = [self.dbh.get_device(d) for d in self.pdq2_devices]
|
||||||
self.trigger = ttl.LLTTLOut(
|
self.trigger = self.dbh.get_device(self.trigger_device)
|
||||||
core=self.core, channel=self.rtio_trigger)
|
self.frame0 = self.dbh.get_device(self.frame_devices[0])
|
||||||
self.frame0 = ttl.LLTTLOut(
|
self.frame1 = self.dbh.get_device(self.frame_devices[1])
|
||||||
core=self.core, channel=self.rtio_frame[0])
|
self.frame2 = self.dbh.get_device(self.frame_devices[2])
|
||||||
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.frames = []
|
self.frames = []
|
||||||
self.current_frame = -1
|
self.current_frame = -1
|
||||||
|
|
|
@ -28,7 +28,6 @@ class int64(int):
|
||||||
True
|
True
|
||||||
>>> a + b
|
>>> a + b
|
||||||
6
|
6
|
||||||
|
|
||||||
"""
|
"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
@ -62,7 +61,6 @@ def round64(x):
|
||||||
This function is equivalent to ``int64(round(x))`` but, when targeting
|
This function is equivalent to ``int64(round(x))`` but, when targeting
|
||||||
static compilation, prevents overflow when the rounded value is too large
|
static compilation, prevents overflow when the rounded value is too large
|
||||||
to fit in a 32-bit integer.
|
to fit in a 32-bit integer.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
return int64(round(x))
|
return int64(round(x))
|
||||||
|
|
||||||
|
@ -88,7 +86,6 @@ def kernel(arg):
|
||||||
|
|
||||||
The decorator takes an optional parameter that defaults to ``core`` and
|
The decorator takes an optional parameter that defaults to ``core`` and
|
||||||
specifies the name of the attribute to use as core device driver.
|
specifies the name of the attribute to use as core device driver.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
if isinstance(arg, str):
|
if isinstance(arg, str):
|
||||||
def real_decorator(k_function):
|
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
|
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
|
core device). A decorated function called from a kernel will be executed
|
||||||
on the core device (no RPC).
|
on the core device (no RPC).
|
||||||
|
|
||||||
"""
|
"""
|
||||||
f.k_function_info = _KernelFunctionInfo(core_name="", k_function=f)
|
f.k_function_info = _KernelFunctionInfo(core_name="", k_function=f)
|
||||||
return f
|
return f
|
||||||
|
@ -131,9 +127,10 @@ class _DummyTimeManager:
|
||||||
enter_sequential = _not_implemented
|
enter_sequential = _not_implemented
|
||||||
enter_parallel = _not_implemented
|
enter_parallel = _not_implemented
|
||||||
exit = _not_implemented
|
exit = _not_implemented
|
||||||
|
take_time_mu = _not_implemented
|
||||||
|
get_time_mu = _not_implemented
|
||||||
|
set_time_mu = _not_implemented
|
||||||
take_time = _not_implemented
|
take_time = _not_implemented
|
||||||
get_time = _not_implemented
|
|
||||||
set_time = _not_implemented
|
|
||||||
|
|
||||||
_time_manager = _DummyTimeManager()
|
_time_manager = _DummyTimeManager()
|
||||||
|
|
||||||
|
@ -143,7 +140,6 @@ def set_time_manager(time_manager):
|
||||||
directly inside the Python interpreter. The time manager responds to the
|
directly inside the Python interpreter. The time manager responds to the
|
||||||
entering and leaving of parallel/sequential blocks, delays, etc. and
|
entering and leaving of parallel/sequential blocks, delays, etc. and
|
||||||
provides a time-stamped logging facility for events.
|
provides a time-stamped logging facility for events.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
global _time_manager
|
global _time_manager
|
||||||
_time_manager = time_manager
|
_time_manager = time_manager
|
||||||
|
@ -160,7 +156,6 @@ _syscall_manager = _DummySyscallManager()
|
||||||
def set_syscall_manager(syscall_manager):
|
def set_syscall_manager(syscall_manager):
|
||||||
"""Set the system call manager used for simulating the core device's
|
"""Set the system call manager used for simulating the core device's
|
||||||
runtime in the Python interpreter.
|
runtime in the Python interpreter.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
global _syscall_manager
|
global _syscall_manager
|
||||||
_syscall_manager = syscall_manager
|
_syscall_manager = syscall_manager
|
||||||
|
@ -168,7 +163,8 @@ def set_syscall_manager(syscall_manager):
|
||||||
# global namespace for kernels
|
# global namespace for kernels
|
||||||
|
|
||||||
kernel_globals = ("sequential", "parallel",
|
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")
|
"syscall", "watchdog")
|
||||||
|
|
||||||
|
|
||||||
|
@ -190,7 +186,6 @@ class _Parallel:
|
||||||
The execution time of a parallel block is the execution time of its longest
|
The execution time of a parallel block is the execution time of its longest
|
||||||
statement. A parallel block may contain sequential blocks, which themselves
|
statement. A parallel block may contain sequential blocks, which themselves
|
||||||
may contain parallel blocks, etc.
|
may contain parallel blocks, etc.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
def __enter__(self):
|
def __enter__(self):
|
||||||
_time_manager.enter_parallel()
|
_time_manager.enter_parallel()
|
||||||
|
@ -200,50 +195,49 @@ class _Parallel:
|
||||||
parallel = _Parallel()
|
parallel = _Parallel()
|
||||||
|
|
||||||
|
|
||||||
def delay(duration):
|
def delay_mu(duration):
|
||||||
"""Increases the RTIO time by the given amount.
|
"""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)
|
_time_manager.take_time(duration)
|
||||||
|
|
||||||
|
|
||||||
def now():
|
def seconds_to_mu(seconds, core=None):
|
||||||
"""Retrieves the current RTIO time, in seconds.
|
"""Converts seconds to the corresponding number of machine units
|
||||||
|
(RTIO cycles).
|
||||||
|
|
||||||
"""
|
:param seconds: time (in seconds) to convert.
|
||||||
return _time_manager.get_time()
|
:param core: core device for which to perform the conversion. Specify only
|
||||||
|
|
||||||
|
|
||||||
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
|
|
||||||
when running in the interpreter (not in kernel).
|
when running in the interpreter (not in kernel).
|
||||||
"""
|
"""
|
||||||
if core is None:
|
if core is None:
|
||||||
raise ValueError("Core device must be specified for time conversion")
|
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):
|
def syscall(*args):
|
||||||
|
|
|
@ -7,13 +7,15 @@ from artiq.sim import time
|
||||||
|
|
||||||
|
|
||||||
class Core(AutoDB):
|
class Core(AutoDB):
|
||||||
_level = 0
|
def build(self):
|
||||||
|
self.ref_period = 1
|
||||||
|
self._level = 0
|
||||||
|
|
||||||
def run(self, k_function, k_args, k_kwargs):
|
def run(self, k_function, k_args, k_kwargs):
|
||||||
Core._level += 1
|
self._level += 1
|
||||||
r = k_function(*k_args, **k_kwargs)
|
r = k_function(*k_args, **k_kwargs)
|
||||||
Core._level -= 1
|
self._level -= 1
|
||||||
if Core._level == 0:
|
if self._level == 0:
|
||||||
print(time.manager.format_timeline())
|
print(time.manager.format_timeline())
|
||||||
return r
|
return r
|
||||||
|
|
||||||
|
|
|
@ -30,37 +30,39 @@ class Manager:
|
||||||
self.timeline = []
|
self.timeline = []
|
||||||
|
|
||||||
def enter_sequential(self):
|
def enter_sequential(self):
|
||||||
new_context = SequentialTimeContext(self.get_time())
|
new_context = SequentialTimeContext(self.get_time_mu())
|
||||||
self.stack.append(new_context)
|
self.stack.append(new_context)
|
||||||
|
|
||||||
def enter_parallel(self):
|
def enter_parallel(self):
|
||||||
new_context = ParallelTimeContext(self.get_time())
|
new_context = ParallelTimeContext(self.get_time_mu())
|
||||||
self.stack.append(new_context)
|
self.stack.append(new_context)
|
||||||
|
|
||||||
def exit(self):
|
def exit(self):
|
||||||
old_context = self.stack.pop()
|
old_context = self.stack.pop()
|
||||||
self.take_time(old_context.block_duration)
|
self.take_time(old_context.block_duration)
|
||||||
|
|
||||||
def take_time(self, duration):
|
def take_time_mu(self, duration):
|
||||||
self.stack[-1].take_time(duration)
|
self.stack[-1].take_time(duration)
|
||||||
|
|
||||||
def get_time(self):
|
def get_time_mu(self):
|
||||||
return self.stack[-1].current_time
|
return self.stack[-1].current_time
|
||||||
|
|
||||||
def set_time(self, t):
|
def set_time_mu(self, t):
|
||||||
dt = t - self.get_time()
|
dt = t - self.get_time_mu()
|
||||||
if dt < 0*s:
|
if dt < 0*s:
|
||||||
raise ValueError("Attempted to go back in time")
|
raise ValueError("Attempted to go back in time")
|
||||||
self.take_time(dt)
|
self.take_time(dt)
|
||||||
|
|
||||||
|
take_time = take_time_mu
|
||||||
|
|
||||||
def event(self, description):
|
def event(self, description):
|
||||||
self.timeline.append((self.get_time(), description))
|
self.timeline.append((self.get_time_mu(), description))
|
||||||
|
|
||||||
def format_timeline(self):
|
def format_timeline(self):
|
||||||
r = ""
|
r = ""
|
||||||
prev_time = 0*s
|
prev_time = 0*s
|
||||||
for time, description in sorted(self.timeline, key=itemgetter(0)):
|
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:
|
for item in description:
|
||||||
r += "{:16}".format(str(item))
|
r += "{:16}".format(str(item))
|
||||||
r += "\n"
|
r += "\n"
|
||||||
|
|
|
@ -74,17 +74,20 @@ class _PulseLogger(AutoDB):
|
||||||
self.first_timestamp = t
|
self.first_timestamp = t
|
||||||
self.output_list.append((self.name, t-self.first_timestamp, l, f))
|
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):
|
def on(self, t, f):
|
||||||
self._append(t, True, f)
|
self._append(self.int_usec(t), True, f)
|
||||||
|
|
||||||
def off(self, t):
|
def off(self, t):
|
||||||
self._append(t, False, 0)
|
self._append(self.int_usec(t), False, 0)
|
||||||
|
|
||||||
@kernel
|
@kernel
|
||||||
def pulse(self, f, duration):
|
def pulse(self, f, duration):
|
||||||
self.on(round(now()*1000000000), f)
|
self.on(now_mu(), f)
|
||||||
delay(duration)
|
delay(duration)
|
||||||
self.off(round(now()*1000000000))
|
self.off(now_mu())
|
||||||
|
|
||||||
|
|
||||||
class _Pulses(AutoDB):
|
class _Pulses(AutoDB):
|
||||||
|
@ -288,9 +291,9 @@ class _RTIOSequenceError(AutoDB):
|
||||||
|
|
||||||
@kernel
|
@kernel
|
||||||
def run(self):
|
def run(self):
|
||||||
t = now()
|
t = now_mu()
|
||||||
self.o.pulse(25*us)
|
self.o.pulse(25*us)
|
||||||
at(t)
|
at_mu(t)
|
||||||
self.o.pulse(25*us)
|
self.o.pulse(25*us)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,7 @@ optimize_in = """
|
||||||
|
|
||||||
def run():
|
def run():
|
||||||
dds_sysclk = Fraction(1000000000, 1)
|
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:
|
with sequential:
|
||||||
frequency = 345 * Fraction(1000000, 1)
|
frequency = 345 * Fraction(1000000, 1)
|
||||||
frequency_to_ftw_return = int((((2 ** 32) * frequency) / dds_sysclk))
|
frequency_to_ftw_return = int((((2 ** 32) * frequency) / dds_sysclk))
|
||||||
|
@ -19,7 +19,7 @@ def run():
|
||||||
ftw2 = ftw
|
ftw2 = ftw
|
||||||
ftw_to_frequency_return = ((ftw2 * dds_sysclk) / (2 ** 32))
|
ftw_to_frequency_return = ((ftw2 * dds_sysclk) / (2 ** 32))
|
||||||
f = ftw_to_frequency_return
|
f = ftw_to_frequency_return
|
||||||
phi = ((1000 * cycles_to_time(n)) * f)
|
phi = ((1000 * mu_to_seconds(n)) * f)
|
||||||
do_something(int(phi))
|
do_something(int(phi))
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
|
@ -28,13 +28,14 @@ def _get_duration(stmt):
|
||||||
return -1
|
return -1
|
||||||
elif isinstance(stmt, ast.Call):
|
elif isinstance(stmt, ast.Call):
|
||||||
name = stmt.func.id
|
name = stmt.func.id
|
||||||
if name == "delay":
|
assert(name != "delay")
|
||||||
|
if name == "delay_mu":
|
||||||
try:
|
try:
|
||||||
da = eval_constant(stmt.args[0])
|
da = eval_constant(stmt.args[0])
|
||||||
except NotConstant:
|
except NotConstant:
|
||||||
da = -1
|
da = -1
|
||||||
return da
|
return da
|
||||||
elif name == "at":
|
elif name == "at_mu":
|
||||||
return -1
|
return -1
|
||||||
else:
|
else:
|
||||||
return 0
|
return 0
|
||||||
|
@ -69,7 +70,7 @@ def _interleave_timelines(timelines):
|
||||||
ref_stmt = stmt.stmt
|
ref_stmt = stmt.stmt
|
||||||
delay_stmt = ast.copy_location(
|
delay_stmt = ast.copy_location(
|
||||||
ast.Expr(ast.Call(
|
ast.Expr(ast.Call(
|
||||||
func=ast.Name("delay", ast.Load()),
|
func=ast.Name("delay_mu", ast.Load()),
|
||||||
args=[value_to_ast(dt)],
|
args=[value_to_ast(dt)],
|
||||||
keywords=[], starargs=[], kwargs=[])),
|
keywords=[], starargs=[], kwargs=[])),
|
||||||
ref_stmt)
|
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:
|
using an accumulator 'now' and simple replacement rules:
|
||||||
|
|
||||||
delay(t) -> now += t
|
delay_mu(t) -> now += t
|
||||||
now() -> now
|
now_mu() -> now
|
||||||
at(t) -> now = t
|
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
|
The accumulator is initialized to an int64 value at the beginning of the
|
||||||
output function.
|
output function.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import ast
|
import ast
|
||||||
|
@ -17,7 +17,7 @@ import ast
|
||||||
|
|
||||||
class _TimeLowerer(ast.NodeTransformer):
|
class _TimeLowerer(ast.NodeTransformer):
|
||||||
def visit_Call(self, node):
|
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)
|
return ast.copy_location(ast.Name("now", ast.Load()), node)
|
||||||
else:
|
else:
|
||||||
self.generic_visit(node)
|
self.generic_visit(node)
|
||||||
|
@ -27,13 +27,13 @@ class _TimeLowerer(ast.NodeTransformer):
|
||||||
r = node
|
r = node
|
||||||
if isinstance(node.value, ast.Call):
|
if isinstance(node.value, ast.Call):
|
||||||
funcname = node.value.func.id
|
funcname = node.value.func.id
|
||||||
if funcname == "delay":
|
if funcname == "delay_mu":
|
||||||
r = ast.copy_location(
|
r = ast.copy_location(
|
||||||
ast.AugAssign(target=ast.Name("now", ast.Store()),
|
ast.AugAssign(target=ast.Name("now", ast.Store()),
|
||||||
op=ast.Add(),
|
op=ast.Add(),
|
||||||
value=node.value.args[0]),
|
value=node.value.args[0]),
|
||||||
node)
|
node)
|
||||||
elif funcname == "at":
|
elif funcname == "at_mu":
|
||||||
r = ast.copy_location(
|
r = ast.copy_location(
|
||||||
ast.Assign(targets=[ast.Name("now", ast.Store())],
|
ast.Assign(targets=[ast.Name("now", ast.Store())],
|
||||||
value=node.value.args[0]),
|
value=node.value.args[0]),
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
"""
|
"""
|
||||||
This transform turns calls to delay/now/at that use non-integer time
|
This transform turns calls to delay() that use non-integer time
|
||||||
expressed in seconds into calls that use int64 time expressed in multiples of
|
expressed in seconds into calls to delay_mu() that use int64 time
|
||||||
ref_period.
|
expressed in multiples of ref_period.
|
||||||
|
|
||||||
It does so by inserting multiplication/division/rounding operations around
|
It does so by inserting multiplication/division/rounding operations around
|
||||||
those calls.
|
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.
|
implemented here, as well as watchdog to syscall conversion.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
@ -15,14 +15,7 @@ import ast
|
||||||
from artiq.transforms.tools import value_to_ast
|
from artiq.transforms.tools import value_to_ast
|
||||||
|
|
||||||
|
|
||||||
def _call_now(node):
|
def _seconds_to_mu(ref_period, 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):
|
|
||||||
divided = ast.copy_location(
|
divided = ast.copy_location(
|
||||||
ast.BinOp(left=node,
|
ast.BinOp(left=node,
|
||||||
op=ast.Div(),
|
op=ast.Div(),
|
||||||
|
@ -35,7 +28,7 @@ def _time_to_cycles(ref_period, node):
|
||||||
divided)
|
divided)
|
||||||
|
|
||||||
|
|
||||||
def _cycles_to_time(ref_period, node):
|
def _mu_to_seconds(ref_period, node):
|
||||||
return ast.copy_location(
|
return ast.copy_location(
|
||||||
ast.BinOp(left=node,
|
ast.BinOp(left=node,
|
||||||
op=ast.Mult(),
|
op=ast.Mult(),
|
||||||
|
@ -50,29 +43,22 @@ class _TimeQuantizer(ast.NodeTransformer):
|
||||||
|
|
||||||
def visit_Call(self, node):
|
def visit_Call(self, node):
|
||||||
funcname = node.func.id
|
funcname = node.func.id
|
||||||
if funcname == "now":
|
if funcname == "delay":
|
||||||
return _cycles_to_time(self.ref_period, _call_now(node))
|
node.func.id = "delay_mu"
|
||||||
elif funcname == "delay" or funcname == "at":
|
|
||||||
if (isinstance(node.args[0], ast.Call)
|
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:
|
# 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])
|
node.args[0] = self.visit(node.args[0].args[0])
|
||||||
else:
|
else:
|
||||||
node.args[0] = _time_to_cycles(self.ref_period,
|
node.args[0] = _seconds_to_mu(self.ref_period,
|
||||||
self.visit(node.args[0]))
|
self.visit(node.args[0]))
|
||||||
return node
|
return node
|
||||||
elif funcname == "time_to_cycles":
|
elif funcname == "seconds_to_mu":
|
||||||
if (isinstance(node.args[0], ast.Call)
|
return _seconds_to_mu(self.ref_period,
|
||||||
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,
|
|
||||||
self.visit(node.args[0]))
|
self.visit(node.args[0]))
|
||||||
elif funcname == "cycles_to_time":
|
elif funcname == "mu_to_seconds":
|
||||||
return _cycles_to_time(self.ref_period,
|
return _mu_to_seconds(self.ref_period,
|
||||||
self.visit(node.args[0]))
|
self.visit(node.args[0]))
|
||||||
else:
|
else:
|
||||||
self.generic_visit(node)
|
self.generic_visit(node)
|
||||||
|
@ -123,6 +109,5 @@ class _TimeQuantizer(ast.NodeTransformer):
|
||||||
return node
|
return node
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def quantize_time(func_def, ref_period):
|
def quantize_time(func_def, ref_period):
|
||||||
_TimeQuantizer(ref_period).visit(func_def)
|
_TimeQuantizer(ref_period).visit(func_def)
|
||||||
|
|
|
@ -6,8 +6,9 @@ from artiq.language import units
|
||||||
|
|
||||||
|
|
||||||
embeddable_funcs = (
|
embeddable_funcs = (
|
||||||
core_language.delay, core_language.at, core_language.now,
|
core_language.delay_mu, core_language.at_mu, core_language.now_mu,
|
||||||
core_language.time_to_cycles, core_language.cycles_to_time,
|
core_language.delay,
|
||||||
|
core_language.seconds_to_mu, core_language.mu_to_seconds,
|
||||||
core_language.syscall, core_language.watchdog,
|
core_language.syscall, core_language.watchdog,
|
||||||
range, bool, int, float, round, len,
|
range, bool, int, float, round, len,
|
||||||
core_language.int64, core_language.round64,
|
core_language.int64, core_language.round64,
|
||||||
|
@ -92,7 +93,7 @@ def eval_constant(node):
|
||||||
_replaceable_funcs = {
|
_replaceable_funcs = {
|
||||||
"bool", "int", "float", "round",
|
"bool", "int", "float", "round",
|
||||||
"int64", "round64", "Fraction",
|
"int64", "round64", "Fraction",
|
||||||
"time_to_cycles", "cycles_to_time"
|
"seconds_to_mu", "mu_to_seconds"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -43,6 +43,12 @@
|
||||||
"class": "TTLOut",
|
"class": "TTLOut",
|
||||||
"arguments": {"channel": 4}
|
"arguments": {"channel": 4}
|
||||||
},
|
},
|
||||||
|
"ttl3": {
|
||||||
|
"type": "local",
|
||||||
|
"module": "artiq.coredevice.ttl",
|
||||||
|
"class": "TTLOut",
|
||||||
|
"arguments": {"channel": 5}
|
||||||
|
},
|
||||||
"led": {
|
"led": {
|
||||||
"type": "local",
|
"type": "local",
|
||||||
"module": "artiq.coredevice.ttl",
|
"module": "artiq.coredevice.ttl",
|
||||||
|
@ -113,10 +119,9 @@
|
||||||
"class": "CompoundPDQ2",
|
"class": "CompoundPDQ2",
|
||||||
"arguments": {
|
"arguments": {
|
||||||
"pdq2_devices": ["qc_q1_0", "qc_q1_1", "qc_q1_2", "qc_q1_3"],
|
"pdq2_devices": ["qc_q1_0", "qc_q1_1", "qc_q1_2", "qc_q1_3"],
|
||||||
"rtio_trigger": 7,
|
"trigger_device": "ttl3",
|
||||||
"rtio_frame": [2, 3, 4]
|
"frame_devices": ["ttl0", "ttl1", "ttl2"]
|
||||||
},
|
}
|
||||||
"comment": "Uses ttl 0, 1, 2 and 5"
|
|
||||||
},
|
},
|
||||||
|
|
||||||
"lda": {
|
"lda": {
|
||||||
|
|
Loading…
Reference in New Issue