From 17807593274b0b6195ea59286674f4b8678b3861 Mon Sep 17 00:00:00 2001 From: Sebastien Bourdeauducq Date: Thu, 20 Nov 2014 12:32:56 -0800 Subject: [PATCH] dds: phase control (mostly untested) --- artiq/coredevice/dds.py | 84 +++++++++++++++++++++++++++++-------- artiq/coredevice/runtime.py | 5 ++- soc/runtime/dds.c | 49 ++++++++++++++++++---- soc/runtime/dds.h | 5 ++- soc/runtime/main.c | 1 - soc/runtime/rtio.c | 13 +++--- soc/runtime/rtio.h | 1 + soc/runtime/services.c | 1 + 8 files changed, 125 insertions(+), 34 deletions(-) diff --git a/artiq/coredevice/dds.py b/artiq/coredevice/dds.py index dce18f2e9..527c6d9ee 100644 --- a/artiq/coredevice/dds.py +++ b/artiq/coredevice/dds.py @@ -3,6 +3,12 @@ from artiq.language.units import * from artiq.coredevice import rtio +PHASE_MODE_DEFAULT = -1 +PHASE_MODE_CONTINUOUS = 0 +PHASE_MODE_ABSOLUTE = 1 +PHASE_MODE_TRACKING = 2 + + class DDS(AutoContext): """Core device Direct Digital Synthesis (DDS) driver. @@ -21,6 +27,7 @@ class DDS(AutoContext): def build(self): self.previous_frequency = 0*MHz + self.set_phase_mode(PHASE_MODE_CONTINUOUS) self.sw = rtio.RTIOOut(self, channel=self.rtio_switch) @portable @@ -40,33 +47,71 @@ class DDS(AutoContext): return ftw*self.dds_sysclk/2**32 @kernel - def on(self, frequency): - """Sets the DDS channel to the specified frequency and turns it on. + def set_phase_mode(self, phase_mode): + """Sets the phase mode of the DDS channel. Supported phase modes are: - If the DDS channel was already on, a real-time frequency update is - performed. + * ``PHASE_MODE_CONTINUOUS``: the phase accumulator is unchanged when + switching frequencies. The DDS phase is the sum of the phase + accumulator and the phase offset. The only discrete jumps in the + DDS output phase come from changes to the phase offset. + + * ``PHASE_MODE_ABSOLUTE``: the phase accumulator is reset when + switching frequencies. Thus, the phase of the DDS at the time of + the frequency change is equal to the phase offset. + + * ``PHASE_MODE_TRACKING``: when switching frequencies, the phase + accumulator is set to the value it would have if the DDS had been + running at the specified frequency since the start of the + experiment. """ + self.phase_mode = phase_mode + syscall("dds_phase_clear_en", self.reg_channel, + self.phase_mode != PHASE_MODE_CONTINUOUS) + + @kernel + def on(self, frequency, phase_mode=PHASE_MODE_DEFAULT, phase_offset=0): + """Sets the DDS channel to the specified frequency and turns it on. + + If the DDS channel was already on, a real-time frequency and phase + update is performed. + + :param frequency: frequency to generate. + :param phase_mode: if specified, overrides the default phase mode set + by ``set_phase_mode`` for this call. + :param phase_offset: adds an offset, in turns, to the phase. + + """ + if phase_mode != PHASE_MODE_DEFAULT: + old_phase_mode = self.phase_mode + self.set_phase_mode(phase_mode) + if self.previous_frequency != frequency: merge = self.sw.previous_timestamp == time_to_cycles(now()) if not merge: self.sw.sync() - if merge or bool(self.sw.previous_value): - # Channel is already on. - # Precise timing of frequency change is required. - fud_time = time_to_cycles(now()) + # Channel is already on: + # Precise timing of frequency change is required. + # Channel is off: + # Use soft timing on FUD to prevent conflicts when reprogramming + # several channels that need to be turned on at the same time. + rt_fud = merge or bool(self.sw.previous_value) + ftw = self.frequency_to_ftw(frequency) + if self.phase_mode != PHASE_MODE_CONTINUOUS: + phase_per_microcycle = ftw*int64( + self.dds_sysclk.amount*self.core.runtime_env.ref_period) else: - # Channel is off. - # Use soft timing on FUD to prevent conflicts when - # reprogramming several channels that need to be turned on at - # the same time. - fud_time = -1 - syscall("dds_program", self.reg_channel, - self.frequency_to_ftw(frequency), - fud_time) + phase_per_microcycle = int64(0) + syscall("dds_program", time_to_cycles(now()), self.reg_channel, + ftw, int(phase_offset*2**14), + phase_per_microcycle, + rt_fud, self.phase_mode == PHASE_MODE_TRACKING) self.previous_frequency = frequency self.sw.on() + if phase_mode != PHASE_MODE_DEFAULT: + self.set_phase_mode(old_phase_mode) + @kernel def off(self): """Turns the DDS channel off. @@ -75,13 +120,16 @@ class DDS(AutoContext): self.sw.off() @kernel - def pulse(self, frequency, duration): + def pulse(self, frequency, duration, + phase_mode=PHASE_MODE_DEFAULT, phase_offset=0): """Pulses the DDS channel for the specified duration at the specified frequency. + See ``on`` for a description of the parameters. + Equivalent to a ``on``, ``delay``, ``off`` sequence. """ - self.on(frequency) + self.on(frequency, phase_mode, phase_offset) delay(duration) self.off() diff --git a/artiq/coredevice/runtime.py b/artiq/coredevice/runtime.py index 896b5abe8..415bd0970 100644 --- a/artiq/coredevice/runtime.py +++ b/artiq/coredevice/runtime.py @@ -19,17 +19,20 @@ _syscalls = { "rtio_sync": "i:n", "rtio_get": "i:I", "rtio_pileup_count": "i:i", - "dds_program": "iiI:n", + "dds_phase_clear_en": "ib:n", + "dds_program": "IiiiIbb:n", } _chr_to_type = { "n": lambda: lc.Type.void(), + "b": lambda: lc.Type.int(1), "i": lambda: lc.Type.int(32), "I": lambda: lc.Type.int(64) } _chr_to_value = { "n": lambda: base_types.VNone(), + "b": lambda: base_types.VBool(), "i": lambda: base_types.VInt(), "I": lambda: base_types.VInt(64) } diff --git a/soc/runtime/dds.c b/soc/runtime/dds.c index 9fcfe9cfb..ec7239a2a 100644 --- a/soc/runtime/dds.c +++ b/soc/runtime/dds.c @@ -9,6 +9,8 @@ #define DDS_FTW1 0x0b #define DDS_FTW2 0x0c #define DDS_FTW3 0x0d +#define DDS_POW0 0x0e +#define DDS_POW1 0x0f #define DDS_GPIO 0x41 #define DDS_READ(addr) \ @@ -25,22 +27,55 @@ void dds_init(void) DDS_WRITE(DDS_GPIO, i); DDS_WRITE(DDS_GPIO, i | (1 << 7)); DDS_WRITE(DDS_GPIO, i); - DDS_WRITE(0x00, 0x78); - DDS_WRITE(0x01, 0x00); - DDS_WRITE(0x02, 0x00); - DDS_WRITE(0x03, 0x00); - rtio_fud(-1); - rtio_fud_sync(); + dds_phase_clear_en(i, 0); } } -void dds_program(int channel, int ftw, long long int fud_time) +void dds_phase_clear_en(int channel, int phase_clear_en) { + DDS_WRITE(0x00, 0x78); + DDS_WRITE(0x01, 0x00); + DDS_WRITE(0x02, phase_clear_en ? 0x40 : 0x00); + DDS_WRITE(0x03, 0x00); +} + +/* + * DDS phase modes: + * - continuous: Set phase_per_microcycle=0 to disable POW alteration. + * phase_tracking is ignored, set to 0. + * Disable phase accumulator clearing prior to programming. + * - absolute: Set phase_per_microcycle to its nominal value + * and phase_tracking=0. + * Enable phase accumulator clearing prior to programming. + * - tracking: Set phase_per_microcycle to its nominal value + * and phase_tracking=1. +* Enable phase accumulator clearing prior to programming. + */ +void dds_program(long long int timestamp, int channel, + int ftw, int pow, long long int phase_per_microcycle, + int rt_fud, int phase_tracking) +{ + long long int fud_time; + long long int phase_time_offset; + rtio_fud_sync(); DDS_WRITE(DDS_GPIO, channel); + DDS_WRITE(DDS_FTW0, ftw & 0xff); DDS_WRITE(DDS_FTW1, (ftw >> 8) & 0xff); DDS_WRITE(DDS_FTW2, (ftw >> 16) & 0xff); DDS_WRITE(DDS_FTW3, (ftw >> 24) & 0xff); + + phase_time_offset = phase_tracking ? timestamp : 0; + if(rt_fud) + fud_time = timestamp; + else { + fud_time = rtio_get_counter() + 8000; + phase_time_offset -= timestamp - fud_time; + } + pow += phase_time_offset*phase_per_microcycle; + DDS_WRITE(DDS_POW0, pow & 0xff); + DDS_WRITE(DDS_POW1, (pow >> 8) & 0x3f); + rtio_fud(fud_time); } diff --git a/soc/runtime/dds.h b/soc/runtime/dds.h index 78fa4e307..21db65131 100644 --- a/soc/runtime/dds.h +++ b/soc/runtime/dds.h @@ -2,6 +2,9 @@ #define __DDS_H void dds_init(void); -void dds_program(int channel, int ftw, long long int fud_time); +void dds_phase_clear_en(int channel, int phase_clear_en); +void dds_program(long long int timestamp, int channel, + int ftw, int pow, long long int phase_per_microcycle, + int rt_fud, int phase_tracking); #endif /* __DDS_H */ diff --git a/soc/runtime/main.c b/soc/runtime/main.c index 33fe0f302..a132ff384 100644 --- a/soc/runtime/main.c +++ b/soc/runtime/main.c @@ -110,7 +110,6 @@ int main(void) uart_init(); puts("ARTIQ runtime built "__DATE__" "__TIME__"\n"); - rtio_init(); dds_init(); blink_led(); comm_serve(load_object, run_kernel); diff --git a/soc/runtime/rtio.c b/soc/runtime/rtio.c index 06d2d0622..27709f286 100644 --- a/soc/runtime/rtio.c +++ b/soc/runtime/rtio.c @@ -10,6 +10,7 @@ void rtio_init(void) previous_fud_end_time = 0; rtio_reset_counter_write(1); rtio_reset_logic_write(1); + rtio_reset_counter_write(0); rtio_reset_logic_write(0); } @@ -21,7 +22,6 @@ void rtio_oe(int channel, int oe) void rtio_set(long long int timestamp, int channel, int value) { - rtio_reset_counter_write(0); rtio_chan_sel_write(channel); rtio_o_timestamp_write(timestamp); rtio_o_value_write(value); @@ -53,6 +53,12 @@ void rtio_sync(int channel) while(rtio_o_level_read() != 0); } +long long int rtio_get_counter(void) +{ + rtio_counter_update_write(1); + return rtio_counter_read(); +} + long long int rtio_get(int channel) { long long int r; @@ -94,12 +100,7 @@ void rtio_fud(long long int fud_time) { long long int fud_end_time; - rtio_reset_counter_write(0); rtio_chan_sel_write(RTIO_FUD_CHANNEL); - if(fud_time < 0) { - rtio_counter_update_write(1); - fud_time = rtio_counter_read() + 4000; - } fud_end_time = fud_time + 3*8; if(fud_time < previous_fud_end_time) exception_raise(EID_RTIO_SEQUENCE_ERROR); diff --git a/soc/runtime/rtio.h b/soc/runtime/rtio.h index 3cd3b4f7c..b26fdc3be 100644 --- a/soc/runtime/rtio.h +++ b/soc/runtime/rtio.h @@ -6,6 +6,7 @@ void rtio_oe(int channel, int oe); void rtio_set(long long int timestamp, int channel, int value); void rtio_replace(long long int timestamp, int channel, int value); void rtio_sync(int channel); +long long int rtio_get_counter(void); long long int rtio_get(int channel); int rtio_pileup_count(int channel); diff --git a/soc/runtime/services.c b/soc/runtime/services.c index d6edba503..7fabde3c1 100644 --- a/soc/runtime/services.c +++ b/soc/runtime/services.c @@ -17,6 +17,7 @@ static const struct symbol syscalls[] = { {"rtio_sync", rtio_sync}, {"rtio_get", rtio_get}, {"rtio_pileup_count", rtio_pileup_count}, + {"dds_phase_clear_en", dds_phase_clear_en}, {"dds_program", dds_program}, {NULL, NULL} };