forked from M-Labs/artiq
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).
This commit is contained in:
parent
2a0e1dabfb
commit
cbdef0225c
|
@ -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
|
||||
|
|
|
@ -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.")
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
Loading…
Reference in New Issue