artiq/artiq/test/coredevice/test_rtio.py

458 lines
12 KiB
Python
Raw Normal View History

# Copyright (C) 2014, 2015 M-Labs Limited
# Copyright (C) 2014, 2015 Robert Jordens <jordens@gmail.com>
import os, unittest
2015-06-29 10:08:39 +08:00
from math import sqrt
from artiq.experiment import *
2015-06-29 10:08:39 +08:00
from artiq.test.hardware_testbench import ExperimentCase
2016-03-19 06:48:26 +08:00
from artiq.coredevice import exceptions
2015-06-26 12:19:11 +08:00
artiq_low_latency = os.getenv("ARTIQ_LOW_LATENCY")
2015-06-26 12:19:11 +08:00
class PulseNotReceived(Exception):
pass
2015-07-14 04:08:20 +08:00
class RTT(EnvExperiment):
def build(self):
2015-10-04 00:18:21 +08:00
self.setattr_device("core")
self.setattr_device("ttl_inout")
2015-06-29 10:08:39 +08:00
@kernel
def run(self):
2016-06-29 02:37:39 +08:00
self.core.reset()
2015-06-29 10:08:39 +08:00
self.ttl_inout.output()
delay(1*us)
with interleave:
# make sure not to send two commands into the same RTIO
# channel with the same timestamp
self.ttl_inout.gate_rising(5*us)
2015-06-29 10:08:39 +08:00
with sequential:
delay(1*us)
2015-07-02 04:34:49 +08:00
t0 = now_mu()
2015-06-26 12:19:11 +08:00
self.ttl_inout.pulse(1*us)
t1 = self.ttl_inout.timestamp_mu()
if t1 < 0:
raise PulseNotReceived()
self.set_dataset("rtt", self.core.mu_to_seconds(t1 - t0))
2015-06-29 10:08:39 +08:00
2015-07-14 04:08:20 +08:00
class Loopback(EnvExperiment):
def build(self):
2015-10-04 00:18:21 +08:00
self.setattr_device("core")
self.setattr_device("loop_in")
self.setattr_device("loop_out")
2015-06-29 10:08:39 +08:00
@kernel
def run(self):
2016-06-29 02:37:39 +08:00
self.core.reset()
2015-07-05 00:35:11 +08:00
self.loop_in.input()
self.loop_out.off()
2015-07-05 00:35:11 +08:00
delay(1*us)
with parallel:
2015-06-29 10:08:39 +08:00
self.loop_in.gate_rising(2*us)
with sequential:
delay(1*us)
2015-07-02 04:34:49 +08:00
t0 = now_mu()
2015-06-29 10:08:39 +08:00
self.loop_out.pulse(1*us)
t1 = self.loop_in.timestamp_mu()
if t1 < 0:
raise PulseNotReceived()
self.set_dataset("rtt", self.core.mu_to_seconds(t1 - t0))
2015-06-29 10:08:39 +08:00
2015-07-14 04:08:20 +08:00
class ClockGeneratorLoopback(EnvExperiment):
def build(self):
2015-10-04 00:18:21 +08:00
self.setattr_device("core")
self.setattr_device("loop_clock_in")
self.setattr_device("loop_clock_out")
@kernel
def run(self):
2016-06-29 02:37:39 +08:00
self.core.reset()
self.loop_clock_in.input()
self.loop_clock_out.stop()
2016-06-29 11:38:36 +08:00
delay(10*us)
with parallel:
self.loop_clock_in.gate_rising(10*us)
with sequential:
delay(200*ns)
self.loop_clock_out.set(1*MHz)
self.set_dataset("count", self.loop_clock_in.count())
2015-07-14 04:08:20 +08:00
class PulseRate(EnvExperiment):
def build(self):
2015-10-04 00:18:21 +08:00
self.setattr_device("core")
self.setattr_device("ttl_out")
2015-06-29 10:08:39 +08:00
@kernel
def run(self):
2016-06-29 02:37:39 +08:00
self.core.reset()
dt = self.core.seconds_to_mu(300*ns)
2015-06-29 10:08:39 +08:00
while True:
for i in range(10000):
try:
self.ttl_out.pulse_mu(dt)
2015-07-02 04:34:49 +08:00
delay_mu(dt)
except RTIOUnderflow:
dt += 1
self.core.break_realtime()
break
2015-06-29 10:08:39 +08:00
else:
self.set_dataset("pulse_rate", self.core.mu_to_seconds(dt))
return
2015-06-29 10:08:39 +08:00
2015-06-26 12:19:11 +08:00
class PulseRateDDS(EnvExperiment):
def build(self):
self.setattr_device("core")
self.setattr_device("core_dds")
self.setattr_device("dds0")
self.setattr_device("dds1")
@kernel
def run(self):
2016-06-29 02:37:39 +08:00
self.core.reset()
dt = self.core.seconds_to_mu(5*us)
while True:
delay(10*ms)
2016-03-31 16:04:55 +08:00
for i in range(1250):
try:
with self.core_dds.batch:
self.dds0.set(100*MHz)
self.dds1.set(100*MHz)
delay_mu(dt)
except RTIOUnderflow:
dt += 100
self.core.break_realtime()
break
else:
self.set_dataset("pulse_rate", self.core.mu_to_seconds(dt//2))
return
2015-07-14 04:08:20 +08:00
class Watchdog(EnvExperiment):
def build(self):
2015-10-04 00:18:21 +08:00
self.setattr_device("core")
@kernel
def run(self):
with watchdog(50*ms):
while True:
pass
class LoopbackCount(EnvExperiment):
2016-04-16 19:31:07 +08:00
def build(self, npulses):
self.setattr_device("core")
self.setattr_device("loop_in")
self.setattr_device("loop_out")
2016-04-16 19:31:07 +08:00
self.npulses = npulses
def set_count(self, count):
self.set_dataset("count", count)
@kernel
def run(self):
2016-06-29 02:37:39 +08:00
self.core.reset()
self.loop_in.input()
self.loop_out.output()
delay(5*us)
with parallel:
self.loop_in.gate_rising(10*us)
with sequential:
for i in range(self.npulses):
delay(25*ns)
self.loop_out.pulse(25*ns)
self.set_dataset("count", self.loop_in.count())
2016-09-07 17:37:49 +08:00
class IncorrectLevel(Exception):
pass
class Level(EnvExperiment):
def build(self):
self.setattr_device("core")
self.setattr_device("loop_in")
self.setattr_device("loop_out")
@kernel
def run(self):
self.core.reset()
self.loop_in.input()
self.loop_out.output()
delay(5*us)
self.loop_out.off()
delay(5*us)
if self.loop_in.sample_get_nonrt():
raise IncorrectLevel
self.loop_out.on()
delay(5*us)
if not self.loop_in.sample_get_nonrt():
raise IncorrectLevel
class Watch(EnvExperiment):
def build(self):
self.setattr_device("core")
self.setattr_device("loop_in")
self.setattr_device("loop_out")
@kernel
def run(self):
self.core.reset()
self.loop_in.input()
self.loop_out.output()
delay(5*us)
self.loop_out.off()
delay(5*us)
if not self.loop_in.watch_stay_off():
raise IncorrectLevel
delay(10*us)
if not self.loop_in.watch_done():
raise IncorrectLevel
delay(10*us)
if not self.loop_in.watch_stay_off():
raise IncorrectLevel
delay(3*us)
self.loop_out.on()
delay(10*us)
if self.loop_in.watch_done():
raise IncorrectLevel
2015-07-14 04:08:20 +08:00
class Underflow(EnvExperiment):
def build(self):
2015-10-04 00:18:21 +08:00
self.setattr_device("core")
self.setattr_device("ttl_out")
@kernel
def run(self):
2016-06-29 02:37:39 +08:00
self.core.reset()
while True:
delay(25*ns)
self.ttl_out.pulse(25*ns)
2015-07-14 04:08:20 +08:00
class SequenceError(EnvExperiment):
def build(self):
2015-10-04 00:18:21 +08:00
self.setattr_device("core")
self.setattr_device("ttl_out")
@kernel
def run(self):
2016-06-29 02:37:39 +08:00
self.core.reset()
t = now_mu()
self.ttl_out.pulse(25*us)
at_mu(t)
self.ttl_out.pulse(25*us)
2016-03-08 15:38:08 +08:00
class Collision(EnvExperiment):
2015-07-29 19:43:35 +08:00
def build(self):
2015-10-04 00:18:21 +08:00
self.setattr_device("core")
self.setattr_device("ttl_out_serdes")
2015-07-29 19:43:35 +08:00
@kernel
def run(self):
2016-06-29 02:37:39 +08:00
self.core.reset()
2015-07-29 19:43:35 +08:00
delay(5*ms) # make sure we won't get underflow
for i in range(16):
self.ttl_out_serdes.pulse_mu(1)
delay_mu(1)
class AddressCollision(EnvExperiment):
def build(self):
self.setattr_device("core")
self.setattr_device("loop_in")
@kernel
def run(self):
2016-06-29 02:37:39 +08:00
self.core.reset()
self.loop_in.input()
self.loop_in.pulse(10*us)
2015-07-14 04:08:20 +08:00
class TimeKeepsRunning(EnvExperiment):
def build(self):
2015-10-04 00:18:21 +08:00
self.setattr_device("core")
2015-07-07 21:29:38 +08:00
@kernel
def run(self):
self.core.reset()
self.set_dataset("time_at_start", now_mu())
2015-07-07 21:29:38 +08:00
2015-07-14 04:08:20 +08:00
class Handover(EnvExperiment):
def build(self):
2015-10-04 00:18:21 +08:00
self.setattr_device("core")
2015-07-07 21:29:38 +08:00
@kernel
def k(self, var):
self.set_dataset(var, now_mu())
delay_mu(1234)
2015-07-07 21:29:38 +08:00
def run(self):
self.k("t1")
self.k("t2")
2015-07-07 21:29:38 +08:00
class DummyException(Exception):
pass
class HandoverException(EnvExperiment):
def build(self):
self.setattr_device("core")
@kernel
def k(self, var):
self.set_dataset(var, now_mu())
delay_mu(1234)
raise DummyException()
def run(self):
try:
self.k("t1")
except DummyException:
pass
try:
self.k("t2")
except DummyException:
pass
2015-06-29 10:08:39 +08:00
class CoredeviceTest(ExperimentCase):
def test_loopback(self):
2015-07-05 10:05:11 +08:00
self.execute(Loopback)
2015-10-12 19:20:04 +08:00
rtt = self.dataset_mgr.get("rtt")
2015-06-29 10:08:39 +08:00
print(rtt)
self.assertGreater(rtt, 0*ns)
self.assertLess(rtt, 60*ns)
2015-06-26 12:19:11 +08:00
def test_clock_generator_loopback(self):
2015-07-05 10:05:11 +08:00
self.execute(ClockGeneratorLoopback)
2015-10-12 19:20:04 +08:00
count = self.dataset_mgr.get("count")
self.assertEqual(count, 10)
2015-06-29 10:08:39 +08:00
def test_pulse_rate(self):
"""Minimum interval for sustained TTL output switching"""
2015-07-05 10:05:11 +08:00
self.execute(PulseRate)
2015-10-12 19:20:04 +08:00
rate = self.dataset_mgr.get("pulse_rate")
2015-06-29 10:08:39 +08:00
print(rate)
2015-06-26 12:19:11 +08:00
self.assertGreater(rate, 100*ns)
self.assertLess(rate, 700*ns)
2015-06-29 10:08:39 +08:00
def test_pulse_rate_dds(self):
"""Minimum interval for sustained DDS frequency switching"""
self.execute(PulseRateDDS)
rate = self.dataset_mgr.get("pulse_rate")
print(rate)
self.assertGreater(rate, 1*us)
2016-06-29 11:38:36 +08:00
self.assertLess(rate, 6.2*us)
def test_loopback_count(self):
npulses = 2
self.execute(LoopbackCount, npulses=npulses)
count = self.dataset_mgr.get("count")
self.assertEqual(count, npulses)
2016-09-07 17:37:49 +08:00
def test_level(self):
self.execute(Level)
def test_watch(self):
self.execute(Watch)
def test_underflow(self):
with self.assertRaises(RTIOUnderflow):
self.execute(Underflow)
def test_sequence_error(self):
with self.assertRaises(RTIOSequenceError):
self.execute(SequenceError)
2016-03-08 15:38:08 +08:00
def test_collision(self):
with self.assertRaises(RTIOCollision):
self.execute(Collision)
2015-07-29 19:43:35 +08:00
def test_address_collision(self):
with self.assertRaises(RTIOCollision):
self.execute(AddressCollision)
def test_watchdog(self):
# watchdog only works on the device
2016-03-19 06:48:26 +08:00
with self.assertRaises(exceptions.WatchdogExpired):
self.execute(Watchdog)
@unittest.skipUnless(artiq_low_latency,
"timings are dependent on CPU load and network conditions")
2015-07-07 21:29:38 +08:00
def test_time_keeps_running(self):
self.execute(TimeKeepsRunning)
2015-10-12 19:20:04 +08:00
t1 = self.dataset_mgr.get("time_at_start")
2015-07-07 21:29:38 +08:00
self.execute(TimeKeepsRunning)
2015-10-12 19:20:04 +08:00
t2 = self.dataset_mgr.get("time_at_start")
2016-11-21 14:40:47 +08:00
dead_time = self.device_mgr.get("core").mu_to_seconds(t2 - t1)
print(dead_time)
self.assertGreater(dead_time, 1*ms)
self.assertLess(dead_time, 2500*ms)
2015-07-07 21:29:38 +08:00
def test_handover(self):
self.execute(Handover)
self.assertEqual(self.dataset_mgr.get("t1") + 1234,
2015-10-12 19:20:04 +08:00
self.dataset_mgr.get("t2"))
2015-07-07 21:29:38 +08:00
def test_handover_exception(self):
self.execute(HandoverException)
self.assertEqual(self.dataset_mgr.get("t1") + 1234,
self.dataset_mgr.get("t2"))
2015-06-29 10:08:39 +08:00
2015-07-14 04:08:20 +08:00
class RPCTiming(EnvExperiment):
2016-04-16 19:31:07 +08:00
def build(self, repeats=100):
2015-10-04 00:18:21 +08:00
self.setattr_device("core")
2016-04-16 19:31:07 +08:00
self.repeats = repeats
2015-06-29 10:08:39 +08:00
2015-09-01 22:38:53 +08:00
def nop(self):
2015-06-29 10:08:39 +08:00
pass
@kernel
def bench(self):
for i in range(self.repeats):
2015-07-02 04:34:49 +08:00
t1 = self.core.get_rtio_counter_mu()
2015-09-01 22:38:53 +08:00
self.nop()
2015-07-02 04:34:49 +08:00
t2 = self.core.get_rtio_counter_mu()
self.ts[i] = self.core.mu_to_seconds(t2 - t1)
2015-06-29 10:08:39 +08:00
def run(self):
self.ts = [0. for _ in range(self.repeats)]
2015-06-29 10:08:39 +08:00
self.bench()
mean = sum(self.ts)/self.repeats
2015-10-12 19:20:04 +08:00
self.set_dataset("rpc_time_stddev", sqrt(
2015-07-14 04:08:20 +08:00
sum([(t - mean)**2 for t in self.ts])/self.repeats))
2015-10-12 19:20:04 +08:00
self.set_dataset("rpc_time_mean", mean)
2015-06-26 12:19:11 +08:00
2015-06-29 10:08:39 +08:00
class RPCTest(ExperimentCase):
@unittest.skipUnless(artiq_low_latency,
"timings are dependent on CPU load and network conditions")
2015-06-29 10:08:39 +08:00
def test_rpc_timing(self):
2015-07-05 10:05:11 +08:00
self.execute(RPCTiming)
rpc_time_mean = self.dataset_mgr.get("rpc_time_mean")
print(rpc_time_mean)
self.assertGreater(rpc_time_mean, 100*ns)
self.assertLess(rpc_time_mean, 2*ms)
self.assertLess(self.dataset_mgr.get("rpc_time_stddev"), 1*ms)