ttl: Add target RTIO time argument to timestamp/count functions

Software-based tracking of timestamps is problematic (e.g. when
using DMA, see GitHub #1113).
pull/1191/head
David Nadlinger 2018-07-23 15:34:18 +01:00 committed by Sébastien Bourdeauducq
parent 2a0e1dabfb
commit cbdef0225c
9 changed files with 55 additions and 36 deletions

View File

@ -112,8 +112,6 @@ class TTLInOut:
self.core = dmgr.get(core_device) self.core = dmgr.get(core_device)
self.channel = channel self.channel = channel
self.i_previous_timestamp = numpy.int64(0)
@kernel @kernel
def set_oe(self, oe): def set_oe(self, oe):
rtio_output(now_mu(), self.channel, 1, 1 if oe else 0) rtio_output(now_mu(), self.channel, 1, 1 if oe else 0)
@ -184,88 +182,112 @@ class TTLInOut:
@kernel @kernel
def _set_sensitivity(self, value): def _set_sensitivity(self, value):
rtio_output(now_mu(), self.channel, 2, value) rtio_output(now_mu(), self.channel, 2, value)
self.i_previous_timestamp = now_mu()
@kernel @kernel
def gate_rising_mu(self, duration): def gate_rising_mu(self, duration):
"""Register rising edge events for the specified duration """Register rising edge events for the specified duration
(in machine units). (in machine units).
The time cursor is advanced by the specified duration.""" The time cursor is advanced by the specified duration.
:return: The timeline cursor at the end of the gate window.
"""
self._set_sensitivity(1) self._set_sensitivity(1)
delay_mu(duration) delay_mu(duration)
self._set_sensitivity(0) self._set_sensitivity(0)
return now_mu()
@kernel @kernel
def gate_falling_mu(self, duration): def gate_falling_mu(self, duration):
"""Register falling edge events for the specified duration """Register falling edge events for the specified duration
(in machine units). (in machine units).
The time cursor is advanced by the specified duration.""" The time cursor is advanced by the specified duration.
:return: The timeline cursor at the end of the gate window.
"""
self._set_sensitivity(2) self._set_sensitivity(2)
delay_mu(duration) delay_mu(duration)
self._set_sensitivity(0) self._set_sensitivity(0)
return now_mu()
@kernel @kernel
def gate_both_mu(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 (in machine units). duration (in machine units).
The time cursor is advanced by the specified duration.""" The time cursor is advanced by the specified duration.
:return: The timeline cursor at the end of the gate window.
"""
self._set_sensitivity(3) self._set_sensitivity(3)
delay_mu(duration) delay_mu(duration)
self._set_sensitivity(0) self._set_sensitivity(0)
return now_mu()
@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). (in seconds).
The time cursor is advanced by the specified duration.""" The time cursor is advanced by the specified duration.
:return: The timeline cursor at the end of the gate window.
"""
self._set_sensitivity(1) self._set_sensitivity(1)
delay(duration) delay(duration)
self._set_sensitivity(0) self._set_sensitivity(0)
return now_mu()
@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). (in seconds).
The time cursor is advanced by the specified duration.""" The time cursor is advanced by the specified duration.
:return: The timeline cursor at the end of the gate window.
"""
self._set_sensitivity(2) self._set_sensitivity(2)
delay(duration) delay(duration)
self._set_sensitivity(0) self._set_sensitivity(0)
return now_mu()
@kernel @kernel
def gate_both(self, duration): def gate_both(self, duration):
"""Register both rising and falling edge events for the specified """Register both rising and falling edge events for the specified
duration (in seconds). duration (in seconds).
The time cursor is advanced by the specified duration.""" The time cursor is advanced by the specified duration.
:return: The timeline cursor at the end of the gate window.
"""
self._set_sensitivity(3) self._set_sensitivity(3)
delay(duration) delay(duration)
self._set_sensitivity(0) self._set_sensitivity(0)
return now_mu()
@kernel @kernel
def count(self): def count(self, up_to_timestamp_mu):
"""Poll the RTIO input during all the previously programmed gate """Poll the RTIO input up to the specified timestamp, and returns the
openings, and returns the number of registered events. number of registered events.
This function does not interact with the time cursor.""" This function does not interact with the timeline cursor."""
count = 0 count = 0
while rtio_input_timestamp(self.i_previous_timestamp, self.channel) >= 0: while rtio_input_timestamp(up_to_timestamp_mu, self.channel) >= 0:
count += 1 count += 1
return count return count
@kernel @kernel
def timestamp_mu(self): def timestamp_mu(self, up_to_timestamp_mu):
"""Poll the RTIO input and returns an event timestamp (in machine """Poll the RTIO input and returns an event timestamp (in machine
units), according to the gating. units) according to the selected gates, or -1 if no event occured before
the specified timestamp.
If the gate is permanently closed, returns a negative value. If the gate is permanently closed, returns a negative value.
This function does not interact with the time cursor.""" This function does not interact with the timeline cursor."""
return rtio_input_timestamp(self.i_previous_timestamp, self.channel) return rtio_input_timestamp(up_to_timestamp_mu, self.channel)
# Input API: sampling # Input API: sampling
@kernel @kernel

View File

@ -152,7 +152,7 @@ class KasliTester(EnvExperiment):
for _ in range(n): for _ in range(n):
ttl_out.pulse(2*us) ttl_out.pulse(2*us)
delay(2*us) delay(2*us)
return ttl_in.count() == n return ttl_in.count(now_mu()) == n
def test_ttl_ins(self): def test_ttl_ins(self):
print("*** Testing TTL inputs.") print("*** Testing TTL inputs.")

View File

@ -38,13 +38,13 @@ class PhotonHistogram(EnvExperiment):
self.bd_dds.set(self.detect_f) self.bd_dds.set(self.detect_f)
with parallel: with parallel:
self.bd_sw.pulse(self.detect_t) self.bd_sw.pulse(self.detect_t)
self.pmt.gate_rising(self.detect_t) gate_end_mu = self.pmt.gate_rising(self.detect_t)
self.program_cooling() self.program_cooling()
self.bd_sw.on() self.bd_sw.on()
self.bdd_sw.on() self.bdd_sw.on()
return self.pmt.count() return self.pmt.count(gate_end_mu)
@kernel @kernel
def run(self): def run(self):

View File

@ -66,8 +66,8 @@ class TDR(EnvExperiment):
self.pmt0.gate_both_mu(2*p) self.pmt0.gate_both_mu(2*p)
self.ttl2.pulse_mu(p) self.ttl2.pulse_mu(p)
for i in range(len(self.t)): for i in range(len(self.t)):
ti = self.pmt0.timestamp_mu() ti = self.pmt0.timestamp_mu(now_mu())
if ti <= 0: if ti <= 0:
raise PulseNotReceivedError() raise PulseNotReceivedError()
self.t[i] = int(self.t[i] + ti - t0) self.t[i] = int(self.t[i] + ti - t0)
self.pmt0.count() # flush self.pmt0.count(now_mu()) # flush

View File

@ -21,7 +21,7 @@ class AluminumSpectroscopy(EnvExperiment):
state_0_count = 0 state_0_count = 0
for count in range(100): for count in range(100):
self.mains_sync.gate_rising(1*s/60) self.mains_sync.gate_rising(1*s/60)
at_mu(self.mains_sync.timestamp_mu() + 100*us) at_mu(self.mains_sync.timestamp_mu(now_mu()) + 100*us)
delay(10*us) delay(10*us)
self.laser_cooling.pulse(100*MHz, 100*us) self.laser_cooling.pulse(100*MHz, 100*us)
delay(5*us) delay(5*us)
@ -35,8 +35,7 @@ class AluminumSpectroscopy(EnvExperiment):
delay(5*us) delay(5*us)
with parallel: with parallel:
self.state_detection.pulse(100*MHz, 10*us) self.state_detection.pulse(100*MHz, 10*us)
self.pmt.gate_rising(10*us) photon_count = self.pmt.count(self.pmt.gate_rising(10*us))
photon_count = self.pmt.count()
if (photon_count < self.photon_limit_low or if (photon_count < self.photon_limit_low or
photon_count > self.photon_limit_high): photon_count > self.photon_limit_high):
break break

View File

@ -49,13 +49,13 @@ class Input:
delay(duration) delay(duration)
@kernel @kernel
def count(self): def count(self, up_to_timestamp_mu):
result = self.prng.randrange(0, 100) result = self.prng.randrange(0, 100)
time.manager.event(("count", self.name, result)) time.manager.event(("count", self.name, result))
return result return result
@kernel @kernel
def timestamp_mu(self): def timestamp_mu(self, up_to_timestamp_mu):
result = time.manager.get_time_mu() result = time.manager.get_time_mu()
result += self.prng.randrange(100, 1000) result += self.prng.randrange(100, 1000)
time.manager.event(("timestamp_mu", self.name, result)) time.manager.event(("timestamp_mu", self.name, result))

View File

@ -21,11 +21,10 @@ class CreateTTLPulse(EnvExperiment):
def run(self): def run(self):
self.core.break_realtime() self.core.break_realtime()
with parallel: with parallel:
self.loop_in.gate_both_mu(1200)
with sequential: with sequential:
delay_mu(100) delay_mu(100)
self.loop_out.pulse_mu(1000) self.loop_out.pulse_mu(1000)
self.loop_in.count() self.loop_in.count(self.loop_in.gate_both_mu(1200))
class WriteLog(EnvExperiment): class WriteLog(EnvExperiment):

View File

@ -68,7 +68,7 @@ class RTT(EnvExperiment):
delay(1*us) delay(1*us)
t0 = now_mu() t0 = now_mu()
self.ttl_inout.pulse(1*us) self.ttl_inout.pulse(1*us)
t1 = self.ttl_inout.timestamp_mu() t1 = self.ttl_inout.timestamp_mu(now_mu())
if t1 < 0: if t1 < 0:
raise PulseNotReceived() raise PulseNotReceived()
self.set_dataset("rtt", self.core.mu_to_seconds(t1 - t0)) self.set_dataset("rtt", self.core.mu_to_seconds(t1 - t0))
@ -92,7 +92,7 @@ class Loopback(EnvExperiment):
delay(1*us) delay(1*us)
t0 = now_mu() t0 = now_mu()
self.loop_out.pulse(1*us) self.loop_out.pulse(1*us)
t1 = self.loop_in.timestamp_mu() t1 = self.loop_in.timestamp_mu(now_mu())
if t1 < 0: if t1 < 0:
raise PulseNotReceived() raise PulseNotReceived()
self.set_dataset("rtt", self.core.mu_to_seconds(t1 - t0)) self.set_dataset("rtt", self.core.mu_to_seconds(t1 - t0))
@ -115,7 +115,7 @@ class ClockGeneratorLoopback(EnvExperiment):
with sequential: with sequential:
delay(200*ns) delay(200*ns)
self.loop_clock_out.set(1*MHz) self.loop_clock_out.set(1*MHz)
self.set_dataset("count", self.loop_clock_in.count()) self.set_dataset("count", self.loop_clock_in.count(now_mu()))
class PulseRate(EnvExperiment): class PulseRate(EnvExperiment):
@ -203,7 +203,7 @@ class LoopbackCount(EnvExperiment):
for i in range(self.npulses): for i in range(self.npulses):
delay(25*ns) delay(25*ns)
self.loop_out.pulse(25*ns) self.loop_out.pulse(25*ns)
self.set_dataset("count", self.loop_in.count()) self.set_dataset("count", self.loop_in.count(now_mu()))
class IncorrectLevel(Exception): class IncorrectLevel(Exception):

View File

@ -159,8 +159,7 @@ Input channels detect events, timestamp them, and place them in a buffer for the
The following example counts the rising edges occurring during a precisely timed 500 ns interval. The following example counts the rising edges occurring during a precisely timed 500 ns interval.
If more than 20 rising edges were received it outputs a pulse:: If more than 20 rising edges were received it outputs a pulse::
input.gate_rising(500*ns) if input.count(input.gate_rising(500*ns)) > 20:
if input.count() > 20:
delay(2*us) delay(2*us)
output.pulse(500*ns) output.pulse(500*ns)