forked from M-Labs/artiq
1
0
Fork 0

Merge branch 'master' into nac3

This commit is contained in:
Sebastien Bourdeauducq 2023-10-06 12:12:55 +08:00
commit fc082b62de
45 changed files with 2105 additions and 403 deletions

View File

@ -25,9 +25,17 @@ Highlights:
support legacy installations, but may be removed in a future release. support legacy installations, but may be removed in a future release.
* Added channel names to RTIO errors. * Added channel names to RTIO errors.
* Full Python 3.10 support. * 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 * 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. 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 * Persistent datasets are now stored in a LMDB database for improved performance. PYON databases can
be converted with the script below. be converted with the script below.

View File

@ -22,10 +22,10 @@ class QCancellableLineEdit(QtWidgets.QLineEdit):
class NumberWidget(QtWidgets.QStackedWidget): class NumberWidget(QtWidgets.QStackedWidget):
def __init__(self, args, ctl): def __init__(self, args, req):
QtWidgets.QStackedWidget.__init__(self) QtWidgets.QStackedWidget.__init__(self)
self.dataset_name = args.dataset self.dataset_name = args.dataset
self.ctl = ctl self.req = req
self.lcd_widget = QResponsiveLCDNumber() self.lcd_widget = QResponsiveLCDNumber()
self.lcd_widget.setDigitCount(args.digit_count) self.lcd_widget.setDigitCount(args.digit_count)
@ -55,7 +55,7 @@ class NumberWidget(QtWidgets.QStackedWidget):
def confirm_edit(self): def confirm_edit(self):
value = float(self.edit_widget.text()) 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) self.setCurrentWidget(self.lcd_widget)
def cancel_edit(self): def cancel_edit(self):

View File

@ -7,7 +7,7 @@ from artiq.applets.simple import SimpleApplet
class Image(pyqtgraph.ImageView): class Image(pyqtgraph.ImageView):
def __init__(self, args, ctl): def __init__(self, args, req):
pyqtgraph.ImageView.__init__(self) pyqtgraph.ImageView.__init__(self)
self.args = args self.args = args

View File

@ -8,7 +8,7 @@ from artiq.applets.simple import TitleApplet
class HistogramPlot(pyqtgraph.PlotWidget): class HistogramPlot(pyqtgraph.PlotWidget):
def __init__(self, args, ctl): def __init__(self, args, req):
pyqtgraph.PlotWidget.__init__(self) pyqtgraph.PlotWidget.__init__(self)
self.args = args self.args = args
self.timer = QTimer() self.timer = QTimer()

View File

@ -9,7 +9,7 @@ from artiq.applets.simple import TitleApplet
class XYPlot(pyqtgraph.PlotWidget): class XYPlot(pyqtgraph.PlotWidget):
def __init__(self, args, ctl): def __init__(self, args, req):
pyqtgraph.PlotWidget.__init__(self) pyqtgraph.PlotWidget.__init__(self)
self.args = args self.args = args
self.timer = QTimer() self.timer = QTimer()

View File

@ -22,7 +22,7 @@ def _compute_ys(histogram_bins, histograms_counts):
# pyqtgraph.GraphicsWindow fails to behave like a regular Qt widget # pyqtgraph.GraphicsWindow fails to behave like a regular Qt widget
# and breaks embedding. Do not use as top widget. # and breaks embedding. Do not use as top widget.
class XYHistPlot(QtWidgets.QSplitter): class XYHistPlot(QtWidgets.QSplitter):
def __init__(self, args, ctl): def __init__(self, args, req):
QtWidgets.QSplitter.__init__(self) QtWidgets.QSplitter.__init__(self)
self.resize(1000, 600) self.resize(1000, 600)
self.setWindowTitle("XY/Histogram") self.setWindowTitle("XY/Histogram")

View File

@ -6,7 +6,7 @@ from artiq.applets.simple import SimpleApplet
class ProgressWidget(QtWidgets.QProgressBar): class ProgressWidget(QtWidgets.QProgressBar):
def __init__(self, args, ctl): def __init__(self, args, req):
QtWidgets.QProgressBar.__init__(self) QtWidgets.QProgressBar.__init__(self)
self.setMinimum(args.min) self.setMinimum(args.min)
self.setMaximum(args.max) self.setMaximum(args.max)

View File

@ -11,11 +11,51 @@ from sipyco.pc_rpc import AsyncioClient as RPCClient
from sipyco import pyon from sipyco import pyon
from sipyco.pipe_ipc import AsyncioChildComm from sipyco.pipe_ipc import AsyncioChildComm
from artiq.language.scan import ScanObject
logger = logging.getLogger(__name__) 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): def __init__(self, ipc):
self.ipc = ipc self.ipc = ipc
@ -37,8 +77,13 @@ class AppletControlIPC:
mod = {"action": "append", "path": [key, 1], "x": value} mod = {"action": "append", "path": [key, 1], "x": value}
self.ipc.update_dataset(mod) 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): def __init__(self, loop, dataset_ctl):
self.loop = loop self.loop = loop
self.dataset_ctl = dataset_ctl self.dataset_ctl = dataset_ctl
@ -137,6 +182,12 @@ class AppletIPCClient(AsyncioChildComm):
self.write_pyon({"action": "update_dataset", self.write_pyon({"action": "update_dataset",
"mod": mod}) "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: class SimpleApplet:
def __init__(self, main_widget_class, cmd_description=None, def __init__(self, main_widget_class, cmd_description=None,
@ -200,21 +251,21 @@ class SimpleApplet:
if self.embed is not None: if self.embed is not None:
self.ipc.close() self.ipc.close()
def ctl_init(self): def req_init(self):
if self.embed is None: if self.embed is None:
dataset_ctl = RPCClient() dataset_ctl = RPCClient()
self.loop.run_until_complete(dataset_ctl.connect_rpc( self.loop.run_until_complete(dataset_ctl.connect_rpc(
self.args.server, self.args.port_control, "master_dataset_db")) 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: else:
self.ctl = AppletControlIPC(self.ipc) self.req = AppletRequestIPC(self.ipc)
def ctl_close(self): def req_close(self):
if self.embed is None: if self.embed is None:
self.ctl.dataset_ctl.close_rpc() self.req.dataset_ctl.close_rpc()
def create_main_widget(self): 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: if self.embed is not None:
self.ipc.set_close_cb(self.main_widget.close) self.ipc.set_close_cb(self.main_widget.close)
if os.name == "nt": if os.name == "nt":
@ -316,7 +367,7 @@ class SimpleApplet:
try: try:
self.ipc_init() self.ipc_init()
try: try:
self.ctl_init() self.req_init()
try: try:
self.create_main_widget() self.create_main_widget()
self.subscribe() self.subscribe()
@ -325,7 +376,7 @@ class SimpleApplet:
finally: finally:
self.unsubscribe() self.unsubscribe()
finally: finally:
self.ctl_close() self.req_close()
finally: finally:
self.ipc_close() self.ipc_close()
finally: finally:

View File

@ -10,22 +10,14 @@ import h5py
from sipyco import pyon from sipyco import pyon
from artiq import __artiq_dir__ as artiq_dir 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.gui.entries import procdesc_to_entry
from artiq.master.worker import Worker, log_worker_exception from artiq.master.worker import Worker, log_worker_exception
logger = logging.getLogger(__name__) 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): class _ArgumentEditor(QtWidgets.QTreeWidget):
def __init__(self, dock): def __init__(self, dock):
QtWidgets.QTreeWidget.__init__(self) QtWidgets.QTreeWidget.__init__(self)
@ -46,7 +38,7 @@ class _ArgumentEditor(QtWidgets.QTreeWidget):
self.setStyleSheet("QTreeWidget {background: " + self.setStyleSheet("QTreeWidget {background: " +
self.palette().midlight().color().name() + " ;}") self.palette().midlight().color().name() + " ;}")
self.viewport().installEventFilter(_WheelFilter(self.viewport())) self.viewport().installEventFilter(WheelFilter(self.viewport(), True))
self._groups = dict() self._groups = dict()
self._arg_to_widgets = dict() self._arg_to_widgets = dict()

View File

@ -62,7 +62,11 @@ class Core:
def close(self): def close(self):
self.comm.close() 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: if not self.analyzed:
self.compiler.analyze(core_language._registered_functions, core_language._registered_classes) self.compiler.analyze(core_language._registered_functions, core_language._registered_classes)
self.analyzed = True self.analyzed = True

View File

@ -142,7 +142,7 @@
"properties": { "properties": {
"type": { "type": {
"type": "string", "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": { "board": {
"type": "string" "type": "string"
@ -563,11 +563,11 @@
"required": ["ports"] "required": ["ports"]
} }
},{ },{
"title": "EFC", "title": "Shuttler",
"if": { "if": {
"properties": { "properties": {
"type": { "type": {
"const": "efc" "const": "shuttler"
} }
} }
}, },
@ -580,6 +580,10 @@
}, },
"minItems": 1, "minItems": 1,
"maxItems": 2 "maxItems": 2
},
"drtio_destination": {
"type": "integer",
"default": 4
} }
}, },
"required": ["ports"] "required": ["ports"]

View File

@ -2,23 +2,131 @@ import numpy
from artiq.language.core import * from artiq.language.core import *
from artiq.language.types 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: 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"): def __init__(self, dmgr, channel, core_device="core"):
self.core = dmgr.get(core_device) self.core = dmgr.get(core_device)
self.channel = channel 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 @kernel
def set_config(self, config): def set_clr(self, clr):
rtio_output(self.target_o, config) """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: 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"} kernel_invariants = {"core", "channel", "target_o"}
def __init__(self, dmgr, channel, core_device="core"): def __init__(self, dmgr, channel, core_device="core"):
@ -28,7 +136,35 @@ class Volt:
@kernel @kernel
def set_waveform(self, a0: TInt32, a1: TInt32, a2: TInt64, a3: TInt64): 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, a0,
a1, a1,
a1 >> 16, a1 >> 16,
@ -40,12 +176,36 @@ class Volt:
(a3 >> 32) & 0xFFFF, (a3 >> 32) & 0xFFFF,
] ]
for i in range(len(pdq_words)): for i in range(len(coef_words)):
rtio_output(self.target_o | i, pdq_words[i]) rtio_output(self.target_o | i, coef_words[i])
delay_mu(int64(self.core.ref_multiplier)) delay_mu(int64(self.core.ref_multiplier))
class Dds: 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"} kernel_invariants = {"core", "channel", "target_o"}
def __init__(self, dmgr, channel, core_device="core"): def __init__(self, dmgr, channel, core_device="core"):
@ -56,7 +216,45 @@ class Dds:
@kernel @kernel
def set_waveform(self, b0: TInt32, b1: TInt32, b2: TInt64, b3: TInt64, def set_waveform(self, b0: TInt32, b1: TInt32, b2: TInt64, b3: TInt64,
c0: TInt32, c1: TInt32, c2: TInt32): 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, b0,
b1, b1,
b1 >> 16, b1 >> 16,
@ -73,12 +271,17 @@ class Dds:
c2 >> 16, c2 >> 16,
] ]
for i in range(len(pdq_words)): for i in range(len(coef_words)):
rtio_output(self.target_o | i, pdq_words[i]) rtio_output(self.target_o | i, coef_words[i])
delay_mu(int64(self.core.ref_multiplier)) delay_mu(int64(self.core.ref_multiplier))
class Trigger: 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"} kernel_invariants = {"core", "channel", "target_o"}
def __init__(self, dmgr, channel, core_device="core"): def __init__(self, dmgr, channel, core_device="core"):
@ -88,4 +291,335 @@ class Trigger:
@kernel @kernel
def trigger(self, trig_out): 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) 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)

View File

@ -74,19 +74,20 @@ class CreateEditDialog(QtWidgets.QDialog):
self.key = key self.key = key
self.name_widget.setText(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: if metadata is not None:
scale = scale_from_metadata(metadata) scale = scale_from_metadata(metadata)
decoded_value = pyon.decode(value) t = value.dtype if value is np.ndarray else type(value)
if scale == 1: if scale != 1 and np.issubdtype(t, np.number):
self.value_widget.setText(value) # degenerates to float type
else: value_edit_string = self.value_to_edit_string(
self.value_widget.setText(pyon.encode(decoded_value / scale)) np.float64(value / scale))
self.unit_widget.setText(metadata.get('unit', '')) self.unit_widget.setText(metadata.get('unit', ''))
self.scale_widget.setText(str(metadata.get('scale', ''))) self.scale_widget.setText(str(metadata.get('scale', '')))
self.precision_widget.setText(str(metadata.get('precision', ''))) self.precision_widget.setText(str(metadata.get('precision', '')))
self.value_widget.setText(value_edit_string)
self.box_widget.setChecked(persist) self.box_widget.setChecked(persist)
def accept(self): def accept(self):
@ -104,11 +105,11 @@ class CreateEditDialog(QtWidgets.QDialog):
if precision != "": if precision != "":
metadata['precision'] = int(precision) metadata['precision'] = int(precision)
scale = scale_from_metadata(metadata) 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) t = value.dtype if value is np.ndarray else type(value)
is_floating = scale != 1 or np.issubdtype(t, np.floating) if scale != 1 and np.issubdtype(t, np.number):
if is_floating: # degenerates to float type
value = value * scale value = np.float64(value * scale)
if self.key and self.key != key: if self.key and self.key != key:
asyncio.ensure_future(exc_to_warning(rename(self.key, key, value, metadata, persist, self.dataset_ctl))) asyncio.ensure_future(exc_to_warning(rename(self.key, key, value, metadata, persist, self.dataset_ctl)))
else: else:
@ -119,7 +120,9 @@ class CreateEditDialog(QtWidgets.QDialog):
def dtype(self): def dtype(self):
txt = self.value_widget.text() txt = self.value_widget.text()
try: try:
result = pyon.decode(txt) result = self.parse_edit_string(txt)
# ensure only pyon compatible types are permissable
pyon.encode(result)
except: except:
pixmap = self.style().standardPixmap( pixmap = self.style().standardPixmap(
QtWidgets.QStyle.SP_MessageBoxWarning) QtWidgets.QStyle.SP_MessageBoxWarning)
@ -129,6 +132,35 @@ class CreateEditDialog(QtWidgets.QDialog):
self.data_type.setText(type(result).__name__) self.data_type.setText(type(result).__name__)
self.ok.setEnabled(True) 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): class Model(DictSyncTreeSepModel):
def __init__(self, init): def __init__(self, init):
@ -209,7 +241,6 @@ class DatasetsDock(QtWidgets.QDockWidget):
key = self.table_model.index_to_key(idx) key = self.table_model.index_to_key(idx)
if key is not None: if key is not None:
persist, value, metadata = self.table_model.backing_store[key] persist, value, metadata = self.table_model.backing_store[key]
value = pyon.encode(value)
CreateEditDialog(self, self.dataset_ctl, key, value, metadata, persist).open() CreateEditDialog(self, self.dataset_ctl, key, value, metadata, persist).open()
def delete_clicked(self): def delete_clicked(self):

View File

@ -11,7 +11,8 @@ from sipyco import pyon
from artiq.gui.entries import procdesc_to_entry, ScanEntry from artiq.gui.entries import procdesc_to_entry, ScanEntry
from artiq.gui.fuzzy_select import FuzzySelectWidget 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__) logger = logging.getLogger(__name__)
@ -23,15 +24,6 @@ logger = logging.getLogger(__name__)
# 2. file:<class name>@<file 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): class _ArgumentEditor(QtWidgets.QTreeWidget):
def __init__(self, manager, dock, expurl): def __init__(self, manager, dock, expurl):
self.manager = manager self.manager = manager
@ -55,7 +47,7 @@ class _ArgumentEditor(QtWidgets.QTreeWidget):
self.setStyleSheet("QTreeWidget {background: " + self.setStyleSheet("QTreeWidget {background: " +
self.palette().midlight().color().name() + " ;}") self.palette().midlight().color().name() + " ;}")
self.viewport().installEventFilter(_WheelFilter(self.viewport())) self.viewport().installEventFilter(WheelFilter(self.viewport(), True))
self._groups = dict() self._groups = dict()
self._arg_to_widgets = dict() self._arg_to_widgets = dict()
@ -159,6 +151,23 @@ class _ArgumentEditor(QtWidgets.QTreeWidget):
self._groups[name] = group self._groups[name] = group
return 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): def _recompute_argument_clicked(self, name):
asyncio.ensure_future(self._recompute_argument(name)) asyncio.ensure_future(self._recompute_argument(name))
@ -175,22 +184,7 @@ class _ArgumentEditor(QtWidgets.QTreeWidget):
state = procdesc_to_entry(procdesc).default_state(procdesc) state = procdesc_to_entry(procdesc).default_state(procdesc)
argument["desc"] = procdesc argument["desc"] = procdesc
argument["state"] = state argument["state"] = state
self.update_argument(name, argument)
# 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()
def _disable_other_scans(self, current_name): def _disable_other_scans(self, current_name):
for name, widgets in self._arg_to_widgets.items(): for name, widgets in self._arg_to_widgets.items():
@ -665,6 +659,20 @@ class ExperimentManager:
self.argument_ui_names[expurl] = ui_name self.argument_ui_names[expurl] = ui_name
return arguments 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): def get_submission_arguments(self, expurl):
if expurl in self.submission_arguments: if expurl in self.submission_arguments:
return self.submission_arguments[expurl] return self.submission_arguments[expurl]

View File

@ -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"
}
]
}

View File

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

View File

@ -4,13 +4,13 @@ from artiq.applets.simple import SimpleApplet
class DemoWidget(QtWidgets.QLabel): class DemoWidget(QtWidgets.QLabel):
def __init__(self, args): def __init__(self, args, ctl):
QtWidgets.QLabel.__init__(self) QtWidgets.QLabel.__init__(self)
self.dataset_name = args.dataset self.dataset_name = args.dataset
def data_changed(self, data, mods): def data_changed(self, value, metadata, persist, mods):
try: try:
n = str(data[self.dataset_name][1]) n = str(value[self.dataset_name])
except (KeyError, ValueError, TypeError): except (KeyError, ValueError, TypeError):
n = "---" n = "---"
n = "<font size=15>" + n + "</font>" n = "<font size=15>" + n + "</font>"

View File

@ -7,6 +7,8 @@ const QRCML_REG : u8 = 0x08;
const CLKMODE_REG : u8 = 0x14; const CLKMODE_REG : u8 = 0x14;
const VERSION_REG : u8 = 0x1F; const VERSION_REG : u8 = 0x1F;
const RETIMER_CLK_PHASE : u8 = 0b11;
fn hard_reset() { fn hard_reset() {
unsafe { unsafe {
// Min Pulse Width: 50ns // 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); debug!("DAC AD9117 Channel {} has incorrect hardware version. VERSION reg: {:02x}", channel, reg);
return Err("DAC AD9117 hardware version is not equal to 0x0A"); 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)?; let reg = read(channel, CLKMODE_REG)?;
if reg >> 4 & 1 != 0 { if reg >> 4 & 1 != 0 {
debug!("DAC AD9117 Channel {} retiming fails. CLKMODE reg: {:02x}", channel, reg); debug!("DAC AD9117 Channel {} retiming fails. CLKMODE reg: {:02x}", channel, reg);
return Err("DAC AD9117 retiming failure"); 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 the DACs input data format to be twos complement
// Set IFIRST and IRISING to True // Set IFIRST and IRISING to True
write(channel, DATA_CTRL_REG, 1 << 7 | 1 << 5 | 1 << 4)?; write(channel, DATA_CTRL_REG, 1 << 7 | 1 << 5 | 1 << 4)?;

View File

@ -52,16 +52,18 @@ pub mod remote_analyzer {
pub data: Vec<u8> pub data: Vec<u8>
} }
pub fn get_data(io: &Io, aux_mutex: &Mutex, ddma_mutex: &Mutex, pub fn get_data(io: &Io, aux_mutex: &Mutex, routing_table: &drtio_routing::RoutingTable,
routing_table: &drtio_routing::RoutingTable, up_destinations: &Urc<RefCell<[bool; drtio_routing::DEST_COUNT]>>
up_destinations: &Urc<RefCell<[bool; drtio_routing::DEST_COUNT]>>) -> Result<RemoteBuffer, &'static str> { ) -> Result<RemoteBuffer, &'static str> {
// gets data from satellites and returns consolidated data // gets data from satellites and returns consolidated data
let mut remote_data: Vec<u8> = Vec::new(); let mut remote_data: Vec<u8> = Vec::new();
let mut remote_overflow = false; let mut remote_overflow = false;
let mut remote_sent_bytes = 0; let mut remote_sent_bytes = 0;
let mut remote_total_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, Ok(data_vec) => data_vec,
Err(e) => return Err(e) 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, _routing_table: &drtio_routing::RoutingTable,
_up_destinations: &Urc<RefCell<[bool; drtio_routing::DEST_COUNT]>> _up_destinations: &Urc<RefCell<[bool; drtio_routing::DEST_COUNT]>>
) -> Result<(), IoError<SchedError>> { ) -> Result<(), IoError<SchedError>> {
@ -97,7 +99,7 @@ fn worker(stream: &mut TcpStream, _io: &Io, _aux_mutex: &Mutex, _ddma_mutex: &Mu
#[cfg(has_drtio)] #[cfg(has_drtio)]
let remote = remote_analyzer::get_data( 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)] #[cfg(has_drtio)]
let (header, remote_data) = match remote { let (header, remote_data) = match remote {
Ok(remote) => (Header { Ok(remote) => (Header {
@ -144,7 +146,7 @@ fn worker(stream: &mut TcpStream, _io: &Io, _aux_mutex: &Mutex, _ddma_mutex: &Mu
Ok(()) 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>>, routing_table: &Urc<RefCell<drtio_routing::RoutingTable>>,
up_destinations: &Urc<RefCell<[bool; drtio_routing::DEST_COUNT]>>) { up_destinations: &Urc<RefCell<[bool; drtio_routing::DEST_COUNT]>>) {
let listener = TcpListener::new(&io, 65535); let listener = TcpListener::new(&io, 65535);
@ -159,7 +161,7 @@ pub fn thread(io: Io, aux_mutex: &Mutex, ddma_mutex: &Mutex,
disarm(); disarm();
let routing_table = routing_table.borrow(); 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(()) => (), Ok(()) => (),
Err(err) => error!("analyzer aborted: {}", err) Err(err) => error!("analyzer aborted: {}", err)
} }

View File

@ -14,8 +14,11 @@ mod remote_i2c {
use rtio_mgt::drtio; use rtio_mgt::drtio;
use sched::{Io, Mutex}; use sched::{Io, Mutex};
pub fn start(io: &Io, aux_mutex: &Mutex, ddma_mutex: &Mutex, linkno: u8, destination: u8, busno: u8) -> Result<(), &'static str> { pub fn start(io: &Io, aux_mutex: &Mutex,
let reply = drtio::aux_transact(io, aux_mutex, ddma_mutex, linkno, &drtioaux::Packet::I2cStartRequest { linkno: u8, destination: u8, busno: u8
) -> Result<(), &'static str> {
let reply = drtio::aux_transact(io, aux_mutex, linkno,
&drtioaux::Packet::I2cStartRequest {
destination: destination, destination: destination,
busno: busno busno: busno
}); });
@ -34,8 +37,11 @@ mod remote_i2c {
} }
} }
pub fn restart(io: &Io, aux_mutex: &Mutex, ddma_mutex: &Mutex, linkno: u8, destination: u8, busno: u8) -> Result<(), &'static str> { pub fn restart(io: &Io, aux_mutex: &Mutex,
let reply = drtio::aux_transact(io, aux_mutex, ddma_mutex, linkno, &drtioaux::Packet::I2cRestartRequest { linkno: u8, destination: u8, busno: u8
) -> Result<(), &'static str> {
let reply = drtio::aux_transact(io, aux_mutex, linkno,
&drtioaux::Packet::I2cRestartRequest {
destination: destination, destination: destination,
busno: busno busno: busno
}); });
@ -54,8 +60,11 @@ mod remote_i2c {
} }
} }
pub fn stop(io: &Io, aux_mutex: &Mutex, ddma_mutex: &Mutex, linkno: u8, destination: u8, busno: u8) -> Result<(), &'static str> { pub fn stop(io: &Io, aux_mutex: &Mutex,
let reply = drtio::aux_transact(io, aux_mutex, ddma_mutex, linkno, &drtioaux::Packet::I2cStopRequest { linkno: u8, destination: u8, busno: u8
) -> Result<(), &'static str> {
let reply = drtio::aux_transact(io, aux_mutex, linkno,
&drtioaux::Packet::I2cStopRequest {
destination: destination, destination: destination,
busno: busno busno: busno
}); });
@ -74,8 +83,11 @@ 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> { pub fn write(io: &Io, aux_mutex: &Mutex,
let reply = drtio::aux_transact(io, aux_mutex, ddma_mutex, linkno, &drtioaux::Packet::I2cWriteRequest { 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, destination: destination,
busno: busno, busno: busno,
data: data data: data
@ -95,8 +107,11 @@ 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> { pub fn read(io: &Io, aux_mutex: &Mutex,
let reply = drtio::aux_transact(io, aux_mutex, ddma_mutex, linkno, &drtioaux::Packet::I2cReadRequest { 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, destination: destination,
busno: busno, busno: busno,
ack: ack ack: ack
@ -116,8 +131,11 @@ 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> { pub fn switch_select(io: &Io, aux_mutex: &Mutex,
let reply = drtio::aux_transact(io, aux_mutex, ddma_mutex, linkno, &drtioaux::Packet::I2cSwitchSelectRequest { 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, destination: destination,
busno: busno, busno: busno,
address: address, address: address,
@ -145,8 +163,10 @@ mod remote_spi {
use rtio_mgt::drtio; use rtio_mgt::drtio;
use sched::{Io, Mutex}; 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<(), ()> { pub fn set_config(io: &Io, aux_mutex: &Mutex,
let reply = drtio::aux_transact(io, aux_mutex, ddma_mutex, linkno, &drtioaux::Packet::SpiSetConfigRequest { 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, destination: destination,
busno: busno, busno: busno,
flags: flags, 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<(), ()> { pub fn write(io: &Io, aux_mutex: &Mutex,
let reply = drtio::aux_transact(io, aux_mutex, ddma_mutex, linkno, &drtioaux::Packet::SpiWriteRequest { linkno: u8, destination: u8, busno: u8, data: u32
) -> Result<(), ()> {
let reply = drtio::aux_transact(io, aux_mutex, linkno, &drtioaux::Packet::SpiWriteRequest {
destination: destination, destination: destination,
busno: busno, busno: busno,
data: data data: data
@ -190,8 +212,10 @@ mod remote_spi {
} }
} }
pub fn read(io: &Io, aux_mutex: &Mutex, ddma_mutex: &Mutex, linkno: u8, destination: u8, busno: u8) -> Result<u32, ()> { pub fn read(io: &Io, aux_mutex: &Mutex, linkno: u8, destination: u8, busno: u8
let reply = drtio::aux_transact(io, aux_mutex, ddma_mutex, linkno, &drtioaux::Packet::SpiReadRequest { ) -> Result<u32, ()> {
let reply = drtio::aux_transact(io, aux_mutex, linkno,
&drtioaux::Packet::SpiReadRequest {
destination: destination, destination: destination,
busno: busno busno: busno
}); });
@ -214,7 +238,7 @@ mod remote_spi {
#[cfg(has_drtio)] #[cfg(has_drtio)]
macro_rules! dispatch { 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 destination = ($busno >> 16) as u8;
let busno = $busno as u8; let busno = $busno as u8;
let hop = $routing_table.0[destination as usize][0]; let hop = $routing_table.0[destination as usize][0];
@ -222,28 +246,27 @@ macro_rules! dispatch {
$mod_local::$func(busno, $($param, )*) $mod_local::$func(busno, $($param, )*)
} else { } else {
let linkno = hop - 1; 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))] #[cfg(not(has_drtio))]
macro_rules! dispatch { 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; let busno = $busno as u8;
$mod_local::$func(busno, $($param, )*) $mod_local::$func(busno, $($param, )*)
}} }}
} }
pub fn process_kern_hwreq(io: &Io, aux_mutex: &Mutex, pub fn process_kern_hwreq(io: &Io, aux_mutex: &Mutex,
ddma_mutex: &Mutex,
_routing_table: &drtio_routing::RoutingTable, _routing_table: &drtio_routing::RoutingTable,
_up_destinations: &Urc<RefCell<[bool; drtio_routing::DEST_COUNT]>>, _up_destinations: &Urc<RefCell<[bool; drtio_routing::DEST_COUNT]>>,
request: &kern::Message) -> Result<bool, Error<SchedError>> { request: &kern::Message) -> Result<bool, Error<SchedError>> {
match request { match request {
&kern::RtioInitRequest => { &kern::RtioInitRequest => {
info!("resetting RTIO"); info!("resetting RTIO");
rtio_mgt::reset(io, aux_mutex, ddma_mutex); rtio_mgt::reset(io, aux_mutex);
kern_acknowledge() kern_acknowledge()
} }
@ -259,47 +282,47 @@ pub fn process_kern_hwreq(io: &Io, aux_mutex: &Mutex,
} }
&kern::I2cStartRequest { busno } => { &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_send(io, &kern::I2cBasicReply { succeeded: succeeded })
} }
&kern::I2cRestartRequest { busno } => { &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_send(io, &kern::I2cBasicReply { succeeded: succeeded })
} }
&kern::I2cStopRequest { busno } => { &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_send(io, &kern::I2cBasicReply { succeeded: succeeded })
} }
&kern::I2cWriteRequest { busno, data } => { &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 }), Ok(ack) => kern_send(io, &kern::I2cWriteReply { succeeded: true, ack: ack }),
Err(_) => kern_send(io, &kern::I2cWriteReply { succeeded: false, ack: false }) Err(_) => kern_send(io, &kern::I2cWriteReply { succeeded: false, ack: false })
} }
} }
&kern::I2cReadRequest { busno, ack } => { &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 }), Ok(data) => kern_send(io, &kern::I2cReadReply { succeeded: true, data: data }),
Err(_) => kern_send(io, &kern::I2cReadReply { succeeded: false, data: 0xff }) Err(_) => kern_send(io, &kern::I2cReadReply { succeeded: false, data: 0xff })
} }
} }
&kern::I2cSwitchSelectRequest { busno, address, mask } => { &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(); switch_select, address, mask).is_ok();
kern_send(io, &kern::I2cBasicReply { succeeded: succeeded }) kern_send(io, &kern::I2cBasicReply { succeeded: succeeded })
} }
&kern::SpiSetConfigRequest { busno, flags, length, div, cs } => { &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(); set_config, flags, length, div, cs).is_ok();
kern_send(io, &kern::SpiBasicReply { succeeded: succeeded }) kern_send(io, &kern::SpiBasicReply { succeeded: succeeded })
}, },
&kern::SpiWriteRequest { busno, data } => { &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(); write, data).is_ok();
kern_send(io, &kern::SpiBasicReply { succeeded: succeeded }) kern_send(io, &kern::SpiBasicReply { succeeded: succeeded })
} }
&kern::SpiReadRequest { busno } => { &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 }), Ok(data) => kern_send(io, &kern::SpiReadReply { succeeded: true, data: data }),
Err(_) => kern_send(io, &kern::SpiReadReply { succeeded: false, data: 0 }) Err(_) => kern_send(io, &kern::SpiReadReply { succeeded: false, data: 0 })
} }

View File

@ -210,17 +210,15 @@ fn startup() {
#[cfg(any(has_rtio_moninj, has_drtio))] #[cfg(any(has_rtio_moninj, has_drtio))]
{ {
let aux_mutex = aux_mutex.clone(); let aux_mutex = aux_mutex.clone();
let ddma_mutex = ddma_mutex.clone();
let drtio_routing_table = drtio_routing_table.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)] #[cfg(has_rtio_analyzer)]
{ {
let aux_mutex = aux_mutex.clone(); let aux_mutex = aux_mutex.clone();
let drtio_routing_table = drtio_routing_table.clone(); let drtio_routing_table = drtio_routing_table.clone();
let up_destinations = up_destinations.clone(); let up_destinations = up_destinations.clone();
let ddma_mutex = ddma_mutex.clone(); io.spawn(8192, move |io| { analyzer::thread(io, &aux_mutex, &drtio_routing_table, &up_destinations) });
io.spawn(8192, move |io| { analyzer::thread(io, &aux_mutex, &ddma_mutex, &drtio_routing_table, &up_destinations) });
} }
#[cfg(has_grabber)] #[cfg(has_grabber)]

View File

@ -53,8 +53,10 @@ mod remote_moninj {
use rtio_mgt::drtio; use rtio_mgt::drtio;
use sched::{Io, Mutex}; 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 { pub fn read_probe(io: &Io, aux_mutex: &Mutex, linkno: u8,
let reply = drtio::aux_transact(io, aux_mutex, ddma_mutex, linkno, &drtioaux::Packet::MonitorRequest { destination: u8, channel: u16, probe: u8) -> u64 {
let reply = drtio::aux_transact(io, aux_mutex, linkno,
&drtioaux::Packet::MonitorRequest {
destination: destination, destination: destination,
channel: channel, channel: channel,
probe: probe probe: probe
@ -67,7 +69,8 @@ mod remote_moninj {
0 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(); let _lock = aux_mutex.lock(io).unwrap();
drtioaux::send(linkno, &drtioaux::Packet::InjectionRequest { drtioaux::send(linkno, &drtioaux::Packet::InjectionRequest {
destination: destination, destination: destination,
@ -77,8 +80,10 @@ mod remote_moninj {
}).unwrap(); }).unwrap();
} }
pub fn read_injection_status(io: &Io, aux_mutex: &Mutex, ddma_mutex: &Mutex, linkno: u8, destination: u8, channel: u16, overrd: u8) -> u8 { pub fn read_injection_status(io: &Io, aux_mutex: &Mutex, linkno: u8,
let reply = drtio::aux_transact(io, aux_mutex, ddma_mutex, linkno, &drtioaux::Packet::InjectionStatusRequest { destination: u8, channel: u16, overrd: u8) -> u8 {
let reply = drtio::aux_transact(io, aux_mutex, linkno,
&drtioaux::Packet::InjectionStatusRequest {
destination: destination, destination: destination,
channel: channel, channel: channel,
overrd: overrd overrd: overrd
@ -94,7 +99,7 @@ mod remote_moninj {
#[cfg(has_drtio)] #[cfg(has_drtio)]
macro_rules! dispatch { 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 destination = ($channel >> 16) as u8;
let channel = $channel as u16; let channel = $channel as u16;
let hop = $routing_table.0[destination as usize][0]; let hop = $routing_table.0[destination as usize][0];
@ -102,20 +107,20 @@ macro_rules! dispatch {
local_moninj::$func(channel, $($param, )*) local_moninj::$func(channel, $($param, )*)
} else { } else {
let linkno = hop - 1; 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))] #[cfg(not(has_drtio))]
macro_rules! dispatch { 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; let channel = $channel as u16;
local_moninj::$func(channel, $($param, )*) local_moninj::$func(channel, $($param, )*)
}} }}
} }
fn connection_worker(io: &Io, _aux_mutex: &Mutex, _ddma_mutex: &Mutex, _routing_table: &drtio_routing::RoutingTable, fn connection_worker(io: &Io, _aux_mutex: &Mutex, _routing_table: &drtio_routing::RoutingTable,
mut stream: &mut TcpStream) -> Result<(), Error<SchedError>> { mut stream: &mut TcpStream) -> Result<(), Error<SchedError>> {
let mut probe_watch_list = BTreeMap::new(); let mut probe_watch_list = BTreeMap::new();
let mut inject_watch_list = BTreeMap::new(); let mut inject_watch_list = BTreeMap::new();
@ -144,9 +149,10 @@ fn connection_worker(io: &Io, _aux_mutex: &Mutex, _ddma_mutex: &Mutex, _routing_
let _ = inject_watch_list.remove(&(channel, overrd)); 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 } => { 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 { let reply = DeviceMessage::InjectionStatus {
channel: channel, channel: channel,
overrd: overrd, overrd: overrd,
@ -163,7 +169,7 @@ fn connection_worker(io: &Io, _aux_mutex: &Mutex, _ddma_mutex: &Mutex, _routing_
if clock::get_ms() > next_check { if clock::get_ms() > next_check {
for (&(channel, probe), previous) in probe_watch_list.iter_mut() { 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 { if previous.is_none() || previous.unwrap() != current {
let message = DeviceMessage::MonitorStatus { let message = DeviceMessage::MonitorStatus {
channel: channel, 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() { 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 { if previous.is_none() || previous.unwrap() != current {
let message = DeviceMessage::InjectionStatus { let message = DeviceMessage::InjectionStatus {
channel: channel, 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); let listener = TcpListener::new(&io, 2047);
listener.listen(1383).expect("moninj: cannot listen"); listener.listen(1383).expect("moninj: cannot listen");
loop { loop {
let aux_mutex = aux_mutex.clone(); let aux_mutex = aux_mutex.clone();
let ddma_mutex = ddma_mutex.clone();
let routing_table = routing_table.clone(); let routing_table = routing_table.clone();
let stream = listener.accept().expect("moninj: cannot accept").into_handle(); let stream = listener.accept().expect("moninj: cannot accept").into_handle();
io.spawn(16384, move |io| { io.spawn(16384, move |io| {
let routing_table = routing_table.borrow(); let routing_table = routing_table.borrow();
let mut stream = TcpStream::from_handle(&io, stream); 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(()) => {}, Ok(()) => {},
Err(err) => error!("moninj aborted: {}", err) Err(err) => error!("moninj aborted: {}", err)
} }

View File

@ -91,12 +91,12 @@ pub mod remote_dma {
Ok(playback_state) Ok(playback_state)
} }
pub fn erase(io: &Io, aux_mutex: &Mutex, routing_table: &RoutingTable, pub fn erase(io: &Io, aux_mutex: &Mutex, ddma_mutex: &Mutex,
ddma_mutex: &Mutex, id: u32) { routing_table: &RoutingTable, id: u32) {
let _lock = ddma_mutex.lock(io).unwrap(); let _lock = ddma_mutex.lock(io).unwrap();
let destinations = unsafe { TRACES.get(&id).unwrap() }; let destinations = unsafe { TRACES.get(&id).unwrap() };
for destination in destinations.keys() { 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(_) => (), Ok(_) => (),
Err(e) => error!("Error erasing trace on DMA: {}", e) Err(e) => error!("Error erasing trace on DMA: {}", e)
} }
@ -104,12 +104,12 @@ pub mod remote_dma {
unsafe { TRACES.remove(&id); } unsafe { TRACES.remove(&id); }
} }
pub fn upload_traces(io: &Io, aux_mutex: &Mutex, routing_table: &RoutingTable, pub fn upload_traces(io: &Io, aux_mutex: &Mutex, ddma_mutex: &Mutex,
ddma_mutex: &Mutex, id: u32) { routing_table: &RoutingTable, id: u32) {
let _lock = ddma_mutex.lock(io); let _lock = ddma_mutex.lock(io);
let traces = unsafe { TRACES.get_mut(&id).unwrap() }; let traces = unsafe { TRACES.get_mut(&id).unwrap() };
for (destination, mut trace) in traces { 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, Ok(_) => trace.state = RemoteState::Loaded,
Err(e) => error!("Error adding DMA trace on destination {}: {}", destination, e) 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, pub fn playback(io: &Io, aux_mutex: &Mutex, ddma_mutex: &Mutex,
ddma_mutex: &Mutex, id: u32, timestamp: u64) { routing_table: &RoutingTable, id: u32, timestamp: u64) {
// triggers playback on satellites // triggers playback on satellites
let destinations = unsafe { let destinations = unsafe {
let _lock = ddma_mutex.lock(io).unwrap(); let _lock = ddma_mutex.lock(io).unwrap();
@ -133,7 +133,7 @@ pub mod remote_dma {
continue; 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(_) => (), Ok(_) => (),
Err(e) => error!("Error during remote DMA playback: {}", e) 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, pub fn destination_changed(io: &Io, aux_mutex: &Mutex, ddma_mutex: &Mutex,
ddma_mutex: &Mutex, destination: u8, up: bool) { routing_table: &RoutingTable, destination: u8, up: bool) {
// update state of the destination, resend traces if it's up // update state of the destination, resend traces if it's up
let _lock = ddma_mutex.lock(io).unwrap(); let _lock = ddma_mutex.lock(io).unwrap();
let traces_iter = unsafe { TRACES.iter_mut() }; let traces_iter = unsafe { TRACES.iter_mut() };
for (id, dest_traces) in traces_iter { for (id, dest_traces) in traces_iter {
if let Some(trace) = dest_traces.get_mut(&destination) { if let Some(trace) = dest_traces.get_mut(&destination) {
if up { 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, Ok(_) => trace.state = RemoteState::Loaded,
Err(e) => error!("Error adding DMA trace on destination {}: {}", destination, e) Err(e) => error!("Error adding DMA trace on destination {}: {}", destination, e)

View File

@ -61,23 +61,27 @@ pub mod drtio {
} }
} }
pub fn aux_transact(io: &Io, aux_mutex: &Mutex, ddma_mutex: &Mutex, fn process_async_packets(io: &Io, ddma_mutex: &Mutex, packet: drtioaux::Packet
linkno: u8, request: &drtioaux::Packet ) -> Result<drtioaux::Packet, &'static str> { ) -> Option<drtioaux::Packet> {
let _lock = aux_mutex.lock(io).unwrap(); // returns None if an async packet has been consumed
drtioaux::send(linkno, request).unwrap(); match packet {
loop { drtioaux::Packet::DmaPlaybackStatus { id, destination, error, channel, timestamp } => {
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); remote_dma::playback_done(io, ddma_mutex, id, destination, error, channel, timestamp);
None
}, },
Ok(packet) => return Ok(packet), other => Some(other)
Err(e) => return Err(e)
}
} }
} }
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; let mut count = 0;
loop { loop {
if !link_rx_up(linkno) { if !link_rx_up(linkno) {
@ -87,7 +91,7 @@ pub mod drtio {
if count > 100 { if count > 100 {
return 0; 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 { match reply {
Ok(drtioaux::Packet::EchoReply) => { Ok(drtioaux::Packet::EchoReply) => {
// make sure receive buffer is drained // 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> { linkno: u8, routing_table: &drtio_routing::RoutingTable) -> Result<(), &'static str> {
for i in 0..drtio_routing::DEST_COUNT { 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, destination: i as u8,
hops: routing_table.0[i] hops: routing_table.0[i]
})?; })?;
@ -137,8 +141,10 @@ pub mod drtio {
Ok(()) Ok(())
} }
fn set_rank(io: &Io, aux_mutex: &Mutex, ddma_mutex: &Mutex, linkno: u8, rank: u8) -> Result<(), &'static str> { fn set_rank(io: &Io, aux_mutex: &Mutex,
let reply = aux_transact(io, aux_mutex, ddma_mutex, linkno, &drtioaux::Packet::RoutingSetRank { linkno: u8, rank: u8) -> Result<(), &'static str> {
let reply = aux_transact(io, aux_mutex, linkno,
&drtioaux::Packet::RoutingSetRank {
rank: rank rank: rank
})?; })?;
if reply != drtioaux::Packet::RoutingAck { if reply != drtioaux::Packet::RoutingAck {
@ -229,37 +235,52 @@ pub mod drtio {
let linkno = hop - 1; let linkno = hop - 1;
if destination_up(up_destinations, destination) { if destination_up(up_destinations, destination) {
if up_links[linkno as usize] { if up_links[linkno as usize] {
let reply = aux_transact(io, aux_mutex, ddma_mutex, linkno, &drtioaux::Packet::DestinationStatusRequest { loop {
let reply = aux_transact(io, aux_mutex, linkno,
&drtioaux::Packet::DestinationStatusRequest {
destination: destination destination: destination
}); });
if let Ok(reply) = reply {
let reply = process_async_packets(io, ddma_mutex, reply);
match reply { match reply {
Ok(drtioaux::Packet::DestinationDownReply) => { Some(drtioaux::Packet::DestinationDownReply) => {
destination_set_up(routing_table, up_destinations, destination, false); 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);
} }
Ok(drtioaux::Packet::DestinationOkReply) => (), Some(drtioaux::Packet::DestinationOkReply) => (),
Ok(drtioaux::Packet::DestinationSequenceErrorReply { channel }) => { Some(drtioaux::Packet::DestinationSequenceErrorReply { channel }) => {
error!("[DEST#{}] RTIO sequence error involving channel 0x{:04x}:{}", destination, channel, resolve_channel_name(channel as u32)); 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 }; unsafe { SEEN_ASYNC_ERRORS |= ASYNC_ERROR_SEQUENCE_ERROR };
} }
Ok(drtioaux::Packet::DestinationCollisionReply { channel }) => { Some(drtioaux::Packet::DestinationCollisionReply { channel }) => {
error!("[DEST#{}] RTIO collision involving channel 0x{:04x}:{}", destination, channel, resolve_channel_name(channel as u32)); error!("[DEST#{}] RTIO collision involving channel 0x{:04x}:{}", destination, channel, resolve_channel_name(channel as u32));
unsafe { SEEN_ASYNC_ERRORS |= ASYNC_ERROR_COLLISION }; unsafe { SEEN_ASYNC_ERRORS |= ASYNC_ERROR_COLLISION };
} }
Ok(drtioaux::Packet::DestinationBusyReply { channel }) => { Some(drtioaux::Packet::DestinationBusyReply { channel }) => {
error!("[DEST#{}] RTIO busy error involving channel 0x{:04x}:{}", destination, channel, resolve_channel_name(channel as u32)); error!("[DEST#{}] RTIO busy error involving channel 0x{:04x}:{}", destination, channel, resolve_channel_name(channel as u32));
unsafe { SEEN_ASYNC_ERRORS |= ASYNC_ERROR_BUSY }; unsafe { SEEN_ASYNC_ERRORS |= ASYNC_ERROR_BUSY };
} }
Ok(packet) => error!("[DEST#{}] received unexpected aux packet: {:?}", destination, packet), Some(packet) => error!("[DEST#{}] received unexpected aux packet: {:?}", destination, packet),
Err(e) => error!("[DEST#{}] communication failed ({})", destination, e) 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());
}
break;
} }
} else { } else {
destination_set_up(routing_table, up_destinations, destination, false); 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 { } else {
if up_links[linkno as usize] { if up_links[linkno as usize] {
let reply = aux_transact(io, aux_mutex, ddma_mutex, linkno, &drtioaux::Packet::DestinationStatusRequest { let reply = aux_transact(io, aux_mutex, linkno,
&drtioaux::Packet::DestinationStatusRequest {
destination: destination destination: destination
}); });
match reply { match reply {
@ -267,7 +288,7 @@ pub mod drtio {
Ok(drtioaux::Packet::DestinationOkReply) => { Ok(drtioaux::Packet::DestinationOkReply) => {
destination_set_up(routing_table, up_destinations, destination, true); destination_set_up(routing_table, up_destinations, destination, true);
init_buffer_space(destination as u8, linkno); 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), Ok(packet) => error!("[DEST#{}] received unexpected aux packet: {:?}", destination, packet),
Err(e) => error!("[DEST#{}] communication failed ({})", destination, e) Err(e) => error!("[DEST#{}] communication failed ({})", destination, e)
@ -299,17 +320,17 @@ pub mod drtio {
/* link was previously down */ /* link was previously down */
if link_rx_up(linkno) { if link_rx_up(linkno) {
info!("[LINK#{}] link RX became up, pinging", 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 { if ping_count > 0 {
info!("[LINK#{}] remote replied after {} packets", linkno, ping_count); info!("[LINK#{}] remote replied after {} packets", linkno, ping_count);
up_links[linkno as usize] = true; up_links[linkno as usize] = true;
if let Err(e) = sync_tsc(&io, aux_mutex, linkno) { if let Err(e) = sync_tsc(&io, aux_mutex, linkno) {
error!("[LINK#{}] failed to sync TSC ({})", linkno, e); 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); 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); error!("[LINK#{}] failed to set rank ({})", linkno, e);
} }
info!("[LINK#{}] link initialization completed", linkno); 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() { for linkno in 0..csr::DRTIO.len() {
unsafe { unsafe {
(csr::DRTIO[linkno].reset_write)(1); (csr::DRTIO[linkno].reset_write)(1);
@ -340,7 +361,7 @@ pub mod drtio {
for linkno in 0..csr::DRTIO.len() { for linkno in 0..csr::DRTIO.len() {
let linkno = linkno as u8; let linkno = linkno as u8;
if link_rx_up(linkno) { 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); &drtioaux::Packet::ResetRequest);
match reply { match reply {
Ok(drtioaux::Packet::ResetAck) => (), Ok(drtioaux::Packet::ResetAck) => (),
@ -351,8 +372,7 @@ pub mod drtio {
} }
} }
pub fn ddma_upload_trace(io: &Io, aux_mutex: &Mutex, ddma_mutex: &Mutex, pub fn ddma_upload_trace(io: &Io, aux_mutex: &Mutex, routing_table: &drtio_routing::RoutingTable,
routing_table: &drtio_routing::RoutingTable,
id: u32, destination: u8, trace: &Vec<u8>) -> Result<(), &'static str> { id: u32, destination: u8, trace: &Vec<u8>) -> Result<(), &'static str> {
let linkno = routing_table.0[destination as usize][0] - 1; let linkno = routing_table.0[destination as usize][0] - 1;
let mut i = 0; let mut i = 0;
@ -362,7 +382,7 @@ pub mod drtio {
let last = i + len == trace.len(); let last = i + len == trace.len();
trace_slice[..len].clone_from_slice(&trace[i..i+len]); trace_slice[..len].clone_from_slice(&trace[i..i+len]);
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 { &drtioaux::Packet::DmaAddTraceRequest {
id: id, destination: destination, last: last, length: len as u16, trace: trace_slice}); id: id, destination: destination, last: last, length: len as u16, trace: trace_slice});
match reply { match reply {
@ -376,12 +396,10 @@ pub mod drtio {
Ok(()) Ok(())
} }
pub fn ddma_send_erase(io: &Io, aux_mutex: &Mutex, routing_table: &drtio_routing::RoutingTable,
pub fn ddma_send_erase(io: &Io, aux_mutex: &Mutex, ddma_mutex: &Mutex,
routing_table: &drtio_routing::RoutingTable,
id: u32, destination: u8) -> Result<(), &'static str> { id: u32, destination: u8) -> Result<(), &'static str> {
let linkno = routing_table.0[destination as usize][0] - 1; 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 }); &drtioaux::Packet::DmaRemoveTraceRequest { id: id, destination: destination });
match reply { match reply {
Ok(drtioaux::Packet::DmaRemoveTraceReply { succeeded: true }) => Ok(()), 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, pub fn ddma_send_playback(io: &Io, aux_mutex: &Mutex, routing_table: &drtio_routing::RoutingTable,
routing_table: &drtio_routing::RoutingTable,
id: u32, destination: u8, timestamp: u64) -> Result<(), &'static str> { id: u32, destination: u8, timestamp: u64) -> Result<(), &'static str> {
let linkno = routing_table.0[destination as usize][0] - 1; let linkno = routing_table.0[destination as usize][0] - 1;
let reply = aux_transact(io, aux_mutex, ddma_mutex, linkno, &drtioaux::Packet::DmaPlaybackRequest{ let reply = aux_transact(io, aux_mutex, linkno,
id: id, destination: destination, timestamp: timestamp }); &drtioaux::Packet::DmaPlaybackRequest{ id: id, destination: destination, timestamp: timestamp });
match reply { match reply {
Ok(drtioaux::Packet::DmaPlaybackReply { succeeded: true }) => return Ok(()), Ok(drtioaux::Packet::DmaPlaybackReply { succeeded: true }) => return Ok(()),
Ok(drtioaux::Packet::DmaPlaybackReply { succeeded: false }) => Ok(drtioaux::Packet::DmaPlaybackReply { succeeded: false }) =>
@ -407,11 +424,10 @@ pub mod drtio {
} }
#[cfg(has_rtio_analyzer)] #[cfg(has_rtio_analyzer)]
fn analyzer_get_data(io: &Io, aux_mutex: &Mutex, ddma_mutex: &Mutex, fn analyzer_get_data(io: &Io, aux_mutex: &Mutex, routing_table: &drtio_routing::RoutingTable,
routing_table: &drtio_routing::RoutingTable, destination: u8 destination: u8) -> Result<RemoteBuffer, &'static str> {
) -> Result<RemoteBuffer, &'static str> {
let linkno = routing_table.0[destination as usize][0] - 1; 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 }); &drtioaux::Packet::AnalyzerHeaderRequest { destination: destination });
let (sent, total, overflow) = match reply { let (sent, total, overflow) = match reply {
Ok(drtioaux::Packet::AnalyzerHeader { Ok(drtioaux::Packet::AnalyzerHeader {
@ -425,7 +441,7 @@ pub mod drtio {
if sent > 0 { if sent > 0 {
let mut last_packet = false; let mut last_packet = false;
while !last_packet { 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 }); &drtioaux::Packet::AnalyzerDataRequest { destination: destination });
match reply { match reply {
Ok(drtioaux::Packet::AnalyzerData { last, length, data }) => { Ok(drtioaux::Packet::AnalyzerData { last, length, data }) => {
@ -447,14 +463,13 @@ pub mod drtio {
} }
#[cfg(has_rtio_analyzer)] #[cfg(has_rtio_analyzer)]
pub fn analyzer_query(io: &Io, aux_mutex: &Mutex, ddma_mutex: &Mutex, pub fn analyzer_query(io: &Io, aux_mutex: &Mutex, routing_table: &drtio_routing::RoutingTable,
routing_table: &drtio_routing::RoutingTable,
up_destinations: &Urc<RefCell<[bool; drtio_routing::DEST_COUNT]>> up_destinations: &Urc<RefCell<[bool; drtio_routing::DEST_COUNT]>>
) -> Result<Vec<RemoteBuffer>, &'static str> { ) -> Result<Vec<RemoteBuffer>, &'static str> {
let mut remote_buffers: Vec<RemoteBuffer> = Vec::new(); let mut remote_buffers: Vec<RemoteBuffer> = Vec::new();
for i in 1..drtio_routing::DEST_COUNT { for i in 1..drtio_routing::DEST_COUNT {
if destination_up(up_destinations, i as u8) { 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) Ok(remote_buffers)
@ -470,7 +485,7 @@ pub mod drtio {
_routing_table: &Urc<RefCell<drtio_routing::RoutingTable>>, _routing_table: &Urc<RefCell<drtio_routing::RoutingTable>>,
_up_destinations: &Urc<RefCell<[bool; drtio_routing::DEST_COUNT]>>, _up_destinations: &Urc<RefCell<[bool; drtio_routing::DEST_COUNT]>>,
_ddma_mutex: &Mutex) {} _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; 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); 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 { unsafe {
csr::rtio_core::reset_write(1); csr::rtio_core::reset_write(1);
} }
drtio::reset(io, aux_mutex, ddma_mutex) drtio::reset(io, aux_mutex)
} }

View File

@ -349,7 +349,7 @@ fn process_kern_message(io: &Io, aux_mutex: &Mutex,
kern_recv_dotrace(request); 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) 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) { if let Some(_id) = session.congress.dma_manager.record_start(name) {
// replace the record // replace the record
#[cfg(has_drtio)] #[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() 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); let _id = session.congress.dma_manager.record_stop(duration, enable_ddma, io, ddma_mutex);
#[cfg(has_drtio)] #[cfg(has_drtio)]
if enable_ddma { 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(); cache::flush_l2_cache();
kern_acknowledge() kern_acknowledge()
@ -393,7 +393,7 @@ fn process_kern_message(io: &Io, aux_mutex: &Mutex,
&kern::DmaEraseRequest { name } => { &kern::DmaEraseRequest { name } => {
#[cfg(has_drtio)] #[cfg(has_drtio)]
if let Some(id) = session.congress.dma_manager.get_id(name) { 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); session.congress.dma_manager.erase(name);
kern_acknowledge() kern_acknowledge()
@ -416,7 +416,7 @@ fn process_kern_message(io: &Io, aux_mutex: &Mutex,
} }
&kern::DmaStartRemoteRequest { id: _id, timestamp: _timestamp } => { &kern::DmaStartRemoteRequest { id: _id, timestamp: _timestamp } => {
#[cfg(has_drtio)] #[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_acknowledge()
} }
&kern::DmaAwaitRemoteRequest { id: _id } => { &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) request => unexpected!("unexpected request {:?} from kernel CPU", request)
}.and(Ok(false)) }.and(Ok(false))
}) })

View File

@ -109,6 +109,12 @@ fn process_aux_packet(_manager: &mut DmaManager, analyzer: &mut Analyzer, _repea
let hop = 0; let hop = 0;
if hop == 0 { if hop == 0 {
// 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; let errors;
unsafe { unsafe {
errors = csr::drtiosat::rtio_error_read(); errors = csr::drtiosat::rtio_error_read();
@ -142,6 +148,7 @@ fn process_aux_packet(_manager: &mut DmaManager, analyzer: &mut Analyzer, _repea
drtioaux::send(0, &drtioaux::Packet::DestinationOkReply)?; drtioaux::send(0, &drtioaux::Packet::DestinationOkReply)?;
} }
} }
}
#[cfg(has_drtio_routing)] #[cfg(has_drtio_routing)]
{ {
@ -643,13 +650,6 @@ pub extern fn main() -> i32 {
error!("aux packet error: {}", e); 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); drtiosat_reset_phy(true);

View File

@ -192,6 +192,7 @@ def main():
d_applets = applets_ccb.AppletsCCBDock(main_window, d_applets = applets_ccb.AppletsCCBDock(main_window,
sub_clients["datasets"], sub_clients["datasets"],
rpc_clients["dataset_db"], rpc_clients["dataset_db"],
expmgr,
extra_substitutes={ extra_substitutes={
"server": args.server, "server": args.server,
"port_notify": args.port_notify, "port_notify": args.port_notify,

View File

@ -4,24 +4,27 @@ import argparse
import sys import sys
import textwrap import textwrap
from collections import defaultdict from collections import defaultdict
from itertools import count from itertools import count, filterfalse
from artiq import __version__ as artiq_version from artiq import __version__ as artiq_version
from artiq.coredevice import jsondesc from artiq.coredevice import jsondesc
from artiq.coredevice.phaser import PHASER_GW_MIQRO, PHASER_GW_BASE 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["target"] == "kasli":
if description["hw_rev"] in ("v1.0", "v1.1"): if description["hw_rev"] in ("v1.0", "v1.1"):
cpu_target = "rv32ima" return "rv32ima"
else: else:
cpu_target = "rv32g" return "rv32g"
elif description["target"] == "kasli_soc": elif description["target"] == "kasli_soc":
cpu_target = "cortexa9" return "cortexa9"
else: else:
raise NotImplementedError raise NotImplementedError
def process_header(output, description):
print(textwrap.dedent(""" print(textwrap.dedent("""
# Autogenerated for the {variant} variant # Autogenerated for the {variant} variant
core_addr = "{core_addr}" core_addr = "{core_addr}"
@ -57,6 +60,8 @@ def process_header(output, description):
"class": "CoreDMA" "class": "CoreDMA"
}}, }},
"satellite_cpu_targets": {{}},
"i2c_switch0": {{ "i2c_switch0": {{
"type": "local", "type": "local",
"module": "artiq.coredevice.i2c", "module": "artiq.coredevice.i2c",
@ -74,7 +79,7 @@ def process_header(output, description):
variant=description["variant"], variant=description["variant"],
core_addr=description["core_addr"], core_addr=description["core_addr"],
ref_period=1/(8*description["rtio_frequency"]), ref_period=1/(8*description["rtio_frequency"]),
cpu_target=cpu_target), cpu_target=get_cpu_target(description)),
file=output) file=output)
@ -593,12 +598,89 @@ class PeripheralManager:
channel=rtio_offset+i) channel=rtio_offset+i)
return 8 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): def process(self, rtio_offset, peripheral):
processor = getattr(self, "process_"+str(peripheral["type"])) processor = getattr(self, "process_"+str(peripheral["type"]))
return processor(rtio_offset, peripheral) 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): 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(""" self.gen("""
device_db["{name}"] = {{ device_db["{name}"] = {{
"type": "local", "type": "local",
@ -606,11 +688,18 @@ class PeripheralManager:
"class": "TTLOut", "class": "TTLOut",
"arguments": {{"channel": 0x{channel:06x}}} "arguments": {{"channel": 0x{channel:06x}}}
}}""", }}""",
name=self.get_name("led"), name=led_name,
channel=rtio_offset+i) channel=rtio_offset+i)
return 2 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): def process(output, primary_description, satellites):
drtio_role = primary_description["drtio_role"] drtio_role = primary_description["drtio_role"]
if drtio_role not in ("standalone", "master"): if drtio_role not in ("standalone", "master"):
@ -623,9 +712,11 @@ def process(output, primary_description, satellites):
pm = PeripheralManager(output, primary_description) pm = PeripheralManager(output, primary_description)
local_peripherals, drtio_peripherals = split_drtio_eem(primary_description["peripherals"])
print("# {} peripherals".format(drtio_role), file=output) print("# {} peripherals".format(drtio_role), file=output)
rtio_offset = 0 rtio_offset = 0
for peripheral in primary_description["peripherals"]: for peripheral in local_peripherals:
n_channels = pm.process(rtio_offset, peripheral) n_channels = pm.process(rtio_offset, peripheral)
rtio_offset += n_channels rtio_offset += n_channels
if drtio_role == "standalone": if drtio_role == "standalone":
@ -635,13 +726,22 @@ def process(output, primary_description, satellites):
for destination, description in satellites: for destination, description in satellites:
if description["drtio_role"] != "satellite": if description["drtio_role"] != "satellite":
raise ValueError("Invalid DRTIO role for satellite at destination {}".format(destination)) 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("# DEST#{} peripherals".format(destination), file=output)
print("device_db[\"satellite_cpu_targets\"][{}] = \"{}\"".format(destination, get_cpu_target(description)), file=output)
rtio_offset = destination << 16 rtio_offset = destination << 16
for peripheral in description["peripherals"]: for peripheral in peripherals:
n_channels = pm.process(rtio_offset, peripheral) n_channels = pm.process(rtio_offset, peripheral)
rtio_offset += n_channels 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(): def main():
parser = argparse.ArgumentParser( parser = argparse.ArgumentParser(

View File

@ -1,4 +1,5 @@
from migen import * from migen import *
from migen.genlib.resetsync import AsyncResetSynchronizer
from misoc.interconnect.csr import * from misoc.interconnect.csr import *
from misoc.cores.code_8b10b import SingleEncoder, Decoder from misoc.cores.code_8b10b import SingleEncoder, Decoder
from artiq.gateware.drtio.core import TransceiverInterface, ChannelInterface from artiq.gateware.drtio.core import TransceiverInterface, ChannelInterface
@ -396,53 +397,78 @@ class SerdesSingle(Module):
class OOBReset(Module): class OOBReset(Module):
def __init__(self, iserdes_o): def __init__(self, platform, iserdes_o):
ce_counter = Signal(13) self.clock_domains.cd_clk100 = ClockDomain()
activity_ce = Signal() self.specials += [
transition_ce = Signal() 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_low = Signal()
idle_high = Signal() idle_high = Signal()
idle = Signal()
self.rst = Signal(reset=1) self.rst = Signal(reset=1)
# Detect the lack of transitions (idle) within 2 clk200 cycles # Detect the lack of transitions (idle) within a clk100 cycle
self.specials += [ for idle, source in [
Instance("FDCE", p_INIT=1, i_D=1, i_CLR=iserdes_o, (idle_low, iserdes_o), (idle_high, ~iserdes_o)]:
i_CE=transition_ce, i_C=ClockSignal("clk200"), o_Q=idle_low_meta, idle_meta = Signal()
attr={"async_reg", "ars_ff1"}), ff_pair = [ff1, ff2] = [
Instance("FDCE", p_INIT=1, i_D=idle_low_meta, i_CLR=0, Instance("FDCE", p_INIT=1, i_D=1, i_CLR=source,
i_CE=transition_ce, i_C=ClockSignal("clk200"), o_Q=idle_low, i_CE=1, i_C=ClockSignal("clk100"), o_Q=idle_meta,
attr={"async_reg", "ars_ff2"}), attr={"async_reg"}),
Instance("FDCE", p_INIT=1, i_D=idle_meta, i_CLR=0,
Instance("FDCE", p_INIT=1, i_D=1, i_CLR=~iserdes_o, i_CE=1, i_C=ClockSignal("clk100"), o_Q=idle,
i_CE=transition_ce, i_C=ClockSignal("clk200"), o_Q=idle_high_meta, attr={"async_reg"}),
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"}),
] ]
self.specials += ff_pair
# Detect activity for the last 2**13 clk200 cycles platform.add_platform_command(
# The 2**13 cycles are fully partitioned into 2**12 time segments of 2 "set_false_path -quiet -to {ff1}/CLR", ff1=ff1)
# cycles in duration. If there exists 2-cycle time segment without # Capture transition detected by FF1/Q in FF2/D
# signal level transition, rst is asserted. platform.add_platform_command(
self.sync.clk200 += [ "set_max_delay 2 -quiet "
If(activity_ce, "-from {ff1}/Q -to {ff2}/D", ff1=ff1, ff2=ff2)
idle.eq(0),
self.rst.eq(idle), # Detect activity for the last 2**15 clk100 cycles
), self.submodules.fsm = fsm = ClockDomainsRenamer("clk100")(
If(idle_low | idle_high, FSM(reset_state="WAIT_TRANSITION"))
idle.eq(1), 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), self.rst.eq(1),
), If(idle_low | idle_high,
] 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): class EEMSerdes(Module, TransceiverInterface, AutoCSR):
@ -526,7 +552,7 @@ class EEMSerdes(Module, TransceiverInterface, AutoCSR):
self.submodules += serdes_list 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 = self.oob_reset.rst
self.rst.attr.add("no_retiming") self.rst.attr.add("no_retiming")

View File

@ -716,11 +716,11 @@ class HVAmp(_EEM):
target.rtio_channels.append(rtio.Channel.from_phy(phy)) target.rtio_channels.append(rtio.Channel.from_phy(phy))
class EFC(_EEM): class Shuttler(_EEM):
@staticmethod @staticmethod
def io(eem, iostandard=default_iostandard): def io(eem, iostandard=default_iostandard):
# Master: Pair 0~3 data IN, 4~7 OUT # 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(*[ Subsignal("p", Pins("{} {} {} {}".format(*[
_eem_pin(eem, i, "p") for i in range(4) _eem_pin(eem, i, "p") for i in range(4)
]))), ]))),
@ -731,7 +731,7 @@ class EFC(_EEM):
Misc("DIFF_TERM=TRUE"), Misc("DIFF_TERM=TRUE"),
) )
data_out = ("efc{}_drtio_tx".format(eem), 0, data_out = ("shuttler{}_drtio_tx".format(eem), 0,
Subsignal("p", Pins("{} {} {} {}".format(*[ Subsignal("p", Pins("{} {} {} {}".format(*[
_eem_pin(eem, i, "p") for i in range(4, 8) _eem_pin(eem, i, "p") for i in range(4, 8)
]))), ]))),
@ -746,4 +746,4 @@ class EFC(_EEM):
@classmethod @classmethod
def add_std(cls, target, eem, eem_aux, iostandard=default_iostandard): def add_std(cls, target, eem, eem_aux, iostandard=default_iostandard):
cls.add_extension(target, eem, is_drtio_over_eem=True, iostandard=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)))

View File

@ -126,7 +126,7 @@ def peripheral_hvamp(module, peripheral, **kwargs):
eem.HVAmp.add_std(module, peripheral["ports"][0], eem.HVAmp.add_std(module, peripheral["ports"][0],
ttl_simple.Output, **kwargs) ttl_simple.Output, **kwargs)
def peripheral_efc(module, peripheral, **kwargs): def peripheral_shuttler(module, peripheral, **kwargs):
if len(peripheral["ports"]) == 1: if len(peripheral["ports"]) == 1:
port = peripheral["ports"][0] port = peripheral["ports"][0]
port_aux = None port_aux = None
@ -134,7 +134,7 @@ def peripheral_efc(module, peripheral, **kwargs):
port, port_aux = peripheral["ports"] port, port_aux = peripheral["ports"]
else: else:
raise ValueError("wrong number of ports") raise ValueError("wrong number of ports")
eem.EFC.add_std(module, port, port_aux) eem.Shuttler.add_std(module, port, port_aux)
peripheral_processors = { peripheral_processors = {
"dio": peripheral_dio, "dio": peripheral_dio,
@ -148,7 +148,7 @@ peripheral_processors = {
"fastino": peripheral_fastino, "fastino": peripheral_fastino,
"phaser": peripheral_phaser, "phaser": peripheral_phaser,
"hvamp": peripheral_hvamp, "hvamp": peripheral_hvamp,
"efc": peripheral_efc, "shuttler": peripheral_shuttler,
} }

View File

@ -1,5 +1,7 @@
# Copyright 2013-2017 Robert Jordens <jordens@gmail.com> # 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 # pdq is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by # it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or # 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") p_DDR_CLK_EDGE="SAME_EDGE")
for bit in range(bit_width)] 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): class Dac(Module):
"""Output module. """Output module.
Holds the two output line executors. Holds the two output line executors.
Attributes: 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 clear (Signal): Clear accumulated phase offset when loading a new
waveform. Input. 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. i (Endpoint[]): Coefficients of the output lines.
""" """
def __init__(self): def __init__(self, sdm=False):
self.clear = Signal() 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), 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.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.i = [ sub.i for sub in subs ]
self.submodules += subs self.submodules += subs
@ -229,11 +299,43 @@ class Dds(Module):
class Config(Module): class Config(Module):
def __init__(self): def __init__(self):
self.clr = Signal(16, reset=0xFFFF) 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 # This introduces 1 extra latency to everything in config
# See the latency/delay attributes in Volt & DDS Endpoints/rtlinks # 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") Phy = namedtuple("Phy", "rtlink probes overrides")
@ -250,7 +352,7 @@ class Shuttler(Module, AutoCSR):
Attributes: Attributes:
phys (list): List of Endpoints. phys (list): List of Endpoints.
""" """
def __init__(self, pads): def __init__(self, pads, sdm=False):
NUM_OF_DACS = 16 NUM_OF_DACS = 16
self.submodules.dac_interface = DacInterface(pads) self.submodules.dac_interface = DacInterface(pads)
@ -258,13 +360,23 @@ class Shuttler(Module, AutoCSR):
self.phys = [] self.phys = []
self.submodules.cfg = Config() self.submodules.cfg = Config()
cfg_rtl_iface = rtlink.Interface(rtlink.OInterface( cfg_rtl_iface = rtlink.Interface(
rtlink.OInterface(
data_width=len(self.cfg.i.data), data_width=len(self.cfg.i.data),
enable_replace=False)) address_width=len(self.cfg.i.addr),
enable_replace=False,
),
rtlink.IInterface(
data_width=len(self.cfg.o.data),
),
)
self.comb += [ self.comb += [
self.cfg.i.stb.eq(cfg_rtl_iface.o.stb), 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), 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, [], [])) self.phys.append(Phy(cfg_rtl_iface, [], []))
@ -274,9 +386,11 @@ class Shuttler(Module, AutoCSR):
self.phys.append(Phy(trigger_iface, [], [])) self.phys.append(Phy(trigger_iface, [], []))
for idx in range(NUM_OF_DACS): for idx in range(NUM_OF_DACS):
dac = Dac() dac = Dac(sdm=sdm)
self.comb += [ self.comb += [
dac.clear.eq(self.cfg.clr[idx]), 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) self.dac_interface.data[idx // 2][idx % 2].eq(dac.data)
] ]

View File

@ -13,6 +13,7 @@ from artiq.gateware.amp import AMPSoC
from artiq.gateware import rtio from artiq.gateware import rtio
from artiq.gateware.rtio.xilinx_clocking import fix_serdes_timing_path 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 ttl_simple
from artiq.gateware.rtio.phy import spi2 as rtio_spi
from artiq.gateware.drtio.transceiver import eem_serdes from artiq.gateware.drtio.transceiver import eem_serdes
from artiq.gateware.drtio.rx_synchronizer import NoRXSynchronizer from artiq.gateware.drtio.rx_synchronizer import NoRXSynchronizer
from artiq.gateware.drtio import * 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('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')), Subsignal('clk', Pins('fmc0:HB06_CC_P')),
IOStandard('LVCMOS18')), 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) platform.add_extension(shuttler_io)
@ -167,6 +182,25 @@ class Satellite(BaseSoC, AMPSoC):
self.csr_devices.append("shuttler") self.csr_devices.append("shuttler")
self.rtio_channels.extend(rtio.Channel.from_phy(phy) for phy in self.shuttler.phys) 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["HAS_RTIO_LOG"] = None
self.config["RTIO_LOG_CHANNEL"] = len(self.rtio_channels) self.config["RTIO_LOG_CHANNEL"] = len(self.rtio_channels)
self.rtio_channels.append(rtio.LogChannel()) self.rtio_channels.append(rtio.LogChannel())

View File

@ -70,6 +70,8 @@ class StandaloneBase(MiniSoC, AMPSoC):
AMPSoC.__init__(self) AMPSoC.__init__(self)
add_identifier(self, gateware_identifier_str=gateware_identifier_str) add_identifier(self, gateware_identifier_str=gateware_identifier_str)
self.config["DRTIO_ROLE"] = "standalone"
if self.platform.hw_rev == "v2.0": if self.platform.hw_rev == "v2.0":
self.submodules.error_led = gpio.GPIOOut(Cat( self.submodules.error_led = gpio.GPIOOut(Cat(
self.platform.request("error_led"))) 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.add_memory_region(memory_name, memory_address | self.shadow_base, 0x800)
self.config["HAS_DRTIO"] = None self.config["HAS_DRTIO"] = None
self.config["HAS_DRTIO_ROUTING"] = None self.config["HAS_DRTIO_ROUTING"] = None
self.config["DRTIO_ROLE"] = "master"
rtio_clk_period = 1e9/rtio_clk_freq rtio_clk_period = 1e9/rtio_clk_freq
gtp = self.gt_drtio.gtps[0] 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.add_memory_region(memory_name, memory_address | self.shadow_base, 0x800)
self.config["HAS_DRTIO"] = None self.config["HAS_DRTIO"] = None
self.config["HAS_DRTIO_ROUTING"] = None self.config["HAS_DRTIO_ROUTING"] = None
self.config["DRTIO_ROLE"] = "satellite"
self.add_csr_group("drtioaux", drtioaux_csr_group) self.add_csr_group("drtioaux", drtioaux_csr_group)
self.add_memory_group("drtioaux_mem", drtioaux_memory_group) self.add_memory_group("drtioaux_mem", drtioaux_memory_group)
self.add_csr_group("drtiorep", drtiorep_csr_group) self.add_csr_group("drtiorep", drtiorep_csr_group)

View File

@ -22,7 +22,6 @@ class GenericStandalone(StandaloneBase):
hw_rev = description["hw_rev"] hw_rev = description["hw_rev"]
self.class_name_override = description["variant"] self.class_name_override = description["variant"]
StandaloneBase.__init__(self, hw_rev=hw_rev, **kwargs) 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) self.config["RTIO_FREQUENCY"] = "{:.1f}".format(description["rtio_frequency"]/1e6)
if "ext_ref_frequency" in description: if "ext_ref_frequency" in description:
self.config["SI5324_EXT_REF"] = None self.config["SI5324_EXT_REF"] = None
@ -71,14 +70,13 @@ class GenericMaster(MasterBase):
if hw_rev is None: if hw_rev is None:
hw_rev = description["hw_rev"] hw_rev = description["hw_rev"]
self.class_name_override = description["variant"] 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, MasterBase.__init__(self,
hw_rev=hw_rev, hw_rev=hw_rev,
rtio_clk_freq=description["rtio_frequency"], rtio_clk_freq=description["rtio_frequency"],
enable_sata=description["enable_sata_drtio"], enable_sata=description["enable_sata_drtio"],
enable_sys5x=has_drtio_over_eem, enable_sys5x=has_drtio_over_eem,
**kwargs) **kwargs)
self.config["DRTIO_ROLE"] = description["drtio_role"]
if "ext_ref_frequency" in description: if "ext_ref_frequency" in description:
self.config["SI5324_EXT_REF"] = None self.config["SI5324_EXT_REF"] = None
self.config["EXT_REF_FREQUENCY"] = "{:.1f}".format( self.config["EXT_REF_FREQUENCY"] = "{:.1f}".format(
@ -123,7 +121,6 @@ class GenericSatellite(SatelliteBase):
rtio_clk_freq=description["rtio_frequency"], rtio_clk_freq=description["rtio_frequency"],
enable_sata=description["enable_sata_drtio"], enable_sata=description["enable_sata_drtio"],
**kwargs) **kwargs)
self.config["DRTIO_ROLE"] = description["drtio_role"]
if hw_rev == "v1.0": if hw_rev == "v1.0":
# EEM clock fan-out from Si5324, not MMCX # EEM clock fan-out from Si5324, not MMCX
self.comb += self.platform.request("clk_sel").eq(1) self.comb += self.platform.request("clk_sel").eq(1)
@ -177,9 +174,9 @@ def main():
else: else:
raise ValueError("Invalid DRTIO role") raise ValueError("Invalid DRTIO role")
has_efc = any(peripheral["type"] == "efc" for peripheral in description["peripherals"]) has_shuttler = any(peripheral["type"] == "shuttler" for peripheral in description["peripherals"])
if has_efc and (description["drtio_role"] == "standalone"): if has_shuttler and (description["drtio_role"] == "standalone"):
raise ValueError("EFC requires DRTIO, please switch role to master") raise ValueError("Shuttler requires DRTIO, please switch role to master")
soc = cls(description, gateware_identifier_str=args.gateware_identifier_str, **soc_kasli_argdict(args)) soc = cls(description, gateware_identifier_str=args.gateware_identifier_str, **soc_kasli_argdict(args))
args.variant = description["variant"] args.variant = description["variant"]

View File

@ -14,17 +14,181 @@ from sipyco.pipe_ipc import AsyncioParentComm
from sipyco.logging_tools import LogParser from sipyco.logging_tools import LogParser
from sipyco import pyon 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__) 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): class AppletIPCServer(AsyncioParentComm):
def __init__(self, dataset_sub, dataset_ctl): def __init__(self, dataset_sub, dataset_ctl, expmgr):
AsyncioParentComm.__init__(self) AsyncioParentComm.__init__(self)
self.dataset_sub = dataset_sub self.dataset_sub = dataset_sub
self.dataset_ctl = dataset_ctl self.dataset_ctl = dataset_ctl
self.expmgr = expmgr
self.datasets = set() self.datasets = set()
self.dataset_prefixes = [] self.dataset_prefixes = []
@ -50,6 +214,13 @@ class AppletIPCServer(AsyncioParentComm):
def _on_mod(self, mod): def _on_mod(self, mod):
if mod["action"] == "init": 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"]) mod = self._synthesize_init(mod["struct"])
else: else:
if mod["path"]: 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"]) await self.dataset_ctl.set(obj["key"], obj["value"], metadata=obj["metadata"], persist=obj["persist"])
elif action == "update_dataset": elif action == "update_dataset":
await self.dataset_ctl.update(obj["mod"]) await self.dataset_ctl.update(obj["mod"])
elif action == "set_argument_value":
self.expmgr.set_argument_value(obj["expurl"], obj["name"], obj["value"])
else: else:
raise ValueError("unknown action in applet message") raise ValueError("unknown action in applet message")
except: except:
@ -108,7 +281,7 @@ class AppletIPCServer(AsyncioParentComm):
class _AppletDock(QDockWidgetCloseDetect): 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) QDockWidgetCloseDetect.__init__(self, "Applet: " + name)
self.setObjectName("applet" + str(uid)) self.setObjectName("applet" + str(uid))
@ -118,6 +291,7 @@ class _AppletDock(QDockWidgetCloseDetect):
self.dataset_sub = dataset_sub self.dataset_sub = dataset_sub
self.dataset_ctl = dataset_ctl self.dataset_ctl = dataset_ctl
self.expmgr = expmgr
self.applet_name = name self.applet_name = name
self.spec = spec self.spec = spec
self.extra_substitutes = extra_substitutes self.extra_substitutes = extra_substitutes
@ -136,7 +310,7 @@ class _AppletDock(QDockWidgetCloseDetect):
return return
self.starting_stopping = True self.starting_stopping = True
try: 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 = os.environ.copy()
env["PYTHONUNBUFFERED"] = "1" env["PYTHONUNBUFFERED"] = "1"
env["ARTIQ_APPLET_EMBED"] = self.ipc.get_address() env["ARTIQ_APPLET_EMBED"] = self.ipc.get_address()
@ -333,7 +507,7 @@ class _CompleterDelegate(QtWidgets.QStyledItemDelegate):
class AppletsDock(QtWidgets.QDockWidget): 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 :param extra_substitutes: Map of extra ``${strings}`` to substitute in applet
commands to their respective values. commands to their respective values.
@ -346,6 +520,7 @@ class AppletsDock(QtWidgets.QDockWidget):
self.main_window = main_window self.main_window = main_window
self.dataset_sub = dataset_sub self.dataset_sub = dataset_sub
self.dataset_ctl = dataset_ctl self.dataset_ctl = dataset_ctl
self.expmgr = expmgr
self.extra_substitutes = extra_substitutes self.extra_substitutes = extra_substitutes
self.applet_uids = set() self.applet_uids = set()
@ -397,11 +572,12 @@ class AppletsDock(QtWidgets.QDockWidget):
delete_action.setShortcutContext(QtCore.Qt.WidgetShortcut) delete_action.setShortcutContext(QtCore.Qt.WidgetShortcut)
delete_action.triggered.connect(self.delete) delete_action.triggered.connect(self.delete)
self.table.addAction(delete_action) self.table.addAction(delete_action)
close_all_action = QtWidgets.QAction("Close all applets", self.table) close_nondocked_action = QtWidgets.QAction("Close non-docked applets", self.table)
close_all_action.setShortcut("CTRL+ALT+W") close_nondocked_action.setShortcut("CTRL+ALT+W")
close_all_action.setShortcutContext(QtCore.Qt.ApplicationShortcut) close_nondocked_action.setShortcutContext(QtCore.Qt.ApplicationShortcut)
close_all_action.triggered.connect(self.close_all) close_nondocked_action.triggered.connect(self.close_nondocked)
self.table.addAction(close_all_action) self.table.addAction(close_nondocked_action)
new_group_action = QtWidgets.QAction("New group", self.table) new_group_action = QtWidgets.QAction("New group", self.table)
new_group_action.triggered.connect(partial(self.new_with_parent, self.new_group)) new_group_action.triggered.connect(partial(self.new_with_parent, self.new_group))
self.table.addAction(new_group_action) self.table.addAction(new_group_action)
@ -447,7 +623,7 @@ class AppletsDock(QtWidgets.QDockWidget):
self.table.itemChanged.connect(self.item_changed) self.table.itemChanged.connect(self.item_changed)
def create(self, item, name, spec): 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) self.main_window.addDockWidget(QtCore.Qt.RightDockWidgetArea, dock)
dock.setFloating(True) dock.setFloating(True)
asyncio.ensure_future(dock.start(), loop=self._loop) asyncio.ensure_future(dock.start(), loop=self._loop)
@ -674,12 +850,15 @@ class AppletsDock(QtWidgets.QDockWidget):
def restore_state(self, state): def restore_state(self, state):
self.restore_state_item(state, None) self.restore_state_item(state, None)
def close_all(self): def close_nondocked(self):
def walk(wi): def walk(wi):
for i in range(wi.childCount()): for i in range(wi.childCount()):
cwi = wi.child(i) cwi = wi.child(i)
if cwi.ty == "applet": if cwi.ty == "applet":
if cwi.checkState(0) == QtCore.Qt.Checked: 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) cwi.setCheckState(0, QtCore.Qt.Unchecked)
elif cwi.ty == "group": elif cwi.ty == "group":
walk(cwi) walk(cwi)

View File

@ -222,9 +222,6 @@ class _RangeScan(LayoutWidget):
start = ScientificSpinBox() start = ScientificSpinBox()
start.setStyleSheet("QDoubleSpinBox {color:blue}") start.setStyleSheet("QDoubleSpinBox {color:blue}")
start.setMinimumSize(110, 0)
start.setSizePolicy(QtWidgets.QSizePolicy(
QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed))
disable_scroll_wheel(start) disable_scroll_wheel(start)
self.addWidget(start, 0, 1) self.addWidget(start, 0, 1)
@ -236,13 +233,15 @@ class _RangeScan(LayoutWidget):
stop = ScientificSpinBox() stop = ScientificSpinBox()
stop.setStyleSheet("QDoubleSpinBox {color:red}") stop.setStyleSheet("QDoubleSpinBox {color:red}")
stop.setMinimumSize(110, 0)
disable_scroll_wheel(stop) disable_scroll_wheel(stop)
self.addWidget(stop, 2, 1) self.addWidget(stop, 2, 1)
randomize = QtWidgets.QCheckBox("Randomize") randomize = QtWidgets.QCheckBox("Randomize")
self.addWidget(randomize, 3, 1) self.addWidget(randomize, 3, 1)
self.layout.setColumnStretch(0, 4)
self.layout.setColumnStretch(1, 1)
apply_properties(start) apply_properties(start)
start.setSigFigs() start.setSigFigs()
start.setRelativeStep() start.setRelativeStep()

View File

@ -16,10 +16,16 @@ def log_level_to_name(level):
return "DEBUG" 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): def eventFilter(self, obj, event):
if (event.type() == QtCore.QEvent.Wheel and if event.type() != QtCore.QEvent.Wheel:
event.modifiers() == QtCore.Qt.NoModifier): return False
has_modifier = event.modifiers() != QtCore.Qt.NoModifier
if has_modifier == self.ignore_with_modifier:
event.ignore() event.ignore()
return True return True
return False return False
@ -27,7 +33,7 @@ class _WheelFilter(QtCore.QObject):
def disable_scroll_wheel(widget): def disable_scroll_wheel(widget):
widget.setFocusPolicy(QtCore.Qt.StrongFocus) widget.setFocusPolicy(QtCore.Qt.StrongFocus)
widget.installEventFilter(_WheelFilter(widget)) widget.installEventFilter(WheelFilter(widget))
class QDockWidgetCloseDetect(QtWidgets.QDockWidget): class QDockWidgetCloseDetect(QtWidgets.QDockWidget):

View File

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

View File

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

View File

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

View File

@ -35,7 +35,7 @@ mock_modules = ["artiq.gui.waitingspinnerwidget",
"sipyco", "sipyco.pc_rpc", "sipyco.sync_struct", "sipyco", "sipyco.pc_rpc", "sipyco.sync_struct",
"sipyco.asyncio_tools", "sipyco.logging_tools", "sipyco.asyncio_tools", "sipyco.logging_tools",
"sipyco.broadcast", "sipyco.packed_exceptions", "sipyco.broadcast", "sipyco.packed_exceptions",
"sipyco.keepalive"] "sipyco.keepalive", "sipyco.pipe_ipc"]
for module in mock_modules: for module in mock_modules:
sys.modules[module] = Mock() sys.modules[module] = Mock()

View File

@ -137,6 +137,12 @@ DAC/ADC drivers
.. automodule:: artiq.coredevice.fastino .. automodule:: artiq.coredevice.fastino
:members: :members:
:mod:`artiq.coredevice.shuttler` module
++++++++++++++++++++++++++++++++++++++++
.. automodule:: artiq.coredevice.shuttler
:members:
Miscellaneous Miscellaneous
------------- -------------

View File

@ -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. 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) Installing via MSYS2 (Windows)
------------------------------ ------------------------------

View File

@ -143,6 +143,77 @@ CCBs are used by experiments to configure applets in the dashboard, for example
.. autoclass:: artiq.dashboard.applets_ccb.AppletsCCBDock .. autoclass:: artiq.dashboard.applets_ccb.AppletsCCBDock
:members: :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 Front-end tool reference
************************ ************************