2
0
mirror of https://github.com/m-labs/artiq.git synced 2024-12-19 08:26:30 +08:00
artiq/soc/runtime/dds.c
whitequark 62fdc75d2d Integrate libdyld and libunwind.
It is currently possible to run the idle experiment, and it
can raise and catch exceptions, but exceptions are not yet
propagated across RPC boundaries.
2015-08-02 15:43:03 +03:00

223 lines
5.9 KiB
C

#include <generated/csr.h>
#include <stdio.h>
#include "artiq_personality.h"
#include "rtio.h"
#include "log.h"
#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); \
rtio_o_timestamp_write(now); \
rtio_write_and_process_status(now, RTIO_DDS_CHANNEL); \
now += DURATION_WRITE; \
} while(0)
void dds_init_all(void)
{
int i;
long long int now;
now = rtio_get_counter() + 10000;
for(i=0;i<DDS_CHANNEL_COUNT;i++) {
dds_init(now, i);
now += DURATION_INIT + DURATION_WRITE; /* + FUD time */
}
while(rtio_get_counter() < now);
}
void dds_init(long long int timestamp, int channel)
{
long long int now;
rtio_chan_sel_write(RTIO_DDS_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); /* reset */
DDS_WRITE(DDS_GPIO, channel);
#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
* to continuous phase mode. */
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;
}
#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 */
#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 */
#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 >> (32-DDS_POW_WIDTH);
if(phase_mode == PHASE_MODE_TRACKING)
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);
}
struct dds_set_params {
int channel;
unsigned int ftw;
unsigned int pow;
int phase_mode;
};
static int batch_mode;
static int batch_count;
static long long int batch_ref_time;
static struct dds_set_params batch[DDS_MAX_BATCH];
void dds_batch_enter(long long int timestamp)
{
if(batch_mode)
artiq_raise_from_c("DDSBatchError", "DDS batch error", 0, 0, 0);
batch_mode = 1;
batch_count = 0;
batch_ref_time = timestamp;
}
void dds_batch_exit(void)
{
long long int now;
int i;
if(!batch_mode)
artiq_raise_from_c("DDSBatchError", "DDS batch error", 0, 0, 0);
rtio_chan_sel_write(RTIO_DDS_CHANNEL);
/* + FUD time */
now = batch_ref_time - batch_count*(DURATION_PROGRAM + DURATION_WRITE);
for(i=0;i<batch_count;i++) {
dds_set_one(now, batch_ref_time,
batch[i].channel, batch[i].ftw, batch[i].pow, batch[i].phase_mode);
now += DURATION_PROGRAM + DURATION_WRITE;
}
batch_mode = 0;
}
void dds_set(long long int timestamp, int channel,
unsigned int ftw, unsigned int pow, int phase_mode)
{
if(batch_mode) {
if(batch_count >= DDS_MAX_BATCH)
artiq_raise_from_c("DDSBatchError", "DDS batch error", 0, 0, 0);
/* timestamp parameter ignored (determined by batch) */
batch[batch_count].channel = channel;
batch[batch_count].ftw = ftw;
batch[batch_count].pow = pow;
batch[batch_count].phase_mode = phase_mode;
batch_count++;
} else {
rtio_chan_sel_write(RTIO_DDS_CHANNEL);
dds_set_one(timestamp - DURATION_PROGRAM, timestamp, channel, ftw, pow, phase_mode);
}
}