Skip to content

Commit

Permalink
complete AD9914 support (no programmable modulus, untested)
Browse files Browse the repository at this point in the history
sbourdeauducq committed Jul 8, 2015
1 parent 0109821 commit 34aacd3
Showing 10 changed files with 221 additions and 52 deletions.
27 changes: 22 additions & 5 deletions artiq/coredevice/dds.py
Original file line number Diff line number Diff line change
@@ -46,11 +46,14 @@ def batch_exit(self):
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 @@ def ftw_to_frequency(self, ftw):
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 @@ def set_phase_mode(self, phase_mode):
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 @@ def set_mu(self, frequency, phase=0, phase_mode=_PHASE_MODE_DEFAULT):
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
8 changes: 4 additions & 4 deletions artiq/gateware/ad9xxx.py
Original file line number Diff line number Diff line change
@@ -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 @@ def __init__(self, pads,
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,
3 changes: 3 additions & 0 deletions doc/manual/core_drivers_reference.rst
Original file line number Diff line number Diff line change
@@ -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:

6 changes: 3 additions & 3 deletions examples/master/ddb.pyon
Original file line number Diff line number Diff line change
@@ -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}
},

4 changes: 2 additions & 2 deletions soc/runtime/bridge.c
Original file line number Diff line number Diff line change
@@ -66,15 +66,15 @@ 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;
}
case MESSAGE_TYPE_BRG_DDS_RESET: {
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();
93 changes: 82 additions & 11 deletions soc/runtime/dds.c
Original file line number Diff line number Diff line change
@@ -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);
}

32 changes: 31 additions & 1 deletion soc/runtime/dds.h
Original file line number Diff line number Diff line change
@@ -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,
24 changes: 10 additions & 14 deletions soc/runtime/main.c
Original file line number Diff line number Diff line change
@@ -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();

75 changes: 63 additions & 12 deletions soc/runtime/test_mode.c
Original file line number Diff line number Diff line change
@@ -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);
}
1 change: 1 addition & 0 deletions soc/targets/artiq_pipistrello.py
Original file line number Diff line number Diff line change
@@ -119,6 +119,7 @@ def __init__(self, platform, cpu_type="or1k", **kwargs):

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,

0 comments on commit 34aacd3

Please sign in to comment.