2015-09-07 06:08:57 +08:00
|
|
|
# Copyright (C) 2014, 2015 M-Labs Limited
|
|
|
|
# Copyright (C) 2014, 2015 Robert Jordens <jordens@gmail.com>
|
|
|
|
|
2016-02-24 21:10:26 +08:00
|
|
|
import os, unittest
|
2017-04-06 02:34:08 +08:00
|
|
|
import numpy as np
|
2016-02-24 21:10:26 +08:00
|
|
|
|
2015-06-29 10:08:39 +08:00
|
|
|
from math import sqrt
|
|
|
|
|
2016-01-26 06:49:08 +08:00
|
|
|
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
|
2017-03-30 02:34:02 +08:00
|
|
|
from artiq.coredevice.comm_mgmt import CommMgmt
|
2017-03-17 22:47:58 +08:00
|
|
|
from artiq.coredevice.comm_analyzer import (StoppedMessage, OutputMessage, InputMessage,
|
|
|
|
decode_dump, get_analyzer_dump)
|
2015-06-26 12:19:11 +08:00
|
|
|
|
2016-03-01 19:03:10 +08:00
|
|
|
|
2016-02-24 21:10:26 +08:00
|
|
|
artiq_low_latency = os.getenv("ARTIQ_LOW_LATENCY")
|
2017-03-29 09:36:51 +08:00
|
|
|
artiq_in_devel = os.getenv("ARTIQ_IN_DEVEL")
|
2016-02-24 21:10:26 +08:00
|
|
|
|
2015-06-26 12:19:11 +08:00
|
|
|
|
2018-01-24 00:28:39 +08:00
|
|
|
class RTIOCounter(EnvExperiment):
|
|
|
|
def build(self):
|
|
|
|
self.setattr_device("core")
|
|
|
|
|
|
|
|
@kernel
|
|
|
|
def run(self):
|
|
|
|
t0 = self.core.get_rtio_counter_mu()
|
|
|
|
t1 = self.core.get_rtio_counter_mu()
|
|
|
|
self.set_dataset("dt", self.core.mu_to_seconds(t1 - t0))
|
|
|
|
|
|
|
|
|
2016-03-10 12:16:18 +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)
|
2016-02-25 02:12:12 +08:00
|
|
|
with interleave:
|
2015-07-02 16:26:00 +08:00
|
|
|
# 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)
|
2016-03-10 12:16:18 +08:00
|
|
|
t1 = self.ttl_inout.timestamp_mu()
|
|
|
|
if t1 < 0:
|
|
|
|
raise PulseNotReceived()
|
2016-11-21 12:27:48 +08:00
|
|
|
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()
|
2016-03-10 13:24:00 +08:00
|
|
|
self.loop_out.off()
|
2015-07-05 00:35:11 +08:00
|
|
|
delay(1*us)
|
2016-02-23 15:48:12 +08:00
|
|
|
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)
|
2016-03-10 12:16:18 +08:00
|
|
|
t1 = self.loop_in.timestamp_mu()
|
|
|
|
if t1 < 0:
|
|
|
|
raise PulseNotReceived()
|
2016-11-21 12:27:48 +08:00
|
|
|
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")
|
2015-07-05 00:36:01 +08:00
|
|
|
|
|
|
|
@kernel
|
|
|
|
def run(self):
|
2016-06-29 02:37:39 +08:00
|
|
|
self.core.reset()
|
2015-07-05 00:36:01 +08:00
|
|
|
self.loop_clock_in.input()
|
|
|
|
self.loop_clock_out.stop()
|
2017-03-27 17:26:23 +08:00
|
|
|
delay(20*us)
|
2016-02-23 15:48:12 +08:00
|
|
|
with parallel:
|
2015-07-05 00:36:01 +08:00
|
|
|
self.loop_clock_in.gate_rising(10*us)
|
|
|
|
with sequential:
|
|
|
|
delay(200*ns)
|
|
|
|
self.loop_clock_out.set(1*MHz)
|
2015-12-16 19:45:57 +08:00
|
|
|
self.set_dataset("count", self.loop_clock_in.count())
|
2015-07-05 00:36:01 +08:00
|
|
|
|
|
|
|
|
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()
|
2016-11-21 12:27:48 +08:00
|
|
|
dt = self.core.seconds_to_mu(300*ns)
|
2015-06-29 10:08:39 +08:00
|
|
|
while True:
|
2016-03-27 06:28:22 +08:00
|
|
|
for i in range(10000):
|
|
|
|
try:
|
2015-07-29 19:42:43 +08:00
|
|
|
self.ttl_out.pulse_mu(dt)
|
2015-07-02 04:34:49 +08:00
|
|
|
delay_mu(dt)
|
2016-03-27 06:28:22 +08:00
|
|
|
except RTIOUnderflow:
|
|
|
|
dt += 1
|
|
|
|
self.core.break_realtime()
|
|
|
|
break
|
2015-06-29 10:08:39 +08:00
|
|
|
else:
|
2016-11-21 12:27:48 +08:00
|
|
|
self.set_dataset("pulse_rate", self.core.mu_to_seconds(dt))
|
2016-03-27 06:28:22 +08:00
|
|
|
return
|
2015-06-29 10:08:39 +08:00
|
|
|
|
2015-06-26 12:19:11 +08:00
|
|
|
|
2016-03-18 12:54:15 +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()
|
2016-11-21 12:27:48 +08:00
|
|
|
dt = self.core.seconds_to_mu(5*us)
|
2017-01-25 19:26:30 +08:00
|
|
|
freq = self.core_dds.frequency_to_ftw(100*MHz)
|
2016-03-18 12:54:15 +08:00
|
|
|
while True:
|
2016-03-27 22:26:44 +08:00
|
|
|
delay(10*ms)
|
2016-03-31 16:04:55 +08:00
|
|
|
for i in range(1250):
|
2016-03-27 22:26:44 +08:00
|
|
|
try:
|
2016-03-18 12:54:15 +08:00
|
|
|
with self.core_dds.batch:
|
2017-01-25 19:26:30 +08:00
|
|
|
self.dds0.set_mu(freq)
|
|
|
|
self.dds1.set_mu(freq)
|
2016-03-18 12:54:15 +08:00
|
|
|
delay_mu(dt)
|
2016-03-27 22:26:44 +08:00
|
|
|
except RTIOUnderflow:
|
|
|
|
dt += 100
|
|
|
|
self.core.break_realtime()
|
|
|
|
break
|
2016-03-18 12:54:15 +08:00
|
|
|
else:
|
2016-11-21 12:27:48 +08:00
|
|
|
self.set_dataset("pulse_rate", self.core.mu_to_seconds(dt//2))
|
2016-03-27 22:26:44 +08:00
|
|
|
return
|
2016-03-18 12:54:15 +08:00
|
|
|
|
|
|
|
|
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")
|
2015-07-05 10:35:02 +08:00
|
|
|
|
|
|
|
@kernel
|
|
|
|
def run(self):
|
|
|
|
with watchdog(50*ms):
|
|
|
|
while True:
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
2016-02-26 02:46:49 +08:00
|
|
|
class LoopbackCount(EnvExperiment):
|
2016-04-16 19:31:07 +08:00
|
|
|
def build(self, npulses):
|
2016-02-26 02:46:49 +08:00
|
|
|
self.setattr_device("core")
|
2016-02-26 14:29:51 +08:00
|
|
|
self.setattr_device("loop_in")
|
|
|
|
self.setattr_device("loop_out")
|
2016-04-16 19:31:07 +08:00
|
|
|
self.npulses = npulses
|
2016-02-26 02:46:49 +08:00
|
|
|
|
|
|
|
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()
|
2016-04-12 18:17:06 +08:00
|
|
|
self.loop_in.input()
|
2016-02-26 02:46:49 +08:00
|
|
|
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")
|
2015-07-05 10:35:02 +08:00
|
|
|
|
|
|
|
@kernel
|
|
|
|
def run(self):
|
2016-06-29 02:37:39 +08:00
|
|
|
self.core.reset()
|
2015-07-05 10:35:02 +08:00
|
|
|
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")
|
2015-07-05 10:35:02 +08:00
|
|
|
|
|
|
|
@kernel
|
|
|
|
def run(self):
|
2016-06-29 02:37:39 +08:00
|
|
|
self.core.reset()
|
2017-09-16 16:28:57 +08:00
|
|
|
delay(55*256*us)
|
|
|
|
for _ in range(256):
|
|
|
|
self.ttl_out.pulse(25*us)
|
|
|
|
delay(-75*us)
|
2015-07-05 10:35:02 +08:00
|
|
|
|
|
|
|
|
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)
|
2017-09-16 17:52:39 +08:00
|
|
|
while self.core.get_rtio_counter_mu() < now_mu():
|
|
|
|
pass
|
2015-07-29 19:43:35 +08:00
|
|
|
|
|
|
|
|
2016-03-08 15:58:25 +08:00
|
|
|
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()
|
2016-03-08 15:58:25 +08:00
|
|
|
self.loop_in.input()
|
|
|
|
self.loop_in.pulse(10*us)
|
2017-09-16 17:52:39 +08:00
|
|
|
while self.core.get_rtio_counter_mu() < now_mu():
|
|
|
|
pass
|
2016-03-08 15:58:25 +08:00
|
|
|
|
|
|
|
|
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):
|
2016-06-29 10:38:37 +08:00
|
|
|
self.core.reset()
|
2015-12-16 19:45:57 +08:00
|
|
|
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
|
2015-12-16 19:45:57 +08:00
|
|
|
def k(self, var):
|
|
|
|
self.set_dataset(var, now_mu())
|
|
|
|
delay_mu(1234)
|
2015-07-07 21:29:38 +08:00
|
|
|
|
|
|
|
def run(self):
|
2015-12-16 19:45:57 +08:00
|
|
|
self.k("t1")
|
|
|
|
self.k("t2")
|
2015-07-07 21:29:38 +08:00
|
|
|
|
|
|
|
|
2017-04-22 01:36:44 +08:00
|
|
|
class Rounding(EnvExperiment):
|
|
|
|
def build(self):
|
|
|
|
self.setattr_device("core")
|
|
|
|
|
|
|
|
@kernel
|
|
|
|
def run(self):
|
|
|
|
self.core.reset()
|
|
|
|
t1 = now_mu()
|
|
|
|
delay(8*us)
|
|
|
|
t2 = now_mu()
|
|
|
|
self.set_dataset("delta", t2 - t1)
|
|
|
|
|
|
|
|
|
2016-06-19 19:15:10 +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):
|
2018-01-24 00:28:39 +08:00
|
|
|
def test_rtio_counter(self):
|
|
|
|
self.execute(RTIOCounter)
|
|
|
|
dt = self.dataset_mgr.get("dt")
|
|
|
|
self.assertGreater(dt, 50*ns)
|
|
|
|
self.assertLess(dt, 200*ns)
|
|
|
|
|
2015-06-29 10:08:39 +08:00
|
|
|
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)
|
2017-10-30 23:07:54 +08:00
|
|
|
self.assertLess(rtt, 140*ns)
|
2015-06-26 12:19:11 +08:00
|
|
|
|
2015-07-05 00:36:01 +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")
|
2015-07-05 00:36:01 +08:00
|
|
|
self.assertEqual(count, 10)
|
|
|
|
|
2015-06-29 10:08:39 +08:00
|
|
|
def test_pulse_rate(self):
|
2016-04-06 00:55:13 +08:00
|
|
|
"""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)
|
2016-04-06 00:55:13 +08:00
|
|
|
self.assertLess(rate, 700*ns)
|
2015-06-29 10:08:39 +08:00
|
|
|
|
2016-03-18 12:54:15 +08:00
|
|
|
def test_pulse_rate_dds(self):
|
2016-04-06 00:55:13 +08:00
|
|
|
"""Minimum interval for sustained DDS frequency switching"""
|
2016-03-18 12:54:15 +08:00
|
|
|
self.execute(PulseRateDDS)
|
|
|
|
rate = self.dataset_mgr.get("pulse_rate")
|
|
|
|
print(rate)
|
2016-04-06 00:55:13 +08:00
|
|
|
self.assertGreater(rate, 1*us)
|
2017-03-17 19:17:47 +08:00
|
|
|
self.assertLess(rate, 16*us)
|
2016-03-18 12:54:15 +08:00
|
|
|
|
2016-02-26 02:46:49 +08:00
|
|
|
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)
|
|
|
|
|
2015-07-05 10:35:02 +08:00
|
|
|
def test_underflow(self):
|
2015-09-01 13:34:28 +08:00
|
|
|
with self.assertRaises(RTIOUnderflow):
|
2015-07-05 10:35:02 +08:00
|
|
|
self.execute(Underflow)
|
|
|
|
|
2017-09-29 14:40:06 +08:00
|
|
|
def execute_and_test_in_log(self, experiment, string):
|
2017-05-22 15:45:45 +08:00
|
|
|
core_addr = self.device_mgr.get_desc("core")["arguments"]["host"]
|
2017-05-22 17:32:11 +08:00
|
|
|
mgmt = CommMgmt(core_addr)
|
2017-03-30 02:34:02 +08:00
|
|
|
mgmt.clear_log()
|
2017-09-29 14:40:06 +08:00
|
|
|
self.execute(experiment)
|
2017-03-30 02:34:02 +08:00
|
|
|
log = mgmt.get_log()
|
2017-09-29 14:40:06 +08:00
|
|
|
self.assertIn(string, log)
|
2017-03-30 02:34:02 +08:00
|
|
|
mgmt.close()
|
2017-03-27 18:08:03 +08:00
|
|
|
|
2017-09-29 14:40:06 +08:00
|
|
|
def test_sequence_error(self):
|
|
|
|
self.execute_and_test_in_log(SequenceError, "RTIO sequence error")
|
|
|
|
|
|
|
|
def test_collision(self):
|
|
|
|
self.execute_and_test_in_log(Collision, "RTIO collision")
|
|
|
|
|
2017-03-27 18:08:03 +08:00
|
|
|
def test_address_collision(self):
|
2017-09-29 14:40:06 +08:00
|
|
|
self.execute_and_test_in_log(AddressCollision, "RTIO collision")
|
2016-03-08 15:58:25 +08:00
|
|
|
|
2015-07-05 10:35:02 +08:00
|
|
|
def test_watchdog(self):
|
|
|
|
# watchdog only works on the device
|
2016-03-19 06:48:26 +08:00
|
|
|
with self.assertRaises(exceptions.WatchdogExpired):
|
2015-07-05 10:35:02 +08:00
|
|
|
self.execute(Watchdog)
|
|
|
|
|
2016-02-24 21:10:26 +08:00
|
|
|
@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-03-08 14:04:51 +08:00
|
|
|
|
2016-11-21 14:40:47 +08:00
|
|
|
dead_time = self.device_mgr.get("core").mu_to_seconds(t2 - t1)
|
2015-07-07 21:48:47 +08:00
|
|
|
print(dead_time)
|
|
|
|
self.assertGreater(dead_time, 1*ms)
|
2015-12-29 02:57:33 +08:00
|
|
|
self.assertLess(dead_time, 2500*ms)
|
2015-07-07 21:29:38 +08:00
|
|
|
|
|
|
|
def test_handover(self):
|
|
|
|
self.execute(Handover)
|
2015-12-16 19:45:57 +08:00
|
|
|
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
|
|
|
|
2016-06-19 19:15:10 +08:00
|
|
|
def test_handover_exception(self):
|
|
|
|
self.execute(HandoverException)
|
|
|
|
self.assertEqual(self.dataset_mgr.get("t1") + 1234,
|
|
|
|
self.dataset_mgr.get("t2"))
|
|
|
|
|
2017-04-22 01:36:44 +08:00
|
|
|
def test_rounding(self):
|
|
|
|
self.execute(Rounding)
|
|
|
|
dt = self.dataset_mgr.get("delta")
|
|
|
|
self.assertEqual(dt, 8000)
|
|
|
|
|
2016-06-19 19:15:10 +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()
|
2016-11-21 12:27:48 +08:00
|
|
|
self.ts[i] = self.core.mu_to_seconds(t2 - t1)
|
2015-06-29 10:08:39 +08:00
|
|
|
|
|
|
|
def run(self):
|
2015-09-01 13:34:28 +08:00
|
|
|
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):
|
2016-02-24 21:10:26 +08:00
|
|
|
@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)
|
2016-11-13 07:06:33 +08:00
|
|
|
rpc_time_mean = self.dataset_mgr.get("rpc_time_mean")
|
|
|
|
print(rpc_time_mean)
|
|
|
|
self.assertGreater(rpc_time_mean, 100*ns)
|
2017-04-08 22:45:08 +08:00
|
|
|
self.assertLess(rpc_time_mean, 3.5*ms)
|
2016-11-13 07:06:33 +08:00
|
|
|
self.assertLess(self.dataset_mgr.get("rpc_time_stddev"), 1*ms)
|
2017-03-01 05:28:27 +08:00
|
|
|
|
|
|
|
|
|
|
|
class _DMA(EnvExperiment):
|
2017-10-30 22:57:12 +08:00
|
|
|
def build(self, trace_name="test_rtio"):
|
2017-03-01 05:28:27 +08:00
|
|
|
self.setattr_device("core")
|
|
|
|
self.setattr_device("core_dma")
|
2017-03-17 22:47:58 +08:00
|
|
|
self.setattr_device("ttl1")
|
2017-03-01 05:28:27 +08:00
|
|
|
self.trace_name = trace_name
|
2017-04-06 02:34:08 +08:00
|
|
|
self.delta = np.int64(0)
|
2017-03-01 05:28:27 +08:00
|
|
|
|
|
|
|
@kernel
|
2017-10-30 22:57:12 +08:00
|
|
|
def record(self, for_handle=True):
|
2017-03-01 05:28:27 +08:00
|
|
|
with self.core_dma.record(self.trace_name):
|
2017-10-30 22:57:12 +08:00
|
|
|
# When not using the handle, retrieving the DMA trace
|
|
|
|
# in dma.playback() can be slow. Allow some time.
|
|
|
|
if not for_handle:
|
|
|
|
delay(1*ms)
|
2017-03-01 05:28:27 +08:00
|
|
|
delay(100*ns)
|
2017-03-17 22:47:58 +08:00
|
|
|
self.ttl1.on()
|
2017-03-01 05:28:27 +08:00
|
|
|
delay(100*ns)
|
2017-03-17 22:47:58 +08:00
|
|
|
self.ttl1.off()
|
2017-03-01 05:28:27 +08:00
|
|
|
|
2017-04-15 15:26:18 +08:00
|
|
|
@kernel
|
|
|
|
def record_many(self, n):
|
|
|
|
t1 = self.core.get_rtio_counter_mu()
|
|
|
|
with self.core_dma.record(self.trace_name):
|
|
|
|
for i in range(n//2):
|
|
|
|
delay(100*ns)
|
|
|
|
self.ttl1.on()
|
|
|
|
delay(100*ns)
|
|
|
|
self.ttl1.off()
|
|
|
|
t2 = self.core.get_rtio_counter_mu()
|
|
|
|
self.set_dataset("dma_record_time", self.core.mu_to_seconds(t2 - t1))
|
|
|
|
|
2017-03-01 05:28:27 +08:00
|
|
|
@kernel
|
2017-10-30 22:57:12 +08:00
|
|
|
def playback(self, use_handle=True):
|
2017-04-18 16:11:14 +08:00
|
|
|
if use_handle:
|
|
|
|
handle = self.core_dma.get_handle(self.trace_name)
|
2017-10-30 22:57:12 +08:00
|
|
|
self.core.break_realtime()
|
|
|
|
start = now_mu()
|
2017-04-18 16:11:14 +08:00
|
|
|
self.core_dma.playback_handle(handle)
|
|
|
|
else:
|
2017-10-30 22:57:12 +08:00
|
|
|
self.core.break_realtime()
|
|
|
|
start = now_mu()
|
2017-04-18 16:11:14 +08:00
|
|
|
self.core_dma.playback(self.trace_name)
|
|
|
|
self.delta = now_mu() - start
|
2017-03-01 05:28:27 +08:00
|
|
|
|
2017-04-06 02:34:08 +08:00
|
|
|
@kernel
|
2017-04-18 16:11:14 +08:00
|
|
|
def playback_many(self, n):
|
|
|
|
handle = self.core_dma.get_handle(self.trace_name)
|
2017-10-30 22:57:12 +08:00
|
|
|
self.core.break_realtime()
|
2017-04-18 16:11:14 +08:00
|
|
|
t1 = self.core.get_rtio_counter_mu()
|
|
|
|
for i in range(n):
|
|
|
|
self.core_dma.playback_handle(handle)
|
|
|
|
t2 = self.core.get_rtio_counter_mu()
|
|
|
|
self.set_dataset("dma_playback_time", self.core.mu_to_seconds(t2 - t1))
|
2017-04-06 02:34:08 +08:00
|
|
|
|
2017-03-01 05:28:27 +08:00
|
|
|
@kernel
|
|
|
|
def erase(self):
|
|
|
|
self.core_dma.erase(self.trace_name)
|
|
|
|
|
|
|
|
@kernel
|
|
|
|
def nested(self):
|
|
|
|
with self.core_dma.record(self.trace_name):
|
|
|
|
with self.core_dma.record(self.trace_name):
|
|
|
|
pass
|
|
|
|
|
2017-04-18 16:11:14 +08:00
|
|
|
@kernel
|
|
|
|
def invalidate(self, mode):
|
|
|
|
self.record()
|
|
|
|
handle = self.core_dma.get_handle(self.trace_name)
|
|
|
|
if mode == 0:
|
|
|
|
self.record()
|
|
|
|
elif mode == 1:
|
|
|
|
self.erase()
|
|
|
|
self.core_dma.playback_handle(handle)
|
|
|
|
|
2017-03-01 05:28:27 +08:00
|
|
|
|
|
|
|
class DMATest(ExperimentCase):
|
|
|
|
def test_dma_storage(self):
|
|
|
|
exp = self.create(_DMA)
|
|
|
|
exp.record()
|
|
|
|
exp.record() # overwrite
|
2017-04-18 16:11:14 +08:00
|
|
|
exp.playback()
|
2017-03-01 05:28:27 +08:00
|
|
|
exp.erase()
|
|
|
|
with self.assertRaises(exceptions.DMAError):
|
2017-04-18 16:11:14 +08:00
|
|
|
exp.playback()
|
2017-03-01 05:28:27 +08:00
|
|
|
|
|
|
|
def test_dma_nested(self):
|
|
|
|
exp = self.create(_DMA)
|
|
|
|
with self.assertRaises(exceptions.DMAError):
|
|
|
|
exp.nested()
|
|
|
|
|
2017-03-17 22:47:58 +08:00
|
|
|
def test_dma_trace(self):
|
2017-05-22 15:45:45 +08:00
|
|
|
core_host = self.device_mgr.get_desc("core")["arguments"]["host"]
|
2017-03-17 22:47:58 +08:00
|
|
|
|
|
|
|
exp = self.create(_DMA)
|
2017-04-18 16:11:14 +08:00
|
|
|
|
|
|
|
for use_handle in [False, True]:
|
2017-10-30 22:57:12 +08:00
|
|
|
exp.record(use_handle)
|
2017-04-18 16:11:14 +08:00
|
|
|
get_analyzer_dump(core_host) # clear analyzer buffer
|
|
|
|
exp.playback(use_handle)
|
|
|
|
|
|
|
|
dump = decode_dump(get_analyzer_dump(core_host))
|
|
|
|
self.assertEqual(len(dump.messages), 3)
|
|
|
|
self.assertIsInstance(dump.messages[-1], StoppedMessage)
|
|
|
|
self.assertIsInstance(dump.messages[0], OutputMessage)
|
|
|
|
self.assertEqual(dump.messages[0].channel, 1)
|
|
|
|
self.assertEqual(dump.messages[0].address, 0)
|
|
|
|
self.assertEqual(dump.messages[0].data, 1)
|
|
|
|
self.assertIsInstance(dump.messages[1], OutputMessage)
|
|
|
|
self.assertEqual(dump.messages[1].channel, 1)
|
|
|
|
self.assertEqual(dump.messages[1].address, 0)
|
|
|
|
self.assertEqual(dump.messages[1].data, 0)
|
|
|
|
self.assertEqual(dump.messages[1].timestamp -
|
|
|
|
dump.messages[0].timestamp, 100)
|
2017-04-06 02:34:08 +08:00
|
|
|
|
|
|
|
def test_dma_delta(self):
|
|
|
|
exp = self.create(_DMA)
|
|
|
|
exp.record()
|
2017-04-18 16:11:14 +08:00
|
|
|
|
2017-10-30 22:57:12 +08:00
|
|
|
exp.record(False)
|
|
|
|
exp.playback(False)
|
|
|
|
self.assertEqual(exp.delta, 1000200)
|
|
|
|
|
|
|
|
exp.record(True)
|
|
|
|
exp.playback(True)
|
|
|
|
self.assertEqual(exp.delta, 200)
|
2017-04-15 15:26:18 +08:00
|
|
|
|
|
|
|
def test_dma_record_time(self):
|
|
|
|
exp = self.create(_DMA)
|
|
|
|
count = 20000
|
|
|
|
exp.record_many(count)
|
|
|
|
dt = self.dataset_mgr.get("dma_record_time")
|
|
|
|
print("dt={}, dt/count={}".format(dt, dt/count))
|
2017-06-21 18:33:58 +08:00
|
|
|
self.assertLess(dt/count, 20*us)
|
2017-04-18 16:11:14 +08:00
|
|
|
|
|
|
|
def test_dma_playback_time(self):
|
|
|
|
exp = self.create(_DMA)
|
|
|
|
count = 20000
|
2017-10-30 23:06:38 +08:00
|
|
|
exp.record_many(40)
|
2017-04-18 16:11:14 +08:00
|
|
|
exp.playback_many(count)
|
|
|
|
dt = self.dataset_mgr.get("dma_playback_time")
|
|
|
|
print("dt={}, dt/count={}".format(dt, dt/count))
|
2017-10-30 23:06:38 +08:00
|
|
|
self.assertLess(dt/count, 4.5*us)
|
2017-04-18 16:11:14 +08:00
|
|
|
|
2017-10-31 00:10:13 +08:00
|
|
|
def test_dma_underflow(self):
|
|
|
|
exp = self.create(_DMA)
|
|
|
|
exp.record()
|
|
|
|
with self.assertRaises(RTIOUnderflow):
|
|
|
|
exp.playback_many(20000)
|
|
|
|
|
2017-04-18 16:11:14 +08:00
|
|
|
def test_handle_invalidation(self):
|
|
|
|
exp = self.create(_DMA)
|
|
|
|
for mode in [0, 1]:
|
|
|
|
with self.assertRaises(exceptions.DMAError):
|
|
|
|
exp.invalidate(mode)
|