forked from M-Labs/artiq
complete support for TTL clock generator
This commit is contained in:
parent
a615a3830a
commit
753d61b38f
@ -21,6 +21,7 @@ _syscalls = {
|
||||
"ttl_set_oe": "Iib:n",
|
||||
"ttl_set_sensitivity": "Iii:n",
|
||||
"ttl_get": "iI:I",
|
||||
"ttl_clock_set": "Iii:n",
|
||||
"dds_init": "Ii:n",
|
||||
"dds_batch_enter": "I:n",
|
||||
"dds_batch_exit": "n:n",
|
||||
|
@ -26,7 +26,8 @@ class TTLOut(AutoDB):
|
||||
|
||||
@kernel
|
||||
def sync(self):
|
||||
"""Busy-waits until all programmed level switches have been effected."""
|
||||
"""Busy-wait until all programmed level switches have been
|
||||
effected."""
|
||||
while syscall("rtio_get_counter") < self.o_previous_timestamp:
|
||||
pass
|
||||
|
||||
@ -37,12 +38,12 @@ class TTLOut(AutoDB):
|
||||
|
||||
@kernel
|
||||
def off(self):
|
||||
"""Sets the output to a logic low state."""
|
||||
"""Set the output to a logic low state."""
|
||||
self.set_o(False)
|
||||
|
||||
@kernel
|
||||
def pulse_mu(self, duration):
|
||||
"""Pulses the output high for the specified duration
|
||||
"""Pulse the output high for the specified duration
|
||||
(in machine units)."""
|
||||
self.on()
|
||||
delay_mu(duration)
|
||||
@ -50,7 +51,7 @@ class TTLOut(AutoDB):
|
||||
|
||||
@kernel
|
||||
def pulse(self, duration):
|
||||
"""Pulses the output high for the specified duration
|
||||
"""Pulse the output high for the specified duration
|
||||
(in seconds)."""
|
||||
self.on()
|
||||
delay(duration)
|
||||
@ -103,18 +104,19 @@ class TTLInOut(AutoDB):
|
||||
|
||||
@kernel
|
||||
def sync(self):
|
||||
"""Busy-waits until all programmed level switches have been effected."""
|
||||
"""Busy-wait until all programmed level switches have been
|
||||
effected."""
|
||||
while syscall("rtio_get_counter") < self.o_previous_timestamp:
|
||||
pass
|
||||
|
||||
@kernel
|
||||
def on(self):
|
||||
"""Sets the output to a logic high state."""
|
||||
"""Set the output to a logic high state."""
|
||||
self.set_o(True)
|
||||
|
||||
@kernel
|
||||
def off(self):
|
||||
"""Sets the output to a logic low state."""
|
||||
"""Set the output to a logic low state."""
|
||||
self.set_o(False)
|
||||
|
||||
@kernel
|
||||
@ -204,3 +206,69 @@ class TTLInOut(AutoDB):
|
||||
If the gate is permanently closed, returns a negative value.
|
||||
"""
|
||||
return syscall("ttl_get", self.channel, self.i_previous_timestamp)
|
||||
|
||||
|
||||
class TTLClockGen(AutoDB):
|
||||
"""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
|
||||
"""
|
||||
class DBKeys:
|
||||
core = Device()
|
||||
channel = Argument()
|
||||
|
||||
def build(self):
|
||||
# in RTIO cycles
|
||||
self.previous_timestamp = int64(0)
|
||||
|
||||
@portable
|
||||
def frequency_to_ftw(self, frequency):
|
||||
"""Returns the frequency tuning word corresponding to the given
|
||||
frequency.
|
||||
"""
|
||||
return round(2**24*frequency*self.core.ref_period)
|
||||
|
||||
@portable
|
||||
def ftw_to_frequency(self, ftw):
|
||||
"""Returns the frequency corresponding to the given frequency tuning
|
||||
word.
|
||||
"""
|
||||
return ftw/self.core.ref_period/2**24
|
||||
|
||||
@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.
|
||||
"""
|
||||
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
|
||||
|
@ -78,7 +78,7 @@ class Inout(Module):
|
||||
|
||||
|
||||
class ClockGen(Module):
|
||||
def __init__(self, pad, ftw_width=16):
|
||||
def __init__(self, pad, ftw_width=24):
|
||||
self.rtlink = rtlink.Interface(
|
||||
rtlink.OInterface(ftw_width, suppress_nop=False))
|
||||
|
||||
@ -89,7 +89,14 @@ class ClockGen(Module):
|
||||
self.sync.rio += If(self.rtlink.o.stb, ftw.eq(self.rtlink.o.data))
|
||||
self.sync.rio_phy += [
|
||||
acc.eq(acc + ftw),
|
||||
# known phase on write: at rising edge
|
||||
If(self.rtlink.o.stb, acc.eq(2**(ftw_width - 1))),
|
||||
If(self.rtlink.o.stb,
|
||||
If(self.rtlink.o.data != 0,
|
||||
# known phase on frequency write: at rising edge
|
||||
acc.eq(2**(ftw_width - 1))
|
||||
).Else(
|
||||
# set output to 0 on stop
|
||||
acc.eq(0)
|
||||
)
|
||||
),
|
||||
pad.eq(acc[-1])
|
||||
]
|
||||
|
@ -46,6 +46,26 @@ class Loopback(Experiment, AutoDB):
|
||||
self.rtt = mu_to_seconds(self.loop_in.timestamp() - t0)
|
||||
|
||||
|
||||
class ClockGeneratorLoopback(Experiment, AutoDB):
|
||||
class DBKeys:
|
||||
core = Device()
|
||||
loop_clock_in = Device()
|
||||
loop_clock_out = Device()
|
||||
count = Result()
|
||||
|
||||
@kernel
|
||||
def run(self):
|
||||
self.loop_clock_in.input()
|
||||
self.loop_clock_out.stop()
|
||||
delay(1*us)
|
||||
with parallel:
|
||||
self.loop_clock_in.gate_rising(10*us)
|
||||
with sequential:
|
||||
delay(200*ns)
|
||||
self.loop_clock_out.set(1*MHz)
|
||||
self.count = self.loop_clock_in.count()
|
||||
|
||||
|
||||
class PulseRate(Experiment, AutoDB):
|
||||
class DBKeys:
|
||||
core = Device()
|
||||
@ -81,6 +101,10 @@ class CoredeviceTest(ExperimentCase):
|
||||
self.assertGreater(rtt, 0*ns)
|
||||
self.assertLess(rtt, 50*ns)
|
||||
|
||||
def test_clock_generator_loopback(self):
|
||||
count = self.execute(ClockGeneratorLoopback)["count"]
|
||||
self.assertEqual(count, 10)
|
||||
|
||||
def test_pulse_rate(self):
|
||||
rate = self.execute(PulseRate)["pulse_rate"]
|
||||
print(rate)
|
||||
|
@ -21,6 +21,7 @@ services = [
|
||||
("ttl_set_oe", "ttl_set_oe"),
|
||||
("ttl_set_sensitivity", "ttl_set_sensitivity"),
|
||||
("ttl_get", "ttl_get"),
|
||||
("ttl_clock_set", "ttl_clock_set"),
|
||||
|
||||
("dds_init", "dds_init"),
|
||||
("dds_batch_enter", "dds_batch_enter"),
|
||||
|
@ -51,7 +51,7 @@ static void moninj_monitor(const ip_addr_t *addr, u16_t port)
|
||||
reply.ttl_levels = 0;
|
||||
reply.ttl_oes = 0;
|
||||
reply.ttl_overrides = 0;
|
||||
for(i=0;i<RTIO_TTL_COUNT;i++) {
|
||||
for(i=0;i<RTIO_REGULAR_TTL_COUNT;i++) {
|
||||
rtio_moninj_mon_chan_sel_write(i);
|
||||
rtio_moninj_mon_probe_sel_write(0);
|
||||
rtio_moninj_mon_value_update_write(1);
|
||||
|
@ -56,3 +56,11 @@ long long int ttl_get(int channel, long long int time_limit)
|
||||
rtio_i_re_write(1);
|
||||
return r;
|
||||
}
|
||||
|
||||
void ttl_clock_set(long long int timestamp, int channel, int ftw)
|
||||
{
|
||||
rtio_chan_sel_write(channel);
|
||||
rtio_o_timestamp_write(timestamp);
|
||||
rtio_o_data_write(ftw);
|
||||
rtio_write_and_process_status(timestamp, channel);
|
||||
}
|
||||
|
@ -5,5 +5,6 @@ void ttl_set_o(long long int timestamp, int channel, int value);
|
||||
void ttl_set_oe(long long int timestamp, int channel, int oe);
|
||||
void ttl_set_sensitivity(long long int timestamp, int channel, int sensitivity);
|
||||
long long int ttl_get(int channel, long long int time_limit);
|
||||
void ttl_clock_set(long long int timestamp, int channel, int ftw);
|
||||
|
||||
#endif /* __TTL_H */
|
||||
|
@ -103,7 +103,7 @@ class NIST_QC1(_NIST_QCx):
|
||||
phy = ttl_simple.Output(platform.request("user_led", 2))
|
||||
self.submodules += phy
|
||||
rtio_channels.append(rtio.Channel.from_phy(phy))
|
||||
self.add_constant("RTIO_TTL_COUNT", len(rtio_channels))
|
||||
self.add_constant("RTIO_REGULAR_TTL_COUNT", len(rtio_channels))
|
||||
|
||||
phy = ttl_simple.ClockGen(platform.request("ttl", 15))
|
||||
self.submodules += phy
|
||||
@ -142,7 +142,7 @@ class NIST_QC2(_NIST_QCx):
|
||||
phy = ttl_simple.Output(platform.request("user_led", 2))
|
||||
self.submodules += phy
|
||||
rtio_channels.append(rtio.Channel.from_phy(phy))
|
||||
self.add_constant("RTIO_TTL_COUNT", len(rtio_channels))
|
||||
self.add_constant("RTIO_REGULAR_TTL_COUNT", len(rtio_channels))
|
||||
|
||||
phy = ttl_simple.ClockGen(platform.request("ttl", 14))
|
||||
self.submodules += phy
|
||||
|
@ -113,7 +113,7 @@ trce -v 12 -fastpaths -tsi {build_name}.tsi -o {build_name}.twr {build_name}.ncd
|
||||
self.submodules += phy
|
||||
rtio_channels.append(rtio.Channel.from_phy(phy, ofifo_depth=4))
|
||||
|
||||
self.add_constant("RTIO_TTL_COUNT", len(rtio_channels))
|
||||
self.add_constant("RTIO_REGULAR_TTL_COUNT", len(rtio_channels))
|
||||
|
||||
phy = ttl_simple.ClockGen(platform.request("ttl", 15))
|
||||
self.submodules += phy
|
||||
|
Loading…
Reference in New Issue
Block a user