forked from M-Labs/artiq
parent
d17675e9b5
commit
8be945d5c7
@ -45,6 +45,7 @@ Highlights:
|
||||
switch is supported.
|
||||
* The "ip" config option can now be set to "use_dhcp" in order to use DHCP to obtain an IP address.
|
||||
DHCP will also be used if no "ip" config option is set.
|
||||
* Urukul monitoring and frequency setting (through dashboard) is now supported.
|
||||
|
||||
Breaking changes:
|
||||
|
||||
|
@ -8,6 +8,8 @@ from PyQt5 import QtCore, QtWidgets, QtGui
|
||||
from sipyco.sync_struct import Subscriber
|
||||
|
||||
from artiq.coredevice.comm_moninj import *
|
||||
from artiq.coredevice.ad9910 import _AD9910_REG_PROFILE0, _AD9910_REG_PROFILE7, _AD9910_REG_FTW
|
||||
from artiq.coredevice.ad9912_reg import AD9912_POW1
|
||||
from artiq.gui.tools import LayoutWidget
|
||||
from artiq.gui.flowlayout import FlowLayout
|
||||
|
||||
@ -179,14 +181,45 @@ class _SimpleDisplayWidget(QtWidgets.QFrame):
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
class _DDSModel:
|
||||
def __init__(self, dds_type, ref_clk, cpld=None, pll=1, clk_div=0):
|
||||
self.cpld = cpld
|
||||
self.cur_frequency = 0
|
||||
self.cur_reg = 0
|
||||
self.dds_type = dds_type
|
||||
self.is_urukul = dds_type in ["AD9910", "AD9912"]
|
||||
|
||||
if dds_type == "AD9914":
|
||||
self.ftw_per_hz = 2**32 / ref_clk
|
||||
else:
|
||||
if dds_type == "AD9910":
|
||||
max_freq = 1 << 32
|
||||
clk_mult = [4, 1, 2, 4]
|
||||
elif dds_type == "AD9912": # AD9912
|
||||
max_freq = 1 << 48
|
||||
clk_mult = [1, 1, 2, 4]
|
||||
else:
|
||||
raise NotImplementedError
|
||||
sysclk = ref_clk / clk_mult[clk_div] * pll
|
||||
self.ftw_per_hz = 1 / sysclk * max_freq
|
||||
|
||||
def monitor_update(self, probe, value):
|
||||
if self.dds_type == "AD9912":
|
||||
value = value << 16
|
||||
self.cur_frequency = self._ftw_to_freq(value)
|
||||
|
||||
def _ftw_to_freq(self, ftw):
|
||||
return ftw / self.ftw_per_hz
|
||||
|
||||
|
||||
class _DDSWidget(QtWidgets.QFrame):
|
||||
def __init__(self, dm, title, bus_channel=0, channel=0, cpld=None):
|
||||
def __init__(self, dm, title, bus_channel=0, channel=0, dds_model=None):
|
||||
self.dm = dm
|
||||
self.bus_channel = bus_channel
|
||||
self.channel = channel
|
||||
self.dds_name = title
|
||||
self.cpld = cpld
|
||||
self.cur_frequency = 0
|
||||
self.dds_model = dds_model
|
||||
|
||||
QtWidgets.QFrame.__init__(self)
|
||||
|
||||
@ -249,7 +282,7 @@ class _DDSWidget(QtWidgets.QFrame):
|
||||
set_grid.addWidget(set_btn, 0, 1, 1, 1)
|
||||
|
||||
# for urukuls also allow switching off RF
|
||||
if self.cpld:
|
||||
if self.dds_model.is_urukul:
|
||||
off_btn = QtWidgets.QToolButton()
|
||||
off_btn.setText("Off")
|
||||
off_btn.setToolTip("Switch off the output")
|
||||
@ -276,7 +309,7 @@ class _DDSWidget(QtWidgets.QFrame):
|
||||
|
||||
set_btn.clicked.connect(self.set_clicked)
|
||||
apply.clicked.connect(self.apply_changes)
|
||||
if self.cpld:
|
||||
if self.dds_model.is_urukul:
|
||||
off_btn.clicked.connect(self.off_clicked)
|
||||
self.value_edit.returnPressed.connect(lambda: self.apply_changes(None))
|
||||
self.value_edit.escapePressedConnect(self.cancel_changes)
|
||||
@ -293,19 +326,20 @@ class _DDSWidget(QtWidgets.QFrame):
|
||||
self.value_edit.selectAll()
|
||||
|
||||
def off_clicked(self, set):
|
||||
self.dm.dds_channel_toggle(self.dds_name, self.cpld, sw=False)
|
||||
self.dm.dds_channel_toggle(self.dds_name, self.dds_model, sw=False)
|
||||
|
||||
def apply_changes(self, apply):
|
||||
self.data_stack.setCurrentIndex(0)
|
||||
self.button_stack.setCurrentIndex(0)
|
||||
frequency = float(self.value_edit.text())*1e6
|
||||
self.dm.dds_set_frequency(self.dds_name, self.cpld, frequency)
|
||||
self.dm.dds_set_frequency(self.dds_name, self.dds_model, frequency)
|
||||
|
||||
def cancel_changes(self, cancel):
|
||||
self.data_stack.setCurrentIndex(0)
|
||||
self.button_stack.setCurrentIndex(0)
|
||||
|
||||
def refresh_display(self):
|
||||
self.cur_frequency = self.dds_model.cur_frequency
|
||||
self.value_label.setText("<font size=\"4\">{:.7f}</font>"
|
||||
.format(self.cur_frequency/1e6))
|
||||
self.value_edit.setText("{:.7f}"
|
||||
@ -356,7 +390,8 @@ def setup_from_ddb(ddb):
|
||||
bus_channel = v["arguments"]["bus_channel"]
|
||||
channel = v["arguments"]["channel"]
|
||||
dds_sysclk = v["arguments"]["sysclk"]
|
||||
widget = _WidgetDesc(k, comment, _DDSWidget, (k, bus_channel, channel))
|
||||
model = _DDSModel(v["class"], dds_sysclk)
|
||||
widget = _WidgetDesc(k, comment, _DDSWidget, (k, bus_channel, channel, model))
|
||||
description.add(widget)
|
||||
elif (v["module"] == "artiq.coredevice.ad9910"
|
||||
and v["class"] == "AD9910") or \
|
||||
@ -368,7 +403,11 @@ def setup_from_ddb(ddb):
|
||||
dds_cpld = v["arguments"]["cpld_device"]
|
||||
spi_dev = ddb[dds_cpld]["arguments"]["spi_device"]
|
||||
bus_channel = ddb[spi_dev]["arguments"]["channel"]
|
||||
widget = _WidgetDesc(k, comment, _DDSWidget, (k, bus_channel, channel, dds_cpld))
|
||||
pll = v["arguments"]["pll_n"]
|
||||
refclk = ddb[dds_cpld]["arguments"]["refclk"]
|
||||
clk_div = v["arguments"].get("clk_div", 0)
|
||||
model = _DDSModel( v["class"], refclk, dds_cpld, pll, clk_div)
|
||||
widget = _WidgetDesc(k, comment, _DDSWidget, (k, bus_channel, channel, model))
|
||||
description.add(widget)
|
||||
elif ( (v["module"] == "artiq.coredevice.ad53xx" and v["class"] == "AD53xx")
|
||||
or (v["module"] == "artiq.coredevice.zotino" and v["class"] == "Zotino")):
|
||||
@ -385,7 +424,7 @@ def setup_from_ddb(ddb):
|
||||
mi_port = v.get("port_proxy", 1383)
|
||||
except KeyError:
|
||||
pass
|
||||
return mi_addr, mi_port, dds_sysclk, description
|
||||
return mi_addr, mi_port, description
|
||||
|
||||
|
||||
class _DeviceManager:
|
||||
@ -415,15 +454,13 @@ class _DeviceManager:
|
||||
return ddb
|
||||
|
||||
def notify(self, mod):
|
||||
mi_addr, mi_port, dds_sysclk, description = setup_from_ddb(self.ddb)
|
||||
mi_addr, mi_port, description = setup_from_ddb(self.ddb)
|
||||
|
||||
if (mi_addr, mi_port) != (self.mi_addr, self.mi_port):
|
||||
self.mi_addr = mi_addr
|
||||
self.mi_port = mi_port
|
||||
self.reconnect_mi.set()
|
||||
|
||||
self.dds_sysclk = dds_sysclk
|
||||
|
||||
for to_remove in self.description - description:
|
||||
widget = self.widgets_by_uid[to_remove.uid]
|
||||
del self.widgets_by_uid[to_remove.uid]
|
||||
@ -512,13 +549,13 @@ class _DeviceManager:
|
||||
scheduling["flush"])
|
||||
logger.info("Submitted '%s', RID is %d", title, rid)
|
||||
|
||||
def dds_set_frequency(self, dds_channel, dds_cpld, freq):
|
||||
def dds_set_frequency(self, dds_channel, dds_model, freq):
|
||||
# create kernel and fill it in and send-by-content
|
||||
if dds_cpld:
|
||||
if dds_model.is_urukul:
|
||||
# urukuls need CPLD init and switch to on
|
||||
# keep previous config if it was set already
|
||||
cpld_dev = """self.setattr_device("core_cache")
|
||||
self.setattr_device("{}")""".format(dds_cpld)
|
||||
self.setattr_device("{}")""".format(dds_model.cpld)
|
||||
cpld_init = """cfg = self.core_cache.get("_{cpld}_cfg")
|
||||
if len(cfg) > 0:
|
||||
self.{cpld}.cfg_reg = cfg[0]
|
||||
@ -526,10 +563,10 @@ class _DeviceManager:
|
||||
self.{cpld}.init()
|
||||
self.core_cache.put("_{cpld}_cfg", [self.{cpld}.cfg_reg])
|
||||
cfg = self.core_cache.get("_{cpld}_cfg")
|
||||
""".format(cpld=dds_cpld)
|
||||
""".format(cpld=dds_model.cpld)
|
||||
cfg_sw = """self.{}.cfg_sw(True)
|
||||
cfg[0] = self.{}.cfg_reg
|
||||
""".format(dds_channel, dds_cpld)
|
||||
""".format(dds_channel, dds_model.cpld)
|
||||
else:
|
||||
cpld_dev = ""
|
||||
cpld_init = ""
|
||||
@ -560,7 +597,7 @@ class _DeviceManager:
|
||||
"SetDDS",
|
||||
"Set DDS {} {}MHz".format(dds_channel, freq/1e6)))
|
||||
|
||||
def dds_channel_toggle(self, dds_channel, dds_cpld, sw=True):
|
||||
def dds_channel_toggle(self, dds_model, sw=True):
|
||||
# urukul only
|
||||
toggle_exp = textwrap.dedent("""
|
||||
from artiq.experiment import *
|
||||
@ -586,7 +623,7 @@ class _DeviceManager:
|
||||
self.{ch}.init()
|
||||
self.{ch}.cfg_sw({sw})
|
||||
cfg[0] = self.{cpld}.cfg_reg
|
||||
""".format(ch=dds_channel, cpld=dds_cpld, sw=sw))
|
||||
""".format(ch=dds_channel, cpld=dds_model.cpld, sw=sw))
|
||||
asyncio.ensure_future(
|
||||
self._submit_by_content(
|
||||
toggle_exp,
|
||||
@ -619,11 +656,11 @@ class _DeviceManager:
|
||||
elif probe == TTLProbe.oe.value:
|
||||
widget.cur_oe = bool(value)
|
||||
widget.refresh_display()
|
||||
if (channel, probe) in self.dds_widgets:
|
||||
elif (channel, probe) in self.dds_widgets:
|
||||
widget = self.dds_widgets[(channel, probe)]
|
||||
widget.cur_frequency = value*self.dds_sysclk/2**32
|
||||
widget.dds_model.monitor_update(probe, value)
|
||||
widget.refresh_display()
|
||||
if (channel, probe) in self.dac_widgets:
|
||||
elif (channel, probe) in self.dac_widgets:
|
||||
widget = self.dac_widgets[(channel, probe)]
|
||||
widget.cur_value = value
|
||||
widget.refresh_display()
|
||||
|
@ -3,7 +3,7 @@ from migen.build.generic_platform import *
|
||||
from migen.genlib.io import DifferentialOutput
|
||||
|
||||
from artiq.gateware import rtio
|
||||
from artiq.gateware.rtio.phy import spi2, ad53xx_monitor, grabber
|
||||
from artiq.gateware.rtio.phy import spi2, ad53xx_monitor, dds, grabber
|
||||
from artiq.gateware.suservo import servo, pads as servo_pads
|
||||
from artiq.gateware.rtio.phy import servo as rtservo, fastino, phaser
|
||||
|
||||
@ -222,13 +222,13 @@ class Urukul(_EEM):
|
||||
return ios
|
||||
|
||||
@classmethod
|
||||
def add_std(cls, target, eem, eem_aux, ttl_out_cls, sync_gen_cls=None, iostandard=default_iostandard):
|
||||
def add_std(cls, target, eem, eem_aux, ttl_out_cls, dds_type, sync_gen_cls=None, iostandard=default_iostandard):
|
||||
cls.add_extension(target, eem, eem_aux, iostandard=iostandard)
|
||||
|
||||
phy = spi2.SPIMaster(target.platform.request("urukul{}_spi_p".format(eem)),
|
||||
spi_phy = spi2.SPIMaster(target.platform.request("urukul{}_spi_p".format(eem)),
|
||||
target.platform.request("urukul{}_spi_n".format(eem)))
|
||||
target.submodules += phy
|
||||
target.rtio_channels.append(rtio.Channel.from_phy(phy, ififo_depth=4))
|
||||
target.submodules += spi_phy
|
||||
target.rtio_channels.append(rtio.Channel.from_phy(spi_phy, ififo_depth=4))
|
||||
|
||||
pads = target.platform.request("urukul{}_dds_reset_sync_in".format(eem))
|
||||
if sync_gen_cls is not None: # AD9910 variant and SYNC_IN from EEM
|
||||
@ -237,9 +237,14 @@ class Urukul(_EEM):
|
||||
target.rtio_channels.append(rtio.Channel.from_phy(phy))
|
||||
|
||||
pads = target.platform.request("urukul{}_io_update".format(eem))
|
||||
phy = ttl_out_cls(pads.p, pads.n)
|
||||
target.submodules += phy
|
||||
target.rtio_channels.append(rtio.Channel.from_phy(phy))
|
||||
io_upd_phy = ttl_out_cls(pads.p, pads.n)
|
||||
target.submodules += io_upd_phy
|
||||
target.rtio_channels.append(rtio.Channel.from_phy(io_upd_phy))
|
||||
|
||||
dds_monitor = dds.UrukulMonitor(spi_phy, io_upd_phy, dds_type)
|
||||
target.submodules += dds_monitor
|
||||
spi_phy.probes.extend(dds_monitor.probes)
|
||||
|
||||
if eem_aux is not None:
|
||||
for signal in "sw0 sw1 sw2 sw3".split():
|
||||
pads = target.platform.request("urukul{}_{}".format(eem, signal))
|
||||
@ -247,6 +252,7 @@ class Urukul(_EEM):
|
||||
target.submodules += phy
|
||||
target.rtio_channels.append(rtio.Channel.from_phy(phy))
|
||||
|
||||
|
||||
class Sampler(_EEM):
|
||||
@staticmethod
|
||||
def io(eem, eem_aux, iostandard):
|
||||
|
@ -47,7 +47,7 @@ def peripheral_urukul(module, peripheral, **kwargs):
|
||||
else:
|
||||
sync_gen_cls = None
|
||||
eem.Urukul.add_std(module, port, port_aux, ttl_serdes_7series.Output_8X,
|
||||
sync_gen_cls, **kwargs)
|
||||
peripheral["dds"], sync_gen_cls, **kwargs)
|
||||
|
||||
|
||||
def peripheral_novogorny(module, peripheral, **kwargs):
|
||||
|
@ -3,6 +3,11 @@ from migen import *
|
||||
from artiq.gateware import ad9_dds
|
||||
from artiq.gateware.rtio.phy.wishbone import RT2WB
|
||||
|
||||
from artiq.coredevice.spi2 import SPI_CONFIG_ADDR, SPI_DATA_ADDR, SPI_END
|
||||
from artiq.coredevice.urukul import CS_DDS_CH0, CS_DDS_MULTI, CFG_IO_UPDATE, CS_CFG
|
||||
|
||||
from artiq.coredevice.ad9912_reg import AD9912_POW1
|
||||
from artiq.coredevice.ad9910 import _AD9910_REG_PROFILE0, _AD9910_REG_PROFILE7, _AD9910_REG_FTW
|
||||
|
||||
class AD9914(Module):
|
||||
def __init__(self, pads, nchannels, onehot=False, **kwargs):
|
||||
@ -54,3 +59,121 @@ class AD9914(Module):
|
||||
self.sync.rio_phy += If(current_address == 2**len(pads.a), [
|
||||
If(selected(c), probe.eq(ftw))
|
||||
for c, (probe, ftw) in enumerate(zip(self.probes, ftws))])
|
||||
|
||||
|
||||
class UrukulMonitor(Module):
|
||||
def __init__(self, spi_phy, io_update_phy, dds, nchannels=4):
|
||||
self.spi_phy = spi_phy
|
||||
self.io_update_phy = io_update_phy
|
||||
|
||||
self.probes = [Signal(32) for i in range(nchannels)]
|
||||
|
||||
self.cs = Signal(8)
|
||||
self.current_data = Signal.like(self.spi_phy.rtlink.o.data)
|
||||
current_address = Signal.like(self.spi_phy.rtlink.o.address)
|
||||
data_length = Signal(8)
|
||||
flags = Signal(8)
|
||||
|
||||
self.sync.rio += If(self.spi_phy.rtlink.o.stb, [
|
||||
current_address.eq(self.spi_phy.rtlink.o.address),
|
||||
self.current_data.eq(self.spi_phy.rtlink.o.data),
|
||||
If(self.spi_phy.rtlink.o.address == SPI_CONFIG_ADDR, [
|
||||
self.cs.eq(self.spi_phy.rtlink.o.data[24:]),
|
||||
data_length.eq(self.spi_phy.rtlink.o.data[8:16] + 1),
|
||||
flags.eq(self.spi_phy.rtlink.o.data[0:8])
|
||||
])
|
||||
])
|
||||
|
||||
for i in range(nchannels):
|
||||
ch_sel = Signal()
|
||||
self.comb += ch_sel.eq(
|
||||
((self.cs == CS_DDS_MULTI) | (self.cs == i + CS_DDS_CH0))
|
||||
& (current_address == SPI_DATA_ADDR)
|
||||
)
|
||||
|
||||
if dds == "ad9912":
|
||||
mon_cls = _AD9912Monitor
|
||||
elif dds == "ad9910":
|
||||
mon_cls = _AD9910Monitor
|
||||
else:
|
||||
raise NotImplementedError
|
||||
|
||||
monitor = mon_cls(self.current_data, data_length, flags, ch_sel)
|
||||
self.submodules += monitor
|
||||
|
||||
self.sync.rio_phy += [
|
||||
If(ch_sel & self.is_io_update(), self.probes[i].eq(monitor.ftw))
|
||||
]
|
||||
|
||||
def is_io_update(self):
|
||||
# shifted 8 bits left for 32-bit bus
|
||||
reg_io_upd = (self.cs == CS_CFG) & self.current_data[8 + CFG_IO_UPDATE]
|
||||
phy_io_upd = False
|
||||
if self.io_update_phy:
|
||||
phy_io_upd = self.io_update_phy.rtlink.o.stb & self.io_update_phy.rtlink.o.data
|
||||
return phy_io_upd | reg_io_upd
|
||||
|
||||
|
||||
class _AD9912Monitor(Module):
|
||||
def __init__(self, current_data, data_length, flags, ch_sel):
|
||||
self.ftw = Signal(32, reset_less=True)
|
||||
|
||||
fsm = ClockDomainsRenamer("rio_phy")(FSM(reset_state="IDLE"))
|
||||
self.submodules += fsm
|
||||
|
||||
reg_addr = current_data[16:29]
|
||||
reg_write = ~current_data[31]
|
||||
|
||||
fsm.act("IDLE",
|
||||
If(ch_sel & reg_write,
|
||||
If((data_length == 16) & (reg_addr == AD9912_POW1),
|
||||
NextState("READ")
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
fsm.act("READ",
|
||||
If(ch_sel,
|
||||
If(flags & SPI_END,
|
||||
# lower 16 bits (16-32 from 48-bit transfer)
|
||||
NextValue(self.ftw[:16], current_data[16:]),
|
||||
NextState("IDLE")
|
||||
).Else(
|
||||
NextValue(self.ftw[16:], current_data[:16])
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
class _AD9910Monitor(Module):
|
||||
def __init__(self, current_data, data_length, flags, ch_sel):
|
||||
self.ftw = Signal(32, reset_less=True)
|
||||
|
||||
fsm = ClockDomainsRenamer("rio_phy")(FSM(reset_state="IDLE"))
|
||||
self.submodules += fsm
|
||||
|
||||
reg_addr = current_data[24:29]
|
||||
reg_write = ~current_data[31]
|
||||
|
||||
fsm.act("IDLE",
|
||||
If(ch_sel & reg_write,
|
||||
If((data_length == 8) & (_AD9910_REG_PROFILE7 >= reg_addr) & (reg_addr >= _AD9910_REG_PROFILE0),
|
||||
NextState("READ")
|
||||
).Elif(reg_addr == _AD9910_REG_FTW,
|
||||
If((data_length == 24) & (flags & SPI_END),
|
||||
NextValue(self.ftw[:16], current_data[8:24])
|
||||
).Elif(data_length == 8,
|
||||
NextState("READ")
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
fsm.act("READ",
|
||||
If(ch_sel,
|
||||
If(flags & SPI_END,
|
||||
NextValue(self.ftw, current_data),
|
||||
NextState("IDLE")
|
||||
)
|
||||
)
|
||||
)
|
||||
|
Loading…
Reference in New Issue
Block a user