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