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.channel = channel
self.i_previous_timestamp = numpy.int64(0)
@kernel
def set_oe(self, oe):
rtio_output(now_mu(), self.channel, 1, 1 if oe else 0)
@ -184,88 +182,112 @@ class TTLInOut:
@kernel
def _set_sensitivity(self, value):
rtio_output(now_mu(), self.channel, 2, value)
self.i_previous_timestamp = now_mu()
@kernel
def gate_rising_mu(self, duration):
"""Register rising edge events for the specified 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(1)
delay_mu(duration)
self._set_sensitivity(0)
return now_mu()
@kernel
def gate_falling_mu(self, duration):
"""Register falling edge events for the specified 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(2)
delay_mu(duration)
self._set_sensitivity(0)
return now_mu()
@kernel
def gate_both_mu(self, duration):
"""Register both rising and falling edge events for the specified
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)
delay_mu(duration)
self._set_sensitivity(0)
return now_mu()
@kernel
def gate_rising(self, duration):
"""Register rising edge events for the specified 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(1)
delay(duration)
self._set_sensitivity(0)
return now_mu()
@kernel
def gate_falling(self, duration):
"""Register falling edge events for the specified 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(2)
delay(duration)
self._set_sensitivity(0)
return now_mu()
@kernel
def gate_both(self, duration):
"""Register both rising and falling edge events for the specified
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)
delay(duration)
self._set_sensitivity(0)
return now_mu()
@kernel
def count(self):
"""Poll the RTIO input during all the previously programmed gate
openings, and returns the number of registered events.
def count(self, up_to_timestamp_mu):
"""Poll the RTIO input up to the specified timestamp, and returns the
number of registered events.
This function does not interact with the time cursor."""
This function does not interact with the timeline cursor."""
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
return count
@kernel
def timestamp_mu(self):
def timestamp_mu(self, up_to_timestamp_mu):
"""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.
This function does not interact with the time cursor."""
return rtio_input_timestamp(self.i_previous_timestamp, self.channel)
This function does not interact with the timeline cursor."""
return rtio_input_timestamp(up_to_timestamp_mu, self.channel)
# Input API: sampling
@kernel

View File

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

View File

@ -38,13 +38,13 @@ class PhotonHistogram(EnvExperiment):
self.bd_dds.set(self.detect_f)
with parallel:
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.bd_sw.on()
self.bdd_sw.on()
return self.pmt.count()
return self.pmt.count(gate_end_mu)
@kernel
def run(self):

View File

@ -66,8 +66,8 @@ class TDR(EnvExperiment):
self.pmt0.gate_both_mu(2*p)
self.ttl2.pulse_mu(p)
for i in range(len(self.t)):
ti = self.pmt0.timestamp_mu()
ti = self.pmt0.timestamp_mu(now_mu())
if ti <= 0:
raise PulseNotReceivedError()
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
for count in range(100):
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)
self.laser_cooling.pulse(100*MHz, 100*us)
delay(5*us)
@ -35,8 +35,7 @@ class AluminumSpectroscopy(EnvExperiment):
delay(5*us)
with parallel:
self.state_detection.pulse(100*MHz, 10*us)
self.pmt.gate_rising(10*us)
photon_count = self.pmt.count()
photon_count = self.pmt.count(self.pmt.gate_rising(10*us))
if (photon_count < self.photon_limit_low or
photon_count > self.photon_limit_high):
break

View File

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

View File

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

View File

@ -68,7 +68,7 @@ class RTT(EnvExperiment):
delay(1*us)
t0 = now_mu()
self.ttl_inout.pulse(1*us)
t1 = self.ttl_inout.timestamp_mu()
t1 = self.ttl_inout.timestamp_mu(now_mu())
if t1 < 0:
raise PulseNotReceived()
self.set_dataset("rtt", self.core.mu_to_seconds(t1 - t0))
@ -92,7 +92,7 @@ class Loopback(EnvExperiment):
delay(1*us)
t0 = now_mu()
self.loop_out.pulse(1*us)
t1 = self.loop_in.timestamp_mu()
t1 = self.loop_in.timestamp_mu(now_mu())
if t1 < 0:
raise PulseNotReceived()
self.set_dataset("rtt", self.core.mu_to_seconds(t1 - t0))
@ -115,7 +115,7 @@ class ClockGeneratorLoopback(EnvExperiment):
with sequential:
delay(200*ns)
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):
@ -203,7 +203,7 @@ class LoopbackCount(EnvExperiment):
for i in range(self.npulses):
delay(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):

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.
If more than 20 rising edges were received it outputs a pulse::
input.gate_rising(500*ns)
if input.count() > 20:
if input.count(input.gate_rising(500*ns)) > 20:
delay(2*us)
output.pulse(500*ns)