2014-09-12 15:28:02 +08:00
|
|
|
from artiq.language.core import *
|
|
|
|
|
|
|
|
|
2015-07-14 04:08:20 +08:00
|
|
|
class TTLOut:
|
2015-05-02 10:35:21 +08:00
|
|
|
"""RTIO TTL output driver.
|
2014-09-30 16:42:07 +08:00
|
|
|
|
2015-05-02 10:35:21 +08:00
|
|
|
This should be used with output-only channels.
|
2014-09-30 16:42:07 +08:00
|
|
|
|
|
|
|
:param core: core device
|
|
|
|
:param channel: channel number
|
|
|
|
"""
|
2015-07-14 04:08:20 +08:00
|
|
|
def __init__(self, dmgr, channel):
|
|
|
|
self.core = dmgr.get("core")
|
|
|
|
self.channel = channel
|
2015-04-14 19:44:45 +08:00
|
|
|
|
2015-05-02 10:35:21 +08:00
|
|
|
# in RTIO cycles
|
|
|
|
self.o_previous_timestamp = int64(0)
|
2015-04-14 19:44:45 +08:00
|
|
|
|
|
|
|
@kernel
|
2015-07-02 04:22:53 +08:00
|
|
|
def set_o(self, o):
|
|
|
|
syscall("ttl_set_o", now_mu(), self.channel, o)
|
|
|
|
self.o_previous_timestamp = now_mu()
|
2014-09-15 22:48:22 +08:00
|
|
|
|
2014-09-12 15:28:02 +08:00
|
|
|
@kernel
|
|
|
|
def sync(self):
|
2015-07-05 00:36:01 +08:00
|
|
|
"""Busy-wait until all programmed level switches have been
|
|
|
|
effected."""
|
2015-05-02 10:35:21 +08:00
|
|
|
while syscall("rtio_get_counter") < self.o_previous_timestamp:
|
2014-11-30 00:13:54 +08:00
|
|
|
pass
|
2014-09-12 15:28:02 +08:00
|
|
|
|
|
|
|
@kernel
|
|
|
|
def on(self):
|
2015-04-14 19:44:45 +08:00
|
|
|
"""Sets the output to a logic high state."""
|
2015-07-02 04:22:53 +08:00
|
|
|
self.set_o(True)
|
2014-09-12 15:28:02 +08:00
|
|
|
|
|
|
|
@kernel
|
|
|
|
def off(self):
|
2015-07-05 00:36:01 +08:00
|
|
|
"""Set the output to a logic low state."""
|
2015-07-02 04:22:53 +08:00
|
|
|
self.set_o(False)
|
|
|
|
|
|
|
|
@kernel
|
|
|
|
def pulse_mu(self, duration):
|
2015-07-05 00:36:01 +08:00
|
|
|
"""Pulse the output high for the specified duration
|
2015-07-02 04:22:53 +08:00
|
|
|
(in machine units)."""
|
|
|
|
self.on()
|
|
|
|
delay_mu(duration)
|
|
|
|
self.off()
|
2014-09-12 15:28:02 +08:00
|
|
|
|
|
|
|
@kernel
|
|
|
|
def pulse(self, duration):
|
2015-07-05 00:36:01 +08:00
|
|
|
"""Pulse the output high for the specified duration
|
2015-07-02 04:22:53 +08:00
|
|
|
(in seconds)."""
|
2014-09-12 15:28:02 +08:00
|
|
|
self.on()
|
|
|
|
delay(duration)
|
|
|
|
self.off()
|
2014-09-13 19:37:57 +08:00
|
|
|
|
|
|
|
|
2015-07-14 04:08:20 +08:00
|
|
|
class TTLInOut:
|
2015-05-02 10:35:21 +08:00
|
|
|
"""RTIO TTL input/output driver.
|
|
|
|
|
|
|
|
In output mode, provides functions to set the logic level on the signal.
|
2014-09-30 16:42:07 +08:00
|
|
|
|
2015-05-02 10:35:21 +08:00
|
|
|
In input mode, provides functions to analyze the incoming signal, with
|
|
|
|
real-time gating to prevent overflows.
|
|
|
|
|
|
|
|
RTIO TTLs supports zero-length transition suppression. For example, if
|
|
|
|
two pulses are emitted back-to-back with no delay between them, they will
|
|
|
|
be merged into a single pulse with a duration equal to the sum of the
|
|
|
|
durations of the original pulses.
|
|
|
|
|
|
|
|
This should be used with bidirectional channels.
|
2014-09-30 16:42:07 +08:00
|
|
|
|
|
|
|
:param core: core device
|
|
|
|
:param channel: channel number
|
|
|
|
"""
|
2015-07-14 04:08:20 +08:00
|
|
|
def __init__(self, dmgr, channel):
|
|
|
|
self.core = dmgr.get("core")
|
|
|
|
self.channel = channel
|
2015-04-14 19:44:45 +08:00
|
|
|
|
2015-05-02 10:35:21 +08:00
|
|
|
# in RTIO cycles
|
|
|
|
self.o_previous_timestamp = int64(0)
|
|
|
|
self.i_previous_timestamp = int64(0)
|
2015-04-14 19:44:45 +08:00
|
|
|
|
|
|
|
@kernel
|
2015-07-02 04:22:53 +08:00
|
|
|
def set_oe(self, oe):
|
|
|
|
syscall("ttl_set_oe", now_mu(), self.channel, oe)
|
2015-05-02 10:35:21 +08:00
|
|
|
|
|
|
|
@kernel
|
|
|
|
def output(self):
|
2015-07-02 04:22:53 +08:00
|
|
|
self.set_oe(True)
|
2015-05-02 10:35:21 +08:00
|
|
|
|
|
|
|
@kernel
|
|
|
|
def input(self):
|
2015-07-02 04:22:53 +08:00
|
|
|
self.set_oe(False)
|
2015-05-02 10:35:21 +08:00
|
|
|
|
|
|
|
@kernel
|
2015-07-02 04:22:53 +08:00
|
|
|
def set_o(self, o):
|
|
|
|
syscall("ttl_set_o", now_mu(), self.channel, o)
|
|
|
|
self.o_previous_timestamp = now_mu()
|
2015-05-02 10:35:21 +08:00
|
|
|
|
|
|
|
@kernel
|
|
|
|
def sync(self):
|
2015-07-05 00:36:01 +08:00
|
|
|
"""Busy-wait until all programmed level switches have been
|
|
|
|
effected."""
|
2015-05-02 10:35:21 +08:00
|
|
|
while syscall("rtio_get_counter") < self.o_previous_timestamp:
|
|
|
|
pass
|
|
|
|
|
|
|
|
@kernel
|
|
|
|
def on(self):
|
2015-07-05 00:36:01 +08:00
|
|
|
"""Set the output to a logic high state."""
|
2015-07-02 04:22:53 +08:00
|
|
|
self.set_o(True)
|
2015-05-02 10:35:21 +08:00
|
|
|
|
|
|
|
@kernel
|
|
|
|
def off(self):
|
2015-07-05 00:36:01 +08:00
|
|
|
"""Set the output to a logic low state."""
|
2015-07-02 04:22:53 +08:00
|
|
|
self.set_o(False)
|
|
|
|
|
|
|
|
@kernel
|
|
|
|
def pulse_mu(self, duration):
|
|
|
|
"""Pulses the output high for the specified duration
|
|
|
|
(in machine units)."""
|
|
|
|
self.on()
|
|
|
|
delay_mu(duration)
|
|
|
|
self.off()
|
2015-05-02 10:35:21 +08:00
|
|
|
|
|
|
|
@kernel
|
|
|
|
def pulse(self, duration):
|
2015-07-02 04:22:53 +08:00
|
|
|
"""Pulses the output high for the specified duration
|
|
|
|
(in seconds)."""
|
2015-05-02 10:35:21 +08:00
|
|
|
self.on()
|
|
|
|
delay(duration)
|
|
|
|
self.off()
|
2015-04-14 19:44:45 +08:00
|
|
|
|
|
|
|
@kernel
|
|
|
|
def _set_sensitivity(self, value):
|
2015-07-02 04:22:53 +08:00
|
|
|
syscall("ttl_set_sensitivity", now_mu(), self.channel, 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)."""
|
|
|
|
self._set_sensitivity(1)
|
|
|
|
delay_mu(duration)
|
|
|
|
self._set_sensitivity(0)
|
|
|
|
|
|
|
|
@kernel
|
|
|
|
def gate_falling_mu(self, duration):
|
|
|
|
"""Register falling edge events for the specified duration
|
|
|
|
(in machine units)."""
|
|
|
|
self._set_sensitivity(2)
|
|
|
|
delay_mu(duration)
|
|
|
|
self._set_sensitivity(0)
|
|
|
|
|
|
|
|
@kernel
|
|
|
|
def gate_both_mu(self, duration):
|
|
|
|
"""Register both rising and falling edge events for the specified
|
|
|
|
duration (in machine units)."""
|
|
|
|
self._set_sensitivity(3)
|
|
|
|
delay_mu(duration)
|
|
|
|
self._set_sensitivity(0)
|
2014-09-13 19:37:57 +08:00
|
|
|
|
|
|
|
@kernel
|
2014-09-30 16:42:07 +08:00
|
|
|
def gate_rising(self, duration):
|
2015-07-02 04:22:53 +08:00
|
|
|
"""Register rising edge events for the specified duration
|
|
|
|
(in seconds)."""
|
2015-04-14 19:44:45 +08:00
|
|
|
self._set_sensitivity(1)
|
2014-09-15 22:48:22 +08:00
|
|
|
delay(duration)
|
2015-04-14 19:44:45 +08:00
|
|
|
self._set_sensitivity(0)
|
2014-09-15 22:48:22 +08:00
|
|
|
|
|
|
|
@kernel
|
2014-09-30 16:42:07 +08:00
|
|
|
def gate_falling(self, duration):
|
2015-07-02 04:22:53 +08:00
|
|
|
"""Register falling edge events for the specified duration
|
|
|
|
(in seconds)."""
|
2015-04-14 19:44:45 +08:00
|
|
|
self._set_sensitivity(2)
|
2014-09-15 22:48:22 +08:00
|
|
|
delay(duration)
|
2015-04-14 19:44:45 +08:00
|
|
|
self._set_sensitivity(0)
|
2014-09-15 22:48:22 +08:00
|
|
|
|
|
|
|
@kernel
|
2015-07-02 04:22:53 +08:00
|
|
|
def gate_both_mu(self, duration):
|
2014-09-30 16:42:07 +08:00
|
|
|
"""Register both rising and falling edge events for the specified
|
2015-07-02 04:22:53 +08:00
|
|
|
duration (in seconds)."""
|
2015-04-14 19:44:45 +08:00
|
|
|
self._set_sensitivity(3)
|
2014-09-15 22:48:22 +08:00
|
|
|
delay(duration)
|
2015-04-14 19:44:45 +08:00
|
|
|
self._set_sensitivity(0)
|
2014-10-21 23:14:01 +08:00
|
|
|
|
2014-09-13 19:37:57 +08:00
|
|
|
@kernel
|
2014-09-30 16:42:07 +08:00
|
|
|
def count(self):
|
|
|
|
"""Poll the RTIO input during all the previously programmed gate
|
2015-04-15 13:53:00 +08:00
|
|
|
openings, and returns the number of registered events."""
|
2014-09-15 22:48:22 +08:00
|
|
|
count = 0
|
2015-05-08 16:20:12 +08:00
|
|
|
while syscall("ttl_get", self.channel,
|
2015-05-02 10:35:21 +08:00
|
|
|
self.i_previous_timestamp) >= 0:
|
2014-09-15 22:48:22 +08:00
|
|
|
count += 1
|
|
|
|
return count
|
2014-10-14 15:54:10 +08:00
|
|
|
|
|
|
|
@kernel
|
|
|
|
def timestamp(self):
|
|
|
|
"""Poll the RTIO input and returns an event timestamp, according to
|
|
|
|
the gating.
|
|
|
|
|
|
|
|
If the gate is permanently closed, returns a negative value.
|
|
|
|
"""
|
2015-07-02 04:22:53 +08:00
|
|
|
return syscall("ttl_get", self.channel, self.i_previous_timestamp)
|
2015-07-05 00:36:01 +08:00
|
|
|
|
|
|
|
|
2015-07-14 04:08:20 +08:00
|
|
|
class TTLClockGen:
|
2015-07-05 00:36:01 +08:00
|
|
|
"""RTIO TTL clock generator driver.
|
|
|
|
|
|
|
|
This should be used with TTL channels that have a clock generator
|
|
|
|
built into the gateware (not compatible with regular TTL channels).
|
|
|
|
|
|
|
|
:param core: core device
|
|
|
|
:param channel: channel number
|
|
|
|
"""
|
2015-07-14 04:08:20 +08:00
|
|
|
def __init__(self, dmgr, channel):
|
|
|
|
self.core = dmgr.get("core")
|
|
|
|
self.channel = channel
|
2015-07-05 00:36:01 +08:00
|
|
|
|
|
|
|
def build(self):
|
|
|
|
# in RTIO cycles
|
|
|
|
self.previous_timestamp = int64(0)
|
2015-07-05 01:21:25 +08:00
|
|
|
self.acc_width = 24
|
2015-07-05 00:36:01 +08:00
|
|
|
|
|
|
|
@portable
|
|
|
|
def frequency_to_ftw(self, frequency):
|
|
|
|
"""Returns the frequency tuning word corresponding to the given
|
|
|
|
frequency.
|
|
|
|
"""
|
2015-07-05 01:21:25 +08:00
|
|
|
return round(2**self.acc_width*frequency*self.core.ref_period)
|
2015-07-05 00:36:01 +08:00
|
|
|
|
|
|
|
@portable
|
|
|
|
def ftw_to_frequency(self, ftw):
|
|
|
|
"""Returns the frequency corresponding to the given frequency tuning
|
|
|
|
word.
|
|
|
|
"""
|
2015-07-05 01:21:25 +08:00
|
|
|
return ftw/self.core.ref_period/2**self.acc_width
|
2015-07-05 00:36:01 +08:00
|
|
|
|
|
|
|
@kernel
|
|
|
|
def set_mu(self, frequency):
|
|
|
|
"""Set the frequency of the clock, in machine units.
|
|
|
|
|
|
|
|
This also sets the phase, as the time of the first generated rising
|
|
|
|
edge corresponds to the time of the call.
|
|
|
|
|
|
|
|
The clock generator contains a 24-bit phase accumulator operating on
|
|
|
|
the RTIO clock. At each RTIO clock tick, the frequency tuning word is
|
|
|
|
added to the phase accumulator. The most significant bit of the phase
|
|
|
|
accumulator is connected to the TTL line. Setting the frequency tuning
|
|
|
|
word has the additional effect of setting the phase accumulator to
|
|
|
|
0x800000.
|
2015-07-06 01:07:13 +08:00
|
|
|
|
|
|
|
Due to the way the clock generator operates, frequency tuning words
|
|
|
|
that are not powers of two cause jitter of one RTIO clock cycle at the
|
|
|
|
output.
|
2015-07-05 00:36:01 +08:00
|
|
|
"""
|
|
|
|
syscall("ttl_clock_set", now_mu(), self.channel, frequency)
|
|
|
|
self.previous_timestamp = now_mu()
|
|
|
|
|
|
|
|
@kernel
|
|
|
|
def set(self, frequency):
|
|
|
|
"""Like ``set_mu``, but using Hz."""
|
|
|
|
self.set_mu(self.frequency_to_ftw(frequency))
|
|
|
|
|
|
|
|
@kernel
|
|
|
|
def stop(self):
|
|
|
|
"""Stop the toggling of the clock and set the output level to 0."""
|
|
|
|
self.set_mu(0)
|
|
|
|
|
|
|
|
@kernel
|
|
|
|
def sync(self):
|
|
|
|
"""Busy-wait until all programmed frequency switches and stops have
|
|
|
|
been effected."""
|
|
|
|
while syscall("rtio_get_counter") < self.o_previous_timestamp:
|
|
|
|
pass
|