dds: phase control (mostly untested)

This commit is contained in:
Sebastien Bourdeauducq 2014-11-20 12:32:56 -08:00
parent e01050b19a
commit 1780759327
8 changed files with 125 additions and 34 deletions

View File

@ -3,6 +3,12 @@ from artiq.language.units import *
from artiq.coredevice import rtio from artiq.coredevice import rtio
PHASE_MODE_DEFAULT = -1
PHASE_MODE_CONTINUOUS = 0
PHASE_MODE_ABSOLUTE = 1
PHASE_MODE_TRACKING = 2
class DDS(AutoContext): class DDS(AutoContext):
"""Core device Direct Digital Synthesis (DDS) driver. """Core device Direct Digital Synthesis (DDS) driver.
@ -21,6 +27,7 @@ class DDS(AutoContext):
def build(self): def build(self):
self.previous_frequency = 0*MHz self.previous_frequency = 0*MHz
self.set_phase_mode(PHASE_MODE_CONTINUOUS)
self.sw = rtio.RTIOOut(self, channel=self.rtio_switch) self.sw = rtio.RTIOOut(self, channel=self.rtio_switch)
@portable @portable
@ -40,33 +47,71 @@ class DDS(AutoContext):
return ftw*self.dds_sysclk/2**32 return ftw*self.dds_sysclk/2**32
@kernel @kernel
def on(self, frequency): def set_phase_mode(self, phase_mode):
"""Sets the DDS channel to the specified frequency and turns it on. """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 * ``PHASE_MODE_CONTINUOUS``: the phase accumulator is unchanged when
performed. 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: if self.previous_frequency != frequency:
merge = self.sw.previous_timestamp == time_to_cycles(now()) merge = self.sw.previous_timestamp == time_to_cycles(now())
if not merge: if not merge:
self.sw.sync() self.sw.sync()
if merge or bool(self.sw.previous_value): # Channel is already on:
# Channel is already on. # Precise timing of frequency change is required.
# Precise timing of frequency change is required. # Channel is off:
fud_time = time_to_cycles(now()) # 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: else:
# Channel is off. phase_per_microcycle = int64(0)
# Use soft timing on FUD to prevent conflicts when syscall("dds_program", time_to_cycles(now()), self.reg_channel,
# reprogramming several channels that need to be turned on at ftw, int(phase_offset*2**14),
# the same time. phase_per_microcycle,
fud_time = -1 rt_fud, self.phase_mode == PHASE_MODE_TRACKING)
syscall("dds_program", self.reg_channel,
self.frequency_to_ftw(frequency),
fud_time)
self.previous_frequency = frequency self.previous_frequency = frequency
self.sw.on() self.sw.on()
if phase_mode != PHASE_MODE_DEFAULT:
self.set_phase_mode(old_phase_mode)
@kernel @kernel
def off(self): def off(self):
"""Turns the DDS channel off. """Turns the DDS channel off.
@ -75,13 +120,16 @@ class DDS(AutoContext):
self.sw.off() self.sw.off()
@kernel @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 """Pulses the DDS channel for the specified duration at the specified
frequency. frequency.
See ``on`` for a description of the parameters.
Equivalent to a ``on``, ``delay``, ``off`` sequence. Equivalent to a ``on``, ``delay``, ``off`` sequence.
""" """
self.on(frequency) self.on(frequency, phase_mode, phase_offset)
delay(duration) delay(duration)
self.off() self.off()

View File

@ -19,17 +19,20 @@ _syscalls = {
"rtio_sync": "i:n", "rtio_sync": "i:n",
"rtio_get": "i:I", "rtio_get": "i:I",
"rtio_pileup_count": "i:i", "rtio_pileup_count": "i:i",
"dds_program": "iiI:n", "dds_phase_clear_en": "ib:n",
"dds_program": "IiiiIbb:n",
} }
_chr_to_type = { _chr_to_type = {
"n": lambda: lc.Type.void(), "n": lambda: lc.Type.void(),
"b": lambda: lc.Type.int(1),
"i": lambda: lc.Type.int(32), "i": lambda: lc.Type.int(32),
"I": lambda: lc.Type.int(64) "I": lambda: lc.Type.int(64)
} }
_chr_to_value = { _chr_to_value = {
"n": lambda: base_types.VNone(), "n": lambda: base_types.VNone(),
"b": lambda: base_types.VBool(),
"i": lambda: base_types.VInt(), "i": lambda: base_types.VInt(),
"I": lambda: base_types.VInt(64) "I": lambda: base_types.VInt(64)
} }

View File

@ -9,6 +9,8 @@
#define DDS_FTW1 0x0b #define DDS_FTW1 0x0b
#define DDS_FTW2 0x0c #define DDS_FTW2 0x0c
#define DDS_FTW3 0x0d #define DDS_FTW3 0x0d
#define DDS_POW0 0x0e
#define DDS_POW1 0x0f
#define DDS_GPIO 0x41 #define DDS_GPIO 0x41
#define DDS_READ(addr) \ #define DDS_READ(addr) \
@ -25,22 +27,55 @@ void dds_init(void)
DDS_WRITE(DDS_GPIO, i); DDS_WRITE(DDS_GPIO, i);
DDS_WRITE(DDS_GPIO, i | (1 << 7)); DDS_WRITE(DDS_GPIO, i | (1 << 7));
DDS_WRITE(DDS_GPIO, i); DDS_WRITE(DDS_GPIO, i);
DDS_WRITE(0x00, 0x78); dds_phase_clear_en(i, 0);
DDS_WRITE(0x01, 0x00);
DDS_WRITE(0x02, 0x00);
DDS_WRITE(0x03, 0x00);
rtio_fud(-1);
rtio_fud_sync();
} }
} }
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(); rtio_fud_sync();
DDS_WRITE(DDS_GPIO, channel); DDS_WRITE(DDS_GPIO, channel);
DDS_WRITE(DDS_FTW0, ftw & 0xff); DDS_WRITE(DDS_FTW0, ftw & 0xff);
DDS_WRITE(DDS_FTW1, (ftw >> 8) & 0xff); DDS_WRITE(DDS_FTW1, (ftw >> 8) & 0xff);
DDS_WRITE(DDS_FTW2, (ftw >> 16) & 0xff); DDS_WRITE(DDS_FTW2, (ftw >> 16) & 0xff);
DDS_WRITE(DDS_FTW3, (ftw >> 24) & 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); rtio_fud(fud_time);
} }

View File

@ -2,6 +2,9 @@
#define __DDS_H #define __DDS_H
void dds_init(void); 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 */ #endif /* __DDS_H */

View File

@ -110,7 +110,6 @@ int main(void)
uart_init(); uart_init();
puts("ARTIQ runtime built "__DATE__" "__TIME__"\n"); puts("ARTIQ runtime built "__DATE__" "__TIME__"\n");
rtio_init();
dds_init(); dds_init();
blink_led(); blink_led();
comm_serve(load_object, run_kernel); comm_serve(load_object, run_kernel);

View File

@ -10,6 +10,7 @@ void rtio_init(void)
previous_fud_end_time = 0; previous_fud_end_time = 0;
rtio_reset_counter_write(1); rtio_reset_counter_write(1);
rtio_reset_logic_write(1); rtio_reset_logic_write(1);
rtio_reset_counter_write(0);
rtio_reset_logic_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) void rtio_set(long long int timestamp, int channel, int value)
{ {
rtio_reset_counter_write(0);
rtio_chan_sel_write(channel); rtio_chan_sel_write(channel);
rtio_o_timestamp_write(timestamp); rtio_o_timestamp_write(timestamp);
rtio_o_value_write(value); rtio_o_value_write(value);
@ -53,6 +53,12 @@ void rtio_sync(int channel)
while(rtio_o_level_read() != 0); 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 rtio_get(int channel)
{ {
long long int r; long long int r;
@ -94,12 +100,7 @@ void rtio_fud(long long int fud_time)
{ {
long long int fud_end_time; long long int fud_end_time;
rtio_reset_counter_write(0);
rtio_chan_sel_write(RTIO_FUD_CHANNEL); 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; fud_end_time = fud_time + 3*8;
if(fud_time < previous_fud_end_time) if(fud_time < previous_fud_end_time)
exception_raise(EID_RTIO_SEQUENCE_ERROR); exception_raise(EID_RTIO_SEQUENCE_ERROR);

View File

@ -6,6 +6,7 @@ void rtio_oe(int channel, int oe);
void rtio_set(long long int timestamp, int channel, int value); void rtio_set(long long int timestamp, int channel, int value);
void rtio_replace(long long int timestamp, int channel, int value); void rtio_replace(long long int timestamp, int channel, int value);
void rtio_sync(int channel); void rtio_sync(int channel);
long long int rtio_get_counter(void);
long long int rtio_get(int channel); long long int rtio_get(int channel);
int rtio_pileup_count(int channel); int rtio_pileup_count(int channel);

View File

@ -17,6 +17,7 @@ static const struct symbol syscalls[] = {
{"rtio_sync", rtio_sync}, {"rtio_sync", rtio_sync},
{"rtio_get", rtio_get}, {"rtio_get", rtio_get},
{"rtio_pileup_count", rtio_pileup_count}, {"rtio_pileup_count", rtio_pileup_count},
{"dds_phase_clear_en", dds_phase_clear_en},
{"dds_program", dds_program}, {"dds_program", dds_program},
{NULL, NULL} {NULL, NULL}
}; };