forked from M-Labs/artiq
1
0
Fork 0

DDS over RTIO (batch mode not supported yet)

This commit is contained in:
Sebastien Bourdeauducq 2015-05-08 14:44:39 +08:00
parent a91bb48ced
commit a36c51eb83
17 changed files with 201 additions and 271 deletions

View File

@ -1,10 +1,10 @@
from artiq.language.core import * from artiq.language.core import *
from artiq.language.db import * from artiq.language.db import *
from artiq.language.units import * from artiq.language.units import *
from artiq.coredevice import ttl
PHASE_MODE_DEFAULT = -1 PHASE_MODE_DEFAULT = -1
# keep in sync with dds.h
PHASE_MODE_CONTINUOUS = 0 PHASE_MODE_CONTINUOUS = 0
PHASE_MODE_ABSOLUTE = 1 PHASE_MODE_ABSOLUTE = 1
PHASE_MODE_TRACKING = 2 PHASE_MODE_TRACKING = 2
@ -13,34 +13,24 @@ PHASE_MODE_TRACKING = 2
class DDS(AutoDB): class DDS(AutoDB):
"""Core device Direct Digital Synthesis (DDS) driver. """Core device Direct Digital Synthesis (DDS) driver.
Controls DDS devices managed directly by the core device's runtime. It also Controls DDS devices managed directly by the core device's runtime.
uses a RTIO TTL channel (through :class:`artiq.coredevice.ttl.TTLOut`) to
control a RF switch that gates the output of the DDS device.
:param dds_sysclk: DDS system frequency, used for computing the frequency :param dds_sysclk: DDS system frequency, used for computing the frequency
tuning words. tuning words.
:param reg_channel: channel number of the DDS device to control. :param channel: channel number of the DDS device to control.
:param rtio_switch: RTIO channel number of the RF switch associated with
the DDS device.
""" """
class DBKeys: class DBKeys:
core = Device() core = Device()
dds_sysclk = Parameter(1*GHz) dds_sysclk = Argument(1*GHz)
reg_channel = Argument() channel = Argument()
rtio_switch = Argument()
def build(self): def build(self):
self.previous_on = False self.phase_mode = PHASE_MODE_CONTINUOUS
self.previous_frequency = 0*MHz
self.set_phase_mode(PHASE_MODE_CONTINUOUS)
self.sw = ttl.TTLOut(core=self.core, channel=self.rtio_switch)
@portable @portable
def frequency_to_ftw(self, frequency): def frequency_to_ftw(self, frequency):
"""Returns the frequency tuning word corresponding to the given """Returns the frequency tuning word corresponding to the given
frequency. frequency.
""" """
return round(2**32*frequency/self.dds_sysclk) return round(2**32*frequency/self.dds_sysclk)
@ -48,10 +38,14 @@ class DDS(AutoDB):
def ftw_to_frequency(self, ftw): def ftw_to_frequency(self, ftw):
"""Returns the frequency corresponding to the given frequency tuning """Returns the frequency corresponding to the given frequency tuning
word. word.
""" """
return ftw*self.dds_sysclk/2**32 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 @kernel
def set_phase_mode(self, phase_mode): def set_phase_mode(self, phase_mode):
"""Sets the phase mode of the DDS channel. Supported phase modes are: """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 accumulator is set to the value it would have if the DDS had been
running at the specified frequency since the start of the running at the specified frequency since the start of the
experiment. experiment.
""" """
self.phase_mode = phase_mode self.phase_mode = phase_mode
syscall("dds_phase_clear_en", self.reg_channel,
self.phase_mode != PHASE_MODE_CONTINUOUS)
@kernel @kernel
def on(self, frequency, phase_mode=PHASE_MODE_DEFAULT, phase_offset=0): def set(self, frequency, phase_mode=PHASE_MODE_DEFAULT, phase_offset=0):
"""Sets the DDS channel to the specified frequency and turns it on. """Sets the DDS channel to the specified frequency and phase.
If the DDS channel was already on, a real-time frequency and phase
update is performed.
:param frequency: frequency to generate. :param frequency: frequency to generate.
:param phase_mode: if specified, overrides the default phase mode set :param phase_mode: if specified, overrides the default phase mode set
by ``set_phase_mode`` for this call. by ``set_phase_mode`` for this call.
:param phase_offset: adds an offset, in turns, to the phase. :param phase_offset: adds an offset, in turns, to the phase.
""" """
if phase_mode != PHASE_MODE_DEFAULT: if phase_mode == PHASE_MODE_DEFAULT:
old_phase_mode = self.phase_mode phase_mode = self.phase_mode
self.set_phase_mode(phase_mode)
if self.previous_frequency != frequency: syscall("dds_set", time_to_cycles(now()), self.channel,
merge = self.sw.o_previous_timestamp == time_to_cycles(now()) self.frequency_to_ftw(frequency), round(phase_offset*2**14),
if not merge: self.phase_mode)
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()

View File

@ -21,8 +21,8 @@ _syscalls = {
"rtio_set_sensitivity": "Iii:n", "rtio_set_sensitivity": "Iii:n",
"rtio_get_counter": "n:I", "rtio_get_counter": "n:I",
"rtio_get": "iI:I", "rtio_get": "iI:I",
"dds_phase_clear_en": "ib:n", "dds_init": "Ii:n",
"dds_program": "Iiiiibb:n", "dds_set": "Iiiii:n",
} }

View File

@ -36,8 +36,8 @@ class AD9858(Module):
Read timing: Read timing:
Address is set one cycle before assertion of rd_n. Address is set one cycle before assertion of rd_n.
rd_n is asserted for 3 cycles. rd_n is asserted for read_wait_cycles, data is sampled at the end.
Data is sampled 2 cycles into the assertion of rd_n. rd_n is deasserted and data bus is not driven again before hiz_wait_cycles.
Design: Design:
All IO pads are registered. 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 Round-trip addr A setup (> RX, RD, D to Z), RD prop, D valid (< D
valid), D prop is ~15 + 10 + 20 + 10 = 55ns 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, read_wait_cycles=10, hiz_wait_cycles=3,
bus=None): bus=None):
if bus is None: if bus is None:
@ -84,9 +84,8 @@ class AD9858(Module):
bus.dat_r.eq(dr) bus.dat_r.eq(dr)
) )
if drive_fud: fud = Signal()
fud = Signal() self.sync += pads.fud_n.eq(~fud)
self.sync += pads.fud_n.eq(~fud)
pads.wr_n.reset = 1 pads.wr_n.reset = 1
pads.rd_n.reset = 1 pads.rd_n.reset = 1
@ -106,7 +105,7 @@ class AD9858(Module):
If(bus.adr[0], If(bus.adr[0],
NextState("GPIO") NextState("GPIO")
).Else( ).Else(
NextState("FUD") if drive_fud else None NextState("FUD")
) )
).Else( ).Else(
If(bus.we, If(bus.we,
@ -141,19 +140,18 @@ class AD9858(Module):
) )
fsm.act("WAIT_HIZ", fsm.act("WAIT_HIZ",
rx.eq(1), 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), hold_address.eq(1),
self.hiz_timer.wait.eq(1), self.hiz_timer.wait.eq(1),
If(self.hiz_timer.done, NextState("IDLE")) If(self.hiz_timer.done, NextState("IDLE"))
) )
if drive_fud: fsm.act("FUD",
fsm.act("FUD", # 4ns FUD setup to SYNCLK
# 4ns FUD setup to SYNCLK # 0ns FUD hold to SYNCLK
# 0ns FUD hold to SYNCLK fud.eq(1),
fud.eq(1), bus.ack.eq(1),
bus.ack.eq(1), NextState("IDLE")
NextState("IDLE") )
)
fsm.act("GPIO", fsm.act("GPIO",
bus.ack.eq(1), bus.ack.eq(1),
bus_r_gpio.eq(1), bus_r_gpio.eq(1),

View File

@ -291,6 +291,10 @@ class RTIO(Module):
fine_ts_width = max(rtlink.get_fine_ts_width(c.interface) fine_ts_width = max(rtlink.get_fine_ts_width(c.interface)
for c in channels) for c in channels)
self.data_width = data_width
self.address_width = address_width
self.fine_ts_width = fine_ts_width
# CSRs # CSRs
self.kcsrs = _KernelCSRs(bits_for(len(channels)-1), self.kcsrs = _KernelCSRs(bits_for(len(channels)-1),
data_width, address_width, data_width, address_width,

View File

@ -1,10 +1,14 @@
from migen.fhdl.std import * from migen.fhdl.std import *
from migen.bus import wishbone
from artiq.gateware.rtio import rtlink from artiq.gateware.rtio import rtlink
class RT2WB(Module): 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( self.rtlink = rtlink.Interface(
rtlink.OInterface( rtlink.OInterface(
flen(wb.dat_w), flen(wb.dat_w),
@ -18,7 +22,7 @@ class RT2WB(Module):
# # # # # #
active = Signal() active = Signal()
self.sync.rio_phy += [ self.sync.rio += [
If(self.rtlink.o.stb, If(self.rtlink.o.stb,
active.eq(1), active.eq(1),
wb.adr.eq(self.rtlink.o.address[:address_width]), wb.adr.eq(self.rtlink.o.address[:address_width]),
@ -35,6 +39,6 @@ class RT2WB(Module):
wb.cyc.eq(active), wb.cyc.eq(active),
wb.stb.eq(active), wb.stb.eq(active),
self.i.stb.eq(wb.ack & ~wb.we), self.rtlink.i.stb.eq(wb.ack & ~wb.we),
self.i.data.eq(wb.dat_r) self.rtlink.i.data.eq(wb.dat_r)
] ]

View File

@ -54,19 +54,19 @@
"type": "local", "type": "local",
"module": "artiq.coredevice.dds", "module": "artiq.coredevice.dds",
"class": "DDS", "class": "DDS",
"arguments": {"reg_channel": 0, "rtio_switch": 5} "arguments": {"channel": 0}
}, },
"dds1": { "dds1": {
"type": "local", "type": "local",
"module": "artiq.coredevice.dds", "module": "artiq.coredevice.dds",
"class": "DDS", "class": "DDS",
"arguments": {"reg_channel": 1, "rtio_switch": 6} "arguments": {"channel": 1}
}, },
"dds2": { "dds2": {
"type": "local", "type": "local",
"module": "artiq.coredevice.dds", "module": "artiq.coredevice.dds",
"class": "DDS", "class": "DDS",
"arguments": {"reg_channel": 2, "rtio_switch": 7} "arguments": {"channel": 2}
}, },
"qc_q1_0": { "qc_q1_0": {

View File

@ -9,10 +9,17 @@ class DDSTest(Experiment, AutoDB):
dds0 = Device() dds0 = Device()
dds1 = Device() dds1 = Device()
dds2 = Device() dds2 = Device()
ttl0 = Device()
ttl1 = Device()
ttl2 = Device()
led = Device() led = Device()
@kernel @kernel
def run(self): def run(self):
# with dds_batch:
# self.dds1.set(120*MHz)
# self.dds2.set(200*MHz)
for i in range(10000): for i in range(10000):
if i & 0x200: if i & 0x200:
self.led.on() self.led.on()
@ -20,7 +27,8 @@ class DDSTest(Experiment, AutoDB):
self.led.off() self.led.off()
with parallel: with parallel:
with sequential: with sequential:
self.dds0.pulse(100*MHz + 4*i*kHz, 500*us) self.dds0.set(100*MHz + 4*i*kHz)
self.dds1.pulse(120*MHz, 500*us) self.ttl0.pulse(500*us)
self.dds2.pulse(200*MHz, 100*us) self.ttl1.pulse(500*us)
self.ttl2.pulse(100*us)
self.led.off() self.led.off()

View File

@ -34,7 +34,9 @@ ksupport.elf: $(OBJECTS_KSUPPORT)
-T ksupport.ld \ -T ksupport.ld \
-N -o $@ \ -N -o $@ \
$(MSCDIR)/software/libbase/crt0-$(CPU).o \ $(MSCDIR)/software/libbase/crt0-$(CPU).o \
$^ $^ \
-L$(MSCDIR)/software/libcompiler-rt \
-lcompiler-rt
@chmod -x $@ @chmod -x $@
ksupport_data.o: ksupport.bin ksupport_data.o: ksupport.bin

View File

@ -4,6 +4,26 @@
#include "dds.h" #include "dds.h"
#include "bridge.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) static void send_ready(void)
{ {
struct msg_base msg; struct msg_base msg;
@ -41,16 +61,16 @@ void bridge_main(void)
struct msg_brg_dds_sel *msg; struct msg_brg_dds_sel *msg;
msg = (struct msg_brg_dds_sel *)umsg; msg = (struct msg_brg_dds_sel *)umsg;
DDS_WRITE(DDS_GPIO, msg->channel); dds_write(DDS_GPIO, msg->channel);
mailbox_acknowledge(); mailbox_acknowledge();
break; break;
} }
case MESSAGE_TYPE_BRG_DDS_RESET: { case MESSAGE_TYPE_BRG_DDS_RESET: {
unsigned int g; unsigned int g;
g = DDS_READ(DDS_GPIO); g = dds_read(DDS_GPIO);
DDS_WRITE(DDS_GPIO, g | (1 << 7)); dds_write(DDS_GPIO, g | (1 << 7));
DDS_WRITE(DDS_GPIO, g); dds_write(DDS_GPIO, g);
mailbox_acknowledge(); mailbox_acknowledge();
break; break;
@ -61,7 +81,7 @@ void bridge_main(void)
msg = (struct msg_brg_dds_read_request *)umsg; msg = (struct msg_brg_dds_read_request *)umsg;
rmsg.type = MESSAGE_TYPE_BRG_DDS_READ_REPLY; 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); mailbox_send_and_wait(&rmsg);
break; break;
} }
@ -69,12 +89,12 @@ void bridge_main(void)
struct msg_brg_dds_write *msg; struct msg_brg_dds_write *msg;
msg = (struct msg_brg_dds_write *)umsg; msg = (struct msg_brg_dds_write *)umsg;
DDS_WRITE(msg->address, msg->data); dds_write(msg->address, msg->data);
mailbox_acknowledge(); mailbox_acknowledge();
break; break;
} }
case MESSAGE_TYPE_BRG_DDS_FUD: case MESSAGE_TYPE_BRG_DDS_FUD:
rtio_fud(rtio_get_counter() + 8000); dds_write(DDS_FUD, 0);
mailbox_acknowledge(); mailbox_acknowledge();
break; break;
default: default:

View File

@ -4,64 +4,66 @@
#include "rtio.h" #include "rtio.h"
#include "dds.h" #include "dds.h"
void dds_init(void) #define DURATION_WRITE 5
{ #define DURATION_PROGRAM (8*DURATION_WRITE)
int i;
for(i=0;i<8;i++) { #define DDS_WRITE(addr, data) do { \
DDS_WRITE(DDS_GPIO, i); rtio_o_address_write(addr); \
DDS_WRITE(DDS_GPIO, i | (1 << 7)); rtio_o_data_write(data); \
DDS_WRITE(DDS_GPIO, i); rtio_o_timestamp_write(now); \
dds_phase_clear_en(i, 0); 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(0x00, 0x78);
DDS_WRITE(0x01, 0x00); DDS_WRITE(0x01, 0x00);
DDS_WRITE(0x02, phase_clear_en ? 0x40 : 0x00); DDS_WRITE(0x02, 0x00);
DDS_WRITE(0x03, 0x00); DDS_WRITE(0x03, 0x00);
} }
/* static void dds_set_one(long long int now, long long int timestamp, int channel,
* DDS phase modes: unsigned int ftw, unsigned int pow, int phase_mode)
* - 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)
{ {
long long int fud_time;
unsigned int phase_time_offset;
rtio_fud_sync();
DDS_WRITE(DDS_GPIO, channel); 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_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(phase_mode == PHASE_MODE_TRACKING)
if(rt_fud) /* We assume that the RTIO clock is DDS SYNCLK */
fud_time = timestamp; pow += (timestamp >> RTIO_FINE_TS_WIDTH)*ftw >> 18;
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;
DDS_WRITE(DDS_POW0, pow & 0xff); DDS_WRITE(DDS_POW0, pow & 0xff);
DDS_WRITE(DDS_POW1, (pow >> 8) & 0x3f); 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);
} }

View File

@ -4,24 +4,23 @@
#include <hw/common.h> #include <hw/common.h>
#include <generated/mem.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_FTW0 0x0a
#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_POW0 0x0e
#define DDS_POW1 0x0f #define DDS_POW1 0x0f
#define DDS_FUD 0x40
#define DDS_GPIO 0x41 #define DDS_GPIO 0x41
void dds_init(void); enum {
void dds_phase_clear_en(int channel, int phase_clear_en); PHASE_MODE_CONTINUOUS = 0,
void dds_program(long long int timestamp, int channel, PHASE_MODE_ABSOLUTE = 1,
unsigned int ftw, unsigned int pow, unsigned int sysclk_per_microcycle, PHASE_MODE_TRACKING = 2
int rt_fud, int phase_tracking); };
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 */ #endif /* __DDS_H */

View File

@ -17,8 +17,8 @@ services = [
("rtio_set_sensitivity", "rtio_set_sensitivity"), ("rtio_set_sensitivity", "rtio_set_sensitivity"),
("rtio_get_counter", "rtio_get_counter"), ("rtio_get_counter", "rtio_get_counter"),
("rtio_get", "rtio_get"), ("rtio_get", "rtio_get"),
("dds_phase_clear_en", "dds_phase_clear_en"), ("dds_init", "dds_init"),
("dds_program", "dds_program"), ("dds_set", "dds_set"),
]), ]),
("eh", [ ("eh", [
("setjmp", "exception_setjmp"), ("setjmp", "exception_setjmp"),

View File

@ -78,7 +78,6 @@ long long int now_init(void)
mailbox_acknowledge(); mailbox_acknowledge();
if(now < 0) { if(now < 0) {
dds_init();
rtio_init(); rtio_init();
now = 125000; now = 125000;
} }

View File

@ -3,42 +3,17 @@
#include "exceptions.h" #include "exceptions.h"
#include "rtio.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) void rtio_init(void)
{ {
previous_fud_end_time = 0;
rtio_reset_write(1); rtio_reset_write(1);
rtio_reset_write(0); rtio_reset_write(0);
rtio_reset_phy_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_counter_update_write(1);
return rtio_counter_read();
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_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_timestamp_write(timestamp);
rtio_o_address_write(0); rtio_o_address_write(0);
rtio_o_data_write(value); 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) 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_timestamp_write(timestamp);
rtio_o_address_write(1); rtio_o_address_write(1);
rtio_o_data_write(oe); 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) 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_timestamp_write(timestamp);
rtio_o_address_write(2); rtio_o_address_write(2);
rtio_o_data_write(sensitivity); rtio_o_data_write(sensitivity);
write_and_process_status(timestamp, channel); rtio_write_and_process_status(timestamp, channel);
}
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 time_limit) 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); rtio_i_re_write(1);
return r; 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);
}
}
}

View File

@ -1,14 +1,43 @@
#ifndef __RTIO_H #ifndef __RTIO_H
#define __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); 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_o(long long int timestamp, int channel, int value);
void rtio_set_oe(long long int timestamp, int channel, int oe); void rtio_set_oe(long long int timestamp, int channel, int oe);
void rtio_set_sensitivity(long long int timestamp, int channel, int sensitivity); 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); 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 */ #endif /* __RTIO_H */

View File

@ -11,6 +11,7 @@ from targets.kc705 import MiniSoC
from artiq.gateware.soc import AMPSoC from artiq.gateware.soc import AMPSoC
from artiq.gateware import rtio, ad9858, nist_qc1 from artiq.gateware import rtio, ad9858, nist_qc1
from artiq.gateware.rtio.phy import ttl_simple from artiq.gateware.rtio.phy import ttl_simple
from artiq.gateware.rtio.phy.wishbone import RT2WB
class _RTIOCRG(Module, AutoCSR): class _RTIOCRG(Module, AutoCSR):
@ -40,7 +41,6 @@ class NIST_QC1(MiniSoC, AMPSoC):
csr_map.update(MiniSoC.csr_map) csr_map.update(MiniSoC.csr_map)
mem_map = { mem_map = {
"rtio": 0x20000000, # (shadow @0xa0000000) "rtio": 0x20000000, # (shadow @0xa0000000)
"dds": 0x50000000, # (shadow @0xd0000000)
"mailbox": 0x70000000 # (shadow @0xf0000000) "mailbox": 0x70000000 # (shadow @0xf0000000)
} }
mem_map.update(MiniSoC.mem_map) mem_map.update(MiniSoC.mem_map)
@ -75,20 +75,20 @@ class NIST_QC1(MiniSoC, AMPSoC):
self.submodules += phy self.submodules += phy
rtio_channels.append(rtio.Channel(phy.rtlink)) rtio_channels.append(rtio.Channel(phy.rtlink))
fud = Signal() self.add_constant("RTIO_DDS_CHANNEL", len(rtio_channels))
self.add_constant("RTIO_FUD_CHANNEL", len(rtio_channels)) self.submodules.dds = RenameClockDomains(
phy = ttl_simple.Output(fud) ad9858.AD9858(platform.request("dds")),
"rio")
phy = RT2WB(7, self.dds.bus)
self.submodules += phy self.submodules += phy
rtio_channels.append(rtio.Channel(phy.rtlink)) rtio_channels.append(rtio.Channel(phy.rtlink, ififo_depth=4))
# RTIO core # RTIO core
self.submodules.rtiocrg = _RTIOCRG(platform, self.crg.pll_sys) self.submodules.rtiocrg = _RTIOCRG(platform, self.crg.pll_sys)
self.submodules.rtio = rtio.RTIO(rtio_channels, self.submodules.rtio = rtio.RTIO(rtio_channels,
clk_freq=125000000) 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): if isinstance(platform.toolchain, XilinxVivadoToolchain):
platform.add_platform_command(""" 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, self.add_csr_region("rtio", self.mem_map["rtio"] | 0x80000000, 32,
rtio_csrs) 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 default_subtarget = NIST_QC1

View File

@ -9,6 +9,7 @@ from targets.pipistrello import BaseSoC
from artiq.gateware.soc import AMPSoC from artiq.gateware.soc import AMPSoC
from artiq.gateware import rtio, ad9858, nist_qc1 from artiq.gateware import rtio, ad9858, nist_qc1
from artiq.gateware.rtio.phy import ttl_simple from artiq.gateware.rtio.phy import ttl_simple
from artiq.gateware.rtio.phy.wishbone import RT2WB
class _RTIOCRG(Module, AutoCSR): class _RTIOCRG(Module, AutoCSR):
@ -61,7 +62,6 @@ class NIST_QC1(BaseSoC, AMPSoC):
csr_map.update(BaseSoC.csr_map) csr_map.update(BaseSoC.csr_map)
mem_map = { mem_map = {
"rtio": 0x20000000, # (shadow @0xa0000000) "rtio": 0x20000000, # (shadow @0xa0000000)
"dds": 0x50000000, # (shadow @0xd0000000)
"mailbox": 0x70000000 # (shadow @0xf0000000) "mailbox": 0x70000000 # (shadow @0xf0000000)
} }
mem_map.update(BaseSoC.mem_map) 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 self.submodules += phy
rtio_channels.append(rtio.Channel(phy.rtlink)) rtio_channels.append(rtio.Channel(phy.rtlink))
fud = Signal() self.add_constant("RTIO_DDS_CHANNEL", len(rtio_channels))
self.add_constant("RTIO_FUD_CHANNEL", len(rtio_channels)) self.submodules.dds = RenameClockDomains(
phy = ttl_simple.Output(fud) ad9858.AD9858(platform.request("dds")),
"rio")
phy = RT2WB(7, self.dds.bus)
self.submodules += phy self.submodules += phy
rtio_channels.append(rtio.Channel(phy.rtlink)) rtio_channels.append(rtio.Channel(phy.rtlink, ififo_depth=4))
# RTIO core # RTIO core
self.submodules.rtiocrg = _RTIOCRG(platform) self.submodules.rtiocrg = _RTIOCRG(platform)
self.submodules.rtio = rtio.RTIO(rtio_channels, self.submodules.rtio = rtio.RTIO(rtio_channels,
clk_freq=125000000) 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)
# CPU connections # CPU connections
rtio_csrs = self.rtio.get_csrs() 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, self.add_csr_region("rtio", self.mem_map["rtio"] | 0x80000000, 32,
rtio_csrs) 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 default_subtarget = NIST_QC1