forked from M-Labs/artiq
parent
3ad68f65c5
commit
69e699c7bd
|
@ -106,11 +106,19 @@ class TTLInOut:
|
||||||
|
|
||||||
:param channel: channel number
|
:param channel: channel number
|
||||||
"""
|
"""
|
||||||
kernel_invariants = {"core", "channel"}
|
kernel_invariants = {"core", "channel", "gate_latency_mu"}
|
||||||
|
|
||||||
def __init__(self, dmgr, channel, core_device="core"):
|
def __init__(self, dmgr, channel, gate_latency_mu=None,
|
||||||
|
core_device="core"):
|
||||||
self.core = dmgr.get(core_device)
|
self.core = dmgr.get(core_device)
|
||||||
self.channel = channel
|
self.channel = channel
|
||||||
|
# With TTLs inputs, the gate control is connected to a high-latency
|
||||||
|
# path through SED. When looking at the RTIO counter to determine if
|
||||||
|
# the gate has closed, we need to take this latency into account.
|
||||||
|
# See: https://github.com/m-labs/artiq/issues/1137
|
||||||
|
if gate_latency_mu is None:
|
||||||
|
gate_latency_mu = 13*self.core.ref_multiplier
|
||||||
|
self.gate_latency_mu = gate_latency_mu
|
||||||
|
|
||||||
@kernel
|
@kernel
|
||||||
def set_oe(self, oe):
|
def set_oe(self, oe):
|
||||||
|
@ -323,7 +331,7 @@ class TTLInOut:
|
||||||
ttl_input.count(ttl_input.gate_rising(100 * us))
|
ttl_input.count(ttl_input.gate_rising(100 * us))
|
||||||
"""
|
"""
|
||||||
count = 0
|
count = 0
|
||||||
while rtio_input_timestamp(up_to_timestamp_mu, self.channel) >= 0:
|
while rtio_input_timestamp(up_to_timestamp_mu + self.gate_latency_mu, self.channel) >= 0:
|
||||||
count += 1
|
count += 1
|
||||||
return count
|
return count
|
||||||
|
|
||||||
|
@ -346,7 +354,7 @@ class TTLInOut:
|
||||||
:return: The timestamp (in machine units) of the first event received;
|
:return: The timestamp (in machine units) of the first event received;
|
||||||
-1 on timeout.
|
-1 on timeout.
|
||||||
"""
|
"""
|
||||||
return rtio_input_timestamp(up_to_timestamp_mu, self.channel)
|
return rtio_input_timestamp(up_to_timestamp_mu + self.gate_latency_mu, self.channel)
|
||||||
|
|
||||||
# Input API: sampling
|
# Input API: sampling
|
||||||
@kernel
|
@kernel
|
||||||
|
@ -414,7 +422,7 @@ class TTLInOut:
|
||||||
rtio_output(now_mu(), self.channel, 2, 0)
|
rtio_output(now_mu(), self.channel, 2, 0)
|
||||||
success = True
|
success = True
|
||||||
try:
|
try:
|
||||||
while rtio_input_timestamp(now_mu(), self.channel) != -1:
|
while rtio_input_timestamp(now_mu() + self.gate_latency_mu, self.channel) != -1:
|
||||||
success = False
|
success = False
|
||||||
except RTIOOverflow:
|
except RTIOOverflow:
|
||||||
success = False
|
success = False
|
||||||
|
|
|
@ -206,6 +206,66 @@ class LoopbackCount(EnvExperiment):
|
||||||
self.set_dataset("count", self.loop_in.count(now_mu()))
|
self.set_dataset("count", self.loop_in.count(now_mu()))
|
||||||
|
|
||||||
|
|
||||||
|
class IncorrectPulseTiming(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class LoopbackGateTiming(EnvExperiment):
|
||||||
|
def build(self):
|
||||||
|
self.setattr_device("core")
|
||||||
|
self.setattr_device("loop_in")
|
||||||
|
self.setattr_device("loop_out")
|
||||||
|
|
||||||
|
@kernel
|
||||||
|
def run(self):
|
||||||
|
# Make sure there are no leftover events.
|
||||||
|
self.core.reset()
|
||||||
|
self.loop_in.input()
|
||||||
|
self.loop_out.output()
|
||||||
|
delay_mu(500)
|
||||||
|
self.loop_out.off()
|
||||||
|
delay_mu(5000)
|
||||||
|
|
||||||
|
# Determine loop delay.
|
||||||
|
with parallel:
|
||||||
|
self.loop_in.gate_rising_mu(10000)
|
||||||
|
with sequential:
|
||||||
|
delay_mu(5000)
|
||||||
|
out_mu = now_mu()
|
||||||
|
self.loop_out.pulse_mu(1000)
|
||||||
|
in_mu = self.loop_in.timestamp_mu(now_mu())
|
||||||
|
if in_mu < 0:
|
||||||
|
raise PulseNotReceived("Cannot determine loop delay")
|
||||||
|
loop_delay_mu = in_mu - out_mu
|
||||||
|
|
||||||
|
# With the exact delay known, make sure tight gate timings work.
|
||||||
|
# In the most common configuration, 24 mu == 24 ns == 3 coarse periods,
|
||||||
|
# which should be plenty of slack.
|
||||||
|
delay_mu(10000)
|
||||||
|
|
||||||
|
gate_start_mu = now_mu()
|
||||||
|
self.loop_in.gate_both_mu(24)
|
||||||
|
gate_end_mu = now_mu()
|
||||||
|
|
||||||
|
# gateware latency offset between gate and input
|
||||||
|
lat_offset = 12*8
|
||||||
|
out_mu = gate_start_mu - loop_delay_mu + lat_offset
|
||||||
|
at_mu(out_mu)
|
||||||
|
self.loop_out.pulse_mu(24)
|
||||||
|
|
||||||
|
in_mu = self.loop_in.timestamp_mu(gate_end_mu)
|
||||||
|
if in_mu < 0:
|
||||||
|
raise PulseNotReceived()
|
||||||
|
if not (gate_start_mu <= (in_mu - lat_offset) <= gate_end_mu):
|
||||||
|
raise IncorrectPulseTiming("Input event should occur during gate")
|
||||||
|
if not (-2 < (in_mu - out_mu - loop_delay_mu) < 2):
|
||||||
|
raise IncorrectPulseTiming("Loop delay should not change")
|
||||||
|
|
||||||
|
in_mu = self.loop_in.timestamp_mu(gate_end_mu)
|
||||||
|
if in_mu > 0:
|
||||||
|
raise IncorrectPulseTiming("Only one pulse should be received")
|
||||||
|
|
||||||
|
|
||||||
class IncorrectLevel(Exception):
|
class IncorrectLevel(Exception):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
@ -430,6 +490,9 @@ class CoredeviceTest(ExperimentCase):
|
||||||
count = self.dataset_mgr.get("count")
|
count = self.dataset_mgr.get("count")
|
||||||
self.assertEqual(count, npulses)
|
self.assertEqual(count, npulses)
|
||||||
|
|
||||||
|
def test_loopback_gate_timing(self):
|
||||||
|
self.execute(LoopbackGateTiming)
|
||||||
|
|
||||||
def test_level(self):
|
def test_level(self):
|
||||||
self.execute(Level)
|
self.execute(Level)
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue