mirror of https://github.com/m-labs/artiq.git
dds: phase control (mostly untested)
This commit is contained in:
parent
e01050b19a
commit
1780759327
|
@ -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.
|
||||||
fud_time = time_to_cycles(now())
|
# 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:
|
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()
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 */
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
@ -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}
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in New Issue