From 753d61b38ffdf97e1c8f3fae28cf1806fa798d63 Mon Sep 17 00:00:00 2001 From: Sebastien Bourdeauducq Date: Sat, 4 Jul 2015 18:36:01 +0200 Subject: [PATCH] complete support for TTL clock generator --- artiq/coredevice/runtime.py | 1 + artiq/coredevice/ttl.py | 82 ++++++++++++++++++++++++--- artiq/gateware/rtio/phy/ttl_simple.py | 13 ++++- artiq/test/coredevice.py | 24 ++++++++ soc/runtime/gen_service_table.py | 1 + soc/runtime/moninj.c | 2 +- soc/runtime/ttl.c | 8 +++ soc/runtime/ttl.h | 1 + soc/targets/artiq_kc705.py | 4 +- soc/targets/artiq_pipistrello.py | 2 +- 10 files changed, 124 insertions(+), 14 deletions(-) diff --git a/artiq/coredevice/runtime.py b/artiq/coredevice/runtime.py index 1fff8f4fc..5841f9d65 100644 --- a/artiq/coredevice/runtime.py +++ b/artiq/coredevice/runtime.py @@ -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", diff --git a/artiq/coredevice/ttl.py b/artiq/coredevice/ttl.py index 9a363b83d..768fc161e 100644 --- a/artiq/coredevice/ttl.py +++ b/artiq/coredevice/ttl.py @@ -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 diff --git a/artiq/gateware/rtio/phy/ttl_simple.py b/artiq/gateware/rtio/phy/ttl_simple.py index 8a5e6e432..0127d19a0 100644 --- a/artiq/gateware/rtio/phy/ttl_simple.py +++ b/artiq/gateware/rtio/phy/ttl_simple.py @@ -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]) ] diff --git a/artiq/test/coredevice.py b/artiq/test/coredevice.py index 3e3ce9150..9d8673dfb 100644 --- a/artiq/test/coredevice.py +++ b/artiq/test/coredevice.py @@ -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) diff --git a/soc/runtime/gen_service_table.py b/soc/runtime/gen_service_table.py index b194662cd..27ae562b0 100755 --- a/soc/runtime/gen_service_table.py +++ b/soc/runtime/gen_service_table.py @@ -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"), diff --git a/soc/runtime/moninj.c b/soc/runtime/moninj.c index 34507d9a6..d7edff77d 100644 --- a/soc/runtime/moninj.c +++ b/soc/runtime/moninj.c @@ -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