forked from M-Labs/artiq
DDS over RTIO (batch mode not supported yet)
This commit is contained in:
parent
a91bb48ced
commit
a36c51eb83
|
@ -1,10 +1,10 @@
|
|||
from artiq.language.core import *
|
||||
from artiq.language.db import *
|
||||
from artiq.language.units import *
|
||||
from artiq.coredevice import ttl
|
||||
|
||||
|
||||
PHASE_MODE_DEFAULT = -1
|
||||
# keep in sync with dds.h
|
||||
PHASE_MODE_CONTINUOUS = 0
|
||||
PHASE_MODE_ABSOLUTE = 1
|
||||
PHASE_MODE_TRACKING = 2
|
||||
|
@ -13,34 +13,24 @@ PHASE_MODE_TRACKING = 2
|
|||
class DDS(AutoDB):
|
||||
"""Core device Direct Digital Synthesis (DDS) driver.
|
||||
|
||||
Controls DDS devices managed directly by the core device's runtime. It also
|
||||
uses a RTIO TTL channel (through :class:`artiq.coredevice.ttl.TTLOut`) to
|
||||
control a RF switch that gates the output of the DDS device.
|
||||
Controls DDS devices managed directly by the core device's runtime.
|
||||
|
||||
:param dds_sysclk: DDS system frequency, used for computing the frequency
|
||||
tuning words.
|
||||
:param reg_channel: channel number of the DDS device to control.
|
||||
:param rtio_switch: RTIO channel number of the RF switch associated with
|
||||
the DDS device.
|
||||
|
||||
:param channel: channel number of the DDS device to control.
|
||||
"""
|
||||
class DBKeys:
|
||||
core = Device()
|
||||
dds_sysclk = Parameter(1*GHz)
|
||||
reg_channel = Argument()
|
||||
rtio_switch = Argument()
|
||||
dds_sysclk = Argument(1*GHz)
|
||||
channel = Argument()
|
||||
|
||||
def build(self):
|
||||
self.previous_on = False
|
||||
self.previous_frequency = 0*MHz
|
||||
self.set_phase_mode(PHASE_MODE_CONTINUOUS)
|
||||
self.sw = ttl.TTLOut(core=self.core, channel=self.rtio_switch)
|
||||
self.phase_mode = PHASE_MODE_CONTINUOUS
|
||||
|
||||
@portable
|
||||
def frequency_to_ftw(self, frequency):
|
||||
"""Returns the frequency tuning word corresponding to the given
|
||||
frequency.
|
||||
|
||||
"""
|
||||
return round(2**32*frequency/self.dds_sysclk)
|
||||
|
||||
|
@ -48,10 +38,14 @@ class DDS(AutoDB):
|
|||
def ftw_to_frequency(self, ftw):
|
||||
"""Returns the frequency corresponding to the given frequency tuning
|
||||
word.
|
||||
|
||||
"""
|
||||
return ftw*self.dds_sysclk/2**32
|
||||
|
||||
@kernel
|
||||
def init(self):
|
||||
"""Resets and initializes the DDS."""
|
||||
syscall("dds_init", time_to_cycles(now()), self.channel)
|
||||
|
||||
@kernel
|
||||
def set_phase_mode(self, phase_mode):
|
||||
"""Sets the phase mode of the DDS channel. Supported phase modes are:
|
||||
|
@ -69,74 +63,21 @@ class DDS(AutoDB):
|
|||
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.
|
||||
def set(self, frequency, phase_mode=PHASE_MODE_DEFAULT, phase_offset=0):
|
||||
"""Sets the DDS channel to the specified frequency and phase.
|
||||
|
||||
: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 phase_mode == PHASE_MODE_DEFAULT:
|
||||
phase_mode = self.phase_mode
|
||||
|
||||
if self.previous_frequency != frequency:
|
||||
merge = self.sw.o_previous_timestamp == time_to_cycles(now())
|
||||
if not merge:
|
||||
self.sw.sync()
|
||||
# Channel is already on:
|
||||
# Precise timing of frequency change is required.
|
||||
# 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 self.previous_on
|
||||
if self.phase_mode != PHASE_MODE_CONTINUOUS:
|
||||
sysclk_per_microcycle = int(self.dds_sysclk*
|
||||
self.core.ref_period)
|
||||
else:
|
||||
sysclk_per_microcycle = 0
|
||||
syscall("dds_program", time_to_cycles(now()), self.reg_channel,
|
||||
self.frequency_to_ftw(frequency), int(phase_offset*2**14),
|
||||
sysclk_per_microcycle,
|
||||
rt_fud, self.phase_mode == PHASE_MODE_TRACKING)
|
||||
self.previous_frequency = frequency
|
||||
self.sw.on()
|
||||
self.previous_on = True
|
||||
|
||||
if phase_mode != PHASE_MODE_DEFAULT:
|
||||
self.set_phase_mode(old_phase_mode)
|
||||
|
||||
@kernel
|
||||
def off(self):
|
||||
"""Turns the DDS channel off.
|
||||
|
||||
"""
|
||||
self.sw.off()
|
||||
self.previous_on = False
|
||||
|
||||
@kernel
|
||||
def pulse(self, frequency, duration,
|
||||
phase_mode=PHASE_MODE_DEFAULT, phase_offset=0):
|
||||
"""Pulses the DDS channel for the specified duration at the specified
|
||||
frequency.
|
||||
|
||||
See ``on`` for a description of the parameters.
|
||||
|
||||
Equivalent to a ``on``, ``delay``, ``off`` sequence.
|
||||
|
||||
"""
|
||||
self.on(frequency, phase_mode, phase_offset)
|
||||
delay(duration)
|
||||
self.off()
|
||||
syscall("dds_set", time_to_cycles(now()), self.channel,
|
||||
self.frequency_to_ftw(frequency), round(phase_offset*2**14),
|
||||
self.phase_mode)
|
||||
|
|
|
@ -21,8 +21,8 @@ _syscalls = {
|
|||
"rtio_set_sensitivity": "Iii:n",
|
||||
"rtio_get_counter": "n:I",
|
||||
"rtio_get": "iI:I",
|
||||
"dds_phase_clear_en": "ib:n",
|
||||
"dds_program": "Iiiiibb:n",
|
||||
"dds_init": "Ii:n",
|
||||
"dds_set": "Iiiii:n",
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -36,8 +36,8 @@ class AD9858(Module):
|
|||
|
||||
Read timing:
|
||||
Address is set one cycle before assertion of rd_n.
|
||||
rd_n is asserted for 3 cycles.
|
||||
Data is sampled 2 cycles into the assertion of rd_n.
|
||||
rd_n is asserted for read_wait_cycles, data is sampled at the end.
|
||||
rd_n is deasserted and data bus is not driven again before hiz_wait_cycles.
|
||||
|
||||
Design:
|
||||
All IO pads are registered.
|
||||
|
@ -48,7 +48,7 @@ class AD9858(Module):
|
|||
Round-trip addr A setup (> RX, RD, D to Z), RD prop, D valid (< D
|
||||
valid), D prop is ~15 + 10 + 20 + 10 = 55ns
|
||||
"""
|
||||
def __init__(self, pads, drive_fud=False,
|
||||
def __init__(self, pads,
|
||||
read_wait_cycles=10, hiz_wait_cycles=3,
|
||||
bus=None):
|
||||
if bus is None:
|
||||
|
@ -84,9 +84,8 @@ class AD9858(Module):
|
|||
bus.dat_r.eq(dr)
|
||||
)
|
||||
|
||||
if drive_fud:
|
||||
fud = Signal()
|
||||
self.sync += pads.fud_n.eq(~fud)
|
||||
fud = Signal()
|
||||
self.sync += pads.fud_n.eq(~fud)
|
||||
|
||||
pads.wr_n.reset = 1
|
||||
pads.rd_n.reset = 1
|
||||
|
@ -106,7 +105,7 @@ class AD9858(Module):
|
|||
If(bus.adr[0],
|
||||
NextState("GPIO")
|
||||
).Else(
|
||||
NextState("FUD") if drive_fud else None
|
||||
NextState("FUD")
|
||||
)
|
||||
).Else(
|
||||
If(bus.we,
|
||||
|
@ -141,19 +140,18 @@ class AD9858(Module):
|
|||
)
|
||||
fsm.act("WAIT_HIZ",
|
||||
rx.eq(1),
|
||||
# For some reason, AD9858 has a address hold time to RD inactive.
|
||||
# For some reason, AD9858 has an address hold time to RD inactive.
|
||||
hold_address.eq(1),
|
||||
self.hiz_timer.wait.eq(1),
|
||||
If(self.hiz_timer.done, NextState("IDLE"))
|
||||
)
|
||||
if drive_fud:
|
||||
fsm.act("FUD",
|
||||
# 4ns FUD setup to SYNCLK
|
||||
# 0ns FUD hold to SYNCLK
|
||||
fud.eq(1),
|
||||
bus.ack.eq(1),
|
||||
NextState("IDLE")
|
||||
)
|
||||
fsm.act("FUD",
|
||||
# 4ns FUD setup to SYNCLK
|
||||
# 0ns FUD hold to SYNCLK
|
||||
fud.eq(1),
|
||||
bus.ack.eq(1),
|
||||
NextState("IDLE")
|
||||
)
|
||||
fsm.act("GPIO",
|
||||
bus.ack.eq(1),
|
||||
bus_r_gpio.eq(1),
|
||||
|
|
|
@ -291,6 +291,10 @@ class RTIO(Module):
|
|||
fine_ts_width = max(rtlink.get_fine_ts_width(c.interface)
|
||||
for c in channels)
|
||||
|
||||
self.data_width = data_width
|
||||
self.address_width = address_width
|
||||
self.fine_ts_width = fine_ts_width
|
||||
|
||||
# CSRs
|
||||
self.kcsrs = _KernelCSRs(bits_for(len(channels)-1),
|
||||
data_width, address_width,
|
||||
|
|
|
@ -1,10 +1,14 @@
|
|||
from migen.fhdl.std import *
|
||||
from migen.bus import wishbone
|
||||
|
||||
from artiq.gateware.rtio import rtlink
|
||||
|
||||
|
||||
class RT2WB(Module):
|
||||
def __init__(self, wb, address_width):
|
||||
def __init__(self, address_width, wb=None):
|
||||
if wb is None:
|
||||
wb = wishbone.Interface()
|
||||
self.wb = wb
|
||||
self.rtlink = rtlink.Interface(
|
||||
rtlink.OInterface(
|
||||
flen(wb.dat_w),
|
||||
|
@ -18,7 +22,7 @@ class RT2WB(Module):
|
|||
# # #
|
||||
|
||||
active = Signal()
|
||||
self.sync.rio_phy += [
|
||||
self.sync.rio += [
|
||||
If(self.rtlink.o.stb,
|
||||
active.eq(1),
|
||||
wb.adr.eq(self.rtlink.o.address[:address_width]),
|
||||
|
@ -35,6 +39,6 @@ class RT2WB(Module):
|
|||
wb.cyc.eq(active),
|
||||
wb.stb.eq(active),
|
||||
|
||||
self.i.stb.eq(wb.ack & ~wb.we),
|
||||
self.i.data.eq(wb.dat_r)
|
||||
self.rtlink.i.stb.eq(wb.ack & ~wb.we),
|
||||
self.rtlink.i.data.eq(wb.dat_r)
|
||||
]
|
||||
|
|
|
@ -54,19 +54,19 @@
|
|||
"type": "local",
|
||||
"module": "artiq.coredevice.dds",
|
||||
"class": "DDS",
|
||||
"arguments": {"reg_channel": 0, "rtio_switch": 5}
|
||||
"arguments": {"channel": 0}
|
||||
},
|
||||
"dds1": {
|
||||
"type": "local",
|
||||
"module": "artiq.coredevice.dds",
|
||||
"class": "DDS",
|
||||
"arguments": {"reg_channel": 1, "rtio_switch": 6}
|
||||
"arguments": {"channel": 1}
|
||||
},
|
||||
"dds2": {
|
||||
"type": "local",
|
||||
"module": "artiq.coredevice.dds",
|
||||
"class": "DDS",
|
||||
"arguments": {"reg_channel": 2, "rtio_switch": 7}
|
||||
"arguments": {"channel": 2}
|
||||
},
|
||||
|
||||
"qc_q1_0": {
|
||||
|
|
|
@ -9,10 +9,17 @@ class DDSTest(Experiment, AutoDB):
|
|||
dds0 = Device()
|
||||
dds1 = Device()
|
||||
dds2 = Device()
|
||||
ttl0 = Device()
|
||||
ttl1 = Device()
|
||||
ttl2 = Device()
|
||||
led = Device()
|
||||
|
||||
@kernel
|
||||
def run(self):
|
||||
# with dds_batch:
|
||||
# self.dds1.set(120*MHz)
|
||||
# self.dds2.set(200*MHz)
|
||||
|
||||
for i in range(10000):
|
||||
if i & 0x200:
|
||||
self.led.on()
|
||||
|
@ -20,7 +27,8 @@ class DDSTest(Experiment, AutoDB):
|
|||
self.led.off()
|
||||
with parallel:
|
||||
with sequential:
|
||||
self.dds0.pulse(100*MHz + 4*i*kHz, 500*us)
|
||||
self.dds1.pulse(120*MHz, 500*us)
|
||||
self.dds2.pulse(200*MHz, 100*us)
|
||||
self.dds0.set(100*MHz + 4*i*kHz)
|
||||
self.ttl0.pulse(500*us)
|
||||
self.ttl1.pulse(500*us)
|
||||
self.ttl2.pulse(100*us)
|
||||
self.led.off()
|
||||
|
|
|
@ -34,7 +34,9 @@ ksupport.elf: $(OBJECTS_KSUPPORT)
|
|||
-T ksupport.ld \
|
||||
-N -o $@ \
|
||||
$(MSCDIR)/software/libbase/crt0-$(CPU).o \
|
||||
$^
|
||||
$^ \
|
||||
-L$(MSCDIR)/software/libcompiler-rt \
|
||||
-lcompiler-rt
|
||||
@chmod -x $@
|
||||
|
||||
ksupport_data.o: ksupport.bin
|
||||
|
|
|
@ -4,6 +4,26 @@
|
|||
#include "dds.h"
|
||||
#include "bridge.h"
|
||||
|
||||
static void dds_write(int addr, int data)
|
||||
{
|
||||
rtio_chan_sel_write(RTIO_DDS_CHANNEL);
|
||||
rtio_o_address_write(addr);
|
||||
rtio_o_data_write(data);
|
||||
rtio_o_timestamp_write(rtio_get_counter() + 8000);
|
||||
rtio_o_we_write(1);
|
||||
}
|
||||
|
||||
static int dds_read(int addr)
|
||||
{
|
||||
int r;
|
||||
|
||||
dds_write(addr | 128, 0);
|
||||
while(rtio_i_status_read() & RTIO_I_STATUS_EMPTY);
|
||||
r = rtio_i_data_read();
|
||||
rtio_i_re_write(1);
|
||||
return r;
|
||||
}
|
||||
|
||||
static void send_ready(void)
|
||||
{
|
||||
struct msg_base msg;
|
||||
|
@ -41,16 +61,16 @@ 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);
|
||||
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);
|
||||
g = dds_read(DDS_GPIO);
|
||||
dds_write(DDS_GPIO, g | (1 << 7));
|
||||
dds_write(DDS_GPIO, g);
|
||||
|
||||
mailbox_acknowledge();
|
||||
break;
|
||||
|
@ -61,7 +81,7 @@ void bridge_main(void)
|
|||
|
||||
msg = (struct msg_brg_dds_read_request *)umsg;
|
||||
rmsg.type = MESSAGE_TYPE_BRG_DDS_READ_REPLY;
|
||||
rmsg.data = DDS_READ(msg->address);
|
||||
rmsg.data = dds_read(msg->address);
|
||||
mailbox_send_and_wait(&rmsg);
|
||||
break;
|
||||
}
|
||||
|
@ -69,12 +89,12 @@ void bridge_main(void)
|
|||
struct msg_brg_dds_write *msg;
|
||||
|
||||
msg = (struct msg_brg_dds_write *)umsg;
|
||||
DDS_WRITE(msg->address, msg->data);
|
||||
dds_write(msg->address, msg->data);
|
||||
mailbox_acknowledge();
|
||||
break;
|
||||
}
|
||||
case MESSAGE_TYPE_BRG_DDS_FUD:
|
||||
rtio_fud(rtio_get_counter() + 8000);
|
||||
dds_write(DDS_FUD, 0);
|
||||
mailbox_acknowledge();
|
||||
break;
|
||||
default:
|
||||
|
|
|
@ -4,64 +4,66 @@
|
|||
#include "rtio.h"
|
||||
#include "dds.h"
|
||||
|
||||
void dds_init(void)
|
||||
{
|
||||
int i;
|
||||
#define DURATION_WRITE 5
|
||||
#define DURATION_PROGRAM (8*DURATION_WRITE)
|
||||
|
||||
for(i=0;i<8;i++) {
|
||||
DDS_WRITE(DDS_GPIO, i);
|
||||
DDS_WRITE(DDS_GPIO, i | (1 << 7));
|
||||
DDS_WRITE(DDS_GPIO, i);
|
||||
dds_phase_clear_en(i, 0);
|
||||
}
|
||||
}
|
||||
#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_phase_clear_en(int channel, int phase_clear_en)
|
||||
void dds_init(long long int timestamp, int channel)
|
||||
{
|
||||
long long int now;
|
||||
|
||||
rtio_chan_sel_write(RTIO_DDS_CHANNEL);
|
||||
|
||||
now = timestamp - 7*DURATION_WRITE;
|
||||
|
||||
DDS_WRITE(DDS_GPIO, channel);
|
||||
DDS_WRITE(DDS_GPIO, channel | (1 << 7));
|
||||
DDS_WRITE(DDS_GPIO, channel);
|
||||
|
||||
DDS_WRITE(0x00, 0x78);
|
||||
DDS_WRITE(0x01, 0x00);
|
||||
DDS_WRITE(0x02, phase_clear_en ? 0x40 : 0x00);
|
||||
DDS_WRITE(0x02, 0x00);
|
||||
DDS_WRITE(0x03, 0x00);
|
||||
}
|
||||
|
||||
/*
|
||||
* DDS phase modes:
|
||||
* - continuous: Set sysclk_per_microcycle=0 to disable POW alteration.
|
||||
* phase_tracking is ignored, set to 0.
|
||||
* Disable phase accumulator clearing prior to programming.
|
||||
* - absolute: Set sysclk_per_microcycle to its nominal value
|
||||
* and phase_tracking=0.
|
||||
* Enable phase accumulator clearing prior to programming.
|
||||
* - tracking: Set sysclk_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,
|
||||
unsigned int ftw, unsigned int pow, unsigned int sysclk_per_microcycle,
|
||||
int rt_fud, int phase_tracking)
|
||||
static void dds_set_one(long long int now, long long int timestamp, int channel,
|
||||
unsigned int ftw, unsigned int pow, int phase_mode)
|
||||
{
|
||||
long long int fud_time;
|
||||
unsigned int phase_time_offset;
|
||||
|
||||
rtio_fud_sync();
|
||||
DDS_WRITE(DDS_GPIO, channel);
|
||||
|
||||
if(phase_mode == PHASE_MODE_CONTINUOUS)
|
||||
/* Do not clear phase accumulator on FUD */
|
||||
DDS_WRITE(0x02, 0x00);
|
||||
else
|
||||
/* Clear phase accumulator on FUD */
|
||||
DDS_WRITE(0x02, 0x40);
|
||||
|
||||
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);
|
||||
|
||||
phase_time_offset = phase_tracking ? timestamp : 0;
|
||||
if(rt_fud)
|
||||
fud_time = timestamp;
|
||||
else {
|
||||
fud_time = rtio_get_counter() + 8000;
|
||||
/* POW is mod 2**14, so wraparound on negative values is OK */
|
||||
phase_time_offset -= timestamp - fud_time;
|
||||
}
|
||||
pow += phase_time_offset*ftw*sysclk_per_microcycle >> 18;
|
||||
if(phase_mode == PHASE_MODE_TRACKING)
|
||||
/* We assume that the RTIO clock is DDS SYNCLK */
|
||||
pow += (timestamp >> RTIO_FINE_TS_WIDTH)*ftw >> 18;
|
||||
DDS_WRITE(DDS_POW0, pow & 0xff);
|
||||
DDS_WRITE(DDS_POW1, (pow >> 8) & 0x3f);
|
||||
|
||||
rtio_fud(fud_time);
|
||||
}
|
||||
|
||||
void dds_set(long long int timestamp, int channel,
|
||||
unsigned int ftw, unsigned int pow, int phase_mode)
|
||||
{
|
||||
long long int now;
|
||||
|
||||
rtio_chan_sel_write(RTIO_DDS_CHANNEL);
|
||||
dds_set_one(timestamp - DURATION_PROGRAM, timestamp, channel, ftw, pow, phase_mode);
|
||||
now = timestamp;
|
||||
DDS_WRITE(DDS_FUD, 0);
|
||||
}
|
||||
|
|
|
@ -4,24 +4,23 @@
|
|||
#include <hw/common.h>
|
||||
#include <generated/mem.h>
|
||||
|
||||
#define DDS_READ(addr) \
|
||||
MMPTR(DDS_BASE + (addr)*4)
|
||||
|
||||
#define DDS_WRITE(addr, data) \
|
||||
MMPTR(DDS_BASE + (addr)*4) = data
|
||||
|
||||
#define DDS_FTW0 0x0a
|
||||
#define DDS_FTW1 0x0b
|
||||
#define DDS_FTW2 0x0c
|
||||
#define DDS_FTW3 0x0d
|
||||
#define DDS_POW0 0x0e
|
||||
#define DDS_POW1 0x0f
|
||||
#define DDS_FUD 0x40
|
||||
#define DDS_GPIO 0x41
|
||||
|
||||
void dds_init(void);
|
||||
void dds_phase_clear_en(int channel, int phase_clear_en);
|
||||
void dds_program(long long int timestamp, int channel,
|
||||
unsigned int ftw, unsigned int pow, unsigned int sysclk_per_microcycle,
|
||||
int rt_fud, int phase_tracking);
|
||||
enum {
|
||||
PHASE_MODE_CONTINUOUS = 0,
|
||||
PHASE_MODE_ABSOLUTE = 1,
|
||||
PHASE_MODE_TRACKING = 2
|
||||
};
|
||||
|
||||
void dds_init(long long int timestamp, int channel);
|
||||
void dds_set(long long int timestamp, int channel,
|
||||
unsigned int ftw, unsigned int pow, int phase_mode);
|
||||
|
||||
#endif /* __DDS_H */
|
||||
|
|
|
@ -17,8 +17,8 @@ services = [
|
|||
("rtio_set_sensitivity", "rtio_set_sensitivity"),
|
||||
("rtio_get_counter", "rtio_get_counter"),
|
||||
("rtio_get", "rtio_get"),
|
||||
("dds_phase_clear_en", "dds_phase_clear_en"),
|
||||
("dds_program", "dds_program"),
|
||||
("dds_init", "dds_init"),
|
||||
("dds_set", "dds_set"),
|
||||
]),
|
||||
("eh", [
|
||||
("setjmp", "exception_setjmp"),
|
||||
|
|
|
@ -78,7 +78,6 @@ long long int now_init(void)
|
|||
mailbox_acknowledge();
|
||||
|
||||
if(now < 0) {
|
||||
dds_init();
|
||||
rtio_init();
|
||||
now = 125000;
|
||||
}
|
||||
|
|
|
@ -3,42 +3,17 @@
|
|||
#include "exceptions.h"
|
||||
#include "rtio.h"
|
||||
|
||||
#define RTIO_O_STATUS_FULL 1
|
||||
#define RTIO_O_STATUS_UNDERFLOW 2
|
||||
#define RTIO_O_STATUS_SEQUENCE_ERROR 4
|
||||
#define RTIO_I_STATUS_EMPTY 1
|
||||
#define RTIO_I_STATUS_OVERFLOW 2
|
||||
|
||||
long long int previous_fud_end_time;
|
||||
|
||||
void rtio_init(void)
|
||||
{
|
||||
previous_fud_end_time = 0;
|
||||
rtio_reset_write(1);
|
||||
rtio_reset_write(0);
|
||||
rtio_reset_phy_write(0);
|
||||
}
|
||||
|
||||
static void write_and_process_status(long long int timestamp, int channel)
|
||||
long long int rtio_get_counter(void)
|
||||
{
|
||||
int status;
|
||||
|
||||
rtio_o_we_write(1);
|
||||
status = rtio_o_status_read();
|
||||
if(status) {
|
||||
if(status & RTIO_O_STATUS_FULL)
|
||||
while(rtio_o_status_read() & RTIO_O_STATUS_FULL);
|
||||
if(status & RTIO_O_STATUS_UNDERFLOW) {
|
||||
rtio_o_underflow_reset_write(1);
|
||||
exception_raise_params(EID_RTIO_UNDERFLOW,
|
||||
timestamp, channel, rtio_get_counter());
|
||||
}
|
||||
if(status & RTIO_O_STATUS_SEQUENCE_ERROR) {
|
||||
rtio_o_sequence_error_reset_write(1);
|
||||
exception_raise_params(EID_RTIO_SEQUENCE_ERROR,
|
||||
timestamp, channel, 0);
|
||||
}
|
||||
}
|
||||
rtio_counter_update_write(1);
|
||||
return rtio_counter_read();
|
||||
}
|
||||
|
||||
void rtio_set_o(long long int timestamp, int channel, int value)
|
||||
|
@ -47,7 +22,7 @@ void rtio_set_o(long long int timestamp, int channel, int value)
|
|||
rtio_o_timestamp_write(timestamp);
|
||||
rtio_o_address_write(0);
|
||||
rtio_o_data_write(value);
|
||||
write_and_process_status(timestamp, channel);
|
||||
rtio_write_and_process_status(timestamp, channel);
|
||||
}
|
||||
|
||||
void rtio_set_oe(long long int timestamp, int channel, int oe)
|
||||
|
@ -56,7 +31,7 @@ void rtio_set_oe(long long int timestamp, int channel, int oe)
|
|||
rtio_o_timestamp_write(timestamp);
|
||||
rtio_o_address_write(1);
|
||||
rtio_o_data_write(oe);
|
||||
write_and_process_status(timestamp, channel);
|
||||
rtio_write_and_process_status(timestamp, channel);
|
||||
}
|
||||
|
||||
void rtio_set_sensitivity(long long int timestamp, int channel, int sensitivity)
|
||||
|
@ -65,13 +40,7 @@ void rtio_set_sensitivity(long long int timestamp, int channel, int sensitivity)
|
|||
rtio_o_timestamp_write(timestamp);
|
||||
rtio_o_address_write(2);
|
||||
rtio_o_data_write(sensitivity);
|
||||
write_and_process_status(timestamp, channel);
|
||||
}
|
||||
|
||||
long long int rtio_get_counter(void)
|
||||
{
|
||||
rtio_counter_update_write(1);
|
||||
return rtio_counter_read();
|
||||
rtio_write_and_process_status(timestamp, channel);
|
||||
}
|
||||
|
||||
long long int rtio_get(int channel, long long int time_limit)
|
||||
|
@ -99,39 +68,3 @@ long long int rtio_get(int channel, long long int time_limit)
|
|||
rtio_i_re_write(1);
|
||||
return r;
|
||||
}
|
||||
|
||||
void rtio_fud_sync(void)
|
||||
{
|
||||
while(rtio_get_counter() < previous_fud_end_time);
|
||||
}
|
||||
|
||||
void rtio_fud(long long int fud_time)
|
||||
{
|
||||
long long int fud_end_time;
|
||||
int status;
|
||||
|
||||
rtio_chan_sel_write(RTIO_FUD_CHANNEL);
|
||||
rtio_o_address_write(0);
|
||||
fud_end_time = fud_time + 3*8;
|
||||
previous_fud_end_time = fud_end_time;
|
||||
|
||||
rtio_o_timestamp_write(fud_time);
|
||||
rtio_o_data_write(1);
|
||||
rtio_o_we_write(1);
|
||||
rtio_o_timestamp_write(fud_end_time);
|
||||
rtio_o_data_write(0);
|
||||
rtio_o_we_write(1);
|
||||
status = rtio_o_status_read();
|
||||
if(status) {
|
||||
if(status & RTIO_O_STATUS_UNDERFLOW) {
|
||||
rtio_o_underflow_reset_write(1);
|
||||
exception_raise_params(EID_RTIO_UNDERFLOW,
|
||||
fud_time, RTIO_FUD_CHANNEL, rtio_get_counter());
|
||||
}
|
||||
if(status & RTIO_O_STATUS_SEQUENCE_ERROR) {
|
||||
rtio_o_sequence_error_reset_write(1);
|
||||
exception_raise_params(EID_RTIO_SEQUENCE_ERROR,
|
||||
fud_time, RTIO_FUD_CHANNEL, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,14 +1,43 @@
|
|||
#ifndef __RTIO_H
|
||||
#define __RTIO_H
|
||||
|
||||
#include <generated/csr.h>
|
||||
#include "exceptions.h"
|
||||
|
||||
#define RTIO_O_STATUS_FULL 1
|
||||
#define RTIO_O_STATUS_UNDERFLOW 2
|
||||
#define RTIO_O_STATUS_SEQUENCE_ERROR 4
|
||||
#define RTIO_I_STATUS_EMPTY 1
|
||||
#define RTIO_I_STATUS_OVERFLOW 2
|
||||
|
||||
void rtio_init(void);
|
||||
long long int rtio_get_counter(void);
|
||||
|
||||
static inline void rtio_write_and_process_status(long long int timestamp, int channel)
|
||||
{
|
||||
int status;
|
||||
|
||||
rtio_o_we_write(1);
|
||||
status = rtio_o_status_read();
|
||||
if(status) {
|
||||
if(status & RTIO_O_STATUS_FULL)
|
||||
while(rtio_o_status_read() & RTIO_O_STATUS_FULL);
|
||||
if(status & RTIO_O_STATUS_UNDERFLOW) {
|
||||
rtio_o_underflow_reset_write(1);
|
||||
exception_raise_params(EID_RTIO_UNDERFLOW,
|
||||
timestamp, channel, rtio_get_counter());
|
||||
}
|
||||
if(status & RTIO_O_STATUS_SEQUENCE_ERROR) {
|
||||
rtio_o_sequence_error_reset_write(1);
|
||||
exception_raise_params(EID_RTIO_SEQUENCE_ERROR,
|
||||
timestamp, channel, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void rtio_set_o(long long int timestamp, int channel, int value);
|
||||
void rtio_set_oe(long long int timestamp, int channel, int oe);
|
||||
void rtio_set_sensitivity(long long int timestamp, int channel, int sensitivity);
|
||||
long long int rtio_get_counter(void);
|
||||
long long int rtio_get(int channel, long long int time_limit);
|
||||
|
||||
void rtio_fud_sync(void);
|
||||
void rtio_fud(long long int fud_time);
|
||||
|
||||
#endif /* __RTIO_H */
|
||||
|
|
|
@ -11,6 +11,7 @@ from targets.kc705 import MiniSoC
|
|||
from artiq.gateware.soc import AMPSoC
|
||||
from artiq.gateware import rtio, ad9858, nist_qc1
|
||||
from artiq.gateware.rtio.phy import ttl_simple
|
||||
from artiq.gateware.rtio.phy.wishbone import RT2WB
|
||||
|
||||
|
||||
class _RTIOCRG(Module, AutoCSR):
|
||||
|
@ -40,7 +41,6 @@ class NIST_QC1(MiniSoC, AMPSoC):
|
|||
csr_map.update(MiniSoC.csr_map)
|
||||
mem_map = {
|
||||
"rtio": 0x20000000, # (shadow @0xa0000000)
|
||||
"dds": 0x50000000, # (shadow @0xd0000000)
|
||||
"mailbox": 0x70000000 # (shadow @0xf0000000)
|
||||
}
|
||||
mem_map.update(MiniSoC.mem_map)
|
||||
|
@ -75,20 +75,20 @@ class NIST_QC1(MiniSoC, AMPSoC):
|
|||
self.submodules += phy
|
||||
rtio_channels.append(rtio.Channel(phy.rtlink))
|
||||
|
||||
fud = Signal()
|
||||
self.add_constant("RTIO_FUD_CHANNEL", len(rtio_channels))
|
||||
phy = ttl_simple.Output(fud)
|
||||
self.add_constant("RTIO_DDS_CHANNEL", len(rtio_channels))
|
||||
self.submodules.dds = RenameClockDomains(
|
||||
ad9858.AD9858(platform.request("dds")),
|
||||
"rio")
|
||||
phy = RT2WB(7, self.dds.bus)
|
||||
self.submodules += phy
|
||||
rtio_channels.append(rtio.Channel(phy.rtlink))
|
||||
rtio_channels.append(rtio.Channel(phy.rtlink, ififo_depth=4))
|
||||
|
||||
# RTIO core
|
||||
self.submodules.rtiocrg = _RTIOCRG(platform, self.crg.pll_sys)
|
||||
self.submodules.rtio = rtio.RTIO(rtio_channels,
|
||||
clk_freq=125000000)
|
||||
self.add_constant("RTIO_FINE_TS_WIDTH", self.rtio.fine_ts_width)
|
||||
|
||||
dds_pads = platform.request("dds")
|
||||
self.submodules.dds = ad9858.AD9858(dds_pads)
|
||||
self.comb += dds_pads.fud_n.eq(~fud)
|
||||
|
||||
if isinstance(platform.toolchain, XilinxVivadoToolchain):
|
||||
platform.add_platform_command("""
|
||||
|
@ -106,9 +106,5 @@ set_false_path -from [get_clocks rio_clk] -to [get_clocks rsys_clk]
|
|||
self.add_csr_region("rtio", self.mem_map["rtio"] | 0x80000000, 32,
|
||||
rtio_csrs)
|
||||
|
||||
self.kernel_cpu.add_wb_slave(mem_decoder(self.mem_map["dds"]),
|
||||
self.dds.bus)
|
||||
self.add_memory_region("dds", self.mem_map["dds"] | 0x80000000, 64*4)
|
||||
|
||||
|
||||
default_subtarget = NIST_QC1
|
||||
|
|
|
@ -9,6 +9,7 @@ from targets.pipistrello import BaseSoC
|
|||
from artiq.gateware.soc import AMPSoC
|
||||
from artiq.gateware import rtio, ad9858, nist_qc1
|
||||
from artiq.gateware.rtio.phy import ttl_simple
|
||||
from artiq.gateware.rtio.phy.wishbone import RT2WB
|
||||
|
||||
|
||||
class _RTIOCRG(Module, AutoCSR):
|
||||
|
@ -61,7 +62,6 @@ class NIST_QC1(BaseSoC, AMPSoC):
|
|||
csr_map.update(BaseSoC.csr_map)
|
||||
mem_map = {
|
||||
"rtio": 0x20000000, # (shadow @0xa0000000)
|
||||
"dds": 0x50000000, # (shadow @0xd0000000)
|
||||
"mailbox": 0x70000000 # (shadow @0xf0000000)
|
||||
}
|
||||
mem_map.update(BaseSoC.mem_map)
|
||||
|
@ -110,20 +110,19 @@ trce -v 12 -fastpaths -tsi {build_name}.tsi -o {build_name}.twr {build_name}.ncd
|
|||
self.submodules += phy
|
||||
rtio_channels.append(rtio.Channel(phy.rtlink))
|
||||
|
||||
fud = Signal()
|
||||
self.add_constant("RTIO_FUD_CHANNEL", len(rtio_channels))
|
||||
phy = ttl_simple.Output(fud)
|
||||
self.add_constant("RTIO_DDS_CHANNEL", len(rtio_channels))
|
||||
self.submodules.dds = RenameClockDomains(
|
||||
ad9858.AD9858(platform.request("dds")),
|
||||
"rio")
|
||||
phy = RT2WB(7, self.dds.bus)
|
||||
self.submodules += phy
|
||||
rtio_channels.append(rtio.Channel(phy.rtlink))
|
||||
rtio_channels.append(rtio.Channel(phy.rtlink, ififo_depth=4))
|
||||
|
||||
# RTIO core
|
||||
self.submodules.rtiocrg = _RTIOCRG(platform)
|
||||
self.submodules.rtio = rtio.RTIO(rtio_channels,
|
||||
clk_freq=125000000)
|
||||
|
||||
dds_pads = platform.request("dds")
|
||||
self.submodules.dds = ad9858.AD9858(dds_pads)
|
||||
self.comb += dds_pads.fud_n.eq(~fud)
|
||||
self.add_constant("RTIO_FINE_TS_WIDTH", self.rtio.fine_ts_width)
|
||||
|
||||
# CPU connections
|
||||
rtio_csrs = self.rtio.get_csrs()
|
||||
|
@ -133,9 +132,5 @@ trce -v 12 -fastpaths -tsi {build_name}.tsi -o {build_name}.twr {build_name}.ncd
|
|||
self.add_csr_region("rtio", self.mem_map["rtio"] | 0x80000000, 32,
|
||||
rtio_csrs)
|
||||
|
||||
self.kernel_cpu.add_wb_slave(mem_decoder(self.mem_map["dds"]),
|
||||
self.dds.bus)
|
||||
self.add_memory_region("dds", self.mem_map["dds"] | 0x80000000, 64*4)
|
||||
|
||||
|
||||
default_subtarget = NIST_QC1
|
||||
|
|
Loading…
Reference in New Issue