diff --git a/artiq/coredevice/dds.py b/artiq/coredevice/dds.py
index 8ab434664..903112680 100644
--- a/artiq/coredevice/dds.py
+++ b/artiq/coredevice/dds.py
@@ -51,13 +51,12 @@ class DDS(AutoDB):
Controls one DDS channel managed directly by the core device's runtime.
- :param dds_sysclk: DDS system frequency, used for computing the frequency
- tuning words.
+ :param sysclk: DDS system frequency.
:param channel: channel number of the DDS device to control.
"""
class DBKeys:
core = Device()
- dds_sysclk = Argument(1*GHz)
+ sysclk = Argument()
channel = Argument()
def build(self):
@@ -68,14 +67,14 @@ class DDS(AutoDB):
"""Returns the frequency tuning word corresponding to the given
frequency.
"""
- return round(2**32*frequency/self.dds_sysclk)
+ return round(2**32*frequency/self.sysclk)
@portable
def ftw_to_frequency(self, ftw):
"""Returns the frequency corresponding to the given frequency tuning
word.
"""
- return ftw*self.dds_sysclk/2**32
+ return ftw*self.sysclk/2**32
@kernel
def init(self):
diff --git a/artiq/gateware/rtio/moninj.py b/artiq/gateware/rtio/moninj.py
index a09422945..9bb0d0283 100644
--- a/artiq/gateware/rtio/moninj.py
+++ b/artiq/gateware/rtio/moninj.py
@@ -11,6 +11,7 @@ class Monitor(Module, AutoCSR):
max_probe_len = max(flen(p) for cp in chan_probes for p in cp)
self.chan_sel = CSRStorage(bits_for(len(chan_probes)-1))
self.probe_sel = CSRStorage(bits_for(max_chan_probes-1))
+ self.value_update = CSR()
self.value = CSRStatus(max_probe_len)
# # #
@@ -25,8 +26,9 @@ class Monitor(Module, AutoCSR):
cp_sys.append(vs.o)
cp_sys += [0]*(max_chan_probes-len(cp))
chan_probes_sys.append(Array(cp_sys)[self.probe_sel.storage])
- self.comb += self.value.status.eq(
- Array(chan_probes_sys)[self.chan_sel.storage])
+ self.sync += If(self.value_update.re,
+ self.value.status.eq(
+ Array(chan_probes_sys)[self.chan_sel.storage]))
class Injector(Module, AutoCSR):
diff --git a/artiq/gateware/rtio/phy/dds.py b/artiq/gateware/rtio/phy/dds.py
new file mode 100644
index 000000000..b39b86180
--- /dev/null
+++ b/artiq/gateware/rtio/phy/dds.py
@@ -0,0 +1,36 @@
+from migen.fhdl.std import *
+
+from artiq.gateware import ad9858 as ad9858_ll
+from artiq.gateware.rtio.phy.wishbone import RT2WB
+
+
+class AD9858(Module):
+ def __init__(self, pads, nchannels=8, **kwargs):
+ self.submodules._ll = RenameClockDomains(
+ ad9858_ll.AD9858(pads, **kwargs), "rio")
+ self.submodules._rt2wb = RT2WB(7, self._ll.bus)
+ self.rtlink = self._rt2wb.rtlink
+ self.probes = [Signal(32) for i in range(nchannels)]
+
+ # # #
+
+ # keep track of the currently selected channel
+ current_channel = Signal(max=nchannels)
+ self.sync.rio += If(self.rtlink.o.stb & (self.rtlink.o.address == 65),
+ current_channel.eq(self.rtlink.o.data))
+
+ # keep track of frequency tuning words, before they are FUDed
+ ftws = [Signal(32) for i in range(nchannels)]
+ for i in range(4):
+ for c, ftw in enumerate(ftws):
+ self.sync.rio += \
+ If(self.rtlink.o.stb & \
+ (self.rtlink.o.address == 0x0a+i) & \
+ (current_channel == c),
+ ftw[i*8:(i+1)*8].eq(self.rtlink.o.data)
+ )
+
+ # FTW to probe on FUD
+ for c, (probe, ftw) in enumerate(zip(self.probes, ftw)):
+ fud = self.rtlink.o.stb & (self.rtlink.o.address == 64)
+ self.sync.rio += If(fud & (current_channel == c), probe.eq(ftw))
diff --git a/artiq/gui/moninj.py b/artiq/gui/moninj.py
index 9683754d3..c106d1a5a 100644
--- a/artiq/gui/moninj.py
+++ b/artiq/gui/moninj.py
@@ -9,6 +9,7 @@ from pyqtgraph import dockarea
from artiq.tools import TaskObject
from artiq.protocols.sync_struct import Subscriber
+from artiq.language.units import strip_unit
logger = logging.getLogger(__name__)
@@ -112,9 +113,10 @@ class _TTLWidget(QtGui.QFrame):
class _DDSWidget(QtGui.QFrame):
- def __init__(self, send_to_device, channel, name):
+ def __init__(self, send_to_device, channel, sysclk, name):
self.send_to_device = send_to_device
self.channel = channel
+ self.sysclk = sysclk
self.name = name
QtGui.QFrame.__init__(self)
@@ -128,48 +130,16 @@ class _DDSWidget(QtGui.QFrame):
label.setAlignment(QtCore.Qt.AlignCenter)
grid.addWidget(label, 1, 1)
- self._override = QtGui.QLabel()
- self._override.setAlignment(QtCore.Qt.AlignCenter)
- grid.addWidget(self._override, 2, 1)
-
self._value = QtGui.QLabel()
self._value.setAlignment(QtCore.Qt.AlignCenter)
- grid.addWidget(self._value, 3, 1, 6, 1)
+ grid.addWidget(self._value, 2, 1, 6, 1)
- self._value.setContextMenuPolicy(QtCore.Qt.ActionsContextMenu)
- self._override_action = QtGui.QAction("Override", self._value)
- self._override_action.setCheckable(True)
- self._value.addAction(self._override_action)
- self._override_action.triggered.connect(self._override_clicked)
+ self.set_value(0)
- self.set_value(0.0, False)
-
- def _override_clicked(self):
- override_en = self._override_action.isChecked()
- if override_en:
- frequency, ok = QtGui.QInputDialog.getDouble(
- None, "DDS override",
- "Frequency in MHz for {}:".format(self.name),
- value=self._frequency, min=0, decimals=3)
- self._override_action.setChecked(ok)
- if ok:
- print("override set to", frequency)
- else:
- print("override disabled")
-
- def set_value(self, frequency, override):
- self._frequency = frequency
- self._override_action.setChecked(override)
- value_s = "{:.3f} MHz".format(frequency)
- if override:
- value_s = "" + value_s + ""
- color = " color=\"red\""
- self._override.setText("OVERRIDE")
- else:
- color = ""
- self._override.setText("")
- self._value.setText("{}"
- .format(color, value_s))
+ def set_value(self, ftw):
+ frequency = ftw*self.sysclk/2**32
+ self._value.setText("{:.3f} MHz"
+ .format(float(frequency)))
class _DeviceManager:
@@ -200,8 +170,9 @@ class _DeviceManager:
if (v["module"] == "artiq.coredevice.dds"
and v["class"] == "DDS"):
channel = v["arguments"]["channel"]
+ sysclk = strip_unit(v["arguments"]["sysclk"], "Hz")
self.dds_widgets[channel] = _DDSWidget(
- self.send_to_device, channel, k)
+ self.send_to_device, channel, sysclk, k)
self.dds_cb()
except KeyError:
pass
@@ -277,11 +248,25 @@ class MonInj(TaskObject):
self.transport = transport
def datagram_received(self, data, addr):
- ttl_levels, ttl_oes, ttl_overrides = struct.unpack(">QQQ", data)
- for channel, w in self.dm.ttl_widgets.items():
- w.set_value(ttl_levels & (1 << channel),
- ttl_oes & (1 << channel),
- ttl_overrides & (1 << channel))
+ try:
+ ttl_levels, ttl_oes, ttl_overrides = \
+ struct.unpack(">QQQ", data[:8*3])
+ for channel, w in self.dm.ttl_widgets.items():
+ w.set_value(ttl_levels & (1 << channel),
+ ttl_oes & (1 << channel),
+ ttl_overrides & (1 << channel))
+ dds_data = data[8*3:]
+ ndds = len(dds_data)//4
+ ftws = struct.unpack(">" + "I"*ndds, dds_data)
+ for channel, w in self.dm.dds_widgets.items():
+ try:
+ ftw = ftws[channel]
+ except KeyError:
+ pass
+ else:
+ w.set_value(ftw)
+ except:
+ logger.warning("failed to process datagram", exc_info=True)
def error_received(self, exc):
logger.warning("datagram endpoint error")
diff --git a/examples/master/ddb.pyon b/examples/master/ddb.pyon
index 7e7778f66..5dd3db64c 100644
--- a/examples/master/ddb.pyon
+++ b/examples/master/ddb.pyon
@@ -60,19 +60,22 @@
"type": "local",
"module": "artiq.coredevice.dds",
"class": "DDS",
- "arguments": {"channel": 0}
+ "arguments": {"sysclk": Quantity(Fraction(1000000000, 1), "Hz"),
+ "channel": 0}
},
"dds1": {
"type": "local",
"module": "artiq.coredevice.dds",
"class": "DDS",
- "arguments": {"channel": 1}
+ "arguments": {"sysclk": Quantity(Fraction(1000000000, 1), "Hz"),
+ "channel": 1}
},
"dds2": {
"type": "local",
"module": "artiq.coredevice.dds",
"class": "DDS",
- "arguments": {"channel": 2}
+ "arguments": {"sysclk": Quantity(Fraction(1000000000, 1), "Hz"),
+ "channel": 2}
},
"qc_q1_0": {
diff --git a/soc/runtime/dds.c b/soc/runtime/dds.c
index 5e94d5e1c..9c0d17e61 100644
--- a/soc/runtime/dds.c
+++ b/soc/runtime/dds.c
@@ -61,7 +61,7 @@ static void dds_set_one(long long int now, long long int ref_time, int channel,
DDS_WRITE(DDS_FTW3, (ftw >> 24) & 0xff);
/* We need the RTIO fine timestamp clock to be phase-locked
- * to DDS SYNCLK, and divided by an integer DDS_RTIO_CLK_RATIO.
+ * 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 */
diff --git a/soc/runtime/dds.h b/soc/runtime/dds.h
index e6adc326c..e145b5b01 100644
--- a/soc/runtime/dds.h
+++ b/soc/runtime/dds.h
@@ -4,9 +4,6 @@
#include
#include
-/* Number of DDS channels to initialize */
-#define DDS_CHANNEL_COUNT 8
-
/* Maximum number of commands in a batch */
#define DDS_MAX_BATCH 16
diff --git a/soc/runtime/moninj.c b/soc/runtime/moninj.c
index 336c88ce1..34507d9a6 100644
--- a/soc/runtime/moninj.c
+++ b/soc/runtime/moninj.c
@@ -39,6 +39,7 @@ struct monitor_reply {
long long int ttl_levels;
long long int ttl_oes;
long long int ttl_overrides;
+ unsigned int dds_ftws[DDS_CHANNEL_COUNT];
};
static void moninj_monitor(const ip_addr_t *addr, u16_t port)
@@ -53,9 +54,11 @@ static void moninj_monitor(const ip_addr_t *addr, u16_t port)
for(i=0;i