complete AD9914 support (no programmable modulus, untested)

This commit is contained in:
Sebastien Bourdeauducq 2015-07-08 17:22:43 +02:00
parent 0109821078
commit 34aacd3c5f
10 changed files with 221 additions and 52 deletions

View File

@ -46,11 +46,14 @@ class DDSBus(AutoDB):
syscall("dds_batch_exit")
class DDS(AutoDB):
class _DDSGeneric(AutoDB):
"""Core device Direct Digital Synthesis (DDS) driver.
Controls one DDS channel managed directly by the core device's runtime.
This class should not be used directly, instead, use the chip-specific
drivers such as ``AD9858`` and ``AD9914``.
:param sysclk: DDS system frequency.
:param channel: channel number of the DDS device to control.
"""
@ -80,13 +83,13 @@ class DDS(AutoDB):
def turns_to_pow(self, turns):
"""Returns the phase offset word corresponding to the given phase
in turns."""
return round(turns*2**14)
return round(turns*2**self.pow_width)
@portable
def pow_to_turns(self, pow):
"""Returns the phase in turns corresponding to the given phase offset
word."""
return pow/2**14
return pow/2**self.pow_width
@kernel
def init(self):
@ -119,7 +122,9 @@ class DDS(AutoDB):
def set_mu(self, frequency, phase=0, phase_mode=_PHASE_MODE_DEFAULT):
"""Sets the DDS channel to the specified frequency and phase.
This uses machine units (FTW and POW).
This uses machine units (FTW and POW). The frequency tuning word width
is 32, whereas the phase offset word width depends on the type of DDS
chip and can be retrieved via the ``pow_width`` attribute.
:param frequency: frequency to generate.
:param phase: adds an offset, in turns, to the phase.
@ -129,10 +134,22 @@ class DDS(AutoDB):
if phase_mode == _PHASE_MODE_DEFAULT:
phase_mode = self.phase_mode
syscall("dds_set", now_mu(), self.channel,
frequency, round(phase*2**14), phase_mode)
frequency, round(phase*2**self.pow_width), phase_mode)
@kernel
def set(self, frequency, phase=0, phase_mode=_PHASE_MODE_DEFAULT):
"""Like ``set_mu``, but uses Hz and turns."""
self.set_mu(self.frequency_to_ftw(frequency),
self.turns_to_pow(phase), phase_mode)
class AD9858(_DDSGeneric):
"""Driver for AD9858 DDS chips. See ``_DDSGeneric`` for a description
of the functionality."""
pow_width = 14
class AD9914(_DDSGeneric):
"""Driver for AD9914 DDS chips. See ``_DDSGeneric`` for a description
of the functionality."""
pow_width = 16

View File

@ -13,7 +13,7 @@ class AD9xxx(Module):
Write to address 2**flen(pads.a) to pulse the FUD signal.
Address 2**flen(pads.a)+1 is a GPIO register that controls the
sel and reset signals. sel is mapped to the lower bits, followed by reset.
sel and reset signals. rst is mapped to bit 0, followed by sel.
Write timing:
Address is set one cycle before assertion of we_n.
@ -58,11 +58,11 @@ class AD9xxx(Module):
gpio = Signal(flen(pads.sel) + 1)
gpio_load = Signal()
self.sync += If(gpio_load, gpio.eq(bus.dat_w))
self.comb += pads.sel.eq(gpio),
if hasattr(pads, "rst"):
self.comb += pads.rst.eq(gpio[-1])
self.comb += pads.rst.eq(gpio[0])
else:
self.comb += pads.rst_n.eq(~gpio[-1])
self.comb += pads.rst_n.eq(~gpio[0])
self.comb += pads.sel.eq(gpio[1:])
bus_r_gpio = Signal()
self.comb += If(bus_r_gpio,

View File

@ -12,6 +12,9 @@ These drivers are for peripherals closely integrated into the core device, which
:mod:`artiq.coredevice.dds` module
----------------------------------
.. autoclass:: artiq.coredevice.dds._DDSGeneric
:members:
.. automodule:: artiq.coredevice.dds
:members:

View File

@ -65,19 +65,19 @@
"dds0": {
"type": "local",
"module": "artiq.coredevice.dds",
"class": "DDS",
"class": "AD9858",
"arguments": {"sysclk": 1e9, "channel": 0}
},
"dds1": {
"type": "local",
"module": "artiq.coredevice.dds",
"class": "DDS",
"class": "AD9858",
"arguments": {"sysclk": 1e9, "channel": 1}
},
"dds2": {
"type": "local",
"module": "artiq.coredevice.dds",
"class": "DDS",
"class": "AD9858",
"arguments": {"sysclk": 1e9, "channel": 2}
},

View File

@ -66,7 +66,7 @@ void bridge_main(void)
struct msg_brg_dds_sel *msg;
msg = (struct msg_brg_dds_sel *)umsg;
dds_write(DDS_GPIO, msg->channel);
dds_write(DDS_GPIO, msg->channel << 1);
mailbox_acknowledge();
break;
}
@ -74,7 +74,7 @@ void bridge_main(void)
unsigned int g;
g = dds_read(DDS_GPIO);
dds_write(DDS_GPIO, g | (1 << 7));
dds_write(DDS_GPIO, g | 1);
dds_write(DDS_GPIO, g);
mailbox_acknowledge();

View File

@ -7,9 +7,24 @@
#include "dds.h"
#define DURATION_WRITE (5 << RTIO_FINE_TS_WIDTH)
#if defined DDS_AD9858
/* Assume 8-bit bus */
#define DURATION_INIT (7*DURATION_WRITE) /* not counting FUD */
#define DURATION_PROGRAM (8*DURATION_WRITE) /* not counting FUD */
#elif defined DDS_AD9914
/* Assume 16-bit bus */
/* DAC calibration takes max. 135us as per datasheet. Take a good margin. */
#define DURATION_DAC_CAL (30000 << RTIO_FINE_TS_WIDTH)
/* not counting final FUD */
#define DURATION_INIT (8*DURATION_WRITE + DURATION_DAC_CAL)
#define DURATION_PROGRAM (5*DURATION_WRITE) /* not counting FUD */
#else
#error Unknown DDS configuration
#endif
#define DDS_WRITE(addr, data) do { \
rtio_o_address_write(addr); \
rtio_o_data_write(data); \
@ -39,16 +54,43 @@ void dds_init(long long int timestamp, int channel)
now = timestamp - DURATION_INIT;
#ifdef DDS_ONEHOT_SEL
channel = 1 << channel;
#endif
channel <<= 1;
DDS_WRITE(DDS_GPIO, channel);
DDS_WRITE(DDS_GPIO, channel | (1 << 5));
DDS_WRITE(DDS_GPIO, channel | 1); /* reset */
DDS_WRITE(DDS_GPIO, channel);
DDS_WRITE(0x00, 0x78);
DDS_WRITE(0x01, 0x00);
DDS_WRITE(0x02, 0x00);
DDS_WRITE(0x03, 0x00);
#ifdef DDS_AD9858
/*
* 2GHz divider disable
* SYNCLK disable
* Mixer power-down
* Phase detect power down
*/
DDS_WRITE(DDS_CFR0, 0x78);
DDS_WRITE(DDS_CFR1, 0x00);
DDS_WRITE(DDS_CFR2, 0x00);
DDS_WRITE(DDS_CFR3, 0x00);
DDS_WRITE(DDS_FUD, 0);
#endif
#ifdef DDS_AD9914
/*
* Enable cosine output (to match AD9858 behavior)
* Enable DAC calibration
* Leave SYNCLK enabled and PLL/divider disabled
*/
DDS_WRITE(DDS_CFR1L, 0x0008);
DDS_WRITE(DDS_CFR1H, 0x0000);
DDS_WRITE(DDS_CFR4H, 0x0105);
DDS_WRITE(DDS_FUD, 0);
/* Disable DAC calibration */
now += DURATION_DAC_CAL;
DDS_WRITE(DDS_CFR4H, 0x0005);
DDS_WRITE(DDS_FUD, 0);
#endif
}
/* Compensation to keep phase continuity when switching from absolute or tracking
@ -58,38 +100,67 @@ static unsigned int continuous_phase_comp[DDS_CHANNEL_COUNT];
static void dds_set_one(long long int now, long long int ref_time, unsigned int channel,
unsigned int ftw, unsigned int pow, int phase_mode)
{
unsigned int channel_enc;
if(channel >= DDS_CHANNEL_COUNT) {
log("Attempted to set invalid DDS channel");
return;
}
DDS_WRITE(DDS_GPIO, channel);
#ifdef DDS_ONEHOT_SEL
channel_enc = 1 << channel;
#else
channel_enc = channel;
#endif
DDS_WRITE(DDS_GPIO, channel_enc << 1);
#ifdef DDS_AD9858
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);
#endif
#ifdef DDS_AD9914
DDS_WRITE(DDS_FTWL, ftw & 0xffff);
DDS_WRITE(DDS_FTWH, (ftw >> 16) & 0xffff);
#endif
/* We need the RTIO fine timestamp clock to be phase-locked
* to DDS SYSCLK, and divided by an integer DDS_RTIO_CLK_RATIO.
*/
if(phase_mode == PHASE_MODE_CONTINUOUS) {
/* Do not clear phase accumulator on FUD */
DDS_WRITE(0x02, 0x00);
#ifdef DDS_AD9858
DDS_WRITE(DDS_CFR2, 0x00);
#endif
#ifdef DDS_AD9914
DDS_WRITE(DDS_CFR1L, 0x0008);
#endif
pow += continuous_phase_comp[channel];
} else {
long long int fud_time;
/* Clear phase accumulator on FUD */
DDS_WRITE(0x02, 0x40);
#ifdef DDS_AD9858
DDS_WRITE(DDS_CFR2, 0x40);
#endif
#ifdef DDS_AD9914
DDS_WRITE(DDS_CFR1L, 0x2008);
#endif
fud_time = now + 2*DURATION_WRITE;
pow -= (ref_time - fud_time)*DDS_RTIO_CLK_RATIO*ftw >> 18;
pow -= (ref_time - fud_time)*DDS_RTIO_CLK_RATIO*ftw >> (32-DDS_POW_WIDTH);
if(phase_mode == PHASE_MODE_TRACKING)
pow += ref_time*DDS_RTIO_CLK_RATIO*ftw >> 18;
pow += ref_time*DDS_RTIO_CLK_RATIO*ftw >> (32-DDS_POW_WIDTH);
continuous_phase_comp[channel] = pow;
}
#ifdef DDS_AD9858
DDS_WRITE(DDS_POW0, pow & 0xff);
DDS_WRITE(DDS_POW1, (pow >> 8) & 0x3f);
#endif
#ifdef DDS_AD9914
DDS_WRITE(DDS_POW, pow);
#endif
DDS_WRITE(DDS_FUD, 0);
}

View File

@ -2,12 +2,17 @@
#define __DDS_H
#include <hw/common.h>
#include <generated/csr.h>
#include <generated/mem.h>
/* Maximum number of commands in a batch */
#define DDS_MAX_BATCH 16
/* DDS core registers */
#ifdef DDS_AD9858
#define DDS_CFR0 0x00
#define DDS_CFR1 0x01
#define DDS_CFR2 0x02
#define DDS_CFR3 0x03
#define DDS_FTW0 0x0a
#define DDS_FTW1 0x0b
#define DDS_FTW2 0x0c
@ -16,6 +21,31 @@
#define DDS_POW1 0x0f
#define DDS_FUD 0x40
#define DDS_GPIO 0x41
#endif
#ifdef DDS_AD9914
#define DDS_CFR1L 0x01
#define DDS_CFR1H 0x03
#define DDS_CFR2L 0x05
#define DDS_CFR2H 0x07
#define DDS_CFR3L 0x09
#define DDS_CFR3H 0x0b
#define DDS_CFR4L 0x0d
#define DDS_CFR4H 0x0f
#define DDS_FTWL 0x2d
#define DDS_FTWH 0x2f
#define DDS_POW 0x31
#define DDS_FUD 0x80
#define DDS_GPIO 0x81
#endif
#ifdef DDS_AD9858
#define DDS_POW_WIDTH 14
#endif
#ifdef DDS_AD9914
#define DDS_POW_WIDTH 16
#endif
enum {
PHASE_MODE_CONTINUOUS = 0,

View File

@ -4,7 +4,6 @@
#include <uart.h>
#include <console.h>
#include <system.h>
#include <time.h>
#include <generated/csr.h>
#include <hw/flags.h>
@ -32,7 +31,6 @@
static void common_init(void)
{
clock_init();
brg_start();
brg_ddsinitall();
kloader_stop();
@ -211,34 +209,31 @@ static void regular_main(void)
static void blink_led(void)
{
int i, ev, p;
int i;
long long int t;
p = identifier_frequency_read()/10;
time_init();
for(i=0;i<3;i++) {
leds_out_write(1);
while(!elapsed(&ev, p));
t = clock_get_ms();
while(clock_get_ms() < t + 250);
leds_out_write(0);
while(!elapsed(&ev, p));
t = clock_get_ms();
while(clock_get_ms() < t + 250);
}
}
static int check_test_mode(void)
{
char c;
long long int t;
timer0_en_write(0);
timer0_reload_write(0);
timer0_load_write(identifier_frequency_read() >> 2);
timer0_en_write(1);
timer0_update_value_write(1);
while(timer0_value_read()) {
t = clock_get_ms();
while(clock_get_ms() < t + 1000) {
if(readchar_nonblock()) {
c = readchar();
if((c == 't')||(c == 'T'))
return 1;
}
timer0_update_value_write(1);
}
return 0;
}
@ -251,6 +246,7 @@ int main(void)
puts("ARTIQ runtime built "__DATE__" "__TIME__"\n");
clock_init();
puts("Press 't' to enter test mode...");
blink_led();

View File

@ -10,6 +10,7 @@
#include "dds.h"
#include "flash_storage.h"
#include "bridge_ctl.h"
#include "clock.h"
#include "test_mode.h"
static void leds(char *value)
@ -114,6 +115,9 @@ static void ddssel(char *n)
return;
}
#ifdef DDS_ONEHOT_SEL
n2 = 1 << n2;
#endif
brg_ddssel(n2);
}
@ -157,7 +161,12 @@ static void ddsr(char *addr)
return;
}
#ifdef DDS_AD9858
printf("0x%02x\n", brg_ddsread(addr2));
#endif
#ifdef DDS_AD9914
printf("0x%04x\n", brg_ddsread(addr2));
#endif
}
static void ddsfud(void)
@ -186,11 +195,22 @@ static void ddsftw(char *n, char *ftw)
return;
}
#ifdef DDS_ONEHOT_SEL
n2 = 1 << n2;
#endif
brg_ddssel(n2);
#ifdef DDS_AD9858
brg_ddswrite(DDS_FTW0, ftw2 & 0xff);
brg_ddswrite(DDS_FTW1, (ftw2 >> 8) & 0xff);
brg_ddswrite(DDS_FTW2, (ftw2 >> 16) & 0xff);
brg_ddswrite(DDS_FTW3, (ftw2 >> 24) & 0xff);
#endif
#ifdef DDS_AD9914
brg_ddswrite(DDS_FTWL, ftw2 & 0xffff);
brg_ddswrite(DDS_FTWH, (ftw2 >> 16) & 0xffff);
#endif
brg_ddsfud();
}
@ -199,15 +219,34 @@ static void ddsreset(void)
brg_ddsreset();
}
#ifdef DDS_AD9858
static void ddsinit(void)
{
brg_ddsreset();
brg_ddswrite(0x00, 0x78);
brg_ddswrite(0x01, 0x00);
brg_ddswrite(0x02, 0x00);
brg_ddswrite(0x03, 0x00);
brg_ddswrite(DDS_CFR0, 0x78);
brg_ddswrite(DDS_CFR1, 0x00);
brg_ddswrite(DDS_CFR2, 0x00);
brg_ddswrite(DDS_CFR3, 0x00);
brg_ddsfud();
}
#endif
#ifdef DDS_AD9914
static void ddsinit(void)
{
long long int t;
brg_ddsreset();
brg_ddswrite(DDS_CFR1L, 0x0008);
brg_ddswrite(DDS_CFR1H, 0x0000);
brg_ddswrite(DDS_CFR4H, 0x0105);
brg_ddswrite(DDS_FUD, 0);
t = clock_get_ms();
while(clock_get_ms() < t + 2);
brg_ddswrite(DDS_CFR4H, 0x0005);
brg_ddsfud();
}
#endif
static void ddstest_one(unsigned int i)
{
@ -223,15 +262,27 @@ static void ddstest_one(unsigned int i)
for(j=0; j<12; j++) {
f = v[j];
brg_ddswrite(0x0a, f & 0xff);
brg_ddswrite(0x0b, (f >> 8) & 0xff);
brg_ddswrite(0x0c, (f >> 16) & 0xff);
brg_ddswrite(0x0d, (f >> 24) & 0xff);
#ifdef DDS_AD9858
brg_ddswrite(DDS_FTW0, f & 0xff);
brg_ddswrite(DDS_FTW1, (f >> 8) & 0xff);
brg_ddswrite(DDS_FTW2, (f >> 16) & 0xff);
brg_ddswrite(DDS_FTW3, (f >> 24) & 0xff);
#endif
#ifdef DDS_AD9914
brg_ddswrite(DDS_FTWL, f & 0xffff);
brg_ddswrite(DDS_FTWH, (f >> 16) & 0xffff);
#endif
brg_ddsfud();
g = brg_ddsread(0x0a);
g |= brg_ddsread(0x0b) << 8;
g |= brg_ddsread(0x0c) << 16;
g |= brg_ddsread(0x0d) << 24;
#ifdef DDS_AD9858
g = brg_ddsread(DDS_FTW0);
g |= brg_ddsread(DDS_FTW1) << 8;
g |= brg_ddsread(DDS_FTW2) << 16;
g |= brg_ddsread(DDS_FTW3) << 24;
#endif
#ifdef DDS_AD9914
g = brg_ddsread(DDS_FTWL);
g |= brg_ddsread(DDS_FTWH) << 16;
#endif
if(g != f)
printf("readback fail on DDS %d, 0x%08x != 0x%08x\n", i, g, f);
}

View File

@ -119,6 +119,7 @@ trce -v 12 -fastpaths -tsi {build_name}.tsi -o {build_name}.twr {build_name}.ncd
self.add_constant("RTIO_DDS_CHANNEL", len(rtio_channels))
self.add_constant("DDS_CHANNEL_COUNT", 8)
self.add_constant("DDS_AD9858")
phy = dds.AD9858(platform.request("dds"), 8)
self.submodules += phy
rtio_channels.append(rtio.Channel.from_phy(phy,