forked from M-Labs/artiq
DDS monitoring
This commit is contained in:
parent
03fe71228b
commit
5a9bdb2e33
|
@ -51,13 +51,12 @@ class DDS(AutoDB):
|
||||||
|
|
||||||
Controls one DDS channel managed directly by the core device's runtime.
|
Controls one DDS channel managed directly by the core device's runtime.
|
||||||
|
|
||||||
:param dds_sysclk: DDS system frequency, used for computing the frequency
|
:param sysclk: DDS system frequency.
|
||||||
tuning words.
|
|
||||||
:param channel: channel number of the DDS device to control.
|
:param channel: channel number of the DDS device to control.
|
||||||
"""
|
"""
|
||||||
class DBKeys:
|
class DBKeys:
|
||||||
core = Device()
|
core = Device()
|
||||||
dds_sysclk = Argument(1*GHz)
|
sysclk = Argument()
|
||||||
channel = Argument()
|
channel = Argument()
|
||||||
|
|
||||||
def build(self):
|
def build(self):
|
||||||
|
@ -68,14 +67,14 @@ class DDS(AutoDB):
|
||||||
"""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.sysclk)
|
||||||
|
|
||||||
@portable
|
@portable
|
||||||
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.sysclk/2**32
|
||||||
|
|
||||||
@kernel
|
@kernel
|
||||||
def init(self):
|
def init(self):
|
||||||
|
|
|
@ -11,6 +11,7 @@ class Monitor(Module, AutoCSR):
|
||||||
max_probe_len = max(flen(p) for cp in chan_probes for p in cp)
|
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.chan_sel = CSRStorage(bits_for(len(chan_probes)-1))
|
||||||
self.probe_sel = CSRStorage(bits_for(max_chan_probes-1))
|
self.probe_sel = CSRStorage(bits_for(max_chan_probes-1))
|
||||||
|
self.value_update = CSR()
|
||||||
self.value = CSRStatus(max_probe_len)
|
self.value = CSRStatus(max_probe_len)
|
||||||
|
|
||||||
# # #
|
# # #
|
||||||
|
@ -25,8 +26,9 @@ class Monitor(Module, AutoCSR):
|
||||||
cp_sys.append(vs.o)
|
cp_sys.append(vs.o)
|
||||||
cp_sys += [0]*(max_chan_probes-len(cp))
|
cp_sys += [0]*(max_chan_probes-len(cp))
|
||||||
chan_probes_sys.append(Array(cp_sys)[self.probe_sel.storage])
|
chan_probes_sys.append(Array(cp_sys)[self.probe_sel.storage])
|
||||||
self.comb += self.value.status.eq(
|
self.sync += If(self.value_update.re,
|
||||||
Array(chan_probes_sys)[self.chan_sel.storage])
|
self.value.status.eq(
|
||||||
|
Array(chan_probes_sys)[self.chan_sel.storage]))
|
||||||
|
|
||||||
|
|
||||||
class Injector(Module, AutoCSR):
|
class Injector(Module, AutoCSR):
|
||||||
|
|
|
@ -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))
|
|
@ -9,6 +9,7 @@ from pyqtgraph import dockarea
|
||||||
|
|
||||||
from artiq.tools import TaskObject
|
from artiq.tools import TaskObject
|
||||||
from artiq.protocols.sync_struct import Subscriber
|
from artiq.protocols.sync_struct import Subscriber
|
||||||
|
from artiq.language.units import strip_unit
|
||||||
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
@ -112,9 +113,10 @@ class _TTLWidget(QtGui.QFrame):
|
||||||
|
|
||||||
|
|
||||||
class _DDSWidget(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.send_to_device = send_to_device
|
||||||
self.channel = channel
|
self.channel = channel
|
||||||
|
self.sysclk = sysclk
|
||||||
self.name = name
|
self.name = name
|
||||||
|
|
||||||
QtGui.QFrame.__init__(self)
|
QtGui.QFrame.__init__(self)
|
||||||
|
@ -128,48 +130,16 @@ class _DDSWidget(QtGui.QFrame):
|
||||||
label.setAlignment(QtCore.Qt.AlignCenter)
|
label.setAlignment(QtCore.Qt.AlignCenter)
|
||||||
grid.addWidget(label, 1, 1)
|
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 = QtGui.QLabel()
|
||||||
self._value.setAlignment(QtCore.Qt.AlignCenter)
|
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.set_value(0)
|
||||||
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.0, False)
|
def set_value(self, ftw):
|
||||||
|
frequency = ftw*self.sysclk/2**32
|
||||||
def _override_clicked(self):
|
self._value.setText("<font size=\"9\">{:.3f} MHz</font>"
|
||||||
override_en = self._override_action.isChecked()
|
.format(float(frequency)))
|
||||||
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 = "<b>" + value_s + "</b>"
|
|
||||||
color = " color=\"red\""
|
|
||||||
self._override.setText("<font size=\"1\" color=\"red\">OVERRIDE</font>")
|
|
||||||
else:
|
|
||||||
color = ""
|
|
||||||
self._override.setText("")
|
|
||||||
self._value.setText("<font size=\"9\"{}>{}</font>"
|
|
||||||
.format(color, value_s))
|
|
||||||
|
|
||||||
|
|
||||||
class _DeviceManager:
|
class _DeviceManager:
|
||||||
|
@ -200,8 +170,9 @@ class _DeviceManager:
|
||||||
if (v["module"] == "artiq.coredevice.dds"
|
if (v["module"] == "artiq.coredevice.dds"
|
||||||
and v["class"] == "DDS"):
|
and v["class"] == "DDS"):
|
||||||
channel = v["arguments"]["channel"]
|
channel = v["arguments"]["channel"]
|
||||||
|
sysclk = strip_unit(v["arguments"]["sysclk"], "Hz")
|
||||||
self.dds_widgets[channel] = _DDSWidget(
|
self.dds_widgets[channel] = _DDSWidget(
|
||||||
self.send_to_device, channel, k)
|
self.send_to_device, channel, sysclk, k)
|
||||||
self.dds_cb()
|
self.dds_cb()
|
||||||
except KeyError:
|
except KeyError:
|
||||||
pass
|
pass
|
||||||
|
@ -277,11 +248,25 @@ class MonInj(TaskObject):
|
||||||
self.transport = transport
|
self.transport = transport
|
||||||
|
|
||||||
def datagram_received(self, data, addr):
|
def datagram_received(self, data, addr):
|
||||||
ttl_levels, ttl_oes, ttl_overrides = struct.unpack(">QQQ", data)
|
try:
|
||||||
|
ttl_levels, ttl_oes, ttl_overrides = \
|
||||||
|
struct.unpack(">QQQ", data[:8*3])
|
||||||
for channel, w in self.dm.ttl_widgets.items():
|
for channel, w in self.dm.ttl_widgets.items():
|
||||||
w.set_value(ttl_levels & (1 << channel),
|
w.set_value(ttl_levels & (1 << channel),
|
||||||
ttl_oes & (1 << channel),
|
ttl_oes & (1 << channel),
|
||||||
ttl_overrides & (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):
|
def error_received(self, exc):
|
||||||
logger.warning("datagram endpoint error")
|
logger.warning("datagram endpoint error")
|
||||||
|
|
|
@ -60,19 +60,22 @@
|
||||||
"type": "local",
|
"type": "local",
|
||||||
"module": "artiq.coredevice.dds",
|
"module": "artiq.coredevice.dds",
|
||||||
"class": "DDS",
|
"class": "DDS",
|
||||||
"arguments": {"channel": 0}
|
"arguments": {"sysclk": Quantity(Fraction(1000000000, 1), "Hz"),
|
||||||
|
"channel": 0}
|
||||||
},
|
},
|
||||||
"dds1": {
|
"dds1": {
|
||||||
"type": "local",
|
"type": "local",
|
||||||
"module": "artiq.coredevice.dds",
|
"module": "artiq.coredevice.dds",
|
||||||
"class": "DDS",
|
"class": "DDS",
|
||||||
"arguments": {"channel": 1}
|
"arguments": {"sysclk": Quantity(Fraction(1000000000, 1), "Hz"),
|
||||||
|
"channel": 1}
|
||||||
},
|
},
|
||||||
"dds2": {
|
"dds2": {
|
||||||
"type": "local",
|
"type": "local",
|
||||||
"module": "artiq.coredevice.dds",
|
"module": "artiq.coredevice.dds",
|
||||||
"class": "DDS",
|
"class": "DDS",
|
||||||
"arguments": {"channel": 2}
|
"arguments": {"sysclk": Quantity(Fraction(1000000000, 1), "Hz"),
|
||||||
|
"channel": 2}
|
||||||
},
|
},
|
||||||
|
|
||||||
"qc_q1_0": {
|
"qc_q1_0": {
|
||||||
|
|
|
@ -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);
|
DDS_WRITE(DDS_FTW3, (ftw >> 24) & 0xff);
|
||||||
|
|
||||||
/* We need the RTIO fine timestamp clock to be phase-locked
|
/* 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) {
|
if(phase_mode == PHASE_MODE_CONTINUOUS) {
|
||||||
/* Do not clear phase accumulator on FUD */
|
/* Do not clear phase accumulator on FUD */
|
||||||
|
|
|
@ -4,9 +4,6 @@
|
||||||
#include <hw/common.h>
|
#include <hw/common.h>
|
||||||
#include <generated/mem.h>
|
#include <generated/mem.h>
|
||||||
|
|
||||||
/* Number of DDS channels to initialize */
|
|
||||||
#define DDS_CHANNEL_COUNT 8
|
|
||||||
|
|
||||||
/* Maximum number of commands in a batch */
|
/* Maximum number of commands in a batch */
|
||||||
#define DDS_MAX_BATCH 16
|
#define DDS_MAX_BATCH 16
|
||||||
|
|
||||||
|
|
|
@ -39,6 +39,7 @@ struct monitor_reply {
|
||||||
long long int ttl_levels;
|
long long int ttl_levels;
|
||||||
long long int ttl_oes;
|
long long int ttl_oes;
|
||||||
long long int ttl_overrides;
|
long long int ttl_overrides;
|
||||||
|
unsigned int dds_ftws[DDS_CHANNEL_COUNT];
|
||||||
};
|
};
|
||||||
|
|
||||||
static void moninj_monitor(const ip_addr_t *addr, u16_t port)
|
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<RTIO_TTL_COUNT;i++) {
|
for(i=0;i<RTIO_TTL_COUNT;i++) {
|
||||||
rtio_moninj_mon_chan_sel_write(i);
|
rtio_moninj_mon_chan_sel_write(i);
|
||||||
rtio_moninj_mon_probe_sel_write(0);
|
rtio_moninj_mon_probe_sel_write(0);
|
||||||
|
rtio_moninj_mon_value_update_write(1);
|
||||||
if(rtio_moninj_mon_value_read())
|
if(rtio_moninj_mon_value_read())
|
||||||
reply.ttl_levels |= 1LL << i;
|
reply.ttl_levels |= 1LL << i;
|
||||||
rtio_moninj_mon_probe_sel_write(1);
|
rtio_moninj_mon_probe_sel_write(1);
|
||||||
|
rtio_moninj_mon_value_update_write(1);
|
||||||
if(rtio_moninj_mon_value_read())
|
if(rtio_moninj_mon_value_read())
|
||||||
reply.ttl_oes |= 1LL << i;
|
reply.ttl_oes |= 1LL << i;
|
||||||
rtio_moninj_inj_chan_sel_write(i);
|
rtio_moninj_inj_chan_sel_write(i);
|
||||||
|
@ -64,6 +67,13 @@ static void moninj_monitor(const ip_addr_t *addr, u16_t port)
|
||||||
reply.ttl_overrides |= 1LL << i;
|
reply.ttl_overrides |= 1LL << i;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rtio_moninj_mon_chan_sel_write(RTIO_DDS_CHANNEL);
|
||||||
|
for(i=0;i<DDS_CHANNEL_COUNT;i++) {
|
||||||
|
rtio_moninj_mon_probe_sel_write(i);
|
||||||
|
rtio_moninj_mon_value_update_write(1);
|
||||||
|
reply.dds_ftws[i] = rtio_moninj_mon_value_read();
|
||||||
|
}
|
||||||
|
|
||||||
reply_p = pbuf_alloc(PBUF_TRANSPORT, sizeof(struct monitor_reply), PBUF_RAM);
|
reply_p = pbuf_alloc(PBUF_TRANSPORT, sizeof(struct monitor_reply), PBUF_RAM);
|
||||||
if(!reply_p) {
|
if(!reply_p) {
|
||||||
log("Failed to allocate pbuf for monitor reply");
|
log("Failed to allocate pbuf for monitor reply");
|
||||||
|
|
|
@ -10,9 +10,8 @@ from misoclib.mem.sdram.core.minicon import MiniconSettings
|
||||||
from targets.kc705 import MiniSoC
|
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, nist_qc1
|
||||||
from artiq.gateware.rtio.phy import ttl_simple
|
from artiq.gateware.rtio.phy import ttl_simple, dds
|
||||||
from artiq.gateware.rtio.phy.wishbone import RT2WB
|
|
||||||
|
|
||||||
|
|
||||||
class _RTIOCRG(Module, AutoCSR):
|
class _RTIOCRG(Module, AutoCSR):
|
||||||
|
@ -81,10 +80,8 @@ class NIST_QC1(MiniSoC, AMPSoC):
|
||||||
self.add_constant("RTIO_TTL_COUNT", len(rtio_channels))
|
self.add_constant("RTIO_TTL_COUNT", len(rtio_channels))
|
||||||
|
|
||||||
self.add_constant("RTIO_DDS_CHANNEL", len(rtio_channels))
|
self.add_constant("RTIO_DDS_CHANNEL", len(rtio_channels))
|
||||||
self.submodules.dds = RenameClockDomains(
|
self.add_constant("DDS_CHANNEL_COUNT", 8)
|
||||||
ad9858.AD9858(platform.request("dds")),
|
phy = dds.AD9858(platform.request("dds"))
|
||||||
"rio")
|
|
||||||
phy = RT2WB(7, self.dds.bus)
|
|
||||||
self.submodules += phy
|
self.submodules += phy
|
||||||
rtio_channels.append(rtio.Channel.from_phy(phy, ififo_depth=4))
|
rtio_channels.append(rtio.Channel.from_phy(phy, ififo_depth=4))
|
||||||
|
|
||||||
|
|
|
@ -8,9 +8,8 @@ from misoclib.mem.sdram.core.minicon import MiniconSettings
|
||||||
from targets.pipistrello import BaseSoC
|
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, nist_qc1
|
||||||
from artiq.gateware.rtio.phy import ttl_simple
|
from artiq.gateware.rtio.phy import ttl_simple, dds
|
||||||
from artiq.gateware.rtio.phy.wishbone import RT2WB
|
|
||||||
|
|
||||||
|
|
||||||
class _RTIOCRG(Module, AutoCSR):
|
class _RTIOCRG(Module, AutoCSR):
|
||||||
|
@ -116,10 +115,8 @@ trce -v 12 -fastpaths -tsi {build_name}.tsi -o {build_name}.twr {build_name}.ncd
|
||||||
self.add_constant("RTIO_TTL_COUNT", len(rtio_channels))
|
self.add_constant("RTIO_TTL_COUNT", len(rtio_channels))
|
||||||
|
|
||||||
self.add_constant("RTIO_DDS_CHANNEL", len(rtio_channels))
|
self.add_constant("RTIO_DDS_CHANNEL", len(rtio_channels))
|
||||||
self.submodules.dds = RenameClockDomains(
|
self.add_constant("DDS_CHANNEL_COUNT", 8)
|
||||||
ad9858.AD9858(platform.request("dds")),
|
phy = dds.AD9858(platform.request("dds"))
|
||||||
"rio")
|
|
||||||
phy = RT2WB(7, self.dds.bus)
|
|
||||||
self.submodules += phy
|
self.submodules += phy
|
||||||
rtio_channels.append(rtio.Channel.from_phy(phy, ififo_depth=4))
|
rtio_channels.append(rtio.Channel.from_phy(phy, ififo_depth=4))
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue