DDS monitoring

This commit is contained in:
Sebastien Bourdeauducq 2015-06-19 15:30:17 -06:00
parent 03fe71228b
commit 5a9bdb2e33
10 changed files with 99 additions and 73 deletions

View File

@ -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):

View File

@ -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):

View File

@ -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))

View File

@ -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")

View File

@ -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": {

View File

@ -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 */

View File

@ -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

View File

@ -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");

View File

@ -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))

View File

@ -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))