mirror of https://github.com/m-labs/artiq.git
Merge branch 'master' into nac3
This commit is contained in:
commit
fc082b62de
|
@ -25,9 +25,17 @@ Highlights:
|
|||
support legacy installations, but may be removed in a future release.
|
||||
* Added channel names to RTIO errors.
|
||||
* Full Python 3.10 support.
|
||||
* Python's built-in types (such as `float`, or `List[...]`) can now be used in type annotations on
|
||||
kernel functions.
|
||||
* Distributed DMA is now supported, allowing DMA to be run directly on satellites for corresponding
|
||||
RTIO events, increasing bandwidth in scenarios with heavy satellite usage.
|
||||
* API extensions have been implemented, enabling applets to directly modify datasets.
|
||||
* Applet Request Interfaces have been implemented, enabling applets to directly modify datasets
|
||||
and temporarily set arguments in the dashboard.
|
||||
* EntryArea widget has been implemented, allowing argument entry widgets to be used in applets.
|
||||
* Dashboard:
|
||||
- The "Close all applets" command (shortcut: Ctrl-Alt-W) now ignores docked applets,
|
||||
making it a convenient way to clean up after exploratory work without destroying a
|
||||
carefully arranged default workspace.
|
||||
* Persistent datasets are now stored in a LMDB database for improved performance. PYON databases can
|
||||
be converted with the script below.
|
||||
|
||||
|
|
|
@ -22,10 +22,10 @@ class QCancellableLineEdit(QtWidgets.QLineEdit):
|
|||
|
||||
|
||||
class NumberWidget(QtWidgets.QStackedWidget):
|
||||
def __init__(self, args, ctl):
|
||||
def __init__(self, args, req):
|
||||
QtWidgets.QStackedWidget.__init__(self)
|
||||
self.dataset_name = args.dataset
|
||||
self.ctl = ctl
|
||||
self.req = req
|
||||
|
||||
self.lcd_widget = QResponsiveLCDNumber()
|
||||
self.lcd_widget.setDigitCount(args.digit_count)
|
||||
|
@ -55,7 +55,7 @@ class NumberWidget(QtWidgets.QStackedWidget):
|
|||
|
||||
def confirm_edit(self):
|
||||
value = float(self.edit_widget.text())
|
||||
self.ctl.set_dataset(self.dataset_name, value)
|
||||
self.req.set_dataset(self.dataset_name, value)
|
||||
self.setCurrentWidget(self.lcd_widget)
|
||||
|
||||
def cancel_edit(self):
|
||||
|
|
|
@ -7,7 +7,7 @@ from artiq.applets.simple import SimpleApplet
|
|||
|
||||
|
||||
class Image(pyqtgraph.ImageView):
|
||||
def __init__(self, args, ctl):
|
||||
def __init__(self, args, req):
|
||||
pyqtgraph.ImageView.__init__(self)
|
||||
self.args = args
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@ from artiq.applets.simple import TitleApplet
|
|||
|
||||
|
||||
class HistogramPlot(pyqtgraph.PlotWidget):
|
||||
def __init__(self, args, ctl):
|
||||
def __init__(self, args, req):
|
||||
pyqtgraph.PlotWidget.__init__(self)
|
||||
self.args = args
|
||||
self.timer = QTimer()
|
||||
|
|
|
@ -9,7 +9,7 @@ from artiq.applets.simple import TitleApplet
|
|||
|
||||
|
||||
class XYPlot(pyqtgraph.PlotWidget):
|
||||
def __init__(self, args, ctl):
|
||||
def __init__(self, args, req):
|
||||
pyqtgraph.PlotWidget.__init__(self)
|
||||
self.args = args
|
||||
self.timer = QTimer()
|
||||
|
|
|
@ -22,7 +22,7 @@ def _compute_ys(histogram_bins, histograms_counts):
|
|||
# pyqtgraph.GraphicsWindow fails to behave like a regular Qt widget
|
||||
# and breaks embedding. Do not use as top widget.
|
||||
class XYHistPlot(QtWidgets.QSplitter):
|
||||
def __init__(self, args, ctl):
|
||||
def __init__(self, args, req):
|
||||
QtWidgets.QSplitter.__init__(self)
|
||||
self.resize(1000, 600)
|
||||
self.setWindowTitle("XY/Histogram")
|
||||
|
|
|
@ -6,7 +6,7 @@ from artiq.applets.simple import SimpleApplet
|
|||
|
||||
|
||||
class ProgressWidget(QtWidgets.QProgressBar):
|
||||
def __init__(self, args, ctl):
|
||||
def __init__(self, args, req):
|
||||
QtWidgets.QProgressBar.__init__(self)
|
||||
self.setMinimum(args.min)
|
||||
self.setMaximum(args.max)
|
||||
|
|
|
@ -11,11 +11,51 @@ from sipyco.pc_rpc import AsyncioClient as RPCClient
|
|||
from sipyco import pyon
|
||||
from sipyco.pipe_ipc import AsyncioChildComm
|
||||
|
||||
from artiq.language.scan import ScanObject
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class AppletControlIPC:
|
||||
class _AppletRequestInterface:
|
||||
def __init__(self):
|
||||
raise NotImplementedError
|
||||
|
||||
def set_dataset(self, key, value, unit=None, scale=None, precision=None, persist=None):
|
||||
"""
|
||||
Set a dataset.
|
||||
See documentation of ``artiq.language.environment.set_dataset``.
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def mutate_dataset(self, key, index, value):
|
||||
"""
|
||||
Mutate a dataset.
|
||||
See documentation of ``artiq.language.environment.mutate_dataset``.
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def append_to_dataset(self, key, value):
|
||||
"""
|
||||
Append to a dataset.
|
||||
See documentation of ``artiq.language.environment.append_to_dataset``.
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def set_argument_value(self, expurl, name, value):
|
||||
"""
|
||||
Temporarily set the value of an argument in a experiment in the dashboard.
|
||||
The value resets to default value when recomputing the argument.
|
||||
|
||||
:param expurl: Experiment URL identifying the experiment in the dashboard. Example: 'repo:ArgumentsDemo'.
|
||||
:param name: Name of the argument in the experiment.
|
||||
:param value: Object representing the new temporary value of the argument. For ``Scannable`` arguments, this parameter
|
||||
should be a ``ScanObject``. The type of the ``ScanObject`` will be set as the selected type when this function is called.
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
class AppletRequestIPC(_AppletRequestInterface):
|
||||
def __init__(self, ipc):
|
||||
self.ipc = ipc
|
||||
|
||||
|
@ -37,8 +77,13 @@ class AppletControlIPC:
|
|||
mod = {"action": "append", "path": [key, 1], "x": value}
|
||||
self.ipc.update_dataset(mod)
|
||||
|
||||
def set_argument_value(self, expurl, name, value):
|
||||
if isinstance(value, ScanObject):
|
||||
value = value.describe()
|
||||
self.ipc.set_argument_value(expurl, name, value)
|
||||
|
||||
class AppletControlRPC:
|
||||
|
||||
class AppletRequestRPC(_AppletRequestInterface):
|
||||
def __init__(self, loop, dataset_ctl):
|
||||
self.loop = loop
|
||||
self.dataset_ctl = dataset_ctl
|
||||
|
@ -137,6 +182,12 @@ class AppletIPCClient(AsyncioChildComm):
|
|||
self.write_pyon({"action": "update_dataset",
|
||||
"mod": mod})
|
||||
|
||||
def set_argument_value(self, expurl, name, value):
|
||||
self.write_pyon({"action": "set_argument_value",
|
||||
"expurl": expurl,
|
||||
"name": name,
|
||||
"value": value})
|
||||
|
||||
|
||||
class SimpleApplet:
|
||||
def __init__(self, main_widget_class, cmd_description=None,
|
||||
|
@ -200,21 +251,21 @@ class SimpleApplet:
|
|||
if self.embed is not None:
|
||||
self.ipc.close()
|
||||
|
||||
def ctl_init(self):
|
||||
def req_init(self):
|
||||
if self.embed is None:
|
||||
dataset_ctl = RPCClient()
|
||||
self.loop.run_until_complete(dataset_ctl.connect_rpc(
|
||||
self.args.server, self.args.port_control, "master_dataset_db"))
|
||||
self.ctl = AppletControlRPC(self.loop, dataset_ctl)
|
||||
self.req = AppletRequestRPC(self.loop, dataset_ctl)
|
||||
else:
|
||||
self.ctl = AppletControlIPC(self.ipc)
|
||||
self.req = AppletRequestIPC(self.ipc)
|
||||
|
||||
def ctl_close(self):
|
||||
def req_close(self):
|
||||
if self.embed is None:
|
||||
self.ctl.dataset_ctl.close_rpc()
|
||||
self.req.dataset_ctl.close_rpc()
|
||||
|
||||
def create_main_widget(self):
|
||||
self.main_widget = self.main_widget_class(self.args, self.ctl)
|
||||
self.main_widget = self.main_widget_class(self.args, self.req)
|
||||
if self.embed is not None:
|
||||
self.ipc.set_close_cb(self.main_widget.close)
|
||||
if os.name == "nt":
|
||||
|
@ -316,7 +367,7 @@ class SimpleApplet:
|
|||
try:
|
||||
self.ipc_init()
|
||||
try:
|
||||
self.ctl_init()
|
||||
self.req_init()
|
||||
try:
|
||||
self.create_main_widget()
|
||||
self.subscribe()
|
||||
|
@ -325,7 +376,7 @@ class SimpleApplet:
|
|||
finally:
|
||||
self.unsubscribe()
|
||||
finally:
|
||||
self.ctl_close()
|
||||
self.req_close()
|
||||
finally:
|
||||
self.ipc_close()
|
||||
finally:
|
||||
|
|
|
@ -10,22 +10,14 @@ import h5py
|
|||
from sipyco import pyon
|
||||
|
||||
from artiq import __artiq_dir__ as artiq_dir
|
||||
from artiq.gui.tools import LayoutWidget, log_level_to_name, get_open_file_name
|
||||
from artiq.gui.tools import (LayoutWidget, WheelFilter,
|
||||
log_level_to_name, get_open_file_name)
|
||||
from artiq.gui.entries import procdesc_to_entry
|
||||
from artiq.master.worker import Worker, log_worker_exception
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class _WheelFilter(QtCore.QObject):
|
||||
def eventFilter(self, obj, event):
|
||||
if (event.type() == QtCore.QEvent.Wheel and
|
||||
event.modifiers() != QtCore.Qt.NoModifier):
|
||||
event.ignore()
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
class _ArgumentEditor(QtWidgets.QTreeWidget):
|
||||
def __init__(self, dock):
|
||||
QtWidgets.QTreeWidget.__init__(self)
|
||||
|
@ -46,7 +38,7 @@ class _ArgumentEditor(QtWidgets.QTreeWidget):
|
|||
self.setStyleSheet("QTreeWidget {background: " +
|
||||
self.palette().midlight().color().name() + " ;}")
|
||||
|
||||
self.viewport().installEventFilter(_WheelFilter(self.viewport()))
|
||||
self.viewport().installEventFilter(WheelFilter(self.viewport(), True))
|
||||
|
||||
self._groups = dict()
|
||||
self._arg_to_widgets = dict()
|
||||
|
|
|
@ -62,7 +62,11 @@ class Core:
|
|||
def close(self):
|
||||
self.comm.close()
|
||||
|
||||
def compile(self, method, args, kwargs, embedding_map, file_output=None):
|
||||
def compile(self, method, args, kwargs, embedding_map, file_output=None, target=None):
|
||||
if target is not None:
|
||||
# NAC3TODO: subkernels
|
||||
raise NotImplementedError
|
||||
|
||||
if not self.analyzed:
|
||||
self.compiler.analyze(core_language._registered_functions, core_language._registered_classes)
|
||||
self.analyzed = True
|
||||
|
|
|
@ -142,7 +142,7 @@
|
|||
"properties": {
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": ["dio", "dio_spi", "urukul", "sampler", "suservo", "zotino", "grabber", "mirny", "fastino", "phaser", "hvamp", "efc"]
|
||||
"enum": ["dio", "dio_spi", "urukul", "sampler", "suservo", "zotino", "grabber", "mirny", "fastino", "phaser", "hvamp", "shuttler"]
|
||||
},
|
||||
"board": {
|
||||
"type": "string"
|
||||
|
@ -563,11 +563,11 @@
|
|||
"required": ["ports"]
|
||||
}
|
||||
},{
|
||||
"title": "EFC",
|
||||
"title": "Shuttler",
|
||||
"if": {
|
||||
"properties": {
|
||||
"type": {
|
||||
"const": "efc"
|
||||
"const": "shuttler"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -580,6 +580,10 @@
|
|||
},
|
||||
"minItems": 1,
|
||||
"maxItems": 2
|
||||
},
|
||||
"drtio_destination": {
|
||||
"type": "integer",
|
||||
"default": 4
|
||||
}
|
||||
},
|
||||
"required": ["ports"]
|
||||
|
|
|
@ -2,23 +2,131 @@ import numpy
|
|||
|
||||
from artiq.language.core import *
|
||||
from artiq.language.types import *
|
||||
from artiq.coredevice.rtio import rtio_output
|
||||
from artiq.coredevice.rtio import rtio_output, rtio_input_data
|
||||
from artiq.coredevice import spi2 as spi
|
||||
from artiq.language.units import us
|
||||
|
||||
|
||||
@portable
|
||||
def shuttler_volt_to_mu(volt):
|
||||
"""Return the equivalent DAC code. Valid input range is from -10 to
|
||||
10 - LSB.
|
||||
"""
|
||||
return round((1 << 16) * (volt / 20.0)) & 0xffff
|
||||
|
||||
|
||||
class Config:
|
||||
kernel_invariants = {"core", "channel", "target_o"}
|
||||
"""Shuttler configuration registers interface.
|
||||
|
||||
The configuration registers control waveform phase auto-clear, and pre-DAC
|
||||
gain & offset values for calibration with ADC on the Shuttler AFE card.
|
||||
|
||||
To find the calibrated DAC code, the Shuttler Core first multiplies the
|
||||
output data with pre-DAC gain, then adds the offset.
|
||||
|
||||
.. note::
|
||||
The DAC code is capped at 0x7fff and 0x8000.
|
||||
|
||||
:param channel: RTIO channel number of this interface.
|
||||
:param core_device: Core device name.
|
||||
"""
|
||||
kernel_invariants = {
|
||||
"core", "channel", "target_base", "target_read",
|
||||
"target_gain", "target_offset", "target_clr"
|
||||
}
|
||||
|
||||
def __init__(self, dmgr, channel, core_device="core"):
|
||||
self.core = dmgr.get(core_device)
|
||||
self.channel = channel
|
||||
self.target_o = channel << 8
|
||||
self.target_base = channel << 8
|
||||
self.target_read = 1 << 6
|
||||
self.target_gain = 0 * (1 << 4)
|
||||
self.target_offset = 1 * (1 << 4)
|
||||
self.target_clr = 1 * (1 << 5)
|
||||
|
||||
@kernel
|
||||
def set_config(self, config):
|
||||
rtio_output(self.target_o, config)
|
||||
def set_clr(self, clr):
|
||||
"""Set/Unset waveform phase clear bits.
|
||||
|
||||
Each bit corresponds to a Shuttler waveform generator core. Setting a
|
||||
clear bit forces the Shuttler Core to clear the phase accumulator on
|
||||
waveform trigger (See :class:`Trigger` for the trigger method).
|
||||
Otherwise, the phase accumulator increments from its original value.
|
||||
|
||||
:param clr: Waveform phase clear bits. The MSB corresponds to Channel
|
||||
15, LSB corresponds to Channel 0.
|
||||
"""
|
||||
rtio_output(self.target_base | self.target_clr, clr)
|
||||
|
||||
@kernel
|
||||
def set_gain(self, channel, gain):
|
||||
"""Set the 16-bits pre-DAC gain register of a Shuttler Core channel.
|
||||
|
||||
The `gain` parameter represents the decimal portion of the gain
|
||||
factor. The MSB represents 0.5 and the sign bit. Hence, the valid
|
||||
total gain value (1 +/- 0.gain) ranges from 0.5 to 1.5 - LSB.
|
||||
|
||||
:param channel: Shuttler Core channel to be configured.
|
||||
:param gain: Shuttler Core channel gain.
|
||||
"""
|
||||
rtio_output(self.target_base | self.target_gain | channel, gain)
|
||||
|
||||
@kernel
|
||||
def get_gain(self, channel):
|
||||
"""Return the pre-DAC gain value of a Shuttler Core channel.
|
||||
|
||||
:param channel: The Shuttler Core channel.
|
||||
:return: Pre-DAC gain value. See :meth:`set_gain`.
|
||||
"""
|
||||
rtio_output(self.target_base | self.target_gain |
|
||||
self.target_read | channel, 0)
|
||||
return rtio_input_data(self.channel)
|
||||
|
||||
@kernel
|
||||
def set_offset(self, channel, offset):
|
||||
"""Set the 16-bits pre-DAC offset register of a Shuttler Core channel.
|
||||
|
||||
.. seealso::
|
||||
:meth:`shuttler_volt_to_mu`
|
||||
|
||||
:param channel: Shuttler Core channel to be configured.
|
||||
:param offset: Shuttler Core channel offset.
|
||||
"""
|
||||
rtio_output(self.target_base | self.target_offset | channel, offset)
|
||||
|
||||
@kernel
|
||||
def get_offset(self, channel):
|
||||
"""Return the pre-DAC offset value of a Shuttler Core channel.
|
||||
|
||||
:param channel: The Shuttler Core channel.
|
||||
:return: Pre-DAC offset value. See :meth:`set_offset`.
|
||||
"""
|
||||
rtio_output(self.target_base | self.target_offset |
|
||||
self.target_read | channel, 0)
|
||||
return rtio_input_data(self.channel)
|
||||
|
||||
|
||||
class Volt:
|
||||
"""Shuttler Core cubic DC-bias spline.
|
||||
|
||||
A Shuttler channel can generate a waveform `w(t)` that is the sum of a
|
||||
cubic spline `a(t)` and a sinusoid modulated in amplitude by a cubic
|
||||
spline `b(t)` and in phase/frequency by a quadratic spline `c(t)`, where
|
||||
|
||||
.. math::
|
||||
w(t) = a(t) + b(t) * cos(c(t))
|
||||
|
||||
And `t` corresponds to time in seconds.
|
||||
This class controls the cubic spline `a(t)`, in which
|
||||
|
||||
.. math::
|
||||
a(t) = p_0 + p_1t + \\frac{p_2t^2}{2} + \\frac{p_3t^3}{6}
|
||||
|
||||
And `a(t)` is in Volt.
|
||||
|
||||
:param channel: RTIO channel number of this DC-bias spline interface.
|
||||
:param core_device: Core device name.
|
||||
"""
|
||||
kernel_invariants = {"core", "channel", "target_o"}
|
||||
|
||||
def __init__(self, dmgr, channel, core_device="core"):
|
||||
|
@ -28,7 +136,35 @@ class Volt:
|
|||
|
||||
@kernel
|
||||
def set_waveform(self, a0: TInt32, a1: TInt32, a2: TInt64, a3: TInt64):
|
||||
pdq_words = [
|
||||
"""Set the DC-bias spline waveform.
|
||||
|
||||
Given `a(t)` as defined in :class:`Volt`, the coefficients should be
|
||||
configured by the following formulae.
|
||||
|
||||
.. math::
|
||||
T &= 8*10^{-9}
|
||||
|
||||
a_0 &= p_0
|
||||
|
||||
a_1 &= p_1T + \\frac{p_2T^2}{2} + \\frac{p_3T^3}{6}
|
||||
|
||||
a_2 &= p_2T^2 + p_3T^3
|
||||
|
||||
a_3 &= p_3T^3
|
||||
|
||||
:math:`a_0`, :math:`a_1`, :math:`a_2` and :math:`a_3` are 16, 32, 48
|
||||
and 48 bits in width respectively. See :meth:`shuttler_volt_to_mu` for
|
||||
machine unit conversion.
|
||||
|
||||
Note: The waveform is not updated to the Shuttler Core until
|
||||
triggered. See :class:`Trigger` for the update triggering mechanism.
|
||||
|
||||
:param a0: The :math:`a_0` coefficient in machine unit.
|
||||
:param a1: The :math:`a_1` coefficient in machine unit.
|
||||
:param a2: The :math:`a_2` coefficient in machine unit.
|
||||
:param a3: The :math:`a_3` coefficient in machine unit.
|
||||
"""
|
||||
coef_words = [
|
||||
a0,
|
||||
a1,
|
||||
a1 >> 16,
|
||||
|
@ -40,12 +176,36 @@ class Volt:
|
|||
(a3 >> 32) & 0xFFFF,
|
||||
]
|
||||
|
||||
for i in range(len(pdq_words)):
|
||||
rtio_output(self.target_o | i, pdq_words[i])
|
||||
for i in range(len(coef_words)):
|
||||
rtio_output(self.target_o | i, coef_words[i])
|
||||
delay_mu(int64(self.core.ref_multiplier))
|
||||
|
||||
|
||||
class Dds:
|
||||
"""Shuttler Core DDS spline.
|
||||
|
||||
A Shuttler channel can generate a waveform `w(t)` that is the sum of a
|
||||
cubic spline `a(t)` and a sinusoid modulated in amplitude by a cubic
|
||||
spline `b(t)` and in phase/frequency by a quadratic spline `c(t)`, where
|
||||
|
||||
.. math::
|
||||
w(t) = a(t) + b(t) * cos(c(t))
|
||||
|
||||
And `t` corresponds to time in seconds.
|
||||
This class controls the cubic spline `b(t)` and quadratic spline `c(t)`,
|
||||
in which
|
||||
|
||||
.. math::
|
||||
b(t) &= g * (q_0 + q_1t + \\frac{q_2t^2}{2} + \\frac{q_3t^3}{6})
|
||||
|
||||
c(t) &= r_0 + r_1t + \\frac{r_2t^2}{2}
|
||||
|
||||
And `b(t)` is in Volt, `c(t)` is in number of turns. Note that `b(t)`
|
||||
contributes to a constant gain of :math:`g=1.64676`.
|
||||
|
||||
:param channel: RTIO channel number of this DC-bias spline interface.
|
||||
:param core_device: Core device name.
|
||||
"""
|
||||
kernel_invariants = {"core", "channel", "target_o"}
|
||||
|
||||
def __init__(self, dmgr, channel, core_device="core"):
|
||||
|
@ -56,7 +216,45 @@ class Dds:
|
|||
@kernel
|
||||
def set_waveform(self, b0: TInt32, b1: TInt32, b2: TInt64, b3: TInt64,
|
||||
c0: TInt32, c1: TInt32, c2: TInt32):
|
||||
pdq_words = [
|
||||
"""Set the DDS spline waveform.
|
||||
|
||||
Given `b(t)` and `c(t)` as defined in :class:`Dds`, the coefficients
|
||||
should be configured by the following formulae.
|
||||
|
||||
.. math::
|
||||
T &= 8*10^{-9}
|
||||
|
||||
b_0 &= q_0
|
||||
|
||||
b_1 &= q_1T + \\frac{q_2T^2}{2} + \\frac{q_3T^3}{6}
|
||||
|
||||
b_2 &= q_2T^2 + q_3T^3
|
||||
|
||||
b_3 &= q_3T^3
|
||||
|
||||
c_0 &= r_0
|
||||
|
||||
c_1 &= r_1T + \\frac{r_2T^2}{2}
|
||||
|
||||
c_2 &= r_2T^2
|
||||
|
||||
:math:`b_0`, :math:`b_1`, :math:`b_2` and :math:`b_3` are 16, 32, 48
|
||||
and 48 bits in width respectively. See :meth:`shuttler_volt_to_mu` for
|
||||
machine unit conversion. :math:`c_0`, :math:`c_1` and :math:`c_2` are
|
||||
16, 32 and 32 bits in width respectively.
|
||||
|
||||
Note: The waveform is not updated to the Shuttler Core until
|
||||
triggered. See :class:`Trigger` for the update triggering mechanism.
|
||||
|
||||
:param b0: The :math:`b_0` coefficient in machine unit.
|
||||
:param b1: The :math:`b_1` coefficient in machine unit.
|
||||
:param b2: The :math:`b_2` coefficient in machine unit.
|
||||
:param b3: The :math:`b_3` coefficient in machine unit.
|
||||
:param c0: The :math:`c_0` coefficient in machine unit.
|
||||
:param c1: The :math:`c_1` coefficient in machine unit.
|
||||
:param c2: The :math:`c_2` coefficient in machine unit.
|
||||
"""
|
||||
coef_words = [
|
||||
b0,
|
||||
b1,
|
||||
b1 >> 16,
|
||||
|
@ -73,12 +271,17 @@ class Dds:
|
|||
c2 >> 16,
|
||||
]
|
||||
|
||||
for i in range(len(pdq_words)):
|
||||
rtio_output(self.target_o | i, pdq_words[i])
|
||||
for i in range(len(coef_words)):
|
||||
rtio_output(self.target_o | i, coef_words[i])
|
||||
delay_mu(int64(self.core.ref_multiplier))
|
||||
|
||||
|
||||
class Trigger:
|
||||
"""Shuttler Core spline coefficients update trigger.
|
||||
|
||||
:param channel: RTIO channel number of the trigger interface.
|
||||
:param core_device: Core device name.
|
||||
"""
|
||||
kernel_invariants = {"core", "channel", "target_o"}
|
||||
|
||||
def __init__(self, dmgr, channel, core_device="core"):
|
||||
|
@ -88,4 +291,335 @@ class Trigger:
|
|||
|
||||
@kernel
|
||||
def trigger(self, trig_out):
|
||||
"""Triggers coefficient update of (a) Shuttler Core channel(s).
|
||||
|
||||
Each bit corresponds to a Shuttler waveform generator core. Setting
|
||||
`trig_out` bits commits the pending coefficient update (from
|
||||
`set_waveform` in :class:`Volt` and :class:`Dds`) to the Shuttler Core
|
||||
synchronously.
|
||||
|
||||
:param trig_out: Coefficient update trigger bits. The MSB corresponds
|
||||
to Channel 15, LSB corresponds to Channel 0.
|
||||
"""
|
||||
rtio_output(self.target_o, trig_out)
|
||||
|
||||
|
||||
RELAY_SPI_CONFIG = (0*spi.SPI_OFFLINE | 1*spi.SPI_END |
|
||||
0*spi.SPI_INPUT | 0*spi.SPI_CS_POLARITY |
|
||||
0*spi.SPI_CLK_POLARITY | 0*spi.SPI_CLK_PHASE |
|
||||
0*spi.SPI_LSB_FIRST | 0*spi.SPI_HALF_DUPLEX)
|
||||
|
||||
ADC_SPI_CONFIG = (0*spi.SPI_OFFLINE | 0*spi.SPI_END |
|
||||
0*spi.SPI_INPUT | 0*spi.SPI_CS_POLARITY |
|
||||
1*spi.SPI_CLK_POLARITY | 1*spi.SPI_CLK_PHASE |
|
||||
0*spi.SPI_LSB_FIRST | 0*spi.SPI_HALF_DUPLEX)
|
||||
|
||||
# SPI clock write and read dividers
|
||||
# CS should assert at least 9.5 ns after clk pulse
|
||||
SPIT_RELAY_WR = 4
|
||||
# 25 ns high/low pulse hold (limiting for write)
|
||||
SPIT_ADC_WR = 4
|
||||
SPIT_ADC_RD = 16
|
||||
|
||||
# SPI CS line
|
||||
CS_RELAY = 1 << 0
|
||||
CS_LED = 1 << 1
|
||||
CS_ADC = 1 << 0
|
||||
|
||||
# Referenced AD4115 registers
|
||||
_AD4115_REG_STATUS = 0x00
|
||||
_AD4115_REG_ADCMODE = 0x01
|
||||
_AD4115_REG_DATA = 0x04
|
||||
_AD4115_REG_ID = 0x07
|
||||
_AD4115_REG_CH0 = 0x10
|
||||
_AD4115_REG_SETUPCON0 = 0x20
|
||||
|
||||
|
||||
class Relay:
|
||||
"""Shuttler AFE relay switches.
|
||||
|
||||
It controls the AFE relay switches and the LEDs. Switch on the relay to
|
||||
enable AFE output; And off to disable the output. The LEDs indicates the
|
||||
relay status.
|
||||
|
||||
.. note::
|
||||
The relay does not disable ADC measurements. Voltage of any channels
|
||||
can still be read by the ADC even after switching off the relays.
|
||||
|
||||
:param spi_device: SPI bus device name.
|
||||
:param core_device: Core device name.
|
||||
"""
|
||||
kernel_invariant = {"core", "bus"}
|
||||
|
||||
def __init__(self, dmgr, spi_device, core_device="core"):
|
||||
self.core = dmgr.get(core_device)
|
||||
self.bus = dmgr.get(spi_device)
|
||||
|
||||
@kernel
|
||||
def init(self):
|
||||
"""Initialize SPI device.
|
||||
|
||||
Configures the SPI bus to 16-bits, write-only, simultaneous relay
|
||||
switches and LED control.
|
||||
"""
|
||||
self.bus.set_config_mu(
|
||||
RELAY_SPI_CONFIG, 16, SPIT_RELAY_WR, CS_RELAY | CS_LED)
|
||||
|
||||
@kernel
|
||||
def enable(self, en: TInt32):
|
||||
"""Enable/Disable relay switches of corresponding channels.
|
||||
|
||||
Each bit corresponds to the relay switch of a channel. Asserting a bit
|
||||
turns on the corresponding relay switch; Deasserting the same bit
|
||||
turns off the switch instead.
|
||||
|
||||
:param en: Switch enable bits. The MSB corresponds to Channel 15, LSB
|
||||
corresponds to Channel 0.
|
||||
"""
|
||||
self.bus.write(en << 16)
|
||||
|
||||
|
||||
class ADC:
|
||||
"""Shuttler AFE ADC (AD4115) driver.
|
||||
|
||||
:param spi_device: SPI bus device name.
|
||||
:param core_device: Core device name.
|
||||
"""
|
||||
kernel_invariant = {"core", "bus"}
|
||||
|
||||
def __init__(self, dmgr, spi_device, core_device="core"):
|
||||
self.core = dmgr.get(core_device)
|
||||
self.bus = dmgr.get(spi_device)
|
||||
|
||||
@kernel
|
||||
def read_id(self) -> TInt32:
|
||||
"""Read the product ID of the ADC.
|
||||
|
||||
The expected return value is 0x38DX, the 4 LSbs are don't cares.
|
||||
|
||||
:return: The read-back product ID.
|
||||
"""
|
||||
return self.read16(_AD4115_REG_ID)
|
||||
|
||||
@kernel
|
||||
def reset(self):
|
||||
"""AD4115 reset procedure.
|
||||
|
||||
This performs a write operation of 96 serial clock cycles with DIN
|
||||
held at high. It resets the entire device, including the register
|
||||
contents.
|
||||
|
||||
.. note::
|
||||
The datasheet only requires 64 cycles, but reasserting `CS_n` right
|
||||
after the transfer appears to interrupt the start-up sequence.
|
||||
"""
|
||||
self.bus.set_config_mu(ADC_SPI_CONFIG, 32, SPIT_ADC_WR, CS_ADC)
|
||||
self.bus.write(0xffffffff)
|
||||
self.bus.write(0xffffffff)
|
||||
self.bus.set_config_mu(
|
||||
ADC_SPI_CONFIG | spi.SPI_END, 32, SPIT_ADC_WR, CS_ADC)
|
||||
self.bus.write(0xffffffff)
|
||||
|
||||
@kernel
|
||||
def read8(self, addr: TInt32) -> TInt32:
|
||||
"""Read from 8 bit register.
|
||||
|
||||
:param addr: Register address.
|
||||
:return: Read-back register content.
|
||||
"""
|
||||
self.bus.set_config_mu(
|
||||
ADC_SPI_CONFIG | spi.SPI_END | spi.SPI_INPUT,
|
||||
16, SPIT_ADC_RD, CS_ADC)
|
||||
self.bus.write((addr | 0x40) << 24)
|
||||
return self.bus.read() & 0xff
|
||||
|
||||
@kernel
|
||||
def read16(self, addr: TInt32) -> TInt32:
|
||||
"""Read from 16 bit register.
|
||||
|
||||
:param addr: Register address.
|
||||
:return: Read-back register content.
|
||||
"""
|
||||
self.bus.set_config_mu(
|
||||
ADC_SPI_CONFIG | spi.SPI_END | spi.SPI_INPUT,
|
||||
24, SPIT_ADC_RD, CS_ADC)
|
||||
self.bus.write((addr | 0x40) << 24)
|
||||
return self.bus.read() & 0xffff
|
||||
|
||||
@kernel
|
||||
def read24(self, addr: TInt32) -> TInt32:
|
||||
"""Read from 24 bit register.
|
||||
|
||||
:param addr: Register address.
|
||||
:return: Read-back register content.
|
||||
"""
|
||||
self.bus.set_config_mu(
|
||||
ADC_SPI_CONFIG | spi.SPI_END | spi.SPI_INPUT,
|
||||
32, SPIT_ADC_RD, CS_ADC)
|
||||
self.bus.write((addr | 0x40) << 24)
|
||||
return self.bus.read() & 0xffffff
|
||||
|
||||
@kernel
|
||||
def write8(self, addr: TInt32, data: TInt32):
|
||||
"""Write to 8 bit register.
|
||||
|
||||
:param addr: Register address.
|
||||
:param data: Data to be written.
|
||||
"""
|
||||
self.bus.set_config_mu(
|
||||
ADC_SPI_CONFIG | spi.SPI_END, 16, SPIT_ADC_WR, CS_ADC)
|
||||
self.bus.write(addr << 24 | (data & 0xff) << 16)
|
||||
|
||||
@kernel
|
||||
def write16(self, addr: TInt32, data: TInt32):
|
||||
"""Write to 16 bit register.
|
||||
|
||||
:param addr: Register address.
|
||||
:param data: Data to be written.
|
||||
"""
|
||||
self.bus.set_config_mu(
|
||||
ADC_SPI_CONFIG | spi.SPI_END, 24, SPIT_ADC_WR, CS_ADC)
|
||||
self.bus.write(addr << 24 | (data & 0xffff) << 8)
|
||||
|
||||
@kernel
|
||||
def write24(self, addr: TInt32, data: TInt32):
|
||||
"""Write to 24 bit register.
|
||||
|
||||
:param addr: Register address.
|
||||
:param data: Data to be written.
|
||||
"""
|
||||
self.bus.set_config_mu(
|
||||
ADC_SPI_CONFIG | spi.SPI_END, 32, SPIT_ADC_WR, CS_ADC)
|
||||
self.bus.write(addr << 24 | (data & 0xffffff))
|
||||
|
||||
@kernel
|
||||
def read_ch(self, channel: TInt32) -> TFloat:
|
||||
"""Sample a Shuttler channel on the AFE.
|
||||
|
||||
It performs a single conversion using profile 0 and setup 0, on the
|
||||
selected channel. The sample is then recovered and converted to volt.
|
||||
|
||||
:param channel: Shuttler channel to be sampled.
|
||||
:return: Voltage sample in volt.
|
||||
"""
|
||||
# Always configure Profile 0 for single conversion
|
||||
self.write16(_AD4115_REG_CH0, 0x8000 | ((channel * 2 + 1) << 4))
|
||||
self.write16(_AD4115_REG_SETUPCON0, 0x1300)
|
||||
self.single_conversion()
|
||||
|
||||
delay(100*us)
|
||||
adc_code = self.read24(_AD4115_REG_DATA)
|
||||
return ((adc_code / (1 << 23)) - 1) * 2.5 / 0.1
|
||||
|
||||
@kernel
|
||||
def single_conversion(self):
|
||||
"""Place the ADC in single conversion mode.
|
||||
|
||||
The ADC returns to standby mode after the conversion is complete.
|
||||
"""
|
||||
self.write16(_AD4115_REG_ADCMODE, 0x8010)
|
||||
|
||||
@kernel
|
||||
def standby(self):
|
||||
"""Place the ADC in standby mode and disables power down the clock.
|
||||
|
||||
The ADC can be returned to single conversion mode by calling
|
||||
:meth:`single_conversion`.
|
||||
"""
|
||||
# Selecting internal XO (0b00) also disables clock during standby
|
||||
self.write16(_AD4115_REG_ADCMODE, 0x8020)
|
||||
|
||||
@kernel
|
||||
def power_down(self):
|
||||
"""Place the ADC in power-down mode.
|
||||
|
||||
The ADC must be reset before returning to other modes.
|
||||
|
||||
.. note::
|
||||
The AD4115 datasheet suggests placing the ADC in standby mode
|
||||
before power-down. This is to prevent accidental entry into the
|
||||
power-down mode.
|
||||
|
||||
.. seealso::
|
||||
:meth:`standby`
|
||||
|
||||
:meth:`power_up`
|
||||
|
||||
"""
|
||||
self.write16(_AD4115_REG_ADCMODE, 0x8030)
|
||||
|
||||
@kernel
|
||||
def power_up(self):
|
||||
"""Exit the ADC power-down mode.
|
||||
|
||||
The ADC should be in power-down mode before calling this method.
|
||||
|
||||
.. seealso::
|
||||
:meth:`power_down`
|
||||
"""
|
||||
self.reset()
|
||||
# Although the datasheet claims 500 us reset wait time, only waiting
|
||||
# for ~500 us can result in DOUT pin stuck in high
|
||||
delay(2500*us)
|
||||
|
||||
@kernel
|
||||
def calibrate(self, volts, trigger, config, samples=[-5.0, 0.0, 5.0]):
|
||||
"""Calibrate the Shuttler waveform generator using the ADC on the AFE.
|
||||
|
||||
It finds the average slope rate and average offset by samples, and
|
||||
compensate by writing the pre-DAC gain and offset registers in the
|
||||
configuration registers.
|
||||
|
||||
.. note::
|
||||
If the pre-calibration slope rate < 1, the calibration procedure
|
||||
will introduce a pre-DAC gain compensation. However, this may
|
||||
saturate the pre-DAC voltage code. (See :class:`Config` notes).
|
||||
Shuttler cannot cover the entire +/- 10 V range in this case.
|
||||
|
||||
.. seealso::
|
||||
:meth:`Config.set_gain`
|
||||
|
||||
:meth:`Config.set_offset`
|
||||
|
||||
:param volts: A list of all 16 cubic DC-bias spline.
|
||||
(See :class:`Volt`)
|
||||
:param trigger: The Shuttler spline coefficient update trigger.
|
||||
:param config: The Shuttler Core configuration registers.
|
||||
:param samples: A list of sample voltages for calibration. There must
|
||||
be at least 2 samples to perform slope rate calculation.
|
||||
"""
|
||||
assert len(volts) == 16
|
||||
assert len(samples) > 1
|
||||
|
||||
measurements = [0.0] * len(samples)
|
||||
|
||||
for ch in range(16):
|
||||
# Find the average slope rate and offset
|
||||
for i in range(len(samples)):
|
||||
self.core.break_realtime()
|
||||
volts[ch].set_waveform(
|
||||
shuttler_volt_to_mu(samples[i]), 0, 0, 0)
|
||||
trigger.trigger(1 << ch)
|
||||
measurements[i] = self.read_ch(ch)
|
||||
|
||||
# Find the average output slope
|
||||
slope_sum = 0.0
|
||||
for i in range(len(samples) - 1):
|
||||
slope_sum += (measurements[i+1] - measurements[i])/(samples[i+1] - samples[i])
|
||||
slope_avg = slope_sum / (len(samples) - 1)
|
||||
|
||||
gain_code = int32(1 / slope_avg * (2 ** 16)) & 0xffff
|
||||
|
||||
# Scale the measurements by 1/slope, find average offset
|
||||
offset_sum = 0.0
|
||||
for i in range(len(samples)):
|
||||
offset_sum += (measurements[i] / slope_avg) - samples[i]
|
||||
offset_avg = offset_sum / len(samples)
|
||||
|
||||
offset_code = shuttler_volt_to_mu(-offset_avg)
|
||||
|
||||
self.core.break_realtime()
|
||||
config.set_gain(ch, gain_code)
|
||||
|
||||
delay_mu(int64(self.core.ref_multiplier))
|
||||
config.set_offset(ch, offset_code)
|
||||
|
|
|
@ -74,19 +74,20 @@ class CreateEditDialog(QtWidgets.QDialog):
|
|||
|
||||
self.key = key
|
||||
self.name_widget.setText(key)
|
||||
self.value_widget.setText(value)
|
||||
|
||||
value_edit_string = self.value_to_edit_string(value)
|
||||
if metadata is not None:
|
||||
scale = scale_from_metadata(metadata)
|
||||
decoded_value = pyon.decode(value)
|
||||
if scale == 1:
|
||||
self.value_widget.setText(value)
|
||||
else:
|
||||
self.value_widget.setText(pyon.encode(decoded_value / scale))
|
||||
t = value.dtype if value is np.ndarray else type(value)
|
||||
if scale != 1 and np.issubdtype(t, np.number):
|
||||
# degenerates to float type
|
||||
value_edit_string = self.value_to_edit_string(
|
||||
np.float64(value / scale))
|
||||
self.unit_widget.setText(metadata.get('unit', ''))
|
||||
self.scale_widget.setText(str(metadata.get('scale', '')))
|
||||
self.precision_widget.setText(str(metadata.get('precision', '')))
|
||||
|
||||
self.value_widget.setText(value_edit_string)
|
||||
self.box_widget.setChecked(persist)
|
||||
|
||||
def accept(self):
|
||||
|
@ -104,11 +105,11 @@ class CreateEditDialog(QtWidgets.QDialog):
|
|||
if precision != "":
|
||||
metadata['precision'] = int(precision)
|
||||
scale = scale_from_metadata(metadata)
|
||||
value = pyon.decode(value)
|
||||
value = self.parse_edit_string(value)
|
||||
t = value.dtype if value is np.ndarray else type(value)
|
||||
is_floating = scale != 1 or np.issubdtype(t, np.floating)
|
||||
if is_floating:
|
||||
value = value * scale
|
||||
if scale != 1 and np.issubdtype(t, np.number):
|
||||
# degenerates to float type
|
||||
value = np.float64(value * scale)
|
||||
if self.key and self.key != key:
|
||||
asyncio.ensure_future(exc_to_warning(rename(self.key, key, value, metadata, persist, self.dataset_ctl)))
|
||||
else:
|
||||
|
@ -119,7 +120,9 @@ class CreateEditDialog(QtWidgets.QDialog):
|
|||
def dtype(self):
|
||||
txt = self.value_widget.text()
|
||||
try:
|
||||
result = pyon.decode(txt)
|
||||
result = self.parse_edit_string(txt)
|
||||
# ensure only pyon compatible types are permissable
|
||||
pyon.encode(result)
|
||||
except:
|
||||
pixmap = self.style().standardPixmap(
|
||||
QtWidgets.QStyle.SP_MessageBoxWarning)
|
||||
|
@ -129,6 +132,35 @@ class CreateEditDialog(QtWidgets.QDialog):
|
|||
self.data_type.setText(type(result).__name__)
|
||||
self.ok.setEnabled(True)
|
||||
|
||||
@staticmethod
|
||||
def parse_edit_string(s):
|
||||
if s == "":
|
||||
raise TypeError
|
||||
_eval_dict = {
|
||||
"__builtins__": {},
|
||||
"array": np.array,
|
||||
"null": np.nan,
|
||||
"inf": np.inf
|
||||
}
|
||||
for t_ in pyon._numpy_scalar:
|
||||
_eval_dict[t_] = eval("np.{}".format(t_), {"np": np})
|
||||
return eval(s, _eval_dict, {})
|
||||
|
||||
@staticmethod
|
||||
def value_to_edit_string(v):
|
||||
t = type(v)
|
||||
r = ""
|
||||
if isinstance(v, np.generic):
|
||||
r += t.__name__
|
||||
r += "("
|
||||
r += repr(v)
|
||||
r += ")"
|
||||
elif v is None:
|
||||
return r
|
||||
else:
|
||||
r += repr(v)
|
||||
return r
|
||||
|
||||
|
||||
class Model(DictSyncTreeSepModel):
|
||||
def __init__(self, init):
|
||||
|
@ -209,7 +241,6 @@ class DatasetsDock(QtWidgets.QDockWidget):
|
|||
key = self.table_model.index_to_key(idx)
|
||||
if key is not None:
|
||||
persist, value, metadata = self.table_model.backing_store[key]
|
||||
value = pyon.encode(value)
|
||||
CreateEditDialog(self, self.dataset_ctl, key, value, metadata, persist).open()
|
||||
|
||||
def delete_clicked(self):
|
||||
|
|
|
@ -11,7 +11,8 @@ from sipyco import pyon
|
|||
|
||||
from artiq.gui.entries import procdesc_to_entry, ScanEntry
|
||||
from artiq.gui.fuzzy_select import FuzzySelectWidget
|
||||
from artiq.gui.tools import LayoutWidget, log_level_to_name, get_open_file_name
|
||||
from artiq.gui.tools import (LayoutWidget, WheelFilter,
|
||||
log_level_to_name, get_open_file_name)
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
@ -23,15 +24,6 @@ logger = logging.getLogger(__name__)
|
|||
# 2. file:<class name>@<file name>
|
||||
|
||||
|
||||
class _WheelFilter(QtCore.QObject):
|
||||
def eventFilter(self, obj, event):
|
||||
if (event.type() == QtCore.QEvent.Wheel and
|
||||
event.modifiers() != QtCore.Qt.NoModifier):
|
||||
event.ignore()
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
class _ArgumentEditor(QtWidgets.QTreeWidget):
|
||||
def __init__(self, manager, dock, expurl):
|
||||
self.manager = manager
|
||||
|
@ -55,7 +47,7 @@ class _ArgumentEditor(QtWidgets.QTreeWidget):
|
|||
self.setStyleSheet("QTreeWidget {background: " +
|
||||
self.palette().midlight().color().name() + " ;}")
|
||||
|
||||
self.viewport().installEventFilter(_WheelFilter(self.viewport()))
|
||||
self.viewport().installEventFilter(WheelFilter(self.viewport(), True))
|
||||
|
||||
self._groups = dict()
|
||||
self._arg_to_widgets = dict()
|
||||
|
@ -159,6 +151,23 @@ class _ArgumentEditor(QtWidgets.QTreeWidget):
|
|||
self._groups[name] = group
|
||||
return group
|
||||
|
||||
def update_argument(self, name, argument):
|
||||
widgets = self._arg_to_widgets[name]
|
||||
|
||||
# Qt needs a setItemWidget() to handle layout correctly,
|
||||
# simply replacing the entry inside the LayoutWidget
|
||||
# results in a bug.
|
||||
|
||||
widgets["entry"].deleteLater()
|
||||
widgets["entry"] = procdesc_to_entry(argument["desc"])(argument)
|
||||
widgets["disable_other_scans"].setVisible(
|
||||
isinstance(widgets["entry"], ScanEntry))
|
||||
widgets["fix_layout"].deleteLater()
|
||||
widgets["fix_layout"] = LayoutWidget()
|
||||
widgets["fix_layout"].addWidget(widgets["entry"])
|
||||
self.setItemWidget(widgets["widget_item"], 1, widgets["fix_layout"])
|
||||
self.updateGeometries()
|
||||
|
||||
def _recompute_argument_clicked(self, name):
|
||||
asyncio.ensure_future(self._recompute_argument(name))
|
||||
|
||||
|
@ -175,22 +184,7 @@ class _ArgumentEditor(QtWidgets.QTreeWidget):
|
|||
state = procdesc_to_entry(procdesc).default_state(procdesc)
|
||||
argument["desc"] = procdesc
|
||||
argument["state"] = state
|
||||
|
||||
# Qt needs a setItemWidget() to handle layout correctly,
|
||||
# simply replacing the entry inside the LayoutWidget
|
||||
# results in a bug.
|
||||
|
||||
widgets = self._arg_to_widgets[name]
|
||||
|
||||
widgets["entry"].deleteLater()
|
||||
widgets["entry"] = procdesc_to_entry(procdesc)(argument)
|
||||
widgets["disable_other_scans"].setVisible(
|
||||
isinstance(widgets["entry"], ScanEntry))
|
||||
widgets["fix_layout"].deleteLater()
|
||||
widgets["fix_layout"] = LayoutWidget()
|
||||
widgets["fix_layout"].addWidget(widgets["entry"])
|
||||
self.setItemWidget(widgets["widget_item"], 1, widgets["fix_layout"])
|
||||
self.updateGeometries()
|
||||
self.update_argument(name, argument)
|
||||
|
||||
def _disable_other_scans(self, current_name):
|
||||
for name, widgets in self._arg_to_widgets.items():
|
||||
|
@ -664,6 +658,20 @@ class ExperimentManager:
|
|||
self.submission_arguments[expurl] = arguments
|
||||
self.argument_ui_names[expurl] = ui_name
|
||||
return arguments
|
||||
|
||||
def set_argument_value(self, expurl, name, value):
|
||||
try:
|
||||
argument = self.submission_arguments[expurl][name]
|
||||
if argument["desc"]["ty"] == "Scannable":
|
||||
ty = value["ty"]
|
||||
argument["state"]["selected"] = ty
|
||||
argument["state"][ty] = value
|
||||
else:
|
||||
argument["state"] = value
|
||||
if expurl in self.open_experiments.keys():
|
||||
self.open_experiments[expurl].argeditor.update_argument(name, argument)
|
||||
except:
|
||||
logger.warn("Failed to set value for argument \"{}\" in experiment: {}.".format(name, expurl), exc_info=1)
|
||||
|
||||
def get_submission_arguments(self, expurl):
|
||||
if expurl in self.submission_arguments:
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
{
|
||||
"target": "kasli",
|
||||
"variant": "master",
|
||||
"hw_rev": "v2.0",
|
||||
"base": "master",
|
||||
"peripherals": [
|
||||
{
|
||||
"type": "shuttler",
|
||||
"ports": [0]
|
||||
},
|
||||
{
|
||||
"type": "dio",
|
||||
"ports": [1],
|
||||
"bank_direction_low": "input",
|
||||
"bank_direction_high": "output"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,330 @@
|
|||
from artiq.experiment import *
|
||||
from artiq.coredevice.shuttler import shuttler_volt_to_mu
|
||||
|
||||
DAC_Fs_MHZ = 125
|
||||
CORDIC_GAIN = 1.64676
|
||||
|
||||
@portable
|
||||
def shuttler_phase_offset(offset_degree):
|
||||
return round(offset_degree / 360 * (2 ** 16))
|
||||
|
||||
@portable
|
||||
def shuttler_freq_mu(freq_mhz):
|
||||
return round(float(2) ** 32 / DAC_Fs_MHZ * freq_mhz)
|
||||
|
||||
@portable
|
||||
def shuttler_chirp_rate_mu(freq_mhz_per_us):
|
||||
return round(float(2) ** 32 * freq_mhz_per_us / (DAC_Fs_MHZ ** 2))
|
||||
|
||||
@portable
|
||||
def shuttler_freq_sweep(start_f_MHz, end_f_MHz, time_us):
|
||||
return shuttler_chirp_rate_mu((end_f_MHz - start_f_MHz)/(time_us))
|
||||
|
||||
@portable
|
||||
def shuttler_volt_amp_mu(volt):
|
||||
return shuttler_volt_to_mu(volt)
|
||||
|
||||
@portable
|
||||
def shuttler_volt_damp_mu(volt_per_us):
|
||||
return round(float(2) ** 32 * (volt_per_us / 20) / DAC_Fs_MHZ)
|
||||
|
||||
@portable
|
||||
def shuttler_volt_ddamp_mu(volt_per_us_square):
|
||||
return round(float(2) ** 48 * (volt_per_us_square / 20) * 2 / (DAC_Fs_MHZ ** 2))
|
||||
|
||||
@portable
|
||||
def shuttler_volt_dddamp_mu(volt_per_us_cube):
|
||||
return round(float(2) ** 48 * (volt_per_us_cube / 20) * 6 / (DAC_Fs_MHZ ** 3))
|
||||
|
||||
@portable
|
||||
def shuttler_dds_amp_mu(volt):
|
||||
return shuttler_volt_amp_mu(volt / CORDIC_GAIN)
|
||||
|
||||
@portable
|
||||
def shuttler_dds_damp_mu(volt_per_us):
|
||||
return shuttler_volt_damp_mu(volt_per_us / CORDIC_GAIN)
|
||||
|
||||
@portable
|
||||
def shuttler_dds_ddamp_mu(volt_per_us_square):
|
||||
return shuttler_volt_ddamp_mu(volt_per_us_square / CORDIC_GAIN)
|
||||
|
||||
@portable
|
||||
def shuttler_dds_dddamp_mu(volt_per_us_cube):
|
||||
return shuttler_volt_dddamp_mu(volt_per_us_cube / CORDIC_GAIN)
|
||||
|
||||
class Shuttler(EnvExperiment):
|
||||
def build(self):
|
||||
self.setattr_device("core")
|
||||
self.setattr_device("core_dma")
|
||||
self.setattr_device("scheduler")
|
||||
self.shuttler0_leds = [ self.get_device("shuttler0_led{}".format(i)) for i in range(2) ]
|
||||
self.setattr_device("shuttler0_config")
|
||||
self.setattr_device("shuttler0_trigger")
|
||||
self.shuttler0_volt = [ self.get_device("shuttler0_volt{}".format(i)) for i in range(16) ]
|
||||
self.shuttler0_dds = [ self.get_device("shuttler0_dds{}".format(i)) for i in range(16) ]
|
||||
self.setattr_device("shuttler0_relay")
|
||||
self.setattr_device("shuttler0_adc")
|
||||
|
||||
|
||||
@kernel
|
||||
def record(self):
|
||||
with self.core_dma.record("example_waveform"):
|
||||
self.example_waveform()
|
||||
|
||||
@kernel
|
||||
def init(self):
|
||||
self.led()
|
||||
self.relay_init()
|
||||
self.adc_init()
|
||||
self.shuttler_reset()
|
||||
|
||||
@kernel
|
||||
def run(self):
|
||||
self.core.reset()
|
||||
self.core.break_realtime()
|
||||
self.init()
|
||||
|
||||
self.record()
|
||||
example_waveform_handle = self.core_dma.get_handle("example_waveform")
|
||||
|
||||
print("Example Waveforms are on OUT0 and OUT1")
|
||||
self.core.break_realtime()
|
||||
while not(self.scheduler.check_termination()):
|
||||
delay(1*s)
|
||||
self.core_dma.playback_handle(example_waveform_handle)
|
||||
|
||||
@kernel
|
||||
def shuttler_reset(self):
|
||||
for i in range(16):
|
||||
self.shuttler_channel_reset(i)
|
||||
# To avoid RTIO Underflow
|
||||
delay(50*us)
|
||||
|
||||
@kernel
|
||||
def shuttler_channel_reset(self, ch):
|
||||
self.shuttler0_volt[ch].set_waveform(
|
||||
a0=0,
|
||||
a1=0,
|
||||
a2=0,
|
||||
a3=0,
|
||||
)
|
||||
self.shuttler0_dds[ch].set_waveform(
|
||||
b0=0,
|
||||
b1=0,
|
||||
b2=0,
|
||||
b3=0,
|
||||
c0=0,
|
||||
c1=0,
|
||||
c2=0,
|
||||
)
|
||||
self.shuttler0_trigger.trigger(1 << ch)
|
||||
|
||||
@kernel
|
||||
def example_waveform(self):
|
||||
# Equation of Output Waveform
|
||||
# w(t_us) = a(t_us) + b(t_us) * cos(c(t_us))
|
||||
# Step 1:
|
||||
# Enable the Output Relay of OUT0 and OUT1
|
||||
# Step 2: Cosine Wave Frequency Sweep from 10kHz to 50kHz in 500us
|
||||
# OUT0: b(t_us) = 1
|
||||
# c(t_us) = 2 * pi * (0.08 * t_us ^ 2 + 0.01 * t_us)
|
||||
# OUT1: b(t_us) = 1
|
||||
# c(t_us) = 2 * pi * (0.05 * t_us)
|
||||
# Step 3(after 500us): Cosine Wave with 180 Degree Phase Offset
|
||||
# OUT0: b(t_us) = 1
|
||||
# c(t_us) = 2 * pi * (0.05 * t_us) + pi
|
||||
# OUT1: b(t_us) = 1
|
||||
# c(t_us) = 2 * pi * (0.05 * t_us)
|
||||
# Step 4(after 500us): Cosine Wave with Amplitude Envelop
|
||||
# OUT0: b(t_us) = -0.0001367187 * t_us ^ 2 + 0.06835937 * t_us
|
||||
# c(t_us) = 2 * pi * (0.05 * t_us)
|
||||
# OUT1: b(t_us) = -0.0001367187 * t_us ^ 2 + 0.06835937 * t_us
|
||||
# c(t_us) = 0
|
||||
# Step 5(after 500us): Sawtooth Wave Modulated with 50kHz Cosine Wave
|
||||
# OUT0: a(t_us) = 0.01 * t_us - 5
|
||||
# b(t_us) = 1
|
||||
# c(t_us) = 2 * pi * (0.05 * t_us)
|
||||
# OUT1: a(t_us) = 0.01 * t_us - 5
|
||||
# Step 6(after 1000us): A Combination of Previous Waveforms
|
||||
# OUT0: a(t_us) = 0.01 * t_us - 5
|
||||
# b(t_us) = -0.0001367187 * t_us ^ 2 + 0.06835937 * t_us
|
||||
# c(t_us) = 2 * pi * (0.08 * t_us ^ 2 + 0.01 * t_us)
|
||||
# Step 7(after 500us): Mirrored Waveform in Step 6
|
||||
# OUT0: a(t_us) = 2.5 + -0.01 * (1000 ^ 2) * t_us
|
||||
# b(t_us) = 0.0001367187 * t_us ^ 2 - 0.06835937 * t_us
|
||||
# c(t_us) = 2 * pi * (-0.08 * t_us ^ 2 + 0.05 * t_us) + pi
|
||||
# Step 8(after 500us):
|
||||
# Disable Output Relay of OUT0 and OUT1
|
||||
# Reset OUT0 and OUT1
|
||||
|
||||
## Step 1 ##
|
||||
self.shuttler0_relay.enable(0b11)
|
||||
|
||||
## Step 2 ##
|
||||
start_f_MHz = 0.01
|
||||
end_f_MHz = 0.05
|
||||
duration_us = 500
|
||||
# OUT0 and OUT1 have their frequency and phase aligned at 500us
|
||||
self.shuttler0_dds[0].set_waveform(
|
||||
b0=shuttler_dds_amp_mu(1.0),
|
||||
b1=0,
|
||||
b2=0,
|
||||
b3=0,
|
||||
c0=0,
|
||||
c1=shuttler_freq_mu(start_f_MHz),
|
||||
c2=shuttler_freq_sweep(start_f_MHz, end_f_MHz, duration_us),
|
||||
)
|
||||
self.shuttler0_dds[1].set_waveform(
|
||||
b0=shuttler_dds_amp_mu(1.0),
|
||||
b1=0,
|
||||
b2=0,
|
||||
b3=0,
|
||||
c0=0,
|
||||
c1=shuttler_freq_mu(end_f_MHz),
|
||||
c2=0,
|
||||
)
|
||||
self.shuttler0_trigger.trigger(0b11)
|
||||
delay(500*us)
|
||||
|
||||
## Step 3 ##
|
||||
# OUT0 and OUT1 has 180 degree phase difference
|
||||
self.shuttler0_dds[0].set_waveform(
|
||||
b0=shuttler_dds_amp_mu(1.0),
|
||||
b1=0,
|
||||
b2=0,
|
||||
b3=0,
|
||||
c0=shuttler_phase_offset(180.0),
|
||||
c1=shuttler_freq_mu(end_f_MHz),
|
||||
c2=0,
|
||||
)
|
||||
# Phase and Output Setting of OUT1 is retained
|
||||
# if the channel is not triggered or config is not cleared
|
||||
self.shuttler0_trigger.trigger(0b1)
|
||||
delay(500*us)
|
||||
|
||||
## Step 4 ##
|
||||
# b(0) = 0, b(250) = 8.545, b(500) = 0
|
||||
self.shuttler0_dds[0].set_waveform(
|
||||
b0=0,
|
||||
b1=shuttler_dds_damp_mu(0.06835937),
|
||||
b2=shuttler_dds_ddamp_mu(-0.0001367187),
|
||||
b3=0,
|
||||
c0=0,
|
||||
c1=shuttler_freq_mu(end_f_MHz),
|
||||
c2=0,
|
||||
)
|
||||
self.shuttler0_dds[1].set_waveform(
|
||||
b0=0,
|
||||
b1=shuttler_dds_damp_mu(0.06835937),
|
||||
b2=shuttler_dds_ddamp_mu(-0.0001367187),
|
||||
b3=0,
|
||||
c0=0,
|
||||
c1=0,
|
||||
c2=0,
|
||||
)
|
||||
self.shuttler0_trigger.trigger(0b11)
|
||||
delay(500*us)
|
||||
|
||||
## Step 5 ##
|
||||
self.shuttler0_volt[0].set_waveform(
|
||||
a0=shuttler_volt_amp_mu(-5.0),
|
||||
a1=int32(shuttler_volt_damp_mu(0.01)),
|
||||
a2=0,
|
||||
a3=0,
|
||||
)
|
||||
self.shuttler0_dds[0].set_waveform(
|
||||
b0=shuttler_dds_amp_mu(1.0),
|
||||
b1=0,
|
||||
b2=0,
|
||||
b3=0,
|
||||
c0=0,
|
||||
c1=shuttler_freq_mu(end_f_MHz),
|
||||
c2=0,
|
||||
)
|
||||
self.shuttler0_volt[1].set_waveform(
|
||||
a0=shuttler_volt_amp_mu(-5.0),
|
||||
a1=int32(shuttler_volt_damp_mu(0.01)),
|
||||
a2=0,
|
||||
a3=0,
|
||||
)
|
||||
self.shuttler0_dds[1].set_waveform(
|
||||
b0=0,
|
||||
b1=0,
|
||||
b2=0,
|
||||
b3=0,
|
||||
c0=0,
|
||||
c1=0,
|
||||
c2=0,
|
||||
)
|
||||
self.shuttler0_trigger.trigger(0b11)
|
||||
delay(1000*us)
|
||||
|
||||
## Step 6 ##
|
||||
self.shuttler0_volt[0].set_waveform(
|
||||
a0=shuttler_volt_amp_mu(-2.5),
|
||||
a1=int32(shuttler_volt_damp_mu(0.01)),
|
||||
a2=0,
|
||||
a3=0,
|
||||
)
|
||||
self.shuttler0_dds[0].set_waveform(
|
||||
b0=0,
|
||||
b1=shuttler_dds_damp_mu(0.06835937),
|
||||
b2=shuttler_dds_ddamp_mu(-0.0001367187),
|
||||
b3=0,
|
||||
c0=0,
|
||||
c1=shuttler_freq_mu(start_f_MHz),
|
||||
c2=shuttler_freq_sweep(start_f_MHz, end_f_MHz, duration_us),
|
||||
)
|
||||
self.shuttler0_trigger.trigger(0b1)
|
||||
self.shuttler_channel_reset(1)
|
||||
delay(500*us)
|
||||
|
||||
## Step 7 ##
|
||||
self.shuttler0_volt[0].set_waveform(
|
||||
a0=shuttler_volt_amp_mu(2.5),
|
||||
a1=int32(shuttler_volt_damp_mu(-0.01)),
|
||||
a2=0,
|
||||
a3=0,
|
||||
)
|
||||
self.shuttler0_dds[0].set_waveform(
|
||||
b0=0,
|
||||
b1=shuttler_dds_damp_mu(-0.06835937),
|
||||
b2=shuttler_dds_ddamp_mu(0.0001367187),
|
||||
b3=0,
|
||||
c0=shuttler_phase_offset(180.0),
|
||||
c1=shuttler_freq_mu(end_f_MHz),
|
||||
c2=shuttler_freq_sweep(end_f_MHz, start_f_MHz, duration_us),
|
||||
)
|
||||
self.shuttler0_trigger.trigger(0b1)
|
||||
delay(500*us)
|
||||
|
||||
## Step 8 ##
|
||||
self.shuttler0_relay.enable(0)
|
||||
self.shuttler_channel_reset(0)
|
||||
self.shuttler_channel_reset(1)
|
||||
|
||||
@kernel
|
||||
def led(self):
|
||||
for i in range(2):
|
||||
for j in range(3):
|
||||
self.shuttler0_leds[i].pulse(.1*s)
|
||||
delay(.1*s)
|
||||
|
||||
@kernel
|
||||
def relay_init(self):
|
||||
self.shuttler0_relay.init()
|
||||
self.shuttler0_relay.enable(0x0000)
|
||||
|
||||
@kernel
|
||||
def adc_init(self):
|
||||
delay_mu(int64(self.core.ref_multiplier))
|
||||
self.shuttler0_adc.power_up()
|
||||
|
||||
delay_mu(int64(self.core.ref_multiplier))
|
||||
assert self.shuttler0_adc.read_id() >> 4 == 0x038d
|
||||
|
||||
delay_mu(int64(self.core.ref_multiplier))
|
||||
# The actual output voltage is limited by the hardware, the calculated calibration gain and offset.
|
||||
# For example, if the system has a calibration gain of 1.06, then the max output voltage = 10 / 1.06 = 9.43V.
|
||||
# Setting a value larger than 9.43V will result in overflow.
|
||||
self.shuttler0_adc.calibrate(self.shuttler0_volt, self.shuttler0_trigger, self.shuttler0_config)
|
|
@ -4,13 +4,13 @@ from artiq.applets.simple import SimpleApplet
|
|||
|
||||
|
||||
class DemoWidget(QtWidgets.QLabel):
|
||||
def __init__(self, args):
|
||||
def __init__(self, args, ctl):
|
||||
QtWidgets.QLabel.__init__(self)
|
||||
self.dataset_name = args.dataset
|
||||
|
||||
def data_changed(self, data, mods):
|
||||
def data_changed(self, value, metadata, persist, mods):
|
||||
try:
|
||||
n = str(data[self.dataset_name][1])
|
||||
n = str(value[self.dataset_name])
|
||||
except (KeyError, ValueError, TypeError):
|
||||
n = "---"
|
||||
n = "<font size=15>" + n + "</font>"
|
||||
|
|
|
@ -7,6 +7,8 @@ const QRCML_REG : u8 = 0x08;
|
|||
const CLKMODE_REG : u8 = 0x14;
|
||||
const VERSION_REG : u8 = 0x1F;
|
||||
|
||||
const RETIMER_CLK_PHASE : u8 = 0b11;
|
||||
|
||||
fn hard_reset() {
|
||||
unsafe {
|
||||
// Min Pulse Width: 50ns
|
||||
|
@ -51,12 +53,17 @@ pub fn init() -> Result<(), &'static str> {
|
|||
debug!("DAC AD9117 Channel {} has incorrect hardware version. VERSION reg: {:02x}", channel, reg);
|
||||
return Err("DAC AD9117 hardware version is not equal to 0x0A");
|
||||
}
|
||||
// Check for the presence of DCLKIO and CLKIN
|
||||
let reg = read(channel, CLKMODE_REG)?;
|
||||
if reg >> 4 & 1 != 0 {
|
||||
debug!("DAC AD9117 Channel {} retiming fails. CLKMODE reg: {:02x}", channel, reg);
|
||||
return Err("DAC AD9117 retiming failure");
|
||||
}
|
||||
|
||||
// Force RETIMER-CLK to be Phase 1 as DCLKIO and CLKIN is known to be safe at Phase 1
|
||||
// See Issue #2200
|
||||
write(channel, CLKMODE_REG, RETIMER_CLK_PHASE << 6 | 1 << 2 | RETIMER_CLK_PHASE)?;
|
||||
|
||||
// Set the DACs input data format to be twos complement
|
||||
// Set IFIRST and IRISING to True
|
||||
write(channel, DATA_CTRL_REG, 1 << 7 | 1 << 5 | 1 << 4)?;
|
||||
|
|
|
@ -52,16 +52,18 @@ pub mod remote_analyzer {
|
|||
pub data: Vec<u8>
|
||||
}
|
||||
|
||||
pub fn get_data(io: &Io, aux_mutex: &Mutex, ddma_mutex: &Mutex,
|
||||
routing_table: &drtio_routing::RoutingTable,
|
||||
up_destinations: &Urc<RefCell<[bool; drtio_routing::DEST_COUNT]>>) -> Result<RemoteBuffer, &'static str> {
|
||||
pub fn get_data(io: &Io, aux_mutex: &Mutex, routing_table: &drtio_routing::RoutingTable,
|
||||
up_destinations: &Urc<RefCell<[bool; drtio_routing::DEST_COUNT]>>
|
||||
) -> Result<RemoteBuffer, &'static str> {
|
||||
// gets data from satellites and returns consolidated data
|
||||
let mut remote_data: Vec<u8> = Vec::new();
|
||||
let mut remote_overflow = false;
|
||||
let mut remote_sent_bytes = 0;
|
||||
let mut remote_total_bytes = 0;
|
||||
|
||||
let data_vec = match drtio::analyzer_query(io, aux_mutex, ddma_mutex, routing_table, up_destinations) {
|
||||
let data_vec = match drtio::analyzer_query(
|
||||
io, aux_mutex, routing_table, up_destinations
|
||||
) {
|
||||
Ok(data_vec) => data_vec,
|
||||
Err(e) => return Err(e)
|
||||
};
|
||||
|
@ -83,7 +85,7 @@ pub mod remote_analyzer {
|
|||
|
||||
|
||||
|
||||
fn worker(stream: &mut TcpStream, _io: &Io, _aux_mutex: &Mutex, _ddma_mutex: &Mutex,
|
||||
fn worker(stream: &mut TcpStream, _io: &Io, _aux_mutex: &Mutex,
|
||||
_routing_table: &drtio_routing::RoutingTable,
|
||||
_up_destinations: &Urc<RefCell<[bool; drtio_routing::DEST_COUNT]>>
|
||||
) -> Result<(), IoError<SchedError>> {
|
||||
|
@ -97,7 +99,7 @@ fn worker(stream: &mut TcpStream, _io: &Io, _aux_mutex: &Mutex, _ddma_mutex: &Mu
|
|||
|
||||
#[cfg(has_drtio)]
|
||||
let remote = remote_analyzer::get_data(
|
||||
_io, _aux_mutex, _ddma_mutex, _routing_table, _up_destinations);
|
||||
_io, _aux_mutex, _routing_table, _up_destinations);
|
||||
#[cfg(has_drtio)]
|
||||
let (header, remote_data) = match remote {
|
||||
Ok(remote) => (Header {
|
||||
|
@ -144,7 +146,7 @@ fn worker(stream: &mut TcpStream, _io: &Io, _aux_mutex: &Mutex, _ddma_mutex: &Mu
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub fn thread(io: Io, aux_mutex: &Mutex, ddma_mutex: &Mutex,
|
||||
pub fn thread(io: Io, aux_mutex: &Mutex,
|
||||
routing_table: &Urc<RefCell<drtio_routing::RoutingTable>>,
|
||||
up_destinations: &Urc<RefCell<[bool; drtio_routing::DEST_COUNT]>>) {
|
||||
let listener = TcpListener::new(&io, 65535);
|
||||
|
@ -159,7 +161,7 @@ pub fn thread(io: Io, aux_mutex: &Mutex, ddma_mutex: &Mutex,
|
|||
disarm();
|
||||
|
||||
let routing_table = routing_table.borrow();
|
||||
match worker(&mut stream, &io, aux_mutex, ddma_mutex, &routing_table, up_destinations) {
|
||||
match worker(&mut stream, &io, aux_mutex, &routing_table, up_destinations) {
|
||||
Ok(()) => (),
|
||||
Err(err) => error!("analyzer aborted: {}", err)
|
||||
}
|
||||
|
|
|
@ -14,11 +14,14 @@ mod remote_i2c {
|
|||
use rtio_mgt::drtio;
|
||||
use sched::{Io, Mutex};
|
||||
|
||||
pub fn start(io: &Io, aux_mutex: &Mutex, ddma_mutex: &Mutex, linkno: u8, destination: u8, busno: u8) -> Result<(), &'static str> {
|
||||
let reply = drtio::aux_transact(io, aux_mutex, ddma_mutex, linkno, &drtioaux::Packet::I2cStartRequest {
|
||||
destination: destination,
|
||||
busno: busno
|
||||
});
|
||||
pub fn start(io: &Io, aux_mutex: &Mutex,
|
||||
linkno: u8, destination: u8, busno: u8
|
||||
) -> Result<(), &'static str> {
|
||||
let reply = drtio::aux_transact(io, aux_mutex, linkno,
|
||||
&drtioaux::Packet::I2cStartRequest {
|
||||
destination: destination,
|
||||
busno: busno
|
||||
});
|
||||
match reply {
|
||||
Ok(drtioaux::Packet::I2cBasicReply { succeeded }) => {
|
||||
if succeeded { Ok(()) } else { Err("i2c basic reply error") }
|
||||
|
@ -34,11 +37,14 @@ mod remote_i2c {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn restart(io: &Io, aux_mutex: &Mutex, ddma_mutex: &Mutex, linkno: u8, destination: u8, busno: u8) -> Result<(), &'static str> {
|
||||
let reply = drtio::aux_transact(io, aux_mutex, ddma_mutex, linkno, &drtioaux::Packet::I2cRestartRequest {
|
||||
destination: destination,
|
||||
busno: busno
|
||||
});
|
||||
pub fn restart(io: &Io, aux_mutex: &Mutex,
|
||||
linkno: u8, destination: u8, busno: u8
|
||||
) -> Result<(), &'static str> {
|
||||
let reply = drtio::aux_transact(io, aux_mutex, linkno,
|
||||
&drtioaux::Packet::I2cRestartRequest {
|
||||
destination: destination,
|
||||
busno: busno
|
||||
});
|
||||
match reply {
|
||||
Ok(drtioaux::Packet::I2cBasicReply { succeeded }) => {
|
||||
if succeeded { Ok(()) } else { Err("i2c basic reply error") }
|
||||
|
@ -54,11 +60,14 @@ mod remote_i2c {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn stop(io: &Io, aux_mutex: &Mutex, ddma_mutex: &Mutex, linkno: u8, destination: u8, busno: u8) -> Result<(), &'static str> {
|
||||
let reply = drtio::aux_transact(io, aux_mutex, ddma_mutex, linkno, &drtioaux::Packet::I2cStopRequest {
|
||||
destination: destination,
|
||||
busno: busno
|
||||
});
|
||||
pub fn stop(io: &Io, aux_mutex: &Mutex,
|
||||
linkno: u8, destination: u8, busno: u8
|
||||
) -> Result<(), &'static str> {
|
||||
let reply = drtio::aux_transact(io, aux_mutex, linkno,
|
||||
&drtioaux::Packet::I2cStopRequest {
|
||||
destination: destination,
|
||||
busno: busno
|
||||
});
|
||||
match reply {
|
||||
Ok(drtioaux::Packet::I2cBasicReply { succeeded }) => {
|
||||
if succeeded { Ok(()) } else { Err("i2c basic reply error") }
|
||||
|
@ -74,12 +83,15 @@ mod remote_i2c {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn write(io: &Io, aux_mutex: &Mutex, ddma_mutex: &Mutex, linkno: u8, destination: u8, busno: u8, data: u8) -> Result<bool, &'static str> {
|
||||
let reply = drtio::aux_transact(io, aux_mutex, ddma_mutex, linkno, &drtioaux::Packet::I2cWriteRequest {
|
||||
destination: destination,
|
||||
busno: busno,
|
||||
data: data
|
||||
});
|
||||
pub fn write(io: &Io, aux_mutex: &Mutex,
|
||||
linkno: u8, destination: u8, busno: u8, data: u8
|
||||
) -> Result<bool, &'static str> {
|
||||
let reply = drtio::aux_transact(io, aux_mutex, linkno,
|
||||
&drtioaux::Packet::I2cWriteRequest {
|
||||
destination: destination,
|
||||
busno: busno,
|
||||
data: data
|
||||
});
|
||||
match reply {
|
||||
Ok(drtioaux::Packet::I2cWriteReply { succeeded, ack }) => {
|
||||
if succeeded { Ok(ack) } else { Err("i2c write reply error") }
|
||||
|
@ -95,12 +107,15 @@ mod remote_i2c {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn read(io: &Io, aux_mutex: &Mutex, ddma_mutex: &Mutex, linkno: u8, destination: u8, busno: u8, ack: bool) -> Result<u8, &'static str> {
|
||||
let reply = drtio::aux_transact(io, aux_mutex, ddma_mutex, linkno, &drtioaux::Packet::I2cReadRequest {
|
||||
destination: destination,
|
||||
busno: busno,
|
||||
ack: ack
|
||||
});
|
||||
pub fn read(io: &Io, aux_mutex: &Mutex,
|
||||
linkno: u8, destination: u8, busno: u8, ack: bool
|
||||
) -> Result<u8, &'static str> {
|
||||
let reply = drtio::aux_transact(io, aux_mutex, linkno,
|
||||
&drtioaux::Packet::I2cReadRequest {
|
||||
destination: destination,
|
||||
busno: busno,
|
||||
ack: ack
|
||||
});
|
||||
match reply {
|
||||
Ok(drtioaux::Packet::I2cReadReply { succeeded, data }) => {
|
||||
if succeeded { Ok(data) } else { Err("i2c read reply error") }
|
||||
|
@ -116,13 +131,16 @@ mod remote_i2c {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn switch_select(io: &Io, aux_mutex: &Mutex, ddma_mutex: &Mutex, linkno: u8, destination: u8, busno: u8, address: u8, mask: u8) -> Result<(), &'static str> {
|
||||
let reply = drtio::aux_transact(io, aux_mutex, ddma_mutex, linkno, &drtioaux::Packet::I2cSwitchSelectRequest {
|
||||
destination: destination,
|
||||
busno: busno,
|
||||
address: address,
|
||||
mask: mask,
|
||||
});
|
||||
pub fn switch_select(io: &Io, aux_mutex: &Mutex,
|
||||
linkno: u8, destination: u8, busno: u8, address: u8, mask: u8
|
||||
) -> Result<(), &'static str> {
|
||||
let reply = drtio::aux_transact(io, aux_mutex, linkno,
|
||||
&drtioaux::Packet::I2cSwitchSelectRequest {
|
||||
destination: destination,
|
||||
busno: busno,
|
||||
address: address,
|
||||
mask: mask,
|
||||
});
|
||||
match reply {
|
||||
Ok(drtioaux::Packet::I2cBasicReply { succeeded }) => {
|
||||
if succeeded { Ok(()) } else { Err("i2c basic reply error") }
|
||||
|
@ -145,8 +163,10 @@ mod remote_spi {
|
|||
use rtio_mgt::drtio;
|
||||
use sched::{Io, Mutex};
|
||||
|
||||
pub fn set_config(io: &Io, aux_mutex: &Mutex, ddma_mutex: &Mutex, linkno: u8, destination: u8, busno: u8, flags: u8, length: u8, div: u8, cs: u8) -> Result<(), ()> {
|
||||
let reply = drtio::aux_transact(io, aux_mutex, ddma_mutex, linkno, &drtioaux::Packet::SpiSetConfigRequest {
|
||||
pub fn set_config(io: &Io, aux_mutex: &Mutex,
|
||||
linkno: u8, destination: u8, busno: u8, flags: u8, length: u8, div: u8, cs: u8
|
||||
) -> Result<(), ()> {
|
||||
let reply = drtio::aux_transact(io, aux_mutex, linkno, &drtioaux::Packet::SpiSetConfigRequest {
|
||||
destination: destination,
|
||||
busno: busno,
|
||||
flags: flags,
|
||||
|
@ -169,8 +189,10 @@ mod remote_spi {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn write(io: &Io, aux_mutex: &Mutex, ddma_mutex: &Mutex, linkno: u8, destination: u8, busno: u8, data: u32) -> Result<(), ()> {
|
||||
let reply = drtio::aux_transact(io, aux_mutex, ddma_mutex, linkno, &drtioaux::Packet::SpiWriteRequest {
|
||||
pub fn write(io: &Io, aux_mutex: &Mutex,
|
||||
linkno: u8, destination: u8, busno: u8, data: u32
|
||||
) -> Result<(), ()> {
|
||||
let reply = drtio::aux_transact(io, aux_mutex, linkno, &drtioaux::Packet::SpiWriteRequest {
|
||||
destination: destination,
|
||||
busno: busno,
|
||||
data: data
|
||||
|
@ -190,11 +212,13 @@ mod remote_spi {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn read(io: &Io, aux_mutex: &Mutex, ddma_mutex: &Mutex, linkno: u8, destination: u8, busno: u8) -> Result<u32, ()> {
|
||||
let reply = drtio::aux_transact(io, aux_mutex, ddma_mutex, linkno, &drtioaux::Packet::SpiReadRequest {
|
||||
destination: destination,
|
||||
busno: busno
|
||||
});
|
||||
pub fn read(io: &Io, aux_mutex: &Mutex, linkno: u8, destination: u8, busno: u8
|
||||
) -> Result<u32, ()> {
|
||||
let reply = drtio::aux_transact(io, aux_mutex, linkno,
|
||||
&drtioaux::Packet::SpiReadRequest {
|
||||
destination: destination,
|
||||
busno: busno
|
||||
});
|
||||
match reply {
|
||||
Ok(drtioaux::Packet::SpiReadReply { succeeded, data }) => {
|
||||
if succeeded { Ok(data) } else { Err(()) }
|
||||
|
@ -214,7 +238,7 @@ mod remote_spi {
|
|||
|
||||
#[cfg(has_drtio)]
|
||||
macro_rules! dispatch {
|
||||
($io:ident, $aux_mutex:ident, $ddma_mutex:ident, $mod_local:ident, $mod_remote:ident, $routing_table:ident, $busno:expr, $func:ident $(, $param:expr)*) => {{
|
||||
($io:ident, $aux_mutex:ident, $mod_local:ident, $mod_remote:ident, $routing_table:ident, $busno:expr, $func:ident $(, $param:expr)*) => {{
|
||||
let destination = ($busno >> 16) as u8;
|
||||
let busno = $busno as u8;
|
||||
let hop = $routing_table.0[destination as usize][0];
|
||||
|
@ -222,28 +246,27 @@ macro_rules! dispatch {
|
|||
$mod_local::$func(busno, $($param, )*)
|
||||
} else {
|
||||
let linkno = hop - 1;
|
||||
$mod_remote::$func($io, $aux_mutex, $ddma_mutex, linkno, destination, busno, $($param, )*)
|
||||
$mod_remote::$func($io, $aux_mutex, linkno, destination, busno, $($param, )*)
|
||||
}
|
||||
}}
|
||||
}
|
||||
|
||||
#[cfg(not(has_drtio))]
|
||||
macro_rules! dispatch {
|
||||
($io:ident, $aux_mutex:ident, $ddma_mutex:ident, $mod_local:ident, $mod_remote:ident, $routing_table:ident, $busno:expr, $func:ident $(, $param:expr)*) => {{
|
||||
($io:ident, $aux_mutex:ident, $mod_local:ident, $mod_remote:ident, $routing_table:ident, $busno:expr, $func:ident $(, $param:expr)*) => {{
|
||||
let busno = $busno as u8;
|
||||
$mod_local::$func(busno, $($param, )*)
|
||||
}}
|
||||
}
|
||||
|
||||
pub fn process_kern_hwreq(io: &Io, aux_mutex: &Mutex,
|
||||
ddma_mutex: &Mutex,
|
||||
_routing_table: &drtio_routing::RoutingTable,
|
||||
_up_destinations: &Urc<RefCell<[bool; drtio_routing::DEST_COUNT]>>,
|
||||
request: &kern::Message) -> Result<bool, Error<SchedError>> {
|
||||
match request {
|
||||
&kern::RtioInitRequest => {
|
||||
info!("resetting RTIO");
|
||||
rtio_mgt::reset(io, aux_mutex, ddma_mutex);
|
||||
rtio_mgt::reset(io, aux_mutex);
|
||||
kern_acknowledge()
|
||||
}
|
||||
|
||||
|
@ -259,47 +282,47 @@ pub fn process_kern_hwreq(io: &Io, aux_mutex: &Mutex,
|
|||
}
|
||||
|
||||
&kern::I2cStartRequest { busno } => {
|
||||
let succeeded = dispatch!(io, aux_mutex, ddma_mutex, local_i2c, remote_i2c, _routing_table, busno, start).is_ok();
|
||||
let succeeded = dispatch!(io, aux_mutex, local_i2c, remote_i2c, _routing_table, busno, start).is_ok();
|
||||
kern_send(io, &kern::I2cBasicReply { succeeded: succeeded })
|
||||
}
|
||||
&kern::I2cRestartRequest { busno } => {
|
||||
let succeeded = dispatch!(io, aux_mutex, ddma_mutex, local_i2c, remote_i2c, _routing_table, busno, restart).is_ok();
|
||||
let succeeded = dispatch!(io, aux_mutex, local_i2c, remote_i2c, _routing_table, busno, restart).is_ok();
|
||||
kern_send(io, &kern::I2cBasicReply { succeeded: succeeded })
|
||||
}
|
||||
&kern::I2cStopRequest { busno } => {
|
||||
let succeeded = dispatch!(io, aux_mutex, ddma_mutex, local_i2c, remote_i2c, _routing_table, busno, stop).is_ok();
|
||||
let succeeded = dispatch!(io, aux_mutex, local_i2c, remote_i2c, _routing_table, busno, stop).is_ok();
|
||||
kern_send(io, &kern::I2cBasicReply { succeeded: succeeded })
|
||||
}
|
||||
&kern::I2cWriteRequest { busno, data } => {
|
||||
match dispatch!(io, aux_mutex, ddma_mutex, local_i2c, remote_i2c, _routing_table, busno, write, data) {
|
||||
match dispatch!(io, aux_mutex, local_i2c, remote_i2c, _routing_table, busno, write, data) {
|
||||
Ok(ack) => kern_send(io, &kern::I2cWriteReply { succeeded: true, ack: ack }),
|
||||
Err(_) => kern_send(io, &kern::I2cWriteReply { succeeded: false, ack: false })
|
||||
}
|
||||
}
|
||||
&kern::I2cReadRequest { busno, ack } => {
|
||||
match dispatch!(io, aux_mutex, ddma_mutex, local_i2c, remote_i2c, _routing_table, busno, read, ack) {
|
||||
match dispatch!(io, aux_mutex, local_i2c, remote_i2c, _routing_table, busno, read, ack) {
|
||||
Ok(data) => kern_send(io, &kern::I2cReadReply { succeeded: true, data: data }),
|
||||
Err(_) => kern_send(io, &kern::I2cReadReply { succeeded: false, data: 0xff })
|
||||
}
|
||||
}
|
||||
&kern::I2cSwitchSelectRequest { busno, address, mask } => {
|
||||
let succeeded = dispatch!(io, aux_mutex, ddma_mutex, local_i2c, remote_i2c, _routing_table, busno,
|
||||
let succeeded = dispatch!(io, aux_mutex, local_i2c, remote_i2c, _routing_table, busno,
|
||||
switch_select, address, mask).is_ok();
|
||||
kern_send(io, &kern::I2cBasicReply { succeeded: succeeded })
|
||||
}
|
||||
|
||||
&kern::SpiSetConfigRequest { busno, flags, length, div, cs } => {
|
||||
let succeeded = dispatch!(io, aux_mutex, ddma_mutex, local_spi, remote_spi, _routing_table, busno,
|
||||
let succeeded = dispatch!(io, aux_mutex, local_spi, remote_spi, _routing_table, busno,
|
||||
set_config, flags, length, div, cs).is_ok();
|
||||
kern_send(io, &kern::SpiBasicReply { succeeded: succeeded })
|
||||
},
|
||||
&kern::SpiWriteRequest { busno, data } => {
|
||||
let succeeded = dispatch!(io, aux_mutex, ddma_mutex, local_spi, remote_spi, _routing_table, busno,
|
||||
let succeeded = dispatch!(io, aux_mutex, local_spi, remote_spi, _routing_table, busno,
|
||||
write, data).is_ok();
|
||||
kern_send(io, &kern::SpiBasicReply { succeeded: succeeded })
|
||||
}
|
||||
&kern::SpiReadRequest { busno } => {
|
||||
match dispatch!(io, aux_mutex, ddma_mutex, local_spi, remote_spi, _routing_table, busno, read) {
|
||||
match dispatch!(io, aux_mutex, local_spi, remote_spi, _routing_table, busno, read) {
|
||||
Ok(data) => kern_send(io, &kern::SpiReadReply { succeeded: true, data: data }),
|
||||
Err(_) => kern_send(io, &kern::SpiReadReply { succeeded: false, data: 0 })
|
||||
}
|
||||
|
|
|
@ -210,17 +210,15 @@ fn startup() {
|
|||
#[cfg(any(has_rtio_moninj, has_drtio))]
|
||||
{
|
||||
let aux_mutex = aux_mutex.clone();
|
||||
let ddma_mutex = ddma_mutex.clone();
|
||||
let drtio_routing_table = drtio_routing_table.clone();
|
||||
io.spawn(4096, move |io| { moninj::thread(io, &aux_mutex, &ddma_mutex, &drtio_routing_table) });
|
||||
io.spawn(4096, move |io| { moninj::thread(io, &aux_mutex, &drtio_routing_table) });
|
||||
}
|
||||
#[cfg(has_rtio_analyzer)]
|
||||
{
|
||||
let aux_mutex = aux_mutex.clone();
|
||||
let drtio_routing_table = drtio_routing_table.clone();
|
||||
let up_destinations = up_destinations.clone();
|
||||
let ddma_mutex = ddma_mutex.clone();
|
||||
io.spawn(8192, move |io| { analyzer::thread(io, &aux_mutex, &ddma_mutex, &drtio_routing_table, &up_destinations) });
|
||||
io.spawn(8192, move |io| { analyzer::thread(io, &aux_mutex, &drtio_routing_table, &up_destinations) });
|
||||
}
|
||||
|
||||
#[cfg(has_grabber)]
|
||||
|
|
|
@ -53,12 +53,14 @@ mod remote_moninj {
|
|||
use rtio_mgt::drtio;
|
||||
use sched::{Io, Mutex};
|
||||
|
||||
pub fn read_probe(io: &Io, aux_mutex: &Mutex, ddma_mutex: &Mutex, linkno: u8, destination: u8, channel: u16, probe: u8) -> u64 {
|
||||
let reply = drtio::aux_transact(io, aux_mutex, ddma_mutex, linkno, &drtioaux::Packet::MonitorRequest {
|
||||
destination: destination,
|
||||
channel: channel,
|
||||
probe: probe
|
||||
});
|
||||
pub fn read_probe(io: &Io, aux_mutex: &Mutex, linkno: u8,
|
||||
destination: u8, channel: u16, probe: u8) -> u64 {
|
||||
let reply = drtio::aux_transact(io, aux_mutex, linkno,
|
||||
&drtioaux::Packet::MonitorRequest {
|
||||
destination: destination,
|
||||
channel: channel,
|
||||
probe: probe
|
||||
});
|
||||
match reply {
|
||||
Ok(drtioaux::Packet::MonitorReply { value }) => return value,
|
||||
Ok(packet) => error!("received unexpected aux packet: {:?}", packet),
|
||||
|
@ -67,7 +69,8 @@ mod remote_moninj {
|
|||
0
|
||||
}
|
||||
|
||||
pub fn inject(io: &Io, aux_mutex: &Mutex, _ddma_mutex: &Mutex, linkno: u8, destination: u8, channel: u16, overrd: u8, value: u8) {
|
||||
pub fn inject(io: &Io, aux_mutex: &Mutex, linkno: u8,
|
||||
destination: u8, channel: u16, overrd: u8, value: u8) {
|
||||
let _lock = aux_mutex.lock(io).unwrap();
|
||||
drtioaux::send(linkno, &drtioaux::Packet::InjectionRequest {
|
||||
destination: destination,
|
||||
|
@ -77,12 +80,14 @@ mod remote_moninj {
|
|||
}).unwrap();
|
||||
}
|
||||
|
||||
pub fn read_injection_status(io: &Io, aux_mutex: &Mutex, ddma_mutex: &Mutex, linkno: u8, destination: u8, channel: u16, overrd: u8) -> u8 {
|
||||
let reply = drtio::aux_transact(io, aux_mutex, ddma_mutex, linkno, &drtioaux::Packet::InjectionStatusRequest {
|
||||
destination: destination,
|
||||
channel: channel,
|
||||
overrd: overrd
|
||||
});
|
||||
pub fn read_injection_status(io: &Io, aux_mutex: &Mutex, linkno: u8,
|
||||
destination: u8, channel: u16, overrd: u8) -> u8 {
|
||||
let reply = drtio::aux_transact(io, aux_mutex, linkno,
|
||||
&drtioaux::Packet::InjectionStatusRequest {
|
||||
destination: destination,
|
||||
channel: channel,
|
||||
overrd: overrd
|
||||
});
|
||||
match reply {
|
||||
Ok(drtioaux::Packet::InjectionStatusReply { value }) => return value,
|
||||
Ok(packet) => error!("received unexpected aux packet: {:?}", packet),
|
||||
|
@ -94,7 +99,7 @@ mod remote_moninj {
|
|||
|
||||
#[cfg(has_drtio)]
|
||||
macro_rules! dispatch {
|
||||
($io:ident, $aux_mutex:ident, $ddma_mutex:ident, $routing_table:ident, $channel:expr, $func:ident $(, $param:expr)*) => {{
|
||||
($io:ident, $aux_mutex:ident, $routing_table:ident, $channel:expr, $func:ident $(, $param:expr)*) => {{
|
||||
let destination = ($channel >> 16) as u8;
|
||||
let channel = $channel as u16;
|
||||
let hop = $routing_table.0[destination as usize][0];
|
||||
|
@ -102,21 +107,21 @@ macro_rules! dispatch {
|
|||
local_moninj::$func(channel, $($param, )*)
|
||||
} else {
|
||||
let linkno = hop - 1;
|
||||
remote_moninj::$func($io, $aux_mutex, $ddma_mutex, linkno, destination, channel, $($param, )*)
|
||||
remote_moninj::$func($io, $aux_mutex, linkno, destination, channel, $($param, )*)
|
||||
}
|
||||
}}
|
||||
}
|
||||
|
||||
#[cfg(not(has_drtio))]
|
||||
macro_rules! dispatch {
|
||||
($io:ident, $aux_mutex:ident, $ddma_mutex:ident, $routing_table:ident, $channel:expr, $func:ident $(, $param:expr)*) => {{
|
||||
($io:ident, $aux_mutex:ident, $routing_table:ident, $channel:expr, $func:ident $(, $param:expr)*) => {{
|
||||
let channel = $channel as u16;
|
||||
local_moninj::$func(channel, $($param, )*)
|
||||
}}
|
||||
}
|
||||
|
||||
fn connection_worker(io: &Io, _aux_mutex: &Mutex, _ddma_mutex: &Mutex, _routing_table: &drtio_routing::RoutingTable,
|
||||
mut stream: &mut TcpStream) -> Result<(), Error<SchedError>> {
|
||||
fn connection_worker(io: &Io, _aux_mutex: &Mutex, _routing_table: &drtio_routing::RoutingTable,
|
||||
mut stream: &mut TcpStream) -> Result<(), Error<SchedError>> {
|
||||
let mut probe_watch_list = BTreeMap::new();
|
||||
let mut inject_watch_list = BTreeMap::new();
|
||||
let mut next_check = 0;
|
||||
|
@ -144,9 +149,10 @@ fn connection_worker(io: &Io, _aux_mutex: &Mutex, _ddma_mutex: &Mutex, _routing_
|
|||
let _ = inject_watch_list.remove(&(channel, overrd));
|
||||
}
|
||||
},
|
||||
HostMessage::Inject { channel, overrd, value } => dispatch!(io, _aux_mutex, _ddma_mutex, _routing_table, channel, inject, overrd, value),
|
||||
HostMessage::Inject { channel, overrd, value } => dispatch!(
|
||||
io, _aux_mutex, _routing_table, channel, inject, overrd, value),
|
||||
HostMessage::GetInjectionStatus { channel, overrd } => {
|
||||
let value = dispatch!(io, _aux_mutex, _ddma_mutex, _routing_table, channel, read_injection_status, overrd);
|
||||
let value = dispatch!(io, _aux_mutex, _routing_table, channel, read_injection_status, overrd);
|
||||
let reply = DeviceMessage::InjectionStatus {
|
||||
channel: channel,
|
||||
overrd: overrd,
|
||||
|
@ -163,7 +169,7 @@ fn connection_worker(io: &Io, _aux_mutex: &Mutex, _ddma_mutex: &Mutex, _routing_
|
|||
|
||||
if clock::get_ms() > next_check {
|
||||
for (&(channel, probe), previous) in probe_watch_list.iter_mut() {
|
||||
let current = dispatch!(io, _aux_mutex, _ddma_mutex, _routing_table, channel, read_probe, probe);
|
||||
let current = dispatch!(io, _aux_mutex, _routing_table, channel, read_probe, probe);
|
||||
if previous.is_none() || previous.unwrap() != current {
|
||||
let message = DeviceMessage::MonitorStatus {
|
||||
channel: channel,
|
||||
|
@ -178,7 +184,7 @@ fn connection_worker(io: &Io, _aux_mutex: &Mutex, _ddma_mutex: &Mutex, _routing_
|
|||
}
|
||||
}
|
||||
for (&(channel, overrd), previous) in inject_watch_list.iter_mut() {
|
||||
let current = dispatch!(io, _aux_mutex, _ddma_mutex, _routing_table, channel, read_injection_status, overrd);
|
||||
let current = dispatch!(io, _aux_mutex, _routing_table, channel, read_injection_status, overrd);
|
||||
if previous.is_none() || previous.unwrap() != current {
|
||||
let message = DeviceMessage::InjectionStatus {
|
||||
channel: channel,
|
||||
|
@ -199,19 +205,18 @@ fn connection_worker(io: &Io, _aux_mutex: &Mutex, _ddma_mutex: &Mutex, _routing_
|
|||
}
|
||||
}
|
||||
|
||||
pub fn thread(io: Io, aux_mutex: &Mutex, ddma_mutex: &Mutex, routing_table: &Urc<RefCell<drtio_routing::RoutingTable>>) {
|
||||
pub fn thread(io: Io, aux_mutex: &Mutex, routing_table: &Urc<RefCell<drtio_routing::RoutingTable>>) {
|
||||
let listener = TcpListener::new(&io, 2047);
|
||||
listener.listen(1383).expect("moninj: cannot listen");
|
||||
|
||||
loop {
|
||||
let aux_mutex = aux_mutex.clone();
|
||||
let ddma_mutex = ddma_mutex.clone();
|
||||
let routing_table = routing_table.clone();
|
||||
let stream = listener.accept().expect("moninj: cannot accept").into_handle();
|
||||
io.spawn(16384, move |io| {
|
||||
let routing_table = routing_table.borrow();
|
||||
let mut stream = TcpStream::from_handle(&io, stream);
|
||||
match connection_worker(&io, &aux_mutex, &ddma_mutex, &routing_table, &mut stream) {
|
||||
match connection_worker(&io, &aux_mutex, &routing_table, &mut stream) {
|
||||
Ok(()) => {},
|
||||
Err(err) => error!("moninj aborted: {}", err)
|
||||
}
|
||||
|
|
|
@ -91,12 +91,12 @@ pub mod remote_dma {
|
|||
Ok(playback_state)
|
||||
}
|
||||
|
||||
pub fn erase(io: &Io, aux_mutex: &Mutex, routing_table: &RoutingTable,
|
||||
ddma_mutex: &Mutex, id: u32) {
|
||||
pub fn erase(io: &Io, aux_mutex: &Mutex, ddma_mutex: &Mutex,
|
||||
routing_table: &RoutingTable, id: u32) {
|
||||
let _lock = ddma_mutex.lock(io).unwrap();
|
||||
let destinations = unsafe { TRACES.get(&id).unwrap() };
|
||||
for destination in destinations.keys() {
|
||||
match drtio::ddma_send_erase(io, aux_mutex, ddma_mutex, routing_table, id, *destination) {
|
||||
match drtio::ddma_send_erase(io, aux_mutex, routing_table, id, *destination) {
|
||||
Ok(_) => (),
|
||||
Err(e) => error!("Error erasing trace on DMA: {}", e)
|
||||
}
|
||||
|
@ -104,12 +104,12 @@ pub mod remote_dma {
|
|||
unsafe { TRACES.remove(&id); }
|
||||
}
|
||||
|
||||
pub fn upload_traces(io: &Io, aux_mutex: &Mutex, routing_table: &RoutingTable,
|
||||
ddma_mutex: &Mutex, id: u32) {
|
||||
pub fn upload_traces(io: &Io, aux_mutex: &Mutex, ddma_mutex: &Mutex,
|
||||
routing_table: &RoutingTable, id: u32) {
|
||||
let _lock = ddma_mutex.lock(io);
|
||||
let traces = unsafe { TRACES.get_mut(&id).unwrap() };
|
||||
for (destination, mut trace) in traces {
|
||||
match drtio::ddma_upload_trace(io, aux_mutex, ddma_mutex, routing_table, id, *destination, trace.get_trace())
|
||||
match drtio::ddma_upload_trace(io, aux_mutex, routing_table, id, *destination, trace.get_trace())
|
||||
{
|
||||
Ok(_) => trace.state = RemoteState::Loaded,
|
||||
Err(e) => error!("Error adding DMA trace on destination {}: {}", destination, e)
|
||||
|
@ -117,8 +117,8 @@ pub mod remote_dma {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn playback(io: &Io, aux_mutex: &Mutex, routing_table: &RoutingTable,
|
||||
ddma_mutex: &Mutex, id: u32, timestamp: u64) {
|
||||
pub fn playback(io: &Io, aux_mutex: &Mutex, ddma_mutex: &Mutex,
|
||||
routing_table: &RoutingTable, id: u32, timestamp: u64) {
|
||||
// triggers playback on satellites
|
||||
let destinations = unsafe {
|
||||
let _lock = ddma_mutex.lock(io).unwrap();
|
||||
|
@ -133,7 +133,7 @@ pub mod remote_dma {
|
|||
continue;
|
||||
}
|
||||
}
|
||||
match drtio::ddma_send_playback(io, aux_mutex, ddma_mutex, routing_table, id, *destination, timestamp) {
|
||||
match drtio::ddma_send_playback(io, aux_mutex, routing_table, id, *destination, timestamp) {
|
||||
Ok(_) => (),
|
||||
Err(e) => error!("Error during remote DMA playback: {}", e)
|
||||
}
|
||||
|
@ -152,15 +152,15 @@ pub mod remote_dma {
|
|||
};
|
||||
}
|
||||
|
||||
pub fn destination_changed(io: &Io, aux_mutex: &Mutex, routing_table: &RoutingTable,
|
||||
ddma_mutex: &Mutex, destination: u8, up: bool) {
|
||||
pub fn destination_changed(io: &Io, aux_mutex: &Mutex, ddma_mutex: &Mutex,
|
||||
routing_table: &RoutingTable, destination: u8, up: bool) {
|
||||
// update state of the destination, resend traces if it's up
|
||||
let _lock = ddma_mutex.lock(io).unwrap();
|
||||
let traces_iter = unsafe { TRACES.iter_mut() };
|
||||
for (id, dest_traces) in traces_iter {
|
||||
if let Some(trace) = dest_traces.get_mut(&destination) {
|
||||
if up {
|
||||
match drtio::ddma_upload_trace(io, aux_mutex, ddma_mutex, routing_table, *id, destination, trace.get_trace())
|
||||
match drtio::ddma_upload_trace(io, aux_mutex, routing_table, *id, destination, trace.get_trace())
|
||||
{
|
||||
Ok(_) => trace.state = RemoteState::Loaded,
|
||||
Err(e) => error!("Error adding DMA trace on destination {}: {}", destination, e)
|
||||
|
|
|
@ -61,23 +61,27 @@ pub mod drtio {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn aux_transact(io: &Io, aux_mutex: &Mutex, ddma_mutex: &Mutex,
|
||||
linkno: u8, request: &drtioaux::Packet ) -> Result<drtioaux::Packet, &'static str> {
|
||||
let _lock = aux_mutex.lock(io).unwrap();
|
||||
drtioaux::send(linkno, request).unwrap();
|
||||
loop {
|
||||
let reply = recv_aux_timeout(io, linkno, 200);
|
||||
match reply {
|
||||
Ok(drtioaux::Packet::DmaPlaybackStatus { id, destination, error, channel, timestamp }) => {
|
||||
remote_dma::playback_done(io, ddma_mutex, id, destination, error, channel, timestamp);
|
||||
},
|
||||
Ok(packet) => return Ok(packet),
|
||||
Err(e) => return Err(e)
|
||||
}
|
||||
fn process_async_packets(io: &Io, ddma_mutex: &Mutex, packet: drtioaux::Packet
|
||||
) -> Option<drtioaux::Packet> {
|
||||
// returns None if an async packet has been consumed
|
||||
match packet {
|
||||
drtioaux::Packet::DmaPlaybackStatus { id, destination, error, channel, timestamp } => {
|
||||
remote_dma::playback_done(io, ddma_mutex, id, destination, error, channel, timestamp);
|
||||
None
|
||||
},
|
||||
other => Some(other)
|
||||
}
|
||||
}
|
||||
|
||||
fn ping_remote(io: &Io, aux_mutex: &Mutex, ddma_mutex: &Mutex, linkno: u8) -> u32 {
|
||||
pub fn aux_transact(io: &Io, aux_mutex: &Mutex, linkno: u8, request: &drtioaux::Packet
|
||||
) -> Result<drtioaux::Packet, &'static str> {
|
||||
let _lock = aux_mutex.lock(io).unwrap();
|
||||
drtioaux::send(linkno, request).unwrap();
|
||||
let reply = recv_aux_timeout(io, linkno, 200)?;
|
||||
Ok(reply)
|
||||
}
|
||||
|
||||
fn ping_remote(io: &Io, aux_mutex: &Mutex, linkno: u8) -> u32 {
|
||||
let mut count = 0;
|
||||
loop {
|
||||
if !link_rx_up(linkno) {
|
||||
|
@ -87,7 +91,7 @@ pub mod drtio {
|
|||
if count > 100 {
|
||||
return 0;
|
||||
}
|
||||
let reply = aux_transact(io, aux_mutex, ddma_mutex, linkno, &drtioaux::Packet::EchoRequest);
|
||||
let reply = aux_transact(io, aux_mutex, linkno, &drtioaux::Packet::EchoRequest);
|
||||
match reply {
|
||||
Ok(drtioaux::Packet::EchoReply) => {
|
||||
// make sure receive buffer is drained
|
||||
|
@ -123,10 +127,10 @@ pub mod drtio {
|
|||
}
|
||||
}
|
||||
|
||||
fn load_routing_table(io: &Io, aux_mutex: &Mutex, ddma_mutex: &Mutex,
|
||||
fn load_routing_table(io: &Io, aux_mutex: &Mutex,
|
||||
linkno: u8, routing_table: &drtio_routing::RoutingTable) -> Result<(), &'static str> {
|
||||
for i in 0..drtio_routing::DEST_COUNT {
|
||||
let reply = aux_transact(io, aux_mutex, ddma_mutex, linkno, &drtioaux::Packet::RoutingSetPath {
|
||||
let reply = aux_transact(io, aux_mutex, linkno, &drtioaux::Packet::RoutingSetPath {
|
||||
destination: i as u8,
|
||||
hops: routing_table.0[i]
|
||||
})?;
|
||||
|
@ -137,10 +141,12 @@ pub mod drtio {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn set_rank(io: &Io, aux_mutex: &Mutex, ddma_mutex: &Mutex, linkno: u8, rank: u8) -> Result<(), &'static str> {
|
||||
let reply = aux_transact(io, aux_mutex, ddma_mutex, linkno, &drtioaux::Packet::RoutingSetRank {
|
||||
rank: rank
|
||||
})?;
|
||||
fn set_rank(io: &Io, aux_mutex: &Mutex,
|
||||
linkno: u8, rank: u8) -> Result<(), &'static str> {
|
||||
let reply = aux_transact(io, aux_mutex, linkno,
|
||||
&drtioaux::Packet::RoutingSetRank {
|
||||
rank: rank
|
||||
})?;
|
||||
if reply != drtioaux::Packet::RoutingAck {
|
||||
return Err("unexpected reply");
|
||||
}
|
||||
|
@ -229,45 +235,60 @@ pub mod drtio {
|
|||
let linkno = hop - 1;
|
||||
if destination_up(up_destinations, destination) {
|
||||
if up_links[linkno as usize] {
|
||||
let reply = aux_transact(io, aux_mutex, ddma_mutex, linkno, &drtioaux::Packet::DestinationStatusRequest {
|
||||
destination: destination
|
||||
});
|
||||
match reply {
|
||||
Ok(drtioaux::Packet::DestinationDownReply) => {
|
||||
destination_set_up(routing_table, up_destinations, destination, false);
|
||||
remote_dma::destination_changed(io, aux_mutex, routing_table, ddma_mutex, destination, false);
|
||||
loop {
|
||||
let reply = aux_transact(io, aux_mutex, linkno,
|
||||
&drtioaux::Packet::DestinationStatusRequest {
|
||||
destination: destination
|
||||
});
|
||||
if let Ok(reply) = reply {
|
||||
let reply = process_async_packets(io, ddma_mutex, reply);
|
||||
match reply {
|
||||
Some(drtioaux::Packet::DestinationDownReply) => {
|
||||
destination_set_up(routing_table, up_destinations, destination, false);
|
||||
remote_dma::destination_changed(io, aux_mutex, ddma_mutex, routing_table, destination, false);
|
||||
}
|
||||
Some(drtioaux::Packet::DestinationOkReply) => (),
|
||||
Some(drtioaux::Packet::DestinationSequenceErrorReply { channel }) => {
|
||||
error!("[DEST#{}] RTIO sequence error involving channel 0x{:04x}:{}", destination, channel, resolve_channel_name(channel as u32));
|
||||
unsafe { SEEN_ASYNC_ERRORS |= ASYNC_ERROR_SEQUENCE_ERROR };
|
||||
}
|
||||
Some(drtioaux::Packet::DestinationCollisionReply { channel }) => {
|
||||
error!("[DEST#{}] RTIO collision involving channel 0x{:04x}:{}", destination, channel, resolve_channel_name(channel as u32));
|
||||
unsafe { SEEN_ASYNC_ERRORS |= ASYNC_ERROR_COLLISION };
|
||||
}
|
||||
Some(drtioaux::Packet::DestinationBusyReply { channel }) => {
|
||||
error!("[DEST#{}] RTIO busy error involving channel 0x{:04x}:{}", destination, channel, resolve_channel_name(channel as u32));
|
||||
unsafe { SEEN_ASYNC_ERRORS |= ASYNC_ERROR_BUSY };
|
||||
}
|
||||
Some(packet) => error!("[DEST#{}] received unexpected aux packet: {:?}", destination, packet),
|
||||
None => {
|
||||
// continue asking until we get Destination...Reply or error out
|
||||
// wait a bit not to overwhelm the receiver causing gateway errors
|
||||
io.sleep(10).unwrap();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
error!("[DEST#{}] communication failed ({})", destination, reply.unwrap_err());
|
||||
}
|
||||
Ok(drtioaux::Packet::DestinationOkReply) => (),
|
||||
Ok(drtioaux::Packet::DestinationSequenceErrorReply { channel }) => {
|
||||
error!("[DEST#{}] RTIO sequence error involving channel 0x{:04x}:{}", destination, channel, resolve_channel_name(channel as u32));
|
||||
unsafe { SEEN_ASYNC_ERRORS |= ASYNC_ERROR_SEQUENCE_ERROR };
|
||||
}
|
||||
Ok(drtioaux::Packet::DestinationCollisionReply { channel }) => {
|
||||
error!("[DEST#{}] RTIO collision involving channel 0x{:04x}:{}", destination, channel, resolve_channel_name(channel as u32));
|
||||
unsafe { SEEN_ASYNC_ERRORS |= ASYNC_ERROR_COLLISION };
|
||||
}
|
||||
Ok(drtioaux::Packet::DestinationBusyReply { channel }) => {
|
||||
error!("[DEST#{}] RTIO busy error involving channel 0x{:04x}:{}", destination, channel, resolve_channel_name(channel as u32));
|
||||
unsafe { SEEN_ASYNC_ERRORS |= ASYNC_ERROR_BUSY };
|
||||
}
|
||||
Ok(packet) => error!("[DEST#{}] received unexpected aux packet: {:?}", destination, packet),
|
||||
Err(e) => error!("[DEST#{}] communication failed ({})", destination, e)
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
destination_set_up(routing_table, up_destinations, destination, false);
|
||||
remote_dma::destination_changed(io, aux_mutex, routing_table, ddma_mutex, destination, false);
|
||||
remote_dma::destination_changed(io, aux_mutex, ddma_mutex, routing_table, destination, false);
|
||||
}
|
||||
} else {
|
||||
if up_links[linkno as usize] {
|
||||
let reply = aux_transact(io, aux_mutex, ddma_mutex, linkno, &drtioaux::Packet::DestinationStatusRequest {
|
||||
destination: destination
|
||||
});
|
||||
let reply = aux_transact(io, aux_mutex, linkno,
|
||||
&drtioaux::Packet::DestinationStatusRequest {
|
||||
destination: destination
|
||||
});
|
||||
match reply {
|
||||
Ok(drtioaux::Packet::DestinationDownReply) => (),
|
||||
Ok(drtioaux::Packet::DestinationOkReply) => {
|
||||
destination_set_up(routing_table, up_destinations, destination, true);
|
||||
init_buffer_space(destination as u8, linkno);
|
||||
remote_dma::destination_changed(io, aux_mutex, routing_table, ddma_mutex, destination, true);
|
||||
remote_dma::destination_changed(io, aux_mutex, ddma_mutex, routing_table, destination, true);
|
||||
},
|
||||
Ok(packet) => error!("[DEST#{}] received unexpected aux packet: {:?}", destination, packet),
|
||||
Err(e) => error!("[DEST#{}] communication failed ({})", destination, e)
|
||||
|
@ -299,17 +320,17 @@ pub mod drtio {
|
|||
/* link was previously down */
|
||||
if link_rx_up(linkno) {
|
||||
info!("[LINK#{}] link RX became up, pinging", linkno);
|
||||
let ping_count = ping_remote(&io, aux_mutex, ddma_mutex, linkno);
|
||||
let ping_count = ping_remote(&io, aux_mutex, linkno);
|
||||
if ping_count > 0 {
|
||||
info!("[LINK#{}] remote replied after {} packets", linkno, ping_count);
|
||||
up_links[linkno as usize] = true;
|
||||
if let Err(e) = sync_tsc(&io, aux_mutex, linkno) {
|
||||
error!("[LINK#{}] failed to sync TSC ({})", linkno, e);
|
||||
}
|
||||
if let Err(e) = load_routing_table(&io, aux_mutex, ddma_mutex, linkno, routing_table) {
|
||||
if let Err(e) = load_routing_table(&io, aux_mutex, linkno, routing_table) {
|
||||
error!("[LINK#{}] failed to load routing table ({})", linkno, e);
|
||||
}
|
||||
if let Err(e) = set_rank(&io, aux_mutex, ddma_mutex, linkno, 1) {
|
||||
if let Err(e) = set_rank(&io, aux_mutex, linkno, 1) {
|
||||
error!("[LINK#{}] failed to set rank ({})", linkno, e);
|
||||
}
|
||||
info!("[LINK#{}] link initialization completed", linkno);
|
||||
|
@ -324,7 +345,7 @@ pub mod drtio {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn reset(io: &Io, aux_mutex: &Mutex, ddma_mutex: &Mutex) {
|
||||
pub fn reset(io: &Io, aux_mutex: &Mutex) {
|
||||
for linkno in 0..csr::DRTIO.len() {
|
||||
unsafe {
|
||||
(csr::DRTIO[linkno].reset_write)(1);
|
||||
|
@ -340,7 +361,7 @@ pub mod drtio {
|
|||
for linkno in 0..csr::DRTIO.len() {
|
||||
let linkno = linkno as u8;
|
||||
if link_rx_up(linkno) {
|
||||
let reply = aux_transact(io, aux_mutex, ddma_mutex, linkno,
|
||||
let reply = aux_transact(io, aux_mutex, linkno,
|
||||
&drtioaux::Packet::ResetRequest);
|
||||
match reply {
|
||||
Ok(drtioaux::Packet::ResetAck) => (),
|
||||
|
@ -351,8 +372,7 @@ pub mod drtio {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn ddma_upload_trace(io: &Io, aux_mutex: &Mutex, ddma_mutex: &Mutex,
|
||||
routing_table: &drtio_routing::RoutingTable,
|
||||
pub fn ddma_upload_trace(io: &Io, aux_mutex: &Mutex, routing_table: &drtio_routing::RoutingTable,
|
||||
id: u32, destination: u8, trace: &Vec<u8>) -> Result<(), &'static str> {
|
||||
let linkno = routing_table.0[destination as usize][0] - 1;
|
||||
let mut i = 0;
|
||||
|
@ -362,7 +382,7 @@ pub mod drtio {
|
|||
let last = i + len == trace.len();
|
||||
trace_slice[..len].clone_from_slice(&trace[i..i+len]);
|
||||
i += len;
|
||||
let reply = aux_transact(io, aux_mutex, ddma_mutex, linkno,
|
||||
let reply = aux_transact(io, aux_mutex, linkno,
|
||||
&drtioaux::Packet::DmaAddTraceRequest {
|
||||
id: id, destination: destination, last: last, length: len as u16, trace: trace_slice});
|
||||
match reply {
|
||||
|
@ -376,12 +396,10 @@ pub mod drtio {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
pub fn ddma_send_erase(io: &Io, aux_mutex: &Mutex, ddma_mutex: &Mutex,
|
||||
routing_table: &drtio_routing::RoutingTable,
|
||||
pub fn ddma_send_erase(io: &Io, aux_mutex: &Mutex, routing_table: &drtio_routing::RoutingTable,
|
||||
id: u32, destination: u8) -> Result<(), &'static str> {
|
||||
let linkno = routing_table.0[destination as usize][0] - 1;
|
||||
let reply = aux_transact(io, aux_mutex, ddma_mutex, linkno,
|
||||
let reply = aux_transact(io, aux_mutex, linkno,
|
||||
&drtioaux::Packet::DmaRemoveTraceRequest { id: id, destination: destination });
|
||||
match reply {
|
||||
Ok(drtioaux::Packet::DmaRemoveTraceReply { succeeded: true }) => Ok(()),
|
||||
|
@ -391,12 +409,11 @@ pub mod drtio {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn ddma_send_playback(io: &Io, aux_mutex: &Mutex, ddma_mutex: &Mutex,
|
||||
routing_table: &drtio_routing::RoutingTable,
|
||||
pub fn ddma_send_playback(io: &Io, aux_mutex: &Mutex, routing_table: &drtio_routing::RoutingTable,
|
||||
id: u32, destination: u8, timestamp: u64) -> Result<(), &'static str> {
|
||||
let linkno = routing_table.0[destination as usize][0] - 1;
|
||||
let reply = aux_transact(io, aux_mutex, ddma_mutex, linkno, &drtioaux::Packet::DmaPlaybackRequest{
|
||||
id: id, destination: destination, timestamp: timestamp });
|
||||
let reply = aux_transact(io, aux_mutex, linkno,
|
||||
&drtioaux::Packet::DmaPlaybackRequest{ id: id, destination: destination, timestamp: timestamp });
|
||||
match reply {
|
||||
Ok(drtioaux::Packet::DmaPlaybackReply { succeeded: true }) => return Ok(()),
|
||||
Ok(drtioaux::Packet::DmaPlaybackReply { succeeded: false }) =>
|
||||
|
@ -407,11 +424,10 @@ pub mod drtio {
|
|||
}
|
||||
|
||||
#[cfg(has_rtio_analyzer)]
|
||||
fn analyzer_get_data(io: &Io, aux_mutex: &Mutex, ddma_mutex: &Mutex,
|
||||
routing_table: &drtio_routing::RoutingTable, destination: u8
|
||||
) -> Result<RemoteBuffer, &'static str> {
|
||||
fn analyzer_get_data(io: &Io, aux_mutex: &Mutex, routing_table: &drtio_routing::RoutingTable,
|
||||
destination: u8) -> Result<RemoteBuffer, &'static str> {
|
||||
let linkno = routing_table.0[destination as usize][0] - 1;
|
||||
let reply = aux_transact(io, aux_mutex, ddma_mutex, linkno,
|
||||
let reply = aux_transact(io, aux_mutex, linkno,
|
||||
&drtioaux::Packet::AnalyzerHeaderRequest { destination: destination });
|
||||
let (sent, total, overflow) = match reply {
|
||||
Ok(drtioaux::Packet::AnalyzerHeader {
|
||||
|
@ -425,7 +441,7 @@ pub mod drtio {
|
|||
if sent > 0 {
|
||||
let mut last_packet = false;
|
||||
while !last_packet {
|
||||
let reply = aux_transact(io, aux_mutex, ddma_mutex, linkno,
|
||||
let reply = aux_transact(io, aux_mutex, linkno,
|
||||
&drtioaux::Packet::AnalyzerDataRequest { destination: destination });
|
||||
match reply {
|
||||
Ok(drtioaux::Packet::AnalyzerData { last, length, data }) => {
|
||||
|
@ -447,14 +463,13 @@ pub mod drtio {
|
|||
}
|
||||
|
||||
#[cfg(has_rtio_analyzer)]
|
||||
pub fn analyzer_query(io: &Io, aux_mutex: &Mutex, ddma_mutex: &Mutex,
|
||||
routing_table: &drtio_routing::RoutingTable,
|
||||
pub fn analyzer_query(io: &Io, aux_mutex: &Mutex, routing_table: &drtio_routing::RoutingTable,
|
||||
up_destinations: &Urc<RefCell<[bool; drtio_routing::DEST_COUNT]>>
|
||||
) -> Result<Vec<RemoteBuffer>, &'static str> {
|
||||
let mut remote_buffers: Vec<RemoteBuffer> = Vec::new();
|
||||
for i in 1..drtio_routing::DEST_COUNT {
|
||||
if destination_up(up_destinations, i as u8) {
|
||||
remote_buffers.push(analyzer_get_data(io, aux_mutex, ddma_mutex, routing_table, i as u8)?);
|
||||
remote_buffers.push(analyzer_get_data(io, aux_mutex, routing_table, i as u8)?);
|
||||
}
|
||||
}
|
||||
Ok(remote_buffers)
|
||||
|
@ -470,7 +485,7 @@ pub mod drtio {
|
|||
_routing_table: &Urc<RefCell<drtio_routing::RoutingTable>>,
|
||||
_up_destinations: &Urc<RefCell<[bool; drtio_routing::DEST_COUNT]>>,
|
||||
_ddma_mutex: &Mutex) {}
|
||||
pub fn reset(_io: &Io, _aux_mutex: &Mutex, _ddma_mutex: &Mutex) {}
|
||||
pub fn reset(_io: &Io, _aux_mutex: &Mutex) {}
|
||||
}
|
||||
|
||||
static mut SEEN_ASYNC_ERRORS: u8 = 0;
|
||||
|
@ -542,9 +557,9 @@ pub fn startup(io: &Io, aux_mutex: &Mutex,
|
|||
io.spawn(4096, async_error_thread);
|
||||
}
|
||||
|
||||
pub fn reset(io: &Io, aux_mutex: &Mutex, ddma_mutex: &Mutex) {
|
||||
pub fn reset(io: &Io, aux_mutex: &Mutex) {
|
||||
unsafe {
|
||||
csr::rtio_core::reset_write(1);
|
||||
}
|
||||
drtio::reset(io, aux_mutex, ddma_mutex)
|
||||
drtio::reset(io, aux_mutex)
|
||||
}
|
||||
|
|
|
@ -349,7 +349,7 @@ fn process_kern_message(io: &Io, aux_mutex: &Mutex,
|
|||
|
||||
kern_recv_dotrace(request);
|
||||
|
||||
if kern_hwreq::process_kern_hwreq(io, aux_mutex, ddma_mutex, routing_table, up_destinations, request)? {
|
||||
if kern_hwreq::process_kern_hwreq(io, aux_mutex, routing_table, up_destinations, request)? {
|
||||
return Ok(false)
|
||||
}
|
||||
|
||||
|
@ -373,7 +373,7 @@ fn process_kern_message(io: &Io, aux_mutex: &Mutex,
|
|||
if let Some(_id) = session.congress.dma_manager.record_start(name) {
|
||||
// replace the record
|
||||
#[cfg(has_drtio)]
|
||||
remote_dma::erase(io, aux_mutex, routing_table, ddma_mutex, _id);
|
||||
remote_dma::erase(io, aux_mutex, ddma_mutex, routing_table, _id);
|
||||
}
|
||||
kern_acknowledge()
|
||||
}
|
||||
|
@ -385,7 +385,7 @@ fn process_kern_message(io: &Io, aux_mutex: &Mutex,
|
|||
let _id = session.congress.dma_manager.record_stop(duration, enable_ddma, io, ddma_mutex);
|
||||
#[cfg(has_drtio)]
|
||||
if enable_ddma {
|
||||
remote_dma::upload_traces(io, aux_mutex, routing_table, ddma_mutex, _id);
|
||||
remote_dma::upload_traces(io, aux_mutex, ddma_mutex, routing_table, _id);
|
||||
}
|
||||
cache::flush_l2_cache();
|
||||
kern_acknowledge()
|
||||
|
@ -393,7 +393,7 @@ fn process_kern_message(io: &Io, aux_mutex: &Mutex,
|
|||
&kern::DmaEraseRequest { name } => {
|
||||
#[cfg(has_drtio)]
|
||||
if let Some(id) = session.congress.dma_manager.get_id(name) {
|
||||
remote_dma::erase(io, aux_mutex, routing_table, ddma_mutex, *id);
|
||||
remote_dma::erase(io, aux_mutex, ddma_mutex, routing_table, *id);
|
||||
}
|
||||
session.congress.dma_manager.erase(name);
|
||||
kern_acknowledge()
|
||||
|
@ -416,7 +416,7 @@ fn process_kern_message(io: &Io, aux_mutex: &Mutex,
|
|||
}
|
||||
&kern::DmaStartRemoteRequest { id: _id, timestamp: _timestamp } => {
|
||||
#[cfg(has_drtio)]
|
||||
remote_dma::playback(io, aux_mutex, routing_table, ddma_mutex, _id as u32, _timestamp as u64);
|
||||
remote_dma::playback(io, aux_mutex, ddma_mutex, routing_table, _id as u32, _timestamp as u64);
|
||||
kern_acknowledge()
|
||||
}
|
||||
&kern::DmaAwaitRemoteRequest { id: _id } => {
|
||||
|
@ -510,7 +510,6 @@ fn process_kern_message(io: &Io, aux_mutex: &Mutex,
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
request => unexpected!("unexpected request {:?} from kernel CPU", request)
|
||||
}.and(Ok(false))
|
||||
})
|
||||
|
|
|
@ -109,37 +109,44 @@ fn process_aux_packet(_manager: &mut DmaManager, analyzer: &mut Analyzer, _repea
|
|||
let hop = 0;
|
||||
|
||||
if hop == 0 {
|
||||
let errors;
|
||||
unsafe {
|
||||
errors = csr::drtiosat::rtio_error_read();
|
||||
}
|
||||
if errors & 1 != 0 {
|
||||
let channel;
|
||||
// async messages
|
||||
if let Some(status) = _manager.check_state() {
|
||||
info!("playback done, error: {}, channel: {}, timestamp: {}", status.error, status.channel, status.timestamp);
|
||||
drtioaux::send(0, &drtioaux::Packet::DmaPlaybackStatus {
|
||||
destination: _destination, id: status.id, error: status.error, channel: status.channel, timestamp: status.timestamp })?;
|
||||
} else {
|
||||
let errors;
|
||||
unsafe {
|
||||
channel = csr::drtiosat::sequence_error_channel_read();
|
||||
csr::drtiosat::rtio_error_write(1);
|
||||
errors = csr::drtiosat::rtio_error_read();
|
||||
}
|
||||
drtioaux::send(0,
|
||||
&drtioaux::Packet::DestinationSequenceErrorReply { channel })?;
|
||||
} else if errors & 2 != 0 {
|
||||
let channel;
|
||||
unsafe {
|
||||
channel = csr::drtiosat::collision_channel_read();
|
||||
csr::drtiosat::rtio_error_write(2);
|
||||
if errors & 1 != 0 {
|
||||
let channel;
|
||||
unsafe {
|
||||
channel = csr::drtiosat::sequence_error_channel_read();
|
||||
csr::drtiosat::rtio_error_write(1);
|
||||
}
|
||||
drtioaux::send(0,
|
||||
&drtioaux::Packet::DestinationSequenceErrorReply { channel })?;
|
||||
} else if errors & 2 != 0 {
|
||||
let channel;
|
||||
unsafe {
|
||||
channel = csr::drtiosat::collision_channel_read();
|
||||
csr::drtiosat::rtio_error_write(2);
|
||||
}
|
||||
drtioaux::send(0,
|
||||
&drtioaux::Packet::DestinationCollisionReply { channel })?;
|
||||
} else if errors & 4 != 0 {
|
||||
let channel;
|
||||
unsafe {
|
||||
channel = csr::drtiosat::busy_channel_read();
|
||||
csr::drtiosat::rtio_error_write(4);
|
||||
}
|
||||
drtioaux::send(0,
|
||||
&drtioaux::Packet::DestinationBusyReply { channel })?;
|
||||
}
|
||||
drtioaux::send(0,
|
||||
&drtioaux::Packet::DestinationCollisionReply { channel })?;
|
||||
} else if errors & 4 != 0 {
|
||||
let channel;
|
||||
unsafe {
|
||||
channel = csr::drtiosat::busy_channel_read();
|
||||
csr::drtiosat::rtio_error_write(4);
|
||||
else {
|
||||
drtioaux::send(0, &drtioaux::Packet::DestinationOkReply)?;
|
||||
}
|
||||
drtioaux::send(0,
|
||||
&drtioaux::Packet::DestinationBusyReply { channel })?;
|
||||
}
|
||||
else {
|
||||
drtioaux::send(0, &drtioaux::Packet::DestinationOkReply)?;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -643,13 +650,6 @@ pub extern fn main() -> i32 {
|
|||
error!("aux packet error: {}", e);
|
||||
}
|
||||
}
|
||||
if let Some(status) = dma_manager.check_state() {
|
||||
info!("playback done, error: {}, channel: {}, timestamp: {}", status.error, status.channel, status.timestamp);
|
||||
if let Err(e) = drtioaux::send(0, &drtioaux::Packet::DmaPlaybackStatus {
|
||||
destination: rank, id: status.id, error: status.error, channel: status.channel, timestamp: status.timestamp }) {
|
||||
error!("error sending DMA playback status: {}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
drtiosat_reset_phy(true);
|
||||
|
|
|
@ -192,6 +192,7 @@ def main():
|
|||
d_applets = applets_ccb.AppletsCCBDock(main_window,
|
||||
sub_clients["datasets"],
|
||||
rpc_clients["dataset_db"],
|
||||
expmgr,
|
||||
extra_substitutes={
|
||||
"server": args.server,
|
||||
"port_notify": args.port_notify,
|
||||
|
|
|
@ -4,24 +4,27 @@ import argparse
|
|||
import sys
|
||||
import textwrap
|
||||
from collections import defaultdict
|
||||
from itertools import count
|
||||
from itertools import count, filterfalse
|
||||
|
||||
from artiq import __version__ as artiq_version
|
||||
from artiq.coredevice import jsondesc
|
||||
from artiq.coredevice.phaser import PHASER_GW_MIQRO, PHASER_GW_BASE
|
||||
|
||||
|
||||
def process_header(output, description):
|
||||
def get_cpu_target(description):
|
||||
if description.get("type", None) == "shuttler":
|
||||
return "rv32g"
|
||||
if description["target"] == "kasli":
|
||||
if description["hw_rev"] in ("v1.0", "v1.1"):
|
||||
cpu_target = "rv32ima"
|
||||
return "rv32ima"
|
||||
else:
|
||||
cpu_target = "rv32g"
|
||||
return "rv32g"
|
||||
elif description["target"] == "kasli_soc":
|
||||
cpu_target = "cortexa9"
|
||||
return "cortexa9"
|
||||
else:
|
||||
raise NotImplementedError
|
||||
|
||||
def process_header(output, description):
|
||||
print(textwrap.dedent("""
|
||||
# Autogenerated for the {variant} variant
|
||||
core_addr = "{core_addr}"
|
||||
|
@ -57,6 +60,8 @@ def process_header(output, description):
|
|||
"class": "CoreDMA"
|
||||
}},
|
||||
|
||||
"satellite_cpu_targets": {{}},
|
||||
|
||||
"i2c_switch0": {{
|
||||
"type": "local",
|
||||
"module": "artiq.coredevice.i2c",
|
||||
|
@ -74,7 +79,7 @@ def process_header(output, description):
|
|||
variant=description["variant"],
|
||||
core_addr=description["core_addr"],
|
||||
ref_period=1/(8*description["rtio_frequency"]),
|
||||
cpu_target=cpu_target),
|
||||
cpu_target=get_cpu_target(description)),
|
||||
file=output)
|
||||
|
||||
|
||||
|
@ -593,12 +598,89 @@ class PeripheralManager:
|
|||
channel=rtio_offset+i)
|
||||
return 8
|
||||
|
||||
def process_shuttler(self, shuttler_peripheral):
|
||||
shuttler_name = self.get_name("shuttler")
|
||||
rtio_offset = shuttler_peripheral["drtio_destination"] << 16
|
||||
rtio_offset += self.add_board_leds(rtio_offset, board_name=shuttler_name)
|
||||
|
||||
channel = count(0)
|
||||
self.gen("""
|
||||
device_db["{name}_config"] = {{
|
||||
"type": "local",
|
||||
"module": "artiq.coredevice.shuttler",
|
||||
"class": "Config",
|
||||
"arguments": {{"channel": 0x{channel:06x}}},
|
||||
}}""",
|
||||
name=shuttler_name,
|
||||
channel=rtio_offset + next(channel))
|
||||
self.gen("""
|
||||
device_db["{name}_trigger"] = {{
|
||||
"type": "local",
|
||||
"module": "artiq.coredevice.shuttler",
|
||||
"class": "Trigger",
|
||||
"arguments": {{"channel": 0x{channel:06x}}},
|
||||
}}""",
|
||||
name=shuttler_name,
|
||||
channel=rtio_offset + next(channel))
|
||||
for i in range(16):
|
||||
self.gen("""
|
||||
device_db["{name}_volt{ch}"] = {{
|
||||
"type": "local",
|
||||
"module": "artiq.coredevice.shuttler",
|
||||
"class": "Volt",
|
||||
"arguments": {{"channel": 0x{channel:06x}}},
|
||||
}}""",
|
||||
name=shuttler_name,
|
||||
ch=i,
|
||||
channel=rtio_offset + next(channel))
|
||||
self.gen("""
|
||||
device_db["{name}_dds{ch}"] = {{
|
||||
"type": "local",
|
||||
"module": "artiq.coredevice.shuttler",
|
||||
"class": "Dds",
|
||||
"arguments": {{"channel": 0x{channel:06x}}},
|
||||
}}""",
|
||||
name=shuttler_name,
|
||||
ch=i,
|
||||
channel=rtio_offset + next(channel))
|
||||
|
||||
device_class_names = ["Relay", "ADC"]
|
||||
for i, device_name in enumerate(device_class_names):
|
||||
spi_name = "{name}_spi{ch}".format(
|
||||
name=shuttler_name,
|
||||
ch=i)
|
||||
self.gen("""
|
||||
device_db["{spi}"] = {{
|
||||
"type": "local",
|
||||
"module": "artiq.coredevice.spi2",
|
||||
"class": "SPIMaster",
|
||||
"arguments": {{"channel": 0x{channel:06x}}},
|
||||
}}""",
|
||||
spi=spi_name,
|
||||
channel=rtio_offset + next(channel))
|
||||
self.gen("""
|
||||
device_db["{name}_{device}"] = {{
|
||||
"type": "local",
|
||||
"module": "artiq.coredevice.shuttler",
|
||||
"class": "{dev_class}",
|
||||
"arguments": {{"spi_device": "{spi}"}},
|
||||
}}""",
|
||||
name=shuttler_name,
|
||||
device=device_name.lower(),
|
||||
dev_class=device_name,
|
||||
spi=spi_name)
|
||||
return 0
|
||||
|
||||
def process(self, rtio_offset, peripheral):
|
||||
processor = getattr(self, "process_"+str(peripheral["type"]))
|
||||
return processor(rtio_offset, peripheral)
|
||||
|
||||
def add_board_leds(self, rtio_offset):
|
||||
def add_board_leds(self, rtio_offset, board_name=None):
|
||||
for i in range(2):
|
||||
if board_name is None:
|
||||
led_name = self.get_name("led")
|
||||
else:
|
||||
led_name = self.get_name("{}_led".format(board_name))
|
||||
self.gen("""
|
||||
device_db["{name}"] = {{
|
||||
"type": "local",
|
||||
|
@ -606,11 +688,18 @@ class PeripheralManager:
|
|||
"class": "TTLOut",
|
||||
"arguments": {{"channel": 0x{channel:06x}}}
|
||||
}}""",
|
||||
name=self.get_name("led"),
|
||||
name=led_name,
|
||||
channel=rtio_offset+i)
|
||||
return 2
|
||||
|
||||
|
||||
def split_drtio_eem(peripherals):
|
||||
# Shuttler is the only peripheral that uses DRTIO-over-EEM at this moment
|
||||
drtio_eem_filter = lambda peripheral: peripheral["type"] == "shuttler"
|
||||
return filterfalse(drtio_eem_filter, peripherals), \
|
||||
list(filter(drtio_eem_filter, peripherals))
|
||||
|
||||
|
||||
def process(output, primary_description, satellites):
|
||||
drtio_role = primary_description["drtio_role"]
|
||||
if drtio_role not in ("standalone", "master"):
|
||||
|
@ -623,9 +712,11 @@ def process(output, primary_description, satellites):
|
|||
|
||||
pm = PeripheralManager(output, primary_description)
|
||||
|
||||
local_peripherals, drtio_peripherals = split_drtio_eem(primary_description["peripherals"])
|
||||
|
||||
print("# {} peripherals".format(drtio_role), file=output)
|
||||
rtio_offset = 0
|
||||
for peripheral in primary_description["peripherals"]:
|
||||
for peripheral in local_peripherals:
|
||||
n_channels = pm.process(rtio_offset, peripheral)
|
||||
rtio_offset += n_channels
|
||||
if drtio_role == "standalone":
|
||||
|
@ -635,12 +726,21 @@ def process(output, primary_description, satellites):
|
|||
for destination, description in satellites:
|
||||
if description["drtio_role"] != "satellite":
|
||||
raise ValueError("Invalid DRTIO role for satellite at destination {}".format(destination))
|
||||
peripherals, satellite_drtio_peripherals = split_drtio_eem(description["peripherals"])
|
||||
drtio_peripherals.extend(satellite_drtio_peripherals)
|
||||
|
||||
print("# DEST#{} peripherals".format(destination), file=output)
|
||||
print("device_db[\"satellite_cpu_targets\"][{}] = \"{}\"".format(destination, get_cpu_target(description)), file=output)
|
||||
rtio_offset = destination << 16
|
||||
for peripheral in description["peripherals"]:
|
||||
for peripheral in peripherals:
|
||||
n_channels = pm.process(rtio_offset, peripheral)
|
||||
rtio_offset += n_channels
|
||||
|
||||
for peripheral in drtio_peripherals:
|
||||
print("# DEST#{} peripherals".format(peripheral["drtio_destination"]), file=output)
|
||||
print("device_db[\"satellite_cpu_targets\"][{}] = \"{}\"".format(peripheral["drtio_destination"], get_cpu_target(peripheral)), file=output)
|
||||
processor = getattr(pm, "process_"+str(peripheral["type"]))
|
||||
processor(peripheral)
|
||||
|
||||
|
||||
def main():
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
from migen import *
|
||||
from migen.genlib.resetsync import AsyncResetSynchronizer
|
||||
from misoc.interconnect.csr import *
|
||||
from misoc.cores.code_8b10b import SingleEncoder, Decoder
|
||||
from artiq.gateware.drtio.core import TransceiverInterface, ChannelInterface
|
||||
|
@ -396,53 +397,78 @@ class SerdesSingle(Module):
|
|||
|
||||
|
||||
class OOBReset(Module):
|
||||
def __init__(self, iserdes_o):
|
||||
ce_counter = Signal(13)
|
||||
activity_ce = Signal()
|
||||
transition_ce = Signal()
|
||||
def __init__(self, platform, iserdes_o):
|
||||
self.clock_domains.cd_clk100 = ClockDomain()
|
||||
self.specials += [
|
||||
Instance("BUFR",
|
||||
i_I=ClockSignal("clk200"),
|
||||
o_O=ClockSignal("clk100"),
|
||||
p_BUFR_DIVIDE="2"),
|
||||
AsyncResetSynchronizer(self.cd_clk100, ResetSignal("clk200")),
|
||||
]
|
||||
|
||||
self.sync.clk200 += Cat(ce_counter, activity_ce).eq(ce_counter + 1)
|
||||
self.comb += transition_ce.eq(ce_counter[0])
|
||||
|
||||
idle_low_meta = Signal()
|
||||
idle_high_meta = Signal()
|
||||
idle_low = Signal()
|
||||
idle_high = Signal()
|
||||
|
||||
idle = Signal()
|
||||
self.rst = Signal(reset=1)
|
||||
|
||||
# Detect the lack of transitions (idle) within 2 clk200 cycles
|
||||
self.specials += [
|
||||
Instance("FDCE", p_INIT=1, i_D=1, i_CLR=iserdes_o,
|
||||
i_CE=transition_ce, i_C=ClockSignal("clk200"), o_Q=idle_low_meta,
|
||||
attr={"async_reg", "ars_ff1"}),
|
||||
Instance("FDCE", p_INIT=1, i_D=idle_low_meta, i_CLR=0,
|
||||
i_CE=transition_ce, i_C=ClockSignal("clk200"), o_Q=idle_low,
|
||||
attr={"async_reg", "ars_ff2"}),
|
||||
# Detect the lack of transitions (idle) within a clk100 cycle
|
||||
for idle, source in [
|
||||
(idle_low, iserdes_o), (idle_high, ~iserdes_o)]:
|
||||
idle_meta = Signal()
|
||||
ff_pair = [ff1, ff2] = [
|
||||
Instance("FDCE", p_INIT=1, i_D=1, i_CLR=source,
|
||||
i_CE=1, i_C=ClockSignal("clk100"), o_Q=idle_meta,
|
||||
attr={"async_reg"}),
|
||||
Instance("FDCE", p_INIT=1, i_D=idle_meta, i_CLR=0,
|
||||
i_CE=1, i_C=ClockSignal("clk100"), o_Q=idle,
|
||||
attr={"async_reg"}),
|
||||
]
|
||||
self.specials += ff_pair
|
||||
|
||||
Instance("FDCE", p_INIT=1, i_D=1, i_CLR=~iserdes_o,
|
||||
i_CE=transition_ce, i_C=ClockSignal("clk200"), o_Q=idle_high_meta,
|
||||
attr={"async_reg", "ars_ff1"}),
|
||||
Instance("FDCE", p_INIT=1, i_D=idle_high_meta, i_CLR=0,
|
||||
i_CE=transition_ce, i_C=ClockSignal("clk200"), o_Q=idle_high,
|
||||
attr={"async_reg", "ars_ff2"}),
|
||||
]
|
||||
platform.add_platform_command(
|
||||
"set_false_path -quiet -to {ff1}/CLR", ff1=ff1)
|
||||
# Capture transition detected by FF1/Q in FF2/D
|
||||
platform.add_platform_command(
|
||||
"set_max_delay 2 -quiet "
|
||||
"-from {ff1}/Q -to {ff2}/D", ff1=ff1, ff2=ff2)
|
||||
|
||||
# Detect activity for the last 2**13 clk200 cycles
|
||||
# The 2**13 cycles are fully partitioned into 2**12 time segments of 2
|
||||
# cycles in duration. If there exists 2-cycle time segment without
|
||||
# signal level transition, rst is asserted.
|
||||
self.sync.clk200 += [
|
||||
If(activity_ce,
|
||||
idle.eq(0),
|
||||
self.rst.eq(idle),
|
||||
),
|
||||
# Detect activity for the last 2**15 clk100 cycles
|
||||
self.submodules.fsm = fsm = ClockDomainsRenamer("clk100")(
|
||||
FSM(reset_state="WAIT_TRANSITION"))
|
||||
counter = Signal(15, reset=0x7FFF)
|
||||
|
||||
# Keep sysclk reset asserted until transition is detected for a
|
||||
# continuous 2**15 clk100 cycles
|
||||
fsm.act("WAIT_TRANSITION",
|
||||
self.rst.eq(1),
|
||||
If(idle_low | idle_high,
|
||||
idle.eq(1),
|
||||
self.rst.eq(1),
|
||||
),
|
||||
]
|
||||
NextValue(counter, 0x7FFF),
|
||||
).Else(
|
||||
If(counter == 0,
|
||||
NextState("WAIT_NO_TRANSITION"),
|
||||
NextValue(counter, 0x7FFF),
|
||||
).Else(
|
||||
NextValue(counter, counter - 1),
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
# Reassert sysclk reset if there are no transition for the last 2**15
|
||||
# clk100 cycles.
|
||||
fsm.act("WAIT_NO_TRANSITION",
|
||||
self.rst.eq(0),
|
||||
If(idle_low | idle_high,
|
||||
If(counter == 0,
|
||||
NextState("WAIT_TRANSITION"),
|
||||
NextValue(counter, 0x7FFF),
|
||||
).Else(
|
||||
NextValue(counter, counter - 1),
|
||||
)
|
||||
).Else(
|
||||
NextValue(counter, 0x7FFF),
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
class EEMSerdes(Module, TransceiverInterface, AutoCSR):
|
||||
|
@ -526,7 +552,7 @@ class EEMSerdes(Module, TransceiverInterface, AutoCSR):
|
|||
|
||||
self.submodules += serdes_list
|
||||
|
||||
self.submodules.oob_reset = OOBReset(serdes_list[0].rx_serdes.o[0])
|
||||
self.submodules.oob_reset = OOBReset(platform, serdes_list[0].rx_serdes.o[0])
|
||||
self.rst = self.oob_reset.rst
|
||||
self.rst.attr.add("no_retiming")
|
||||
|
||||
|
|
|
@ -716,11 +716,11 @@ class HVAmp(_EEM):
|
|||
target.rtio_channels.append(rtio.Channel.from_phy(phy))
|
||||
|
||||
|
||||
class EFC(_EEM):
|
||||
class Shuttler(_EEM):
|
||||
@staticmethod
|
||||
def io(eem, iostandard=default_iostandard):
|
||||
# Master: Pair 0~3 data IN, 4~7 OUT
|
||||
data_in = ("efc{}_drtio_rx".format(eem), 0,
|
||||
data_in = ("shuttler{}_drtio_rx".format(eem), 0,
|
||||
Subsignal("p", Pins("{} {} {} {}".format(*[
|
||||
_eem_pin(eem, i, "p") for i in range(4)
|
||||
]))),
|
||||
|
@ -731,7 +731,7 @@ class EFC(_EEM):
|
|||
Misc("DIFF_TERM=TRUE"),
|
||||
)
|
||||
|
||||
data_out = ("efc{}_drtio_tx".format(eem), 0,
|
||||
data_out = ("shuttler{}_drtio_tx".format(eem), 0,
|
||||
Subsignal("p", Pins("{} {} {} {}".format(*[
|
||||
_eem_pin(eem, i, "p") for i in range(4, 8)
|
||||
]))),
|
||||
|
@ -746,4 +746,4 @@ class EFC(_EEM):
|
|||
@classmethod
|
||||
def add_std(cls, target, eem, eem_aux, iostandard=default_iostandard):
|
||||
cls.add_extension(target, eem, is_drtio_over_eem=True, iostandard=iostandard)
|
||||
target.eem_drtio_channels.append((target.platform.request("efc{}_drtio_rx".format(eem), 0), target.platform.request("efc{}_drtio_tx".format(eem), 0)))
|
||||
target.eem_drtio_channels.append((target.platform.request("shuttler{}_drtio_rx".format(eem), 0), target.platform.request("shuttler{}_drtio_tx".format(eem), 0)))
|
||||
|
|
|
@ -126,7 +126,7 @@ def peripheral_hvamp(module, peripheral, **kwargs):
|
|||
eem.HVAmp.add_std(module, peripheral["ports"][0],
|
||||
ttl_simple.Output, **kwargs)
|
||||
|
||||
def peripheral_efc(module, peripheral, **kwargs):
|
||||
def peripheral_shuttler(module, peripheral, **kwargs):
|
||||
if len(peripheral["ports"]) == 1:
|
||||
port = peripheral["ports"][0]
|
||||
port_aux = None
|
||||
|
@ -134,7 +134,7 @@ def peripheral_efc(module, peripheral, **kwargs):
|
|||
port, port_aux = peripheral["ports"]
|
||||
else:
|
||||
raise ValueError("wrong number of ports")
|
||||
eem.EFC.add_std(module, port, port_aux)
|
||||
eem.Shuttler.add_std(module, port, port_aux)
|
||||
|
||||
peripheral_processors = {
|
||||
"dio": peripheral_dio,
|
||||
|
@ -148,7 +148,7 @@ peripheral_processors = {
|
|||
"fastino": peripheral_fastino,
|
||||
"phaser": peripheral_phaser,
|
||||
"hvamp": peripheral_hvamp,
|
||||
"efc": peripheral_efc,
|
||||
"shuttler": peripheral_shuttler,
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
# Copyright 2013-2017 Robert Jordens <jordens@gmail.com>
|
||||
#
|
||||
# shuttler is developed based on pdq.
|
||||
#
|
||||
# pdq is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
|
@ -91,20 +93,54 @@ class DacInterface(Module, AutoCSR):
|
|||
p_DDR_CLK_EDGE="SAME_EDGE")
|
||||
for bit in range(bit_width)]
|
||||
|
||||
|
||||
class SigmaDeltaModulator(Module):
|
||||
"""First order Sigma-Delta modulator."""
|
||||
def __init__(self, x_width, y_width):
|
||||
self.x = Signal(x_width)
|
||||
self.y = Signal(y_width)
|
||||
|
||||
# SDM can at most output the max DAC code `Replicate(1, y_width-1)`,
|
||||
# which represents the sample of value
|
||||
# `Replicate(1, y_width-1) << (x_width-y_width)`.
|
||||
#
|
||||
# If the input sample exceeds such limit, SDM may overflow.
|
||||
x_capped = Signal(x_width)
|
||||
max_dac_code = Replicate(1, (y_width-1))
|
||||
self.comb += If(self.x[x_width-y_width:] == max_dac_code,
|
||||
x_capped.eq(Cat(Replicate(0, x_width-y_width), max_dac_code)),
|
||||
).Else(
|
||||
x_capped.eq(self.x),
|
||||
)
|
||||
|
||||
acc = Signal(x_width)
|
||||
|
||||
self.comb += self.y.eq(acc[x_width-y_width:])
|
||||
self.sync.rio += acc.eq(x_capped - Cat(Replicate(0, x_width-y_width), self.y) + acc)
|
||||
|
||||
|
||||
class Dac(Module):
|
||||
"""Output module.
|
||||
|
||||
Holds the two output line executors.
|
||||
|
||||
Attributes:
|
||||
data (Signal[16]): Output value to be send to the DAC.
|
||||
data (Signal[14]): Output value to be send to the DAC.
|
||||
clear (Signal): Clear accumulated phase offset when loading a new
|
||||
waveform. Input.
|
||||
gain (Signal[16]): Output value gain. The gain signal represents the
|
||||
decimal part os the gain in 2's complement.
|
||||
offset (Signal[16]): Output value offset.
|
||||
i (Endpoint[]): Coefficients of the output lines.
|
||||
"""
|
||||
def __init__(self):
|
||||
def __init__(self, sdm=False):
|
||||
self.clear = Signal()
|
||||
self.data = Signal(16)
|
||||
self.data = Signal(14)
|
||||
self.gain = Signal(16)
|
||||
self.offset = Signal(16)
|
||||
|
||||
overflow = Signal()
|
||||
underflow = Signal()
|
||||
|
||||
###
|
||||
|
||||
|
@ -113,10 +149,44 @@ class Dac(Module):
|
|||
Dds(self.clear),
|
||||
]
|
||||
|
||||
# Infer signed multiplication
|
||||
data_raw = Signal((16, True))
|
||||
# Buffer data should have 2 more bits than the desired output width
|
||||
# It is to perform overflow/underflow detection
|
||||
data_buf = Signal(18)
|
||||
data_sink = Signal(16)
|
||||
|
||||
if sdm:
|
||||
self.submodules.sdm = SigmaDeltaModulator(16, 14)
|
||||
|
||||
self.sync.rio += [
|
||||
self.data.eq(reduce(add, [sub.data for sub in subs])),
|
||||
data_raw.eq(reduce(add, [sub.data for sub in subs])),
|
||||
# Extra buffer for timing for the DSP
|
||||
data_buf.eq(((data_raw * Cat(self.gain, ~self.gain[-1])) + (self.offset << 16))[16:]),
|
||||
If(overflow,
|
||||
data_sink.eq(0x7fff),
|
||||
).Elif(underflow,
|
||||
data_sink.eq(0x8000),
|
||||
).Else(
|
||||
data_sink.eq(data_buf),
|
||||
),
|
||||
]
|
||||
|
||||
self.comb += [
|
||||
# Overflow condition
|
||||
overflow.eq(~data_buf[-1] & (data_buf[-2] | data_buf[-3])),
|
||||
# Underflow condition
|
||||
underflow.eq(data_buf[-1] & (~data_buf[-2] | ~data_buf[-3])),
|
||||
]
|
||||
|
||||
if sdm:
|
||||
self.comb += [
|
||||
self.sdm.x.eq(data_sink),
|
||||
self.data.eq(self.sdm.y),
|
||||
]
|
||||
else:
|
||||
self.comb += self.data.eq(data_sink[2:])
|
||||
|
||||
self.i = [ sub.i for sub in subs ]
|
||||
self.submodules += subs
|
||||
|
||||
|
@ -229,11 +299,43 @@ class Dds(Module):
|
|||
class Config(Module):
|
||||
def __init__(self):
|
||||
self.clr = Signal(16, reset=0xFFFF)
|
||||
self.i = Endpoint([("data", 16)])
|
||||
self.gain = [ Signal(16) for _ in range(16) ]
|
||||
self.offset = [ Signal(16) for _ in range(16) ]
|
||||
|
||||
reg_file = Array(self.gain + self.offset + [self.clr])
|
||||
self.i = Endpoint([
|
||||
("data", 16),
|
||||
("addr", 7),
|
||||
])
|
||||
self.o = Endpoint([
|
||||
("data", 16),
|
||||
])
|
||||
|
||||
# This introduces 1 extra latency to everything in config
|
||||
# See the latency/delay attributes in Volt & DDS Endpoints/rtlinks
|
||||
self.sync.rio += If(self.i.stb, self.clr.eq(self.i.data))
|
||||
#
|
||||
# Gain & offsets are intended for initial calibration only, latency
|
||||
# is NOT adjusted to match outputs to the DAC interface
|
||||
#
|
||||
# Interface address bits mapping:
|
||||
# 6: Read bit. Assert to read, deassert to write.
|
||||
# 5: Clear bit. Assert to write clr. clr is write-only.
|
||||
# 4: Gain/Offset. (De)Assert to access (Gain)Offset registers.
|
||||
# 0-3: Channel selection for the Gain & Offset registers.
|
||||
#
|
||||
# Reading Gain / Offset register generates an RTIOInput event
|
||||
self.sync.rio += [
|
||||
self.o.stb.eq(0),
|
||||
If(self.i.stb,
|
||||
If(~self.i.addr[6],
|
||||
reg_file[self.i.addr[:6]].eq(self.i.data),
|
||||
).Else(
|
||||
# clr register is unreadable, as an optimization
|
||||
self.o.data.eq(reg_file[self.i.addr[:5]]),
|
||||
self.o.stb.eq(1),
|
||||
)
|
||||
),
|
||||
]
|
||||
|
||||
|
||||
Phy = namedtuple("Phy", "rtlink probes overrides")
|
||||
|
@ -250,7 +352,7 @@ class Shuttler(Module, AutoCSR):
|
|||
Attributes:
|
||||
phys (list): List of Endpoints.
|
||||
"""
|
||||
def __init__(self, pads):
|
||||
def __init__(self, pads, sdm=False):
|
||||
NUM_OF_DACS = 16
|
||||
|
||||
self.submodules.dac_interface = DacInterface(pads)
|
||||
|
@ -258,13 +360,23 @@ class Shuttler(Module, AutoCSR):
|
|||
self.phys = []
|
||||
|
||||
self.submodules.cfg = Config()
|
||||
cfg_rtl_iface = rtlink.Interface(rtlink.OInterface(
|
||||
data_width=len(self.cfg.i.data),
|
||||
enable_replace=False))
|
||||
cfg_rtl_iface = rtlink.Interface(
|
||||
rtlink.OInterface(
|
||||
data_width=len(self.cfg.i.data),
|
||||
address_width=len(self.cfg.i.addr),
|
||||
enable_replace=False,
|
||||
),
|
||||
rtlink.IInterface(
|
||||
data_width=len(self.cfg.o.data),
|
||||
),
|
||||
)
|
||||
|
||||
self.comb += [
|
||||
self.cfg.i.stb.eq(cfg_rtl_iface.o.stb),
|
||||
self.cfg.i.addr.eq(cfg_rtl_iface.o.address),
|
||||
self.cfg.i.data.eq(cfg_rtl_iface.o.data),
|
||||
cfg_rtl_iface.i.stb.eq(self.cfg.o.stb),
|
||||
cfg_rtl_iface.i.data.eq(self.cfg.o.data),
|
||||
]
|
||||
self.phys.append(Phy(cfg_rtl_iface, [], []))
|
||||
|
||||
|
@ -274,9 +386,11 @@ class Shuttler(Module, AutoCSR):
|
|||
self.phys.append(Phy(trigger_iface, [], []))
|
||||
|
||||
for idx in range(NUM_OF_DACS):
|
||||
dac = Dac()
|
||||
dac = Dac(sdm=sdm)
|
||||
self.comb += [
|
||||
dac.clear.eq(self.cfg.clr[idx]),
|
||||
dac.gain.eq(self.cfg.gain[idx]),
|
||||
dac.offset.eq(self.cfg.offset[idx]),
|
||||
self.dac_interface.data[idx // 2][idx % 2].eq(dac.data)
|
||||
]
|
||||
|
||||
|
|
|
@ -13,6 +13,7 @@ from artiq.gateware.amp import AMPSoC
|
|||
from artiq.gateware import rtio
|
||||
from artiq.gateware.rtio.xilinx_clocking import fix_serdes_timing_path
|
||||
from artiq.gateware.rtio.phy import ttl_simple
|
||||
from artiq.gateware.rtio.phy import spi2 as rtio_spi
|
||||
from artiq.gateware.drtio.transceiver import eem_serdes
|
||||
from artiq.gateware.drtio.rx_synchronizer import NoRXSynchronizer
|
||||
from artiq.gateware.drtio import *
|
||||
|
@ -145,6 +146,20 @@ class Satellite(BaseSoC, AMPSoC):
|
|||
Subsignal('data', Pins('fmc0:HB13_N fmc0:HB12_N fmc0:HB13_P fmc0:HB12_P fmc0:HB15_N fmc0:HB15_P fmc0:HB11_N fmc0:HB09_N fmc0:HB09_P fmc0:HB14_N fmc0:HB14_P fmc0:HB10_N fmc0:HB10_P fmc0:HB11_P')),
|
||||
Subsignal('clk', Pins('fmc0:HB06_CC_P')),
|
||||
IOStandard('LVCMOS18')),
|
||||
('afe_ctrl_dir', 0, Pins('fmc0:LA26_N fmc0:HB00_CC_N fmc0:HB17_CC_P'), IOStandard("LVCMOS18")),
|
||||
('afe_ctrl_oe_n', 0, Pins('fmc0:HB19_N'), IOStandard("LVCMOS18")),
|
||||
('afe_relay', 0,
|
||||
Subsignal('clk', Pins('fmc0:LA02_N')),
|
||||
Subsignal('mosi', Pins('fmc0:LA00_CC_N')),
|
||||
Subsignal('cs_n', Pins('fmc0:LA02_P fmc0:LA01_CC_N')),
|
||||
IOStandard("LVCMOS18")),
|
||||
('afe_adc_spi', 0,
|
||||
Subsignal('clk', Pins('fmc0:LA29_P')),
|
||||
Subsignal('mosi', Pins('fmc0:LA29_N')),
|
||||
Subsignal('miso', Pins('fmc0:LA30_N')),
|
||||
Subsignal('cs_n', Pins('fmc0:LA28_P')),
|
||||
IOStandard("LVCMOS18")),
|
||||
('afe_adc_error_n', 0, Pins('fmc0:LA28_N'), IOStandard("LVCMOS18")),
|
||||
]
|
||||
|
||||
platform.add_extension(shuttler_io)
|
||||
|
@ -167,6 +182,25 @@ class Satellite(BaseSoC, AMPSoC):
|
|||
self.csr_devices.append("shuttler")
|
||||
self.rtio_channels.extend(rtio.Channel.from_phy(phy) for phy in self.shuttler.phys)
|
||||
|
||||
afe_dir = platform.request("afe_ctrl_dir")
|
||||
self.comb += afe_dir.eq(0b011)
|
||||
|
||||
afe_oe = platform.request("afe_ctrl_oe_n")
|
||||
self.comb += afe_oe.eq(0)
|
||||
|
||||
relay_led_phy = rtio_spi.SPIMaster(self.platform.request("afe_relay"))
|
||||
self.submodules += relay_led_phy
|
||||
print("SHUTTLER RELAY at RTIO channel 0x{:06x}".format(len(self.rtio_channels)))
|
||||
self.rtio_channels.append(rtio.Channel.from_phy(relay_led_phy))
|
||||
|
||||
adc_error_n = platform.request("afe_adc_error_n")
|
||||
self.comb += adc_error_n.eq(1)
|
||||
|
||||
adc_spi = rtio_spi.SPIMaster(self.platform.request("afe_adc_spi"))
|
||||
self.submodules += adc_spi
|
||||
print("SHUTTLER ADC at RTIO channel 0x{:06x}".format(len(self.rtio_channels)))
|
||||
self.rtio_channels.append(rtio.Channel.from_phy(adc_spi))
|
||||
|
||||
self.config["HAS_RTIO_LOG"] = None
|
||||
self.config["RTIO_LOG_CHANNEL"] = len(self.rtio_channels)
|
||||
self.rtio_channels.append(rtio.LogChannel())
|
||||
|
|
|
@ -70,6 +70,8 @@ class StandaloneBase(MiniSoC, AMPSoC):
|
|||
AMPSoC.__init__(self)
|
||||
add_identifier(self, gateware_identifier_str=gateware_identifier_str)
|
||||
|
||||
self.config["DRTIO_ROLE"] = "standalone"
|
||||
|
||||
if self.platform.hw_rev == "v2.0":
|
||||
self.submodules.error_led = gpio.GPIOOut(Cat(
|
||||
self.platform.request("error_led")))
|
||||
|
@ -317,6 +319,7 @@ class MasterBase(MiniSoC, AMPSoC):
|
|||
self.add_memory_region(memory_name, memory_address | self.shadow_base, 0x800)
|
||||
self.config["HAS_DRTIO"] = None
|
||||
self.config["HAS_DRTIO_ROUTING"] = None
|
||||
self.config["DRTIO_ROLE"] = "master"
|
||||
|
||||
rtio_clk_period = 1e9/rtio_clk_freq
|
||||
gtp = self.gt_drtio.gtps[0]
|
||||
|
@ -565,6 +568,7 @@ class SatelliteBase(BaseSoC, AMPSoC):
|
|||
self.add_memory_region(memory_name, memory_address | self.shadow_base, 0x800)
|
||||
self.config["HAS_DRTIO"] = None
|
||||
self.config["HAS_DRTIO_ROUTING"] = None
|
||||
self.config["DRTIO_ROLE"] = "satellite"
|
||||
self.add_csr_group("drtioaux", drtioaux_csr_group)
|
||||
self.add_memory_group("drtioaux_mem", drtioaux_memory_group)
|
||||
self.add_csr_group("drtiorep", drtiorep_csr_group)
|
||||
|
|
|
@ -22,7 +22,6 @@ class GenericStandalone(StandaloneBase):
|
|||
hw_rev = description["hw_rev"]
|
||||
self.class_name_override = description["variant"]
|
||||
StandaloneBase.__init__(self, hw_rev=hw_rev, **kwargs)
|
||||
self.config["DRTIO_ROLE"] = description["drtio_role"]
|
||||
self.config["RTIO_FREQUENCY"] = "{:.1f}".format(description["rtio_frequency"]/1e6)
|
||||
if "ext_ref_frequency" in description:
|
||||
self.config["SI5324_EXT_REF"] = None
|
||||
|
@ -71,14 +70,13 @@ class GenericMaster(MasterBase):
|
|||
if hw_rev is None:
|
||||
hw_rev = description["hw_rev"]
|
||||
self.class_name_override = description["variant"]
|
||||
has_drtio_over_eem = any(peripheral["type"] == "efc" for peripheral in description["peripherals"])
|
||||
has_drtio_over_eem = any(peripheral["type"] == "shuttler" for peripheral in description["peripherals"])
|
||||
MasterBase.__init__(self,
|
||||
hw_rev=hw_rev,
|
||||
rtio_clk_freq=description["rtio_frequency"],
|
||||
enable_sata=description["enable_sata_drtio"],
|
||||
enable_sys5x=has_drtio_over_eem,
|
||||
**kwargs)
|
||||
self.config["DRTIO_ROLE"] = description["drtio_role"]
|
||||
if "ext_ref_frequency" in description:
|
||||
self.config["SI5324_EXT_REF"] = None
|
||||
self.config["EXT_REF_FREQUENCY"] = "{:.1f}".format(
|
||||
|
@ -123,7 +121,6 @@ class GenericSatellite(SatelliteBase):
|
|||
rtio_clk_freq=description["rtio_frequency"],
|
||||
enable_sata=description["enable_sata_drtio"],
|
||||
**kwargs)
|
||||
self.config["DRTIO_ROLE"] = description["drtio_role"]
|
||||
if hw_rev == "v1.0":
|
||||
# EEM clock fan-out from Si5324, not MMCX
|
||||
self.comb += self.platform.request("clk_sel").eq(1)
|
||||
|
@ -177,9 +174,9 @@ def main():
|
|||
else:
|
||||
raise ValueError("Invalid DRTIO role")
|
||||
|
||||
has_efc = any(peripheral["type"] == "efc" for peripheral in description["peripherals"])
|
||||
if has_efc and (description["drtio_role"] == "standalone"):
|
||||
raise ValueError("EFC requires DRTIO, please switch role to master")
|
||||
has_shuttler = any(peripheral["type"] == "shuttler" for peripheral in description["peripherals"])
|
||||
if has_shuttler and (description["drtio_role"] == "standalone"):
|
||||
raise ValueError("Shuttler requires DRTIO, please switch role to master")
|
||||
|
||||
soc = cls(description, gateware_identifier_str=args.gateware_identifier_str, **soc_kasli_argdict(args))
|
||||
args.variant = description["variant"]
|
||||
|
|
|
@ -14,17 +14,181 @@ from sipyco.pipe_ipc import AsyncioParentComm
|
|||
from sipyco.logging_tools import LogParser
|
||||
from sipyco import pyon
|
||||
|
||||
from artiq.gui.tools import QDockWidgetCloseDetect, LayoutWidget
|
||||
from artiq.gui.entries import procdesc_to_entry
|
||||
from artiq.gui.tools import (QDockWidgetCloseDetect, LayoutWidget,
|
||||
WheelFilter)
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
class EntryArea(QtWidgets.QTreeWidget):
|
||||
def __init__(self):
|
||||
QtWidgets.QTreeWidget.__init__(self)
|
||||
self.setColumnCount(3)
|
||||
self.header().setStretchLastSection(False)
|
||||
if hasattr(self.header(), "setSectionResizeMode"):
|
||||
set_resize_mode = self.header().setSectionResizeMode
|
||||
else:
|
||||
set_resize_mode = self.header().setResizeMode
|
||||
set_resize_mode(0, QtWidgets.QHeaderView.ResizeToContents)
|
||||
set_resize_mode(1, QtWidgets.QHeaderView.Stretch)
|
||||
self.header().setVisible(False)
|
||||
self.setSelectionMode(self.NoSelection)
|
||||
self.setHorizontalScrollMode(self.ScrollPerPixel)
|
||||
self.setVerticalScrollMode(self.ScrollPerPixel)
|
||||
|
||||
self.setStyleSheet("QTreeWidget {background: " +
|
||||
self.palette().midlight().color().name() + " ;}")
|
||||
|
||||
self.viewport().installEventFilter(WheelFilter(self.viewport(), True))
|
||||
|
||||
self._groups = dict()
|
||||
self._arg_to_widgets = dict()
|
||||
self._arguments = dict()
|
||||
|
||||
self.gradient = QtGui.QLinearGradient(
|
||||
0, 0, 0, QtGui.QFontMetrics(self.font()).lineSpacing()*2.5)
|
||||
self.gradient.setColorAt(0, self.palette().base().color())
|
||||
self.gradient.setColorAt(1, self.palette().midlight().color())
|
||||
|
||||
reset_all_button = QtWidgets.QPushButton("Restore defaults")
|
||||
reset_all_button.setToolTip("Reset all to default values")
|
||||
reset_all_button.setIcon(
|
||||
QtWidgets.QApplication.style().standardIcon(
|
||||
QtWidgets.QStyle.SP_BrowserReload))
|
||||
reset_all_button.clicked.connect(self.reset_all)
|
||||
buttons = LayoutWidget()
|
||||
buttons.layout.setColumnStretch(0, 1)
|
||||
buttons.layout.setColumnStretch(1, 0)
|
||||
buttons.layout.setColumnStretch(2, 1)
|
||||
buttons.addWidget(reset_all_button, 0, 1)
|
||||
self.bottom_item = QtWidgets.QTreeWidgetItem()
|
||||
self.addTopLevelItem(self.bottom_item)
|
||||
self.setItemWidget(self.bottom_item, 1, buttons)
|
||||
self.bottom_item.setHidden(True)
|
||||
|
||||
def setattr_argument(self, name, proc, group=None, tooltip=None):
|
||||
argument = dict()
|
||||
desc = proc.describe()
|
||||
argument["desc"] = desc
|
||||
argument["group"] = group
|
||||
argument["tooltip"] = tooltip
|
||||
self._arguments[name] = argument
|
||||
widgets = dict()
|
||||
self._arg_to_widgets[name] = widgets
|
||||
entry_class = procdesc_to_entry(argument["desc"])
|
||||
argument["state"] = entry_class.default_state(argument["desc"])
|
||||
entry = entry_class(argument)
|
||||
widget_item = QtWidgets.QTreeWidgetItem([name])
|
||||
if argument["tooltip"]:
|
||||
widget_item.setToolTip(0, argument["tooltip"])
|
||||
widgets["entry"] = entry
|
||||
widgets["widget_item"] = widget_item
|
||||
|
||||
if len(self._arguments) > 1:
|
||||
self.bottom_item.setHidden(False)
|
||||
|
||||
for col in range(3):
|
||||
widget_item.setBackground(col, self.gradient)
|
||||
font = widget_item.font(0)
|
||||
font.setBold(True)
|
||||
widget_item.setFont(0, font)
|
||||
|
||||
if argument["group"] is None:
|
||||
self.insertTopLevelItem(self.indexFromItem(self.bottom_item).row(), widget_item)
|
||||
else:
|
||||
self._get_group(argument["group"]).addChild(widget_item)
|
||||
self.bottom_item.setHidden(False)
|
||||
fix_layout = LayoutWidget()
|
||||
widgets["fix_layout"] = fix_layout
|
||||
fix_layout.addWidget(entry)
|
||||
self.setItemWidget(widget_item, 1, fix_layout)
|
||||
|
||||
reset_value = QtWidgets.QToolButton()
|
||||
reset_value.setToolTip("Reset to default value")
|
||||
reset_value.setIcon(
|
||||
QtWidgets.QApplication.style().standardIcon(
|
||||
QtWidgets.QStyle.SP_BrowserReload))
|
||||
reset_value.clicked.connect(partial(self.reset_value, name))
|
||||
|
||||
tool_buttons = LayoutWidget()
|
||||
tool_buttons.addWidget(reset_value, 0)
|
||||
self.setItemWidget(widget_item, 2, tool_buttons)
|
||||
|
||||
def _get_group(self, name):
|
||||
if name in self._groups:
|
||||
return self._groups[name]
|
||||
group = QtWidgets.QTreeWidgetItem([name])
|
||||
for col in range(3):
|
||||
group.setBackground(col, self.palette().mid())
|
||||
group.setForeground(col, self.palette().brightText())
|
||||
font = group.font(col)
|
||||
font.setBold(True)
|
||||
group.setFont(col, font)
|
||||
self.insertTopLevelItem(self.indexFromItem(self.bottom_item).row(), group)
|
||||
self._groups[name] = group
|
||||
return group
|
||||
|
||||
def __getattr__(self, name):
|
||||
return self.get_value(name)
|
||||
|
||||
def get_value(self, name):
|
||||
entry = self._arg_to_widgets[name]["entry"]
|
||||
argument = self._arguments[name]
|
||||
return entry.state_to_value(argument["state"])
|
||||
|
||||
def set_value(self, name, value):
|
||||
ty = self._arguments[name]["desc"]["ty"]
|
||||
if ty == "Scannable":
|
||||
desc = value.describe()
|
||||
self._arguments[name]["state"][desc["ty"]] = desc
|
||||
self._arguments[name]["state"]["selected"] = desc["ty"]
|
||||
else:
|
||||
self._arguments[name]["state"] = value
|
||||
self.update_value(name)
|
||||
|
||||
def get_values(self):
|
||||
d = dict()
|
||||
for name in self._arguments.keys():
|
||||
d[name] = self.get_value(name)
|
||||
return d
|
||||
|
||||
def set_values(self, values):
|
||||
for name, value in values.items():
|
||||
self.set_value(name, value)
|
||||
|
||||
def update_value(self, name):
|
||||
widgets = self._arg_to_widgets[name]
|
||||
argument = self._arguments[name]
|
||||
|
||||
# Qt needs a setItemWidget() to handle layout correctly,
|
||||
# simply replacing the entry inside the LayoutWidget
|
||||
# results in a bug.
|
||||
|
||||
widgets["entry"].deleteLater()
|
||||
widgets["entry"] = procdesc_to_entry(argument["desc"])(argument)
|
||||
widgets["fix_layout"].deleteLater()
|
||||
widgets["fix_layout"] = LayoutWidget()
|
||||
widgets["fix_layout"].addWidget(widgets["entry"])
|
||||
self.setItemWidget(widgets["widget_item"], 1, widgets["fix_layout"])
|
||||
self.updateGeometries()
|
||||
|
||||
def reset_value(self, name):
|
||||
procdesc = self._arguments[name]["desc"]
|
||||
self._arguments[name]["state"] = procdesc_to_entry(procdesc).default_state(procdesc)
|
||||
self.update_value(name)
|
||||
|
||||
def reset_all(self):
|
||||
for name in self._arguments.keys():
|
||||
self.reset_value(name)
|
||||
|
||||
|
||||
class AppletIPCServer(AsyncioParentComm):
|
||||
def __init__(self, dataset_sub, dataset_ctl):
|
||||
def __init__(self, dataset_sub, dataset_ctl, expmgr):
|
||||
AsyncioParentComm.__init__(self)
|
||||
self.dataset_sub = dataset_sub
|
||||
self.dataset_ctl = dataset_ctl
|
||||
self.expmgr = expmgr
|
||||
self.datasets = set()
|
||||
self.dataset_prefixes = []
|
||||
|
||||
|
@ -50,6 +214,13 @@ class AppletIPCServer(AsyncioParentComm):
|
|||
|
||||
def _on_mod(self, mod):
|
||||
if mod["action"] == "init":
|
||||
if not (self.datasets or self.dataset_prefixes):
|
||||
# The dataset db connection just came online, and an applet is
|
||||
# running but did not call `subscribe` yet (e.g. because the
|
||||
# dashboard was just restarted and a previously enabled applet
|
||||
# is being re-opened). We will later synthesize an "init" `mod`
|
||||
# message once the applet actually subscribes.
|
||||
return
|
||||
mod = self._synthesize_init(mod["struct"])
|
||||
else:
|
||||
if mod["path"]:
|
||||
|
@ -83,6 +254,8 @@ class AppletIPCServer(AsyncioParentComm):
|
|||
await self.dataset_ctl.set(obj["key"], obj["value"], metadata=obj["metadata"], persist=obj["persist"])
|
||||
elif action == "update_dataset":
|
||||
await self.dataset_ctl.update(obj["mod"])
|
||||
elif action == "set_argument_value":
|
||||
self.expmgr.set_argument_value(obj["expurl"], obj["name"], obj["value"])
|
||||
else:
|
||||
raise ValueError("unknown action in applet message")
|
||||
except:
|
||||
|
@ -108,7 +281,7 @@ class AppletIPCServer(AsyncioParentComm):
|
|||
|
||||
|
||||
class _AppletDock(QDockWidgetCloseDetect):
|
||||
def __init__(self, dataset_sub, dataset_ctl, uid, name, spec, extra_substitutes):
|
||||
def __init__(self, dataset_sub, dataset_ctl, expmgr, uid, name, spec, extra_substitutes):
|
||||
QDockWidgetCloseDetect.__init__(self, "Applet: " + name)
|
||||
self.setObjectName("applet" + str(uid))
|
||||
|
||||
|
@ -118,6 +291,7 @@ class _AppletDock(QDockWidgetCloseDetect):
|
|||
|
||||
self.dataset_sub = dataset_sub
|
||||
self.dataset_ctl = dataset_ctl
|
||||
self.expmgr = expmgr
|
||||
self.applet_name = name
|
||||
self.spec = spec
|
||||
self.extra_substitutes = extra_substitutes
|
||||
|
@ -136,7 +310,7 @@ class _AppletDock(QDockWidgetCloseDetect):
|
|||
return
|
||||
self.starting_stopping = True
|
||||
try:
|
||||
self.ipc = AppletIPCServer(self.dataset_sub, self.dataset_ctl)
|
||||
self.ipc = AppletIPCServer(self.dataset_sub, self.dataset_ctl, self.expmgr)
|
||||
env = os.environ.copy()
|
||||
env["PYTHONUNBUFFERED"] = "1"
|
||||
env["ARTIQ_APPLET_EMBED"] = self.ipc.get_address()
|
||||
|
@ -333,7 +507,7 @@ class _CompleterDelegate(QtWidgets.QStyledItemDelegate):
|
|||
|
||||
|
||||
class AppletsDock(QtWidgets.QDockWidget):
|
||||
def __init__(self, main_window, dataset_sub, dataset_ctl, extra_substitutes={}, *, loop=None):
|
||||
def __init__(self, main_window, dataset_sub, dataset_ctl, expmgr, extra_substitutes={}, *, loop=None):
|
||||
"""
|
||||
:param extra_substitutes: Map of extra ``${strings}`` to substitute in applet
|
||||
commands to their respective values.
|
||||
|
@ -346,6 +520,7 @@ class AppletsDock(QtWidgets.QDockWidget):
|
|||
self.main_window = main_window
|
||||
self.dataset_sub = dataset_sub
|
||||
self.dataset_ctl = dataset_ctl
|
||||
self.expmgr = expmgr
|
||||
self.extra_substitutes = extra_substitutes
|
||||
self.applet_uids = set()
|
||||
|
||||
|
@ -397,11 +572,12 @@ class AppletsDock(QtWidgets.QDockWidget):
|
|||
delete_action.setShortcutContext(QtCore.Qt.WidgetShortcut)
|
||||
delete_action.triggered.connect(self.delete)
|
||||
self.table.addAction(delete_action)
|
||||
close_all_action = QtWidgets.QAction("Close all applets", self.table)
|
||||
close_all_action.setShortcut("CTRL+ALT+W")
|
||||
close_all_action.setShortcutContext(QtCore.Qt.ApplicationShortcut)
|
||||
close_all_action.triggered.connect(self.close_all)
|
||||
self.table.addAction(close_all_action)
|
||||
close_nondocked_action = QtWidgets.QAction("Close non-docked applets", self.table)
|
||||
close_nondocked_action.setShortcut("CTRL+ALT+W")
|
||||
close_nondocked_action.setShortcutContext(QtCore.Qt.ApplicationShortcut)
|
||||
close_nondocked_action.triggered.connect(self.close_nondocked)
|
||||
self.table.addAction(close_nondocked_action)
|
||||
|
||||
new_group_action = QtWidgets.QAction("New group", self.table)
|
||||
new_group_action.triggered.connect(partial(self.new_with_parent, self.new_group))
|
||||
self.table.addAction(new_group_action)
|
||||
|
@ -447,7 +623,7 @@ class AppletsDock(QtWidgets.QDockWidget):
|
|||
self.table.itemChanged.connect(self.item_changed)
|
||||
|
||||
def create(self, item, name, spec):
|
||||
dock = _AppletDock(self.dataset_sub, self.dataset_ctl, item.applet_uid, name, spec, self.extra_substitutes)
|
||||
dock = _AppletDock(self.dataset_sub, self.dataset_ctl, self.expmgr, item.applet_uid, name, spec, self.extra_substitutes)
|
||||
self.main_window.addDockWidget(QtCore.Qt.RightDockWidgetArea, dock)
|
||||
dock.setFloating(True)
|
||||
asyncio.ensure_future(dock.start(), loop=self._loop)
|
||||
|
@ -674,12 +850,15 @@ class AppletsDock(QtWidgets.QDockWidget):
|
|||
def restore_state(self, state):
|
||||
self.restore_state_item(state, None)
|
||||
|
||||
def close_all(self):
|
||||
def close_nondocked(self):
|
||||
def walk(wi):
|
||||
for i in range(wi.childCount()):
|
||||
cwi = wi.child(i)
|
||||
if cwi.ty == "applet":
|
||||
if cwi.checkState(0) == QtCore.Qt.Checked:
|
||||
if cwi.applet_dock is not None:
|
||||
if not cwi.applet_dock.isFloating():
|
||||
continue
|
||||
cwi.setCheckState(0, QtCore.Qt.Unchecked)
|
||||
elif cwi.ty == "group":
|
||||
walk(cwi)
|
||||
|
|
|
@ -222,9 +222,6 @@ class _RangeScan(LayoutWidget):
|
|||
|
||||
start = ScientificSpinBox()
|
||||
start.setStyleSheet("QDoubleSpinBox {color:blue}")
|
||||
start.setMinimumSize(110, 0)
|
||||
start.setSizePolicy(QtWidgets.QSizePolicy(
|
||||
QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed))
|
||||
disable_scroll_wheel(start)
|
||||
self.addWidget(start, 0, 1)
|
||||
|
||||
|
@ -236,13 +233,15 @@ class _RangeScan(LayoutWidget):
|
|||
|
||||
stop = ScientificSpinBox()
|
||||
stop.setStyleSheet("QDoubleSpinBox {color:red}")
|
||||
stop.setMinimumSize(110, 0)
|
||||
disable_scroll_wheel(stop)
|
||||
self.addWidget(stop, 2, 1)
|
||||
|
||||
randomize = QtWidgets.QCheckBox("Randomize")
|
||||
self.addWidget(randomize, 3, 1)
|
||||
|
||||
self.layout.setColumnStretch(0, 4)
|
||||
self.layout.setColumnStretch(1, 1)
|
||||
|
||||
apply_properties(start)
|
||||
start.setSigFigs()
|
||||
start.setRelativeStep()
|
||||
|
|
|
@ -16,10 +16,16 @@ def log_level_to_name(level):
|
|||
return "DEBUG"
|
||||
|
||||
|
||||
class _WheelFilter(QtCore.QObject):
|
||||
class WheelFilter(QtCore.QObject):
|
||||
def __init__(self, parent, ignore_with_modifier=False):
|
||||
super().__init__(parent)
|
||||
self.ignore_with_modifier = ignore_with_modifier
|
||||
|
||||
def eventFilter(self, obj, event):
|
||||
if (event.type() == QtCore.QEvent.Wheel and
|
||||
event.modifiers() == QtCore.Qt.NoModifier):
|
||||
if event.type() != QtCore.QEvent.Wheel:
|
||||
return False
|
||||
has_modifier = event.modifiers() != QtCore.Qt.NoModifier
|
||||
if has_modifier == self.ignore_with_modifier:
|
||||
event.ignore()
|
||||
return True
|
||||
return False
|
||||
|
@ -27,7 +33,7 @@ class _WheelFilter(QtCore.QObject):
|
|||
|
||||
def disable_scroll_wheel(widget):
|
||||
widget.setFocusPolicy(QtCore.Qt.StrongFocus)
|
||||
widget.installEventFilter(_WheelFilter(widget))
|
||||
widget.installEventFilter(WheelFilter(widget))
|
||||
|
||||
|
||||
class QDockWidgetCloseDetect(QtWidgets.QDockWidget):
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
# RUN: env ARTIQ_DUMP_LLVM=%t %python -m artiq.compiler.testbench.embedding +compile %s
|
||||
# RUN: OutputCheck %s --file-to-check=%t.ll
|
||||
|
||||
from typing import List, Tuple
|
||||
|
||||
import numpy as np
|
||||
|
||||
from artiq.language.core import *
|
||||
from artiq.language.types import *
|
||||
|
||||
# CHECK-L: i64 @_Z13testbench.foozz(i64 %ARG.x, { i1, i32 } %ARG.y)
|
||||
|
||||
@kernel
|
||||
def foo(x: np.int64, y: np.int32 = 1) -> np.int64:
|
||||
print(x + y)
|
||||
return x + y
|
||||
|
||||
# CHECK-L: void @_Z13testbench.barzz()
|
||||
@kernel
|
||||
def bar(x: np.int32) -> None:
|
||||
print(x)
|
||||
|
||||
# CHECK-L: @_Z21testbench.unpack_listzz({ i1, i64 }* nocapture writeonly sret({ i1, i64 }) %.1, { i64*, i32 }* %ARG.xs)
|
||||
@kernel
|
||||
def unpack_list(xs: List[np.int64]) -> Tuple[bool, np.int64]:
|
||||
print(xs)
|
||||
return (len(xs) == 1, xs[0])
|
||||
|
||||
@kernel
|
||||
def entrypoint():
|
||||
print(foo(0, 2))
|
||||
print(foo(1, 3))
|
||||
bar(3)
|
||||
print(unpack_list([1, 2, 3]))
|
|
@ -0,0 +1,51 @@
|
|||
# RUN: %python -m artiq.compiler.testbench.embedding %s
|
||||
|
||||
from artiq.language.core import *
|
||||
|
||||
|
||||
class InnerA:
|
||||
def __init__(self, val):
|
||||
self.val = val
|
||||
|
||||
@kernel
|
||||
def run_once(self):
|
||||
return self.val
|
||||
|
||||
|
||||
class InnerB:
|
||||
def __init__(self, val):
|
||||
self.val = val
|
||||
|
||||
@kernel
|
||||
def run_once(self):
|
||||
return self.val
|
||||
|
||||
|
||||
def make_runner(InnerCls, val):
|
||||
class Runner:
|
||||
def __init__(self):
|
||||
self.inner = InnerCls(val)
|
||||
|
||||
@kernel
|
||||
def run_once(self):
|
||||
return self.inner.run_once()
|
||||
|
||||
return Runner()
|
||||
|
||||
|
||||
class Parent:
|
||||
def __init__(self):
|
||||
self.a = make_runner(InnerA, 1)
|
||||
self.b = make_runner(InnerB, 42.0)
|
||||
|
||||
@kernel
|
||||
def run_once(self):
|
||||
return self.a.run_once() + self.b.run_once()
|
||||
|
||||
|
||||
parent = Parent()
|
||||
|
||||
|
||||
@kernel
|
||||
def entrypoint():
|
||||
parent.run_once()
|
|
@ -0,0 +1,16 @@
|
|||
# RUN: %python -m artiq.compiler.testbench.embedding %s
|
||||
|
||||
from artiq.language.core import *
|
||||
from artiq.language.types import *
|
||||
|
||||
@kernel
|
||||
def consume_tuple(x: TTuple([TInt32, TBool])):
|
||||
print(x)
|
||||
|
||||
@kernel
|
||||
def return_tuple() -> TTuple([TInt32, TBool]):
|
||||
return (123, False)
|
||||
|
||||
@kernel
|
||||
def entrypoint():
|
||||
consume_tuple(return_tuple())
|
|
@ -35,7 +35,7 @@ mock_modules = ["artiq.gui.waitingspinnerwidget",
|
|||
"sipyco", "sipyco.pc_rpc", "sipyco.sync_struct",
|
||||
"sipyco.asyncio_tools", "sipyco.logging_tools",
|
||||
"sipyco.broadcast", "sipyco.packed_exceptions",
|
||||
"sipyco.keepalive"]
|
||||
"sipyco.keepalive", "sipyco.pipe_ipc"]
|
||||
|
||||
for module in mock_modules:
|
||||
sys.modules[module] = Mock()
|
||||
|
|
|
@ -137,6 +137,12 @@ DAC/ADC drivers
|
|||
.. automodule:: artiq.coredevice.fastino
|
||||
:members:
|
||||
|
||||
:mod:`artiq.coredevice.shuttler` module
|
||||
++++++++++++++++++++++++++++++++++++++++
|
||||
|
||||
.. automodule:: artiq.coredevice.shuttler
|
||||
:members:
|
||||
|
||||
|
||||
Miscellaneous
|
||||
-------------
|
||||
|
|
|
@ -87,6 +87,41 @@ You can create directories containing each a ``flake.nix`` that correspond to di
|
|||
|
||||
If your favorite package is not available with Nix, contact us using the helpdesk@ email.
|
||||
|
||||
Troubleshooting
|
||||
^^^^^^^^^^^^^^^
|
||||
|
||||
"Ignoring untrusted substituter, you are not a trusted user"
|
||||
""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
|
||||
|
||||
If the following message displays when running ``nix shell`` or ``nix develop``
|
||||
|
||||
::
|
||||
|
||||
warning: ignoring untrusted substituter 'https://nixbld.m-labs.hk', you are not a trusted user.
|
||||
Run `man nix.conf` for more information on the `substituters` configuration option.
|
||||
|
||||
and Nix proceeds to build some packages from source, this means that you are using `multi-user mode <https://nixos.org/manual/nix/stable/installation/multi-user>`_ in Nix, for example when Nix is installed via ``pacman`` in Arch Linux.
|
||||
|
||||
By default, users accessing Nix in multi-user mode are "unprivileged" and cannot use untrusted substituters. To change this, edit ``/etc/nix/nix.conf`` and add the following line (or append to the key if the key already exists):
|
||||
|
||||
::
|
||||
|
||||
trusted-substituters = https://nixbld.m-labs.hk
|
||||
|
||||
This will add the substituter as a trusted substituter for all users using Nix.
|
||||
|
||||
Alternatively, add the following line:
|
||||
|
||||
::
|
||||
|
||||
trusted-users = <username> # Replace <username> with the user invoking `nix`
|
||||
|
||||
This will set your user as a trusted user, allowing the use of any untrusted substituters.
|
||||
|
||||
.. warning::
|
||||
|
||||
Setting users as trusted users will effectively grant root access to those users. See the `Nix documentation <https://nixos.org/manual/nix/stable/command-ref/conf-file#conf-trusted-users>`_ for more information.
|
||||
|
||||
Installing via MSYS2 (Windows)
|
||||
------------------------------
|
||||
|
||||
|
|
|
@ -143,6 +143,77 @@ CCBs are used by experiments to configure applets in the dashboard, for example
|
|||
.. autoclass:: artiq.dashboard.applets_ccb.AppletsCCBDock
|
||||
:members:
|
||||
|
||||
Applet request interfaces
|
||||
*************************
|
||||
|
||||
Applet request interfaces allow applets to perform actions on the master database and set arguments in the dashboard. Applets may inherit from the ``artiq.applets.simple.SimpleApplet`` and call the methods defined below through the `req` attribute.
|
||||
|
||||
Embedded applets should use `AppletRequestIPC` while standalone applets use `AppletRequestRPC`. `SimpleApplet` automatically chooses the correct interface on initialization.
|
||||
|
||||
.. autoclass:: artiq.applets.simple._AppletRequestInterface
|
||||
:members:
|
||||
|
||||
|
||||
Applet entry area
|
||||
*****************
|
||||
|
||||
Extensions are provided to enable the use of argument widgets in applets through the `EntryArea` class.
|
||||
|
||||
Below is a simple example code snippet using the `EntryArea` class: ::
|
||||
|
||||
# Create the experiment area
|
||||
entry_area = EntryArea()
|
||||
|
||||
# Create a new widget
|
||||
entry_area.setattr_argument("bl", BooleanValue(True))
|
||||
|
||||
# Get the value of the widget (output: True)
|
||||
print(entry_area.bl)
|
||||
|
||||
# Set the value
|
||||
entry_area.set_value("bl", False)
|
||||
|
||||
# False
|
||||
print(entry_area.bl)
|
||||
|
||||
The `EntryArea` object can then be added to a layout and integrated with the applet GUI. Multiple `EntryArea` objects can be used in a single applet.
|
||||
|
||||
.. class:: artiq.gui.applets.EntryArea
|
||||
|
||||
.. method:: setattr_argument(name, proc, group=None, tooltip=None)
|
||||
|
||||
Sets an argument as attribute. The names of the argument and of the
|
||||
attribute are the same.
|
||||
|
||||
:param name: Argument name
|
||||
:param proc: Argument processor, for example ``NumberValue``
|
||||
:param group: Used to group together arguments in the GUI under a common category
|
||||
:param tooltip: Tooltip displayed when hovering over the entry widget
|
||||
|
||||
.. method:: get_value(name)
|
||||
|
||||
Get the value of an entry widget.
|
||||
|
||||
:param name: Argument name
|
||||
|
||||
.. method:: get_values()
|
||||
|
||||
Get all values in the ``EntryArea`` as a dictionary. Names are stored as keys, and argument values as values.
|
||||
|
||||
.. method:: set_value(name, value)
|
||||
|
||||
Set the value of an entry widget. The change is temporary and will reset to default when the reset button is clicked.
|
||||
|
||||
:param name: Argument name
|
||||
:param value: Object representing the new value of the argument. For ``Scannable`` arguments, this parameter
|
||||
should be a ``ScanObject``. The type of the ``ScanObject`` will be set as the selected type when this function is called.
|
||||
|
||||
.. method:: set_values(values)
|
||||
|
||||
Set multiple values from a dictionary input. Calls ``set_value()`` for each key-value pair.
|
||||
|
||||
:param values: Dictionary with names as keys and new argument values as values.
|
||||
|
||||
|
||||
Front-end tool reference
|
||||
************************
|
||||
|
|
Loading…
Reference in New Issue