Move mu_to_seconds, seconds_to_mu to Core.

This commit is contained in:
whitequark 2016-11-21 04:27:48 +00:00
parent 06ea76336d
commit 009d396740
17 changed files with 48 additions and 85 deletions

View File

@ -203,12 +203,6 @@ def fn_delay_mu():
def fn_at_mu(): def fn_at_mu():
return types.TBuiltinFunction("at_mu") return types.TBuiltinFunction("at_mu")
def fn_mu_to_seconds():
return types.TBuiltinFunction("mu_to_seconds")
def fn_seconds_to_mu():
return types.TBuiltinFunction("seconds_to_mu")
def fn_rtio_log(): def fn_rtio_log():
return types.TBuiltinFunction("rtio_log") return types.TBuiltinFunction("rtio_log")

View File

@ -44,8 +44,6 @@ def globals():
"now_mu": builtins.fn_now_mu(), "now_mu": builtins.fn_now_mu(),
"delay_mu": builtins.fn_delay_mu(), "delay_mu": builtins.fn_delay_mu(),
"at_mu": builtins.fn_at_mu(), "at_mu": builtins.fn_at_mu(),
"mu_to_seconds": builtins.fn_mu_to_seconds(),
"seconds_to_mu": builtins.fn_seconds_to_mu(),
# ARTIQ utility functions # ARTIQ utility functions
"rtio_log": builtins.fn_rtio_log(), "rtio_log": builtins.fn_rtio_log(),

View File

@ -1731,20 +1731,6 @@ class ARTIQIRGenerator(algorithm.Visitor):
or types.is_builtin(typ, "at_mu"): or types.is_builtin(typ, "at_mu"):
return self.append(ir.Builtin(typ.name, return self.append(ir.Builtin(typ.name,
[self.visit(arg) for arg in node.args], node.type)) [self.visit(arg) for arg in node.args], node.type))
elif types.is_builtin(typ, "mu_to_seconds"):
if len(node.args) == 1 and len(node.keywords) == 0:
arg = self.visit(node.args[0])
arg_float = self.append(ir.Coerce(arg, builtins.TFloat()))
return self.append(ir.Arith(ast.Mult(loc=None), arg_float, self.ref_period))
else:
assert False
elif types.is_builtin(typ, "seconds_to_mu"):
if len(node.args) == 1 and len(node.keywords) == 0:
arg = self.visit(node.args[0])
arg_mu = self.append(ir.Arith(ast.Div(loc=None), arg, self.ref_period))
return self.append(ir.Coerce(arg_mu, builtins.TInt64()))
else:
assert False
elif types.is_exn_constructor(typ): elif types.is_exn_constructor(typ):
return self.alloc_exn(node.type, *[self.visit(arg_node) for arg_node in node.args]) return self.alloc_exn(node.type, *[self.visit(arg_node) for arg_node in node.args])
elif types.is_constructor(typ): elif types.is_constructor(typ):

View File

@ -899,12 +899,6 @@ class Inferencer(algorithm.Visitor):
elif types.is_builtin(typ, "at_mu"): elif types.is_builtin(typ, "at_mu"):
simple_form("at_mu(time_mu:numpy.int64) -> None", simple_form("at_mu(time_mu:numpy.int64) -> None",
[builtins.TInt64()]) [builtins.TInt64()])
elif types.is_builtin(typ, "mu_to_seconds"):
simple_form("mu_to_seconds(time_mu:numpy.int64) -> float",
[builtins.TInt64()], builtins.TFloat())
elif types.is_builtin(typ, "seconds_to_mu"):
simple_form("seconds_to_mu(time:float) -> numpy.int64",
[builtins.TFloat()], builtins.TInt64())
elif types.is_builtin(typ, "watchdog"): elif types.is_builtin(typ, "watchdog"):
simple_form("watchdog(time:float) -> [builtin context manager]", simple_form("watchdog(time:float) -> [builtin context manager]",
[builtins.TFloat()], builtins.TNone()) [builtins.TFloat()], builtins.TNone())

View File

@ -1,5 +1,4 @@
from artiq.language.core import (kernel, portable, delay_mu, delay, from artiq.language.core import (kernel, portable, delay_mu, delay)
seconds_to_mu)
from artiq.language.units import ns, us from artiq.language.units import ns, us
from artiq.coredevice import spi from artiq.coredevice import spi
@ -166,10 +165,10 @@ class AD5360:
self.bus.write_period_mu + self.bus.write_period_mu +
self.bus.ref_period_mu) - self.bus.ref_period_mu) -
3*self.bus.ref_period_mu - 3*self.bus.ref_period_mu -
seconds_to_mu(1.5*us)) self.core.seconds_to_mu(1.5*us))
for i in range(len(values)): for i in range(len(values)):
self.write_channel(i, values[i], op) self.write_channel(i, values[i], op)
delay_mu(3*self.bus.ref_period_mu + # latency alignment ttl to spi delay_mu(3*self.bus.ref_period_mu + # latency alignment ttl to spi
seconds_to_mu(1.5*us)) # t10 max busy low for one channel self.core.seconds_to_mu(1.5*us)) # t10 max busy low for one channel
self.load() self.load()
delay_mu(-2*self.bus.ref_period_mu) # load(), t13 delay_mu(-2*self.bus.ref_period_mu) # load(), t13

View File

@ -1,4 +1,5 @@
import os, sys import os, sys
import numpy
from pythonparser import diagnostic from pythonparser import diagnostic
@ -124,6 +125,23 @@ class Core:
return result return result
@portable
def seconds_to_mu(self, seconds):
"""Converts seconds to the corresponding number of machine units
(RTIO cycles).
:param seconds: time (in seconds) to convert.
"""
return numpy.int64(seconds//self.ref_period)
@portable
def mu_to_seconds(self, mu):
"""Converts machine units (RTIO cycles) to seconds.
:param mu: cycle count to convert.
"""
return mu*self.ref_period
@kernel @kernel
def get_rtio_counter_mu(self): def get_rtio_counter_mu(self):
return rtio_get_counter() return rtio_get_counter()

View File

@ -1,7 +1,6 @@
import numpy import numpy
from artiq.language.core import (kernel, portable, seconds_to_mu, now_mu, from artiq.language.core import (kernel, portable, now_mu, delay_mu)
delay_mu, mu_to_seconds)
from artiq.language.units import MHz from artiq.language.units import MHz
from artiq.coredevice.rtio import rtio_output, rtio_input_data from artiq.coredevice.rtio import rtio_output, rtio_input_data
@ -59,8 +58,7 @@ class SPIMaster:
""" """
def __init__(self, dmgr, channel, core_device="core"): def __init__(self, dmgr, channel, core_device="core"):
self.core = dmgr.get(core_device) self.core = dmgr.get(core_device)
self.ref_period_mu = seconds_to_mu(self.core.coarse_ref_period, self.ref_period_mu = self.core.seconds_to_mu(self.core.coarse_ref_period)
self.core)
self.channel = channel self.channel = channel
self.write_period_mu = numpy.int64(0) self.write_period_mu = numpy.int64(0)
self.read_period_mu = numpy.int64(0) self.read_period_mu = numpy.int64(0)
@ -68,7 +66,7 @@ class SPIMaster:
@portable @portable
def frequency_to_div(self, f): def frequency_to_div(self, f):
return int(1/(f*mu_to_seconds(self.ref_period_mu))) + 1 return int(1/(f*self.core.mu_to_seconds(self.ref_period_mu))) + 1
@kernel @kernel
def set_config(self, flags=0, write_freq=20*MHz, read_freq=20*MHz): def set_config(self, flags=0, write_freq=20*MHz, read_freq=20*MHz):

View File

@ -94,7 +94,7 @@ class _Frame:
def _arm(self): def _arm(self):
self.segment_delays = [ self.segment_delays = [
seconds_to_mu(s.duration*delay_margin_factor, self.core) self.core.seconds_to_mu(s.duration*delay_margin_factor)
for s in self.segments] for s in self.segments]
def _invalidate(self): def _invalidate(self):
@ -125,7 +125,7 @@ class _Frame:
raise ArmError() raise ArmError()
call_t = now_mu() call_t = now_mu()
trigger_start_t = call_t - seconds_to_mu(trigger_duration/2) trigger_start_t = call_t - self.core.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,7 +136,7 @@ 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
at_mu(trigger_start_t - seconds_to_mu(frame_setup)) at_mu(trigger_start_t - self.core.seconds_to_mu(frame_setup))
self.pdq.frame0.set_o(bool(self.frame_number & 1)) self.pdq.frame0.set_o(bool(self.frame_number & 1))
self.pdq.frame1.set_o(bool((self.frame_number & 2) >> 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.frame2.set_o(bool((self.frame_number & 4) >> 2))

View File

@ -8,7 +8,7 @@ class IdleKernel(EnvExperiment):
@kernel @kernel
def run(self): def run(self):
start_time = now_mu() + seconds_to_mu(500*ms) start_time = now_mu() + self.core.seconds_to_mu(500*ms)
while self.core.get_rtio_counter_mu() < start_time: while self.core.get_rtio_counter_mu() < start_time:
pass pass
self.core.reset() self.core.reset()

View File

@ -42,7 +42,7 @@ class TDR(EnvExperiment):
pulse = 1e-6 # pulse length, larger than rtt pulse = 1e-6 # pulse length, larger than rtt
self.t = [0 for i in range(2)] self.t = [0 for i in range(2)]
try: try:
self.many(n, seconds_to_mu(pulse, self.core)) self.many(n, self.core.seconds_to_mu(pulse))
except PulseNotReceivedError: except PulseNotReceivedError:
print("to few edges: cable too long or wiring bad") print("to few edges: cable too long or wiring bad")
else: else:

View File

@ -15,7 +15,6 @@ __all__ = ["kernel", "portable", "rpc", "syscall", "host_only",
kernel_globals = ( kernel_globals = (
"sequential", "parallel", "interleave", "sequential", "parallel", "interleave",
"delay_mu", "now_mu", "at_mu", "delay", "delay_mu", "now_mu", "at_mu", "delay",
"seconds_to_mu", "mu_to_seconds",
"watchdog" "watchdog"
) )
__all__.extend(kernel_globals) __all__.extend(kernel_globals)
@ -213,31 +212,6 @@ def delay(duration):
_time_manager.take_time(duration) _time_manager.take_time(duration)
def seconds_to_mu(seconds, core=None):
"""Converts seconds to the corresponding number of machine units
(RTIO cycles).
: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 numpy.int64(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
class _DummyWatchdog: class _DummyWatchdog:
def __init__(self, timeout): def __init__(self, timeout):
pass pass

View File

@ -1,4 +1,5 @@
from random import Random from random import Random
import numpy
from artiq.language.core import delay, at_mu, kernel from artiq.language.core import delay, at_mu, kernel
from artiq.sim import time from artiq.sim import time
@ -18,6 +19,12 @@ class Core:
time.manager.timeline.clear() time.manager.timeline.clear()
return r return r
def seconds_to_mu(self, seconds):
return numpy.int64(seconds//self.ref_period)
def mu_to_seconds(self, mu):
return mu*self.ref_period
class Input: class Input:
def __init__(self, dmgr, name): def __init__(self, dmgr, name):

View File

@ -83,7 +83,7 @@ class _PulseLogger(EnvExperiment):
if not hasattr(self.parent_test, "first_timestamp"): if not hasattr(self.parent_test, "first_timestamp"):
self.parent_test.first_timestamp = t self.parent_test.first_timestamp = t
origin = self.parent_test.first_timestamp origin = self.parent_test.first_timestamp
t_usec = round(mu_to_seconds(t-origin, self.core)*1000000) t_usec = round(self.core.mu_to_seconds(t-origin)*1000000)
self.parent_test.output_list.append((self.name, t_usec, l, f)) self.parent_test.output_list.append((self.name, t_usec, l, f))
def on(self, t, f): def on(self, t, f):

View File

@ -38,7 +38,7 @@ class RTT(EnvExperiment):
t1 = self.ttl_inout.timestamp_mu() t1 = self.ttl_inout.timestamp_mu()
if t1 < 0: if t1 < 0:
raise PulseNotReceived() raise PulseNotReceived()
self.set_dataset("rtt", mu_to_seconds(t1 - t0)) self.set_dataset("rtt", self.core.mu_to_seconds(t1 - t0))
class Loopback(EnvExperiment): class Loopback(EnvExperiment):
@ -62,7 +62,7 @@ class Loopback(EnvExperiment):
t1 = self.loop_in.timestamp_mu() t1 = self.loop_in.timestamp_mu()
if t1 < 0: if t1 < 0:
raise PulseNotReceived() raise PulseNotReceived()
self.set_dataset("rtt", mu_to_seconds(t1 - t0)) self.set_dataset("rtt", self.core.mu_to_seconds(t1 - t0))
class ClockGeneratorLoopback(EnvExperiment): class ClockGeneratorLoopback(EnvExperiment):
@ -93,7 +93,7 @@ class PulseRate(EnvExperiment):
@kernel @kernel
def run(self): def run(self):
self.core.reset() self.core.reset()
dt = seconds_to_mu(300*ns) dt = self.core.seconds_to_mu(300*ns)
while True: while True:
for i in range(10000): for i in range(10000):
try: try:
@ -104,7 +104,7 @@ class PulseRate(EnvExperiment):
self.core.break_realtime() self.core.break_realtime()
break break
else: else:
self.set_dataset("pulse_rate", mu_to_seconds(dt)) self.set_dataset("pulse_rate", self.core.mu_to_seconds(dt))
return return
@ -118,7 +118,7 @@ class PulseRateDDS(EnvExperiment):
@kernel @kernel
def run(self): def run(self):
self.core.reset() self.core.reset()
dt = seconds_to_mu(5*us) dt = self.core.seconds_to_mu(5*us)
while True: while True:
delay(10*ms) delay(10*ms)
for i in range(1250): for i in range(1250):
@ -132,7 +132,7 @@ class PulseRateDDS(EnvExperiment):
self.core.break_realtime() self.core.break_realtime()
break break
else: else:
self.set_dataset("pulse_rate", mu_to_seconds(dt//2)) self.set_dataset("pulse_rate", self.core.mu_to_seconds(dt//2))
return return
@ -403,7 +403,7 @@ class CoredeviceTest(ExperimentCase):
self.execute(TimeKeepsRunning) self.execute(TimeKeepsRunning)
t2 = self.dataset_mgr.get("time_at_start") t2 = self.dataset_mgr.get("time_at_start")
dead_time = mu_to_seconds(t2 - t1, self.device_mgr.get("core")) dead_time = self.core.mu_to_seconds(t2 - t1, self.device_mgr.get("core"))
print(dead_time) print(dead_time)
self.assertGreater(dead_time, 1*ms) self.assertGreater(dead_time, 1*ms)
self.assertLess(dead_time, 2500*ms) self.assertLess(dead_time, 2500*ms)
@ -434,7 +434,7 @@ class RPCTiming(EnvExperiment):
t1 = self.core.get_rtio_counter_mu() t1 = self.core.get_rtio_counter_mu()
self.nop() self.nop()
t2 = self.core.get_rtio_counter_mu() t2 = self.core.get_rtio_counter_mu()
self.ts[i] = mu_to_seconds(t2 - t1) self.ts[i] = self.core.mu_to_seconds(t2 - t1)
def run(self): def run(self):
self.ts = [0. for _ in range(self.repeats)] self.ts = [0. for _ in range(self.repeats)]

View File

@ -1,5 +0,0 @@
# RUN: %python -m artiq.compiler.testbench.jit %s
# REQUIRES: time
assert seconds_to_mu(2.0) == 2000000
assert mu_to_seconds(1500000) == 1.5

View File

@ -151,7 +151,7 @@ In the synthetic example above, the compiler will be able to detect that the res
@kernel @kernel
def loop(self): def loop(self):
precomputed_delay_mu = seconds_to_mu(self.worker.interval / 5.0) precomputed_delay_mu = self.core.seconds_to_mu(self.worker.interval / 5.0)
for _ in range(100): for _ in range(100):
delay_mu(precomputed_delay_mu) delay_mu(precomputed_delay_mu)
self.worker.work() self.worker.work()

View File

@ -36,7 +36,7 @@ The wall clock keeps running across experiments.
Absolute timestamps can be large numbers. Absolute timestamps can be large numbers.
They are represented internally as 64-bit integers with a resolution of typically a nanosecond and a range of hundreds of years. They are represented internally as 64-bit integers with a resolution of typically a nanosecond and a range of hundreds of years.
Conversions between such a large integer number and a floating point representation can cause loss of precision through cancellation. Conversions between such a large integer number and a floating point representation can cause loss of precision through cancellation.
When computing the difference of absolute timestamps, use ``mu_to_seconds(t2-t1)``, not ``mu_to_seconds(t2)-mu_to_seconds(t1)`` (see :meth:`artiq.language.core.mu_to_seconds`). When computing the difference of absolute timestamps, use ``self.core.mu_to_seconds(t2-t1)``, not ``self.core.mu_to_seconds(t2)-self.core.mu_to_seconds(t1)`` (see :meth:`artiq.coredevice.Core.mu_to_seconds`).
When accumulating time, do it in machine units and not in SI units, so that rounding errors do not accumulate. When accumulating time, do it in machine units and not in SI units, so that rounding errors do not accumulate.
The following basic example shows how to place output events on the timeline. The following basic example shows how to place output events on the timeline.