From cbdef0225c274979478fed3057b31b5beed5a2be Mon Sep 17 00:00:00 2001 From: David Nadlinger Date: Mon, 23 Jul 2018 15:34:18 +0100 Subject: [PATCH] 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). --- artiq/coredevice/ttl.py | 58 +++++++++++++------ .../kasli_basic/repository/kasli_tester.py | 2 +- .../repository/photon_histogram.py | 4 +- .../kc705_nist_clock/repository/tdr.py | 4 +- .../no_hardware/repository/al_spectroscopy.py | 5 +- artiq/sim/devices.py | 4 +- artiq/test/coredevice/test_analyzer.py | 3 +- artiq/test/coredevice/test_rtio.py | 8 +-- doc/manual/rtio.rst | 3 +- 9 files changed, 55 insertions(+), 36 deletions(-) diff --git a/artiq/coredevice/ttl.py b/artiq/coredevice/ttl.py index df1036c2c..5746172b7 100644 --- a/artiq/coredevice/ttl.py +++ b/artiq/coredevice/ttl.py @@ -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 diff --git a/artiq/examples/kasli_basic/repository/kasli_tester.py b/artiq/examples/kasli_basic/repository/kasli_tester.py index 475925db2..b47a1e0c6 100644 --- a/artiq/examples/kasli_basic/repository/kasli_tester.py +++ b/artiq/examples/kasli_basic/repository/kasli_tester.py @@ -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.") diff --git a/artiq/examples/kc705_nist_clock/repository/photon_histogram.py b/artiq/examples/kc705_nist_clock/repository/photon_histogram.py index 6d860db0e..4a9166a8a 100644 --- a/artiq/examples/kc705_nist_clock/repository/photon_histogram.py +++ b/artiq/examples/kc705_nist_clock/repository/photon_histogram.py @@ -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): diff --git a/artiq/examples/kc705_nist_clock/repository/tdr.py b/artiq/examples/kc705_nist_clock/repository/tdr.py index a32015a7b..c304f268a 100644 --- a/artiq/examples/kc705_nist_clock/repository/tdr.py +++ b/artiq/examples/kc705_nist_clock/repository/tdr.py @@ -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 diff --git a/artiq/examples/no_hardware/repository/al_spectroscopy.py b/artiq/examples/no_hardware/repository/al_spectroscopy.py index de1cc51cc..d48c2ead7 100644 --- a/artiq/examples/no_hardware/repository/al_spectroscopy.py +++ b/artiq/examples/no_hardware/repository/al_spectroscopy.py @@ -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 diff --git a/artiq/sim/devices.py b/artiq/sim/devices.py index ff8e5eb57..41b7b59c5 100644 --- a/artiq/sim/devices.py +++ b/artiq/sim/devices.py @@ -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)) diff --git a/artiq/test/coredevice/test_analyzer.py b/artiq/test/coredevice/test_analyzer.py index ea3c93fe2..335c941c3 100644 --- a/artiq/test/coredevice/test_analyzer.py +++ b/artiq/test/coredevice/test_analyzer.py @@ -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): diff --git a/artiq/test/coredevice/test_rtio.py b/artiq/test/coredevice/test_rtio.py index 9631cd38e..12f167c1b 100644 --- a/artiq/test/coredevice/test_rtio.py +++ b/artiq/test/coredevice/test_rtio.py @@ -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): diff --git a/doc/manual/rtio.rst b/doc/manual/rtio.rst index 2d093b755..ce69b2bcd 100644 --- a/doc/manual/rtio.rst +++ b/doc/manual/rtio.rst @@ -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)