mirror of https://github.com/m-labs/artiq.git
Merge branch 'master' into nac3
This commit is contained in:
commit
69cda517f6
|
@ -13,28 +13,32 @@ Highlights:
|
||||||
- HVAMP_8CH 8 channel HV amplifier for Fastino / Zotinos
|
- HVAMP_8CH 8 channel HV amplifier for Fastino / Zotinos
|
||||||
- Almazny mezzanine board for Mirny
|
- Almazny mezzanine board for Mirny
|
||||||
* Softcore targets now use the RISC-V architecture (VexRiscv) instead of OR1K (mor1kx).
|
* Softcore targets now use the RISC-V architecture (VexRiscv) instead of OR1K (mor1kx).
|
||||||
|
* Gateware FPU is supported on KC705 and Kasli 2.0.
|
||||||
* Faster compilation for large arrays/lists.
|
* Faster compilation for large arrays/lists.
|
||||||
* Phaser:
|
* Phaser:
|
||||||
- Improved documentation
|
- Improved documentation
|
||||||
- Expose the DAC coarse mixer and ``sif_sync``
|
- Expose the DAC coarse mixer and ``sif_sync``
|
||||||
- Exposes upconverter calibration and enabling/disabling of upconverter LO & RF outputs.
|
- Exposes upconverter calibration and enabling/disabling of upconverter LO & RF outputs.
|
||||||
- Add helpers to align Phaser updates to the RTIO timeline (``get_next_frame_mu()``)
|
- Add helpers to align Phaser updates to the RTIO timeline (``get_next_frame_mu()``)
|
||||||
* ``get()``, ``get_mu()``, ``get_att()``, and ``get_att_mu()`` functions added for AD9910 and AD9912
|
* Core device moninj is now proxied via the ``aqctl_moninj_proxy`` controller.
|
||||||
|
* The configuration entry ``rtio_clock`` supports multiple clocking settings, deprecating the usage
|
||||||
|
of compile-time options.
|
||||||
|
* Packaging via Nix Flakes.
|
||||||
|
* Firmware and gateware can now be built on-demand on the M-Labs server using ``afws_client``
|
||||||
|
(subscribers only).
|
||||||
|
* Extended Kasli gateware JSON description with configuration for SPI over DIO.
|
||||||
|
* ``get()``, ``get_mu()``, ``get_att()``, and ``get_att_mu()`` functions added for AD9910 and AD9912.
|
||||||
* On Kasli, the number of FIFO lanes in the scalable events dispatcher (SED) can now be configured in
|
* On Kasli, the number of FIFO lanes in the scalable events dispatcher (SED) can now be configured in
|
||||||
the JSON hardware description file.
|
the JSON hardware description file.
|
||||||
* ``artiq_ddb_template`` generates edge-counter keys that start with the key of the corresponding
|
* ``artiq_ddb_template`` generates edge-counter keys that start with the key of the corresponding
|
||||||
TTL device (e.g. ``ttl_0_counter`` for the edge counter on TTL device ``ttl_0``).
|
TTL device (e.g. ``ttl_0_counter`` for the edge counter on TTL device ``ttl_0``).
|
||||||
* ``artiq_master`` now has an ``--experiment-subdir`` option to scan only a subdirectory of the
|
* ``artiq_master`` now has an ``--experiment-subdir`` option to scan only a subdirectory of the
|
||||||
repository when building the list of experiments.
|
repository when building the list of experiments.
|
||||||
* The configuration entry ``rtio_clock`` supports multiple clocking settings, deprecating the usage
|
* Added support for 100MHz RTIO clock in DRTIO.
|
||||||
of compile-time options.
|
|
||||||
* DRTIO: added support for 100MHz clock.
|
|
||||||
* Previously detected RTIO async errors are reported to the host after each kernel terminates and a
|
* Previously detected RTIO async errors are reported to the host after each kernel terminates and a
|
||||||
warning is logged. The warning is additional to the one already printed in the core device log upon
|
warning is logged. The warning is additional to the one already printed in the core device log upon
|
||||||
detection of the error.
|
detection of the error.
|
||||||
* Removed worker DB warning for writing a dataset that is also in the archive
|
* Removed worker DB warning for writing a dataset that is also in the archive.
|
||||||
* Extended Kasli gateware JSON description with configuration for SPI over DIO.
|
|
||||||
See: https://github.com/m-labs/artiq/pull/1800
|
|
||||||
* ``PCA9548`` I2C switch class renamed to ``I2CSwitch``, to accomodate support for PCA9547, and
|
* ``PCA9548`` I2C switch class renamed to ``I2CSwitch``, to accomodate support for PCA9547, and
|
||||||
possibly other switches in future. Readback has been removed, and now only one channel per
|
possibly other switches in future. Readback has been removed, and now only one channel per
|
||||||
switch is supported.
|
switch is supported.
|
||||||
|
@ -51,7 +55,6 @@ Breaking changes:
|
||||||
* Phaser: fixed coarse mixer frequency configuration
|
* Phaser: fixed coarse mixer frequency configuration
|
||||||
* Mirny: Added extra delays in ``ADF5356.sync()``. This avoids the need of an extra delay before
|
* Mirny: Added extra delays in ``ADF5356.sync()``. This avoids the need of an extra delay before
|
||||||
calling `ADF5356.init()`.
|
calling `ADF5356.init()`.
|
||||||
* DRTIO: Changed message alignment from 32-bits to 64-bits.
|
|
||||||
* The deprecated ``set_dataset(..., save=...)`` is no longer supported.
|
* The deprecated ``set_dataset(..., save=...)`` is no longer supported.
|
||||||
|
|
||||||
ARTIQ-6
|
ARTIQ-6
|
||||||
|
|
|
@ -107,7 +107,7 @@ class Hdf5FileSystemModel(QtWidgets.QFileSystemModel):
|
||||||
v = ("artiq_version: {}\nrepo_rev: {}\nfile: {}\n"
|
v = ("artiq_version: {}\nrepo_rev: {}\nfile: {}\n"
|
||||||
"class_name: {}\nrid: {}\nstart_time: {}").format(
|
"class_name: {}\nrid: {}\nstart_time: {}").format(
|
||||||
h5["artiq_version"][()], expid["repo_rev"],
|
h5["artiq_version"][()], expid["repo_rev"],
|
||||||
expid["file"], expid["class_name"],
|
expid.get("file", "<none>"), expid["class_name"],
|
||||||
h5["rid"][()], start_time)
|
h5["rid"][()], start_time)
|
||||||
return v
|
return v
|
||||||
except:
|
except:
|
||||||
|
@ -179,7 +179,7 @@ class FilesDock(QtWidgets.QDockWidget):
|
||||||
v = {
|
v = {
|
||||||
"artiq_version": f["artiq_version"][()],
|
"artiq_version": f["artiq_version"][()],
|
||||||
"repo_rev": expid["repo_rev"],
|
"repo_rev": expid["repo_rev"],
|
||||||
"file": expid["file"],
|
"file": expid.get("file", "<none>"),
|
||||||
"class_name": expid["class_name"],
|
"class_name": expid["class_name"],
|
||||||
"rid": f["rid"][()],
|
"rid": f["rid"][()],
|
||||||
"start_time": start_time,
|
"start_time": start_time,
|
||||||
|
|
|
@ -277,6 +277,17 @@ class CoreException:
|
||||||
traceback_str +\
|
traceback_str +\
|
||||||
'\n\nEnd of Core Device Traceback\n'
|
'\n\nEnd of Core Device Traceback\n'
|
||||||
|
|
||||||
|
|
||||||
|
def incompatible_versions(v1, v2):
|
||||||
|
if v1.endswith(".beta") or v2.endswith(".beta"):
|
||||||
|
# Beta branches may introduce breaking changes. Check version strictly.
|
||||||
|
return v1 != v2
|
||||||
|
else:
|
||||||
|
# On stable branches, runtime/software protocol backward compatibility is kept.
|
||||||
|
# Runtime and software with the same major version number are compatible.
|
||||||
|
return v1.split(".", maxsplit=1)[0] != v2.split(".", maxsplit=1)[0]
|
||||||
|
|
||||||
|
|
||||||
class CommKernel:
|
class CommKernel:
|
||||||
warned_of_mismatch = False
|
warned_of_mismatch = False
|
||||||
|
|
||||||
|
@ -453,7 +464,7 @@ class CommKernel:
|
||||||
runtime_id = self._read(4)
|
runtime_id = self._read(4)
|
||||||
if runtime_id == b"AROR":
|
if runtime_id == b"AROR":
|
||||||
gateware_version = self._read_string().split(";")[0]
|
gateware_version = self._read_string().split(";")[0]
|
||||||
if gateware_version != software_version and not self.warned_of_mismatch:
|
if not self.warned_of_mismatch and incompatible_versions(gateware_version, software_version):
|
||||||
logger.warning("Mismatch between gateware (%s) "
|
logger.warning("Mismatch between gateware (%s) "
|
||||||
"and software (%s) versions",
|
"and software (%s) versions",
|
||||||
gateware_version, software_version)
|
gateware_version, software_version)
|
||||||
|
|
|
@ -169,7 +169,7 @@ class CommMgmt:
|
||||||
self._write_bytes(value)
|
self._write_bytes(value)
|
||||||
ty = self._read_header()
|
ty = self._read_header()
|
||||||
if ty == Reply.Error:
|
if ty == Reply.Error:
|
||||||
raise IOError("Flash storage is full")
|
raise IOError("Device failed to write config. More information may be available in the log.")
|
||||||
elif ty != Reply.Success:
|
elif ty != Reply.Success:
|
||||||
raise IOError("Incorrect reply from device: {} (expected {})".
|
raise IOError("Incorrect reply from device: {} (expected {})".
|
||||||
format(ty, Reply.Success))
|
format(ty, Reply.Success))
|
||||||
|
|
|
@ -33,14 +33,6 @@ class CommMonInj:
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self._writer.write(b"ARTIQ moninj\n")
|
self._writer.write(b"ARTIQ moninj\n")
|
||||||
# get device endian
|
|
||||||
endian = await self._reader.read(1)
|
|
||||||
if endian == b"e":
|
|
||||||
self.endian = "<"
|
|
||||||
elif endian == b"E":
|
|
||||||
self.endian = ">"
|
|
||||||
else:
|
|
||||||
raise IOError("Incorrect reply from device: expected e/E.")
|
|
||||||
self._receive_task = asyncio.ensure_future(self._receive_cr())
|
self._receive_task = asyncio.ensure_future(self._receive_cr())
|
||||||
except:
|
except:
|
||||||
self._writer.close()
|
self._writer.close()
|
||||||
|
@ -62,19 +54,19 @@ class CommMonInj:
|
||||||
del self._writer
|
del self._writer
|
||||||
|
|
||||||
def monitor_probe(self, enable, channel, probe):
|
def monitor_probe(self, enable, channel, probe):
|
||||||
packet = struct.pack(self.endian + "bblb", 0, enable, channel, probe)
|
packet = struct.pack("<bblb", 0, enable, channel, probe)
|
||||||
self._writer.write(packet)
|
self._writer.write(packet)
|
||||||
|
|
||||||
def monitor_injection(self, enable, channel, overrd):
|
def monitor_injection(self, enable, channel, overrd):
|
||||||
packet = struct.pack(self.endian + "bblb", 3, enable, channel, overrd)
|
packet = struct.pack("<bblb", 3, enable, channel, overrd)
|
||||||
self._writer.write(packet)
|
self._writer.write(packet)
|
||||||
|
|
||||||
def inject(self, channel, override, value):
|
def inject(self, channel, override, value):
|
||||||
packet = struct.pack(self.endian + "blbb", 1, channel, override, value)
|
packet = struct.pack("<blbb", 1, channel, override, value)
|
||||||
self._writer.write(packet)
|
self._writer.write(packet)
|
||||||
|
|
||||||
def get_injection_status(self, channel, override):
|
def get_injection_status(self, channel, override):
|
||||||
packet = struct.pack(self.endian + "blb", 2, channel, override)
|
packet = struct.pack("<blb", 2, channel, override)
|
||||||
self._writer.write(packet)
|
self._writer.write(packet)
|
||||||
|
|
||||||
async def _receive_cr(self):
|
async def _receive_cr(self):
|
||||||
|
@ -84,14 +76,12 @@ class CommMonInj:
|
||||||
if not ty:
|
if not ty:
|
||||||
return
|
return
|
||||||
if ty == b"\x00":
|
if ty == b"\x00":
|
||||||
payload = await self._reader.readexactly(9)
|
payload = await self._reader.readexactly(13)
|
||||||
channel, probe, value = struct.unpack(
|
channel, probe, value = struct.unpack("<lbq", payload)
|
||||||
self.endian + "lbl", payload)
|
|
||||||
self.monitor_cb(channel, probe, value)
|
self.monitor_cb(channel, probe, value)
|
||||||
elif ty == b"\x01":
|
elif ty == b"\x01":
|
||||||
payload = await self._reader.readexactly(6)
|
payload = await self._reader.readexactly(6)
|
||||||
channel, override, value = struct.unpack(
|
channel, override, value = struct.unpack("<lbb", payload)
|
||||||
self.endian + "lbb", payload)
|
|
||||||
self.injection_status_cb(channel, override, value)
|
self.injection_status_cb(channel, override, value)
|
||||||
else:
|
else:
|
||||||
raise ValueError("Unknown packet type", ty)
|
raise ValueError("Unknown packet type", ty)
|
||||||
|
|
|
@ -726,7 +726,7 @@ class ExperimentManager:
|
||||||
else:
|
else:
|
||||||
repo_match = "repo_rev" not in expid
|
repo_match = "repo_rev" not in expid
|
||||||
if (repo_match and
|
if (repo_match and
|
||||||
expid["file"] == file and
|
("file" in expid and expid["file"] == file) and
|
||||||
expid["class_name"] == class_name):
|
expid["class_name"] == class_name):
|
||||||
rids.append(rid)
|
rids.append(rid)
|
||||||
asyncio.ensure_future(self._request_term_multiple(rids))
|
asyncio.ensure_future(self._request_term_multiple(rids))
|
||||||
|
|
|
@ -202,19 +202,16 @@ _WidgetDesc = namedtuple("_WidgetDesc", "uid comment cls arguments")
|
||||||
|
|
||||||
|
|
||||||
def setup_from_ddb(ddb):
|
def setup_from_ddb(ddb):
|
||||||
core_addr = None
|
mi_addr = None
|
||||||
dds_sysclk = None
|
dds_sysclk = None
|
||||||
description = set()
|
description = set()
|
||||||
|
|
||||||
for k, v in ddb.items():
|
for k, v in ddb.items():
|
||||||
comment = None
|
|
||||||
if "comment" in v:
|
|
||||||
comment = v["comment"]
|
|
||||||
try:
|
try:
|
||||||
if isinstance(v, dict) and v["type"] == "local":
|
if isinstance(v, dict):
|
||||||
if k == "core":
|
comment = v.get("comment")
|
||||||
core_addr = v["arguments"]["host"]
|
if v["type"] == "local":
|
||||||
elif v["module"] == "artiq.coredevice.ttl":
|
if v["module"] == "artiq.coredevice.ttl":
|
||||||
channel = v["arguments"]["channel"]
|
channel = v["arguments"]["channel"]
|
||||||
force_out = v["class"] == "TTLOut"
|
force_out = v["class"] == "TTLOut"
|
||||||
widget = _WidgetDesc(k, comment, _TTLWidget, (channel, force_out, k))
|
widget = _WidgetDesc(k, comment, _TTLWidget, (channel, force_out, k))
|
||||||
|
@ -236,17 +233,19 @@ def setup_from_ddb(ddb):
|
||||||
for channel in range(32):
|
for channel in range(32):
|
||||||
widget = _WidgetDesc((k, channel), comment, _DACWidget, (spi_channel, channel, k))
|
widget = _WidgetDesc((k, channel), comment, _DACWidget, (spi_channel, channel, k))
|
||||||
description.add(widget)
|
description.add(widget)
|
||||||
|
elif v["type"] == "controller" and k == "core_moninj":
|
||||||
|
mi_addr = v["host"]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
pass
|
pass
|
||||||
return core_addr, dds_sysclk, description
|
return mi_addr, dds_sysclk, description
|
||||||
|
|
||||||
|
|
||||||
class _DeviceManager:
|
class _DeviceManager:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.core_addr = None
|
self.mi_addr = None
|
||||||
self.reconnect_core = asyncio.Event()
|
self.reconnect_mi = asyncio.Event()
|
||||||
self.core_connection = None
|
self.mi_connection = None
|
||||||
self.core_connector_task = asyncio.ensure_future(self.core_connector())
|
self.mi_connector_task = asyncio.ensure_future(self.mi_connector())
|
||||||
|
|
||||||
self.ddb = dict()
|
self.ddb = dict()
|
||||||
self.description = set()
|
self.description = set()
|
||||||
|
@ -265,11 +264,11 @@ class _DeviceManager:
|
||||||
return ddb
|
return ddb
|
||||||
|
|
||||||
def notify(self, mod):
|
def notify(self, mod):
|
||||||
core_addr, dds_sysclk, description = setup_from_ddb(self.ddb)
|
mi_addr, dds_sysclk, description = setup_from_ddb(self.ddb)
|
||||||
|
|
||||||
if core_addr != self.core_addr:
|
if mi_addr != self.mi_addr:
|
||||||
self.core_addr = core_addr
|
self.mi_addr = mi_addr
|
||||||
self.reconnect_core.set()
|
self.reconnect_mi.set()
|
||||||
|
|
||||||
self.dds_sysclk = dds_sysclk
|
self.dds_sysclk = dds_sysclk
|
||||||
|
|
||||||
|
@ -319,44 +318,44 @@ class _DeviceManager:
|
||||||
self.description = description
|
self.description = description
|
||||||
|
|
||||||
def ttl_set_mode(self, channel, mode):
|
def ttl_set_mode(self, channel, mode):
|
||||||
if self.core_connection is not None:
|
if self.mi_connection is not None:
|
||||||
widget = self.ttl_widgets[channel]
|
widget = self.ttl_widgets[channel]
|
||||||
if mode == "0":
|
if mode == "0":
|
||||||
widget.cur_override = True
|
widget.cur_override = True
|
||||||
widget.cur_level = False
|
widget.cur_level = False
|
||||||
self.core_connection.inject(channel, TTLOverride.level.value, 0)
|
self.mi_connection.inject(channel, TTLOverride.level.value, 0)
|
||||||
self.core_connection.inject(channel, TTLOverride.oe.value, 1)
|
self.mi_connection.inject(channel, TTLOverride.oe.value, 1)
|
||||||
self.core_connection.inject(channel, TTLOverride.en.value, 1)
|
self.mi_connection.inject(channel, TTLOverride.en.value, 1)
|
||||||
elif mode == "1":
|
elif mode == "1":
|
||||||
widget.cur_override = True
|
widget.cur_override = True
|
||||||
widget.cur_level = True
|
widget.cur_level = True
|
||||||
self.core_connection.inject(channel, TTLOverride.level.value, 1)
|
self.mi_connection.inject(channel, TTLOverride.level.value, 1)
|
||||||
self.core_connection.inject(channel, TTLOverride.oe.value, 1)
|
self.mi_connection.inject(channel, TTLOverride.oe.value, 1)
|
||||||
self.core_connection.inject(channel, TTLOverride.en.value, 1)
|
self.mi_connection.inject(channel, TTLOverride.en.value, 1)
|
||||||
elif mode == "exp":
|
elif mode == "exp":
|
||||||
widget.cur_override = False
|
widget.cur_override = False
|
||||||
self.core_connection.inject(channel, TTLOverride.en.value, 0)
|
self.mi_connection.inject(channel, TTLOverride.en.value, 0)
|
||||||
else:
|
else:
|
||||||
raise ValueError
|
raise ValueError
|
||||||
# override state may have changed
|
# override state may have changed
|
||||||
widget.refresh_display()
|
widget.refresh_display()
|
||||||
|
|
||||||
def setup_ttl_monitoring(self, enable, channel):
|
def setup_ttl_monitoring(self, enable, channel):
|
||||||
if self.core_connection is not None:
|
if self.mi_connection is not None:
|
||||||
self.core_connection.monitor_probe(enable, channel, TTLProbe.level.value)
|
self.mi_connection.monitor_probe(enable, channel, TTLProbe.level.value)
|
||||||
self.core_connection.monitor_probe(enable, channel, TTLProbe.oe.value)
|
self.mi_connection.monitor_probe(enable, channel, TTLProbe.oe.value)
|
||||||
self.core_connection.monitor_injection(enable, channel, TTLOverride.en.value)
|
self.mi_connection.monitor_injection(enable, channel, TTLOverride.en.value)
|
||||||
self.core_connection.monitor_injection(enable, channel, TTLOverride.level.value)
|
self.mi_connection.monitor_injection(enable, channel, TTLOverride.level.value)
|
||||||
if enable:
|
if enable:
|
||||||
self.core_connection.get_injection_status(channel, TTLOverride.en.value)
|
self.mi_connection.get_injection_status(channel, TTLOverride.en.value)
|
||||||
|
|
||||||
def setup_dds_monitoring(self, enable, bus_channel, channel):
|
def setup_dds_monitoring(self, enable, bus_channel, channel):
|
||||||
if self.core_connection is not None:
|
if self.mi_connection is not None:
|
||||||
self.core_connection.monitor_probe(enable, bus_channel, channel)
|
self.mi_connection.monitor_probe(enable, bus_channel, channel)
|
||||||
|
|
||||||
def setup_dac_monitoring(self, enable, spi_channel, channel):
|
def setup_dac_monitoring(self, enable, spi_channel, channel):
|
||||||
if self.core_connection is not None:
|
if self.mi_connection is not None:
|
||||||
self.core_connection.monitor_probe(enable, spi_channel, channel)
|
self.mi_connection.monitor_probe(enable, spi_channel, channel)
|
||||||
|
|
||||||
def monitor_cb(self, channel, probe, value):
|
def monitor_cb(self, channel, probe, value):
|
||||||
if channel in self.ttl_widgets:
|
if channel in self.ttl_widgets:
|
||||||
|
@ -385,29 +384,29 @@ class _DeviceManager:
|
||||||
widget.refresh_display()
|
widget.refresh_display()
|
||||||
|
|
||||||
def disconnect_cb(self):
|
def disconnect_cb(self):
|
||||||
logger.error("lost connection to core device moninj")
|
logger.error("lost connection to moninj")
|
||||||
self.reconnect_core.set()
|
self.reconnect_mi.set()
|
||||||
|
|
||||||
async def core_connector(self):
|
async def mi_connector(self):
|
||||||
while True:
|
while True:
|
||||||
await self.reconnect_core.wait()
|
await self.reconnect_mi.wait()
|
||||||
self.reconnect_core.clear()
|
self.reconnect_mi.clear()
|
||||||
if self.core_connection is not None:
|
if self.mi_connection is not None:
|
||||||
await self.core_connection.close()
|
await self.mi_connection.close()
|
||||||
self.core_connection = None
|
self.mi_connection = None
|
||||||
new_core_connection = CommMonInj(self.monitor_cb, self.injection_status_cb,
|
new_mi_connection = CommMonInj(self.monitor_cb, self.injection_status_cb,
|
||||||
self.disconnect_cb)
|
self.disconnect_cb)
|
||||||
try:
|
try:
|
||||||
await new_core_connection.connect(self.core_addr, 1383)
|
await new_mi_connection.connect(self.mi_addr, 1383)
|
||||||
except asyncio.CancelledError:
|
except asyncio.CancelledError:
|
||||||
logger.info("cancelled connection to core device moninj")
|
logger.info("cancelled connection to moninj")
|
||||||
break
|
break
|
||||||
except:
|
except:
|
||||||
logger.error("failed to connect to core device moninj", exc_info=True)
|
logger.error("failed to connect to moninj", exc_info=True)
|
||||||
await asyncio.sleep(10.)
|
await asyncio.sleep(10.)
|
||||||
self.reconnect_core.set()
|
self.reconnect_mi.set()
|
||||||
else:
|
else:
|
||||||
self.core_connection = new_core_connection
|
self.mi_connection = new_mi_connection
|
||||||
for ttl_channel in self.ttl_widgets.keys():
|
for ttl_channel in self.ttl_widgets.keys():
|
||||||
self.setup_ttl_monitoring(True, ttl_channel)
|
self.setup_ttl_monitoring(True, ttl_channel)
|
||||||
for bus_channel, channel in self.dds_widgets.keys():
|
for bus_channel, channel in self.dds_widgets.keys():
|
||||||
|
@ -416,13 +415,13 @@ class _DeviceManager:
|
||||||
self.setup_dac_monitoring(True, spi_channel, channel)
|
self.setup_dac_monitoring(True, spi_channel, channel)
|
||||||
|
|
||||||
async def close(self):
|
async def close(self):
|
||||||
self.core_connector_task.cancel()
|
self.mi_connector_task.cancel()
|
||||||
try:
|
try:
|
||||||
await asyncio.wait_for(self.core_connector_task, None)
|
await asyncio.wait_for(self.mi_connector_task, None)
|
||||||
except asyncio.CancelledError:
|
except asyncio.CancelledError:
|
||||||
pass
|
pass
|
||||||
if self.core_connection is not None:
|
if self.mi_connection is not None:
|
||||||
await self.core_connection.close()
|
await self.mi_connection.close()
|
||||||
|
|
||||||
|
|
||||||
class _MonInjDock(QtWidgets.QDockWidget):
|
class _MonInjDock(QtWidgets.QDockWidget):
|
||||||
|
|
|
@ -48,7 +48,7 @@ class Model(DictSyncModel):
|
||||||
else:
|
else:
|
||||||
return "Outside repo."
|
return "Outside repo."
|
||||||
elif column == 6:
|
elif column == 6:
|
||||||
return v["expid"]["file"]
|
return v["expid"].get("file", "<none>")
|
||||||
elif column == 7:
|
elif column == 7:
|
||||||
if v["expid"]["class_name"] is None:
|
if v["expid"]["class_name"] is None:
|
||||||
return ""
|
return ""
|
||||||
|
|
|
@ -13,6 +13,13 @@ device_db = {
|
||||||
"port": 1068,
|
"port": 1068,
|
||||||
"command": "aqctl_corelog -p {port} --bind {bind} " + core_addr
|
"command": "aqctl_corelog -p {port} --bind {bind} " + core_addr
|
||||||
},
|
},
|
||||||
|
"core_moninj": {
|
||||||
|
"type": "controller",
|
||||||
|
"host": "::1",
|
||||||
|
"port_proxy": 1383,
|
||||||
|
"port": 1384,
|
||||||
|
"command": "aqctl_moninj_proxy --port-proxy {port_proxy} --port-control {port} --bind {bind} " + core_addr
|
||||||
|
},
|
||||||
"core_cache": {
|
"core_cache": {
|
||||||
"type": "local",
|
"type": "local",
|
||||||
"module": "artiq.coredevice.cache",
|
"module": "artiq.coredevice.cache",
|
||||||
|
|
|
@ -17,6 +17,13 @@ device_db = {
|
||||||
"port": 1068,
|
"port": 1068,
|
||||||
"command": "aqctl_corelog -p {port} --bind {bind} " + core_addr
|
"command": "aqctl_corelog -p {port} --bind {bind} " + core_addr
|
||||||
},
|
},
|
||||||
|
"core_moninj": {
|
||||||
|
"type": "controller",
|
||||||
|
"host": "::1",
|
||||||
|
"port_proxy": 1383,
|
||||||
|
"port": 1384,
|
||||||
|
"command": "aqctl_moninj_proxy --port-proxy {port_proxy} --port-control {port} --bind {bind} " + core_addr
|
||||||
|
},
|
||||||
"core_cache": {
|
"core_cache": {
|
||||||
"type": "local",
|
"type": "local",
|
||||||
"module": "artiq.coredevice.cache",
|
"module": "artiq.coredevice.cache",
|
||||||
|
|
|
@ -63,7 +63,10 @@ static mut API: &'static [(&'static str, *const ())] = &[
|
||||||
api!(__powidf2),
|
api!(__powidf2),
|
||||||
|
|
||||||
/* libc */
|
/* libc */
|
||||||
api!(memcmp, extern { fn memcmp(a: *const u8, b: *mut u8, size: usize); }),
|
api!(memcpy, extern { fn memcpy(dest: *mut u8, src: *const u8, n: usize) -> *mut u8; }),
|
||||||
|
api!(memmove, extern { fn memmove(dest: *mut u8, src: *const u8, n: usize) -> *mut u8; }),
|
||||||
|
api!(memset, extern { fn memset(s: *mut u8, c: i32, n: usize) -> *mut u8; }),
|
||||||
|
api!(memcmp, extern { fn memcmp(s1: *const u8, s2: *const u8, n: usize) -> i32; }),
|
||||||
|
|
||||||
/* libm */
|
/* libm */
|
||||||
// commented out functions are not available with the libm used here, but are available in NAR3.
|
// commented out functions are not available with the libm used here, but are available in NAR3.
|
||||||
|
|
|
@ -64,12 +64,14 @@ pub fn config_routing_table(default_n_links: usize) -> RoutingTable {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
} else {
|
||||||
|
warn!("length of the configured routing table is incorrect");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
false
|
false
|
||||||
});
|
});
|
||||||
if !ok {
|
if !ok {
|
||||||
warn!("could not read routing table from configuration, using default");
|
info!("could not read routing table from configuration, using default");
|
||||||
}
|
}
|
||||||
info!("routing table: {}", ret);
|
info!("routing table: {}", ret);
|
||||||
ret
|
ret
|
||||||
|
|
|
@ -34,7 +34,7 @@ pub enum Packet {
|
||||||
RoutingAck,
|
RoutingAck,
|
||||||
|
|
||||||
MonitorRequest { destination: u8, channel: u16, probe: u8 },
|
MonitorRequest { destination: u8, channel: u16, probe: u8 },
|
||||||
MonitorReply { value: u32 },
|
MonitorReply { value: u64 },
|
||||||
InjectionRequest { destination: u8, channel: u16, overrd: u8, value: u8 },
|
InjectionRequest { destination: u8, channel: u16, overrd: u8, value: u8 },
|
||||||
InjectionStatusRequest { destination: u8, channel: u16, overrd: u8 },
|
InjectionStatusRequest { destination: u8, channel: u16, overrd: u8 },
|
||||||
InjectionStatusReply { value: u8 },
|
InjectionStatusReply { value: u8 },
|
||||||
|
@ -105,7 +105,7 @@ impl Packet {
|
||||||
probe: reader.read_u8()?
|
probe: reader.read_u8()?
|
||||||
},
|
},
|
||||||
0x41 => Packet::MonitorReply {
|
0x41 => Packet::MonitorReply {
|
||||||
value: reader.read_u32()?
|
value: reader.read_u64()?
|
||||||
},
|
},
|
||||||
0x50 => Packet::InjectionRequest {
|
0x50 => Packet::InjectionRequest {
|
||||||
destination: reader.read_u8()?,
|
destination: reader.read_u8()?,
|
||||||
|
@ -259,7 +259,7 @@ impl Packet {
|
||||||
},
|
},
|
||||||
Packet::MonitorReply { value } => {
|
Packet::MonitorReply { value } => {
|
||||||
writer.write_u8(0x41)?;
|
writer.write_u8(0x41)?;
|
||||||
writer.write_u32(value)?;
|
writer.write_u64(value)?;
|
||||||
},
|
},
|
||||||
Packet::InjectionRequest { destination, channel, overrd, value } => {
|
Packet::InjectionRequest { destination, channel, overrd, value } => {
|
||||||
writer.write_u8(0x50)?;
|
writer.write_u8(0x50)?;
|
||||||
|
|
|
@ -40,7 +40,7 @@ pub enum HostMessage {
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum DeviceMessage {
|
pub enum DeviceMessage {
|
||||||
MonitorStatus { channel: u32, probe: u8, value: u32 },
|
MonitorStatus { channel: u32, probe: u8, value: u64 },
|
||||||
InjectionStatus { channel: u32, overrd: u8, value: u8 }
|
InjectionStatus { channel: u32, overrd: u8, value: u8 }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -82,7 +82,7 @@ impl DeviceMessage {
|
||||||
writer.write_u8(0)?;
|
writer.write_u8(0)?;
|
||||||
writer.write_u32(channel)?;
|
writer.write_u32(channel)?;
|
||||||
writer.write_u8(probe)?;
|
writer.write_u8(probe)?;
|
||||||
writer.write_u32(value)?;
|
writer.write_u64(value)?;
|
||||||
},
|
},
|
||||||
DeviceMessage::InjectionStatus { channel, overrd, value } => {
|
DeviceMessage::InjectionStatus { channel, overrd, value } => {
|
||||||
writer.write_u8(1)?;
|
writer.write_u8(1)?;
|
||||||
|
|
|
@ -2,7 +2,6 @@ use alloc::collections::btree_map::BTreeMap;
|
||||||
use core::cell::RefCell;
|
use core::cell::RefCell;
|
||||||
|
|
||||||
use io::Error as IoError;
|
use io::Error as IoError;
|
||||||
use io::Write;
|
|
||||||
use moninj_proto::*;
|
use moninj_proto::*;
|
||||||
use sched::{Io, Mutex, TcpListener, TcpStream, Error as SchedError};
|
use sched::{Io, Mutex, TcpListener, TcpStream, Error as SchedError};
|
||||||
use urc::Urc;
|
use urc::Urc;
|
||||||
|
@ -13,12 +12,12 @@ use board_artiq::drtio_routing;
|
||||||
mod local_moninj {
|
mod local_moninj {
|
||||||
use board_misoc::csr;
|
use board_misoc::csr;
|
||||||
|
|
||||||
pub fn read_probe(channel: u16, probe: u8) -> u32 {
|
pub fn read_probe(channel: u16, probe: u8) -> u64 {
|
||||||
unsafe {
|
unsafe {
|
||||||
csr::rtio_moninj::mon_chan_sel_write(channel as _);
|
csr::rtio_moninj::mon_chan_sel_write(channel as _);
|
||||||
csr::rtio_moninj::mon_probe_sel_write(probe);
|
csr::rtio_moninj::mon_probe_sel_write(probe);
|
||||||
csr::rtio_moninj::mon_value_update_write(1);
|
csr::rtio_moninj::mon_value_update_write(1);
|
||||||
csr::rtio_moninj::mon_value_read() as u32
|
csr::rtio_moninj::mon_value_read() as u64
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -41,7 +40,7 @@ mod local_moninj {
|
||||||
|
|
||||||
#[cfg(not(has_rtio_moninj))]
|
#[cfg(not(has_rtio_moninj))]
|
||||||
mod local_moninj {
|
mod local_moninj {
|
||||||
pub fn read_probe(_channel: u16, _probe: u8) -> u32 { 0 }
|
pub fn read_probe(_channel: u16, _probe: u8) -> u64 { 0 }
|
||||||
|
|
||||||
pub fn inject(_channel: u16, _overrd: u8, _value: u8) { }
|
pub fn inject(_channel: u16, _overrd: u8, _value: u8) { }
|
||||||
|
|
||||||
|
@ -54,7 +53,7 @@ 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, linkno: u8, destination: u8, channel: u16, probe: u8) -> u32 {
|
pub fn read_probe(io: &Io, aux_mutex: &Mutex, linkno: u8, destination: u8, channel: u16, probe: u8) -> u64 {
|
||||||
let reply = drtio::aux_transact(io, aux_mutex, linkno, &drtioaux::Packet::MonitorRequest {
|
let reply = drtio::aux_transact(io, aux_mutex, linkno, &drtioaux::Packet::MonitorRequest {
|
||||||
destination: destination,
|
destination: destination,
|
||||||
channel: channel,
|
channel: channel,
|
||||||
|
@ -123,7 +122,6 @@ fn connection_worker(io: &Io, _aux_mutex: &Mutex, _routing_table: &drtio_routing
|
||||||
let mut next_check = 0;
|
let mut next_check = 0;
|
||||||
|
|
||||||
read_magic(&mut stream)?;
|
read_magic(&mut stream)?;
|
||||||
stream.write_all("e".as_bytes())?;
|
|
||||||
info!("new connection from {}", stream.remote_endpoint());
|
info!("new connection from {}", stream.remote_endpoint());
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
|
|
|
@ -201,13 +201,13 @@ fn process_aux_packet(_repeaters: &mut [repeater::Repeater],
|
||||||
csr::rtio_moninj::mon_chan_sel_write(channel as _);
|
csr::rtio_moninj::mon_chan_sel_write(channel as _);
|
||||||
csr::rtio_moninj::mon_probe_sel_write(probe);
|
csr::rtio_moninj::mon_probe_sel_write(probe);
|
||||||
csr::rtio_moninj::mon_value_update_write(1);
|
csr::rtio_moninj::mon_value_update_write(1);
|
||||||
value = csr::rtio_moninj::mon_value_read();
|
value = csr::rtio_moninj::mon_value_read() as u64;
|
||||||
}
|
}
|
||||||
#[cfg(not(has_rtio_moninj))]
|
#[cfg(not(has_rtio_moninj))]
|
||||||
{
|
{
|
||||||
value = 0;
|
value = 0;
|
||||||
}
|
}
|
||||||
let reply = drtioaux::Packet::MonitorReply { value: value as u32 };
|
let reply = drtioaux::Packet::MonitorReply { value: value };
|
||||||
drtioaux::send(0, &reply)
|
drtioaux::send(0, &reply)
|
||||||
},
|
},
|
||||||
drtioaux::Packet::InjectionRequest { destination: _destination, channel, overrd, value } => {
|
drtioaux::Packet::InjectionRequest { destination: _destination, channel, overrd, value } => {
|
||||||
|
|
|
@ -0,0 +1,224 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import logging
|
||||||
|
import asyncio
|
||||||
|
import struct
|
||||||
|
from enum import Enum
|
||||||
|
|
||||||
|
from sipyco.asyncio_tools import AsyncioServer
|
||||||
|
from sipyco.pc_rpc import Server
|
||||||
|
from sipyco import common_args
|
||||||
|
|
||||||
|
from artiq.coredevice.comm_moninj import CommMonInj
|
||||||
|
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class EventType(Enum):
|
||||||
|
PROBE = 0
|
||||||
|
INJECTION = 1
|
||||||
|
|
||||||
|
|
||||||
|
class MonitorMux:
|
||||||
|
def __init__(self):
|
||||||
|
self.listeners = dict()
|
||||||
|
self.comm_moninj = None
|
||||||
|
|
||||||
|
def _monitor(self, listener, event):
|
||||||
|
try:
|
||||||
|
listeners = self.listeners[event]
|
||||||
|
except KeyError:
|
||||||
|
listeners = []
|
||||||
|
self.listeners[event] = listeners
|
||||||
|
if event[0] == EventType.PROBE:
|
||||||
|
logger.debug("starting monitoring channel %d probe %d", event[1], event[2])
|
||||||
|
self.comm_moninj.monitor_probe(True, event[1], event[2])
|
||||||
|
elif event[0] == EventType.INJECTION:
|
||||||
|
logger.debug("starting monitoring channel %d injection %d", event[1], event[2])
|
||||||
|
self.comm_moninj.monitor_injection(True, event[1], event[2])
|
||||||
|
else:
|
||||||
|
raise ValueError
|
||||||
|
if listener in listeners:
|
||||||
|
logger.warning("listener trying to subscribe twice to %s", event)
|
||||||
|
else:
|
||||||
|
listeners.append(listener)
|
||||||
|
|
||||||
|
def _unmonitor(self, listener, event):
|
||||||
|
try:
|
||||||
|
listeners = self.listeners[event]
|
||||||
|
except KeyError:
|
||||||
|
listeners = []
|
||||||
|
try:
|
||||||
|
listeners.remove(listener)
|
||||||
|
except ValueError:
|
||||||
|
logger.warning("listener trying to unsubscribe from %s, but was not subscribed", event)
|
||||||
|
return
|
||||||
|
if not listeners:
|
||||||
|
del self.listeners[event]
|
||||||
|
if event[0] == EventType.PROBE:
|
||||||
|
logger.debug("stopped monitoring channel %d probe %d", event[1], event[2])
|
||||||
|
self.comm_moninj.monitor_probe(False, event[1], event[2])
|
||||||
|
elif event[0] == EventType.INJECTION:
|
||||||
|
logger.debug("stopped monitoring channel %d injection %d", event[1], event[2])
|
||||||
|
self.comm_moninj.monitor_injection(False, event[1], event[2])
|
||||||
|
else:
|
||||||
|
raise ValueError
|
||||||
|
|
||||||
|
def monitor_probe(self, listener, enable, channel, probe):
|
||||||
|
if enable:
|
||||||
|
self._monitor(listener, (EventType.PROBE, channel, probe))
|
||||||
|
else:
|
||||||
|
self._unmonitor(listener, (EventType.PROBE, channel, probe))
|
||||||
|
|
||||||
|
def monitor_injection(self, listener, enable, channel, overrd):
|
||||||
|
if enable:
|
||||||
|
self._monitor(listener, (EventType.INJECTION, channel, overrd))
|
||||||
|
else:
|
||||||
|
self._unmonitor(listener, (EventType.INJECTION, channel, overrd))
|
||||||
|
|
||||||
|
def _event_cb(self, event, value):
|
||||||
|
try:
|
||||||
|
listeners = self.listeners[event]
|
||||||
|
except KeyError:
|
||||||
|
# We may still receive buffered events shortly after an unsubscription. They can be ignored.
|
||||||
|
logger.debug("received event %s but no listener", event)
|
||||||
|
listeners = []
|
||||||
|
for listener in listeners:
|
||||||
|
if event[0] == EventType.PROBE:
|
||||||
|
listener.monitor_cb(event[1], event[2], value)
|
||||||
|
elif event[0] == EventType.INJECTION:
|
||||||
|
listener.injection_status_cb(event[1], event[2], value)
|
||||||
|
else:
|
||||||
|
raise ValueError
|
||||||
|
|
||||||
|
def monitor_cb(self, channel, probe, value):
|
||||||
|
self._event_cb((EventType.PROBE, channel, probe), value)
|
||||||
|
|
||||||
|
def injection_status_cb(self, channel, override, value):
|
||||||
|
self._event_cb((EventType.INJECTION, channel, override), value)
|
||||||
|
|
||||||
|
def remove_listener(self, listener):
|
||||||
|
for event, listeners in list(self.listeners.items()):
|
||||||
|
try:
|
||||||
|
listeners.remove(listener)
|
||||||
|
except ValueError:
|
||||||
|
pass
|
||||||
|
if not listeners:
|
||||||
|
del self.listeners[event]
|
||||||
|
if event[0] == EventType.PROBE:
|
||||||
|
logger.debug("stopped monitoring channel %d probe %d", event[1], event[2])
|
||||||
|
self.comm_moninj.monitor_probe(False, event[1], event[2])
|
||||||
|
elif event[0] == EventType.INJECTION:
|
||||||
|
logger.debug("stopped monitoring channel %d injection %d", event[1], event[2])
|
||||||
|
self.comm_moninj.monitor_injection(False, event[1], event[2])
|
||||||
|
else:
|
||||||
|
raise ValueError
|
||||||
|
|
||||||
|
|
||||||
|
class ProxyConnection:
|
||||||
|
def __init__(self, monitor_mux, reader, writer):
|
||||||
|
self.monitor_mux = monitor_mux
|
||||||
|
self.reader = reader
|
||||||
|
self.writer = writer
|
||||||
|
|
||||||
|
async def handle(self):
|
||||||
|
try:
|
||||||
|
while True:
|
||||||
|
ty = await self.reader.read(1)
|
||||||
|
if not ty:
|
||||||
|
return
|
||||||
|
if ty == b"\x00": # MonitorProbe
|
||||||
|
packet = await self.reader.readexactly(6)
|
||||||
|
enable, channel, probe = struct.unpack("<blb", packet)
|
||||||
|
self.monitor_mux.monitor_probe(self, enable, channel, probe)
|
||||||
|
elif ty == b"\x01": # Inject
|
||||||
|
packet = await self.reader.readexactly(6)
|
||||||
|
channel, overrd, value = struct.unpack("<lbb", packet)
|
||||||
|
self.monitor_mux.comm_moninj.inject(channel, overrd, value)
|
||||||
|
elif ty == b"\x02": # GetInjectionStatus
|
||||||
|
packet = await self.reader.readexactly(5)
|
||||||
|
channel, overrd = struct.unpack("<lb", packet)
|
||||||
|
self.monitor_mux.comm_moninj.get_injection_status(channel, overrd)
|
||||||
|
elif ty == b"\x03": # MonitorInjection
|
||||||
|
packet = await self.reader.readexactly(6)
|
||||||
|
enable, channel, overrd = struct.unpack("<blb", packet)
|
||||||
|
self.monitor_mux.monitor_injection(self, enable, channel, overrd)
|
||||||
|
else:
|
||||||
|
raise ValueError
|
||||||
|
finally:
|
||||||
|
self.monitor_mux.remove_listener(self)
|
||||||
|
|
||||||
|
def monitor_cb(self, channel, probe, value):
|
||||||
|
packet = struct.pack("<blbq", 0, channel, probe, value)
|
||||||
|
self.writer.write(packet)
|
||||||
|
|
||||||
|
def injection_status_cb(self, channel, override, value):
|
||||||
|
packet = struct.pack("<blbb", 1, channel, override, value)
|
||||||
|
self.writer.write(packet)
|
||||||
|
|
||||||
|
|
||||||
|
class ProxyServer(AsyncioServer):
|
||||||
|
def __init__(self, monitor_mux):
|
||||||
|
AsyncioServer.__init__(self)
|
||||||
|
self.monitor_mux = monitor_mux
|
||||||
|
|
||||||
|
async def _handle_connection_cr(self, reader, writer):
|
||||||
|
line = await reader.readline()
|
||||||
|
if line != b"ARTIQ moninj\n":
|
||||||
|
logger.error("incorrect magic")
|
||||||
|
return
|
||||||
|
await ProxyConnection(self.monitor_mux, reader, writer).handle()
|
||||||
|
|
||||||
|
|
||||||
|
def get_argparser():
|
||||||
|
parser = argparse.ArgumentParser(
|
||||||
|
description="ARTIQ moninj proxy")
|
||||||
|
common_args.verbosity_args(parser)
|
||||||
|
common_args.simple_network_args(parser, [
|
||||||
|
("proxy", "proxying", 1383),
|
||||||
|
("control", "control", 1384)
|
||||||
|
])
|
||||||
|
parser.add_argument("core_addr", metavar="CORE_ADDR",
|
||||||
|
help="hostname or IP address of the core device")
|
||||||
|
return parser
|
||||||
|
|
||||||
|
|
||||||
|
class PingTarget:
|
||||||
|
def ping(self):
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
args = get_argparser().parse_args()
|
||||||
|
common_args.init_logger_from_args(args)
|
||||||
|
|
||||||
|
bind_address = common_args.bind_address_from_args(args)
|
||||||
|
|
||||||
|
loop = asyncio.get_event_loop()
|
||||||
|
try:
|
||||||
|
monitor_mux = MonitorMux()
|
||||||
|
comm_moninj = CommMonInj(monitor_mux.monitor_cb, monitor_mux.injection_status_cb)
|
||||||
|
monitor_mux.comm_moninj = comm_moninj
|
||||||
|
loop.run_until_complete(comm_moninj.connect(args.core_addr))
|
||||||
|
try:
|
||||||
|
proxy_server = ProxyServer(monitor_mux)
|
||||||
|
loop.run_until_complete(proxy_server.start(bind_address, args.port_proxy))
|
||||||
|
try:
|
||||||
|
server = Server({"moninj_proxy": PingTarget()}, None, True)
|
||||||
|
loop.run_until_complete(server.start(bind_address, args.port_control))
|
||||||
|
try:
|
||||||
|
loop.run_until_complete(server.wait_terminate())
|
||||||
|
finally:
|
||||||
|
loop.run_until_complete(server.stop())
|
||||||
|
finally:
|
||||||
|
loop.run_until_complete(proxy_server.stop())
|
||||||
|
finally:
|
||||||
|
loop.run_until_complete(comm_moninj.close())
|
||||||
|
finally:
|
||||||
|
loop.close()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
|
@ -67,6 +67,9 @@ def get_argparser():
|
||||||
parser_add.add_argument("-r", "--revision", default=None,
|
parser_add.add_argument("-r", "--revision", default=None,
|
||||||
help="use a specific repository revision "
|
help="use a specific repository revision "
|
||||||
"(defaults to head, ignored without -R)")
|
"(defaults to head, ignored without -R)")
|
||||||
|
parser_add.add_argument("--content", default=False,
|
||||||
|
action="store_true",
|
||||||
|
help="submit by content")
|
||||||
parser_add.add_argument("-c", "--class-name", default=None,
|
parser_add.add_argument("-c", "--class-name", default=None,
|
||||||
help="name of the class to run")
|
help="name of the class to run")
|
||||||
parser_add.add_argument("file", metavar="FILE",
|
parser_add.add_argument("file", metavar="FILE",
|
||||||
|
@ -134,10 +137,16 @@ def _action_submit(remote, args):
|
||||||
|
|
||||||
expid = {
|
expid = {
|
||||||
"log_level": logging.WARNING + args.quiet*10 - args.verbose*10,
|
"log_level": logging.WARNING + args.quiet*10 - args.verbose*10,
|
||||||
"file": args.file,
|
|
||||||
"class_name": args.class_name,
|
"class_name": args.class_name,
|
||||||
"arguments": arguments,
|
"arguments": arguments,
|
||||||
}
|
}
|
||||||
|
if args.content:
|
||||||
|
with open(args.file, "r") as f:
|
||||||
|
expid["content"] = f.read()
|
||||||
|
if args.repository:
|
||||||
|
raise ValueError("Repository cannot be used when submitting by content")
|
||||||
|
else:
|
||||||
|
expid["file"] = args.file
|
||||||
if args.repository:
|
if args.repository:
|
||||||
expid["repo_rev"] = args.revision
|
expid["repo_rev"] = args.revision
|
||||||
if args.timed is None:
|
if args.timed is None:
|
||||||
|
@ -207,7 +216,7 @@ def _show_schedule(schedule):
|
||||||
row.append(expid["repo_rev"])
|
row.append(expid["repo_rev"])
|
||||||
else:
|
else:
|
||||||
row.append("Outside repo.")
|
row.append("Outside repo.")
|
||||||
row.append(expid["file"])
|
row.append(expid.get("file", "<none>"))
|
||||||
if expid["class_name"] is None:
|
if expid["class_name"] is None:
|
||||||
row.append("")
|
row.append("")
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -38,6 +38,13 @@ def process_header(output, description):
|
||||||
"port": 1068,
|
"port": 1068,
|
||||||
"command": "aqctl_corelog -p {{port}} --bind {{bind}} " + core_addr
|
"command": "aqctl_corelog -p {{port}} --bind {{bind}} " + core_addr
|
||||||
}},
|
}},
|
||||||
|
"core_moninj": {{
|
||||||
|
"type": "controller",
|
||||||
|
"host": "::1",
|
||||||
|
"port_proxy": 1383,
|
||||||
|
"port": 1384,
|
||||||
|
"command": "aqctl_moninj_proxy --port-proxy {{port_proxy}} --port-control {{port}} --bind {{bind}} " + core_addr
|
||||||
|
}},
|
||||||
"core_cache": {{
|
"core_cache": {{
|
||||||
"type": "local",
|
"type": "local",
|
||||||
"module": "artiq.coredevice.cache",
|
"module": "artiq.coredevice.cache",
|
||||||
|
|
|
@ -74,7 +74,7 @@ class Worker:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def _get_log_source(self):
|
def _get_log_source(self):
|
||||||
return "worker({},{})".format(self.rid, self.filename)
|
return "worker({},{})".format(self.rid, self.filename if self.filename is not None else "<none>")
|
||||||
|
|
||||||
async def _create_process(self, log_level):
|
async def _create_process(self, log_level):
|
||||||
if self.ipc is not None:
|
if self.ipc is not None:
|
||||||
|
@ -260,6 +260,7 @@ class Worker:
|
||||||
async def build(self, rid, pipeline_name, wd, expid, priority,
|
async def build(self, rid, pipeline_name, wd, expid, priority,
|
||||||
timeout=15.0):
|
timeout=15.0):
|
||||||
self.rid = rid
|
self.rid = rid
|
||||||
|
if "file" in expid:
|
||||||
self.filename = os.path.basename(expid["file"])
|
self.filename = os.path.basename(expid["file"])
|
||||||
await self._create_process(expid["log_level"])
|
await self._create_process(expid["log_level"])
|
||||||
await self._worker_action(
|
await self._worker_action(
|
||||||
|
|
|
@ -13,6 +13,8 @@ import inspect
|
||||||
import logging
|
import logging
|
||||||
import traceback
|
import traceback
|
||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
|
import importlib.util
|
||||||
|
import linecache
|
||||||
|
|
||||||
import h5py
|
import h5py
|
||||||
|
|
||||||
|
@ -129,11 +131,39 @@ class CCB:
|
||||||
issue = staticmethod(make_parent_action("ccb_issue"))
|
issue = staticmethod(make_parent_action("ccb_issue"))
|
||||||
|
|
||||||
|
|
||||||
def get_experiment(file, class_name):
|
def get_experiment_from_file(file, class_name):
|
||||||
module = tools.file_import(file, prefix="artiq_worker_")
|
module = tools.file_import(file, prefix="artiq_worker_")
|
||||||
return tools.get_experiment(module, class_name)
|
return tools.get_experiment(module, class_name)
|
||||||
|
|
||||||
|
|
||||||
|
class StringLoader:
|
||||||
|
def __init__(self, fake_filename, content):
|
||||||
|
self.fake_filename = fake_filename
|
||||||
|
self.content = content
|
||||||
|
|
||||||
|
def get_source(self, fullname):
|
||||||
|
return self.content
|
||||||
|
|
||||||
|
def create_module(self, spec):
|
||||||
|
return None
|
||||||
|
|
||||||
|
def exec_module(self, module):
|
||||||
|
code = compile(self.get_source(self.fake_filename), self.fake_filename, "exec")
|
||||||
|
exec(code, module.__dict__)
|
||||||
|
|
||||||
|
|
||||||
|
def get_experiment_from_content(content, class_name):
|
||||||
|
fake_filename = "expcontent"
|
||||||
|
spec = importlib.util.spec_from_loader(
|
||||||
|
"expmodule",
|
||||||
|
StringLoader(fake_filename, content)
|
||||||
|
)
|
||||||
|
module = importlib.util.module_from_spec(spec)
|
||||||
|
spec.loader.exec_module(module)
|
||||||
|
linecache.lazycache(fake_filename, module.__dict__)
|
||||||
|
return tools.get_experiment(module, class_name)
|
||||||
|
|
||||||
|
|
||||||
register_experiment = make_parent_action("register_experiment")
|
register_experiment = make_parent_action("register_experiment")
|
||||||
|
|
||||||
|
|
||||||
|
@ -246,6 +276,7 @@ def main():
|
||||||
start_time = time.time()
|
start_time = time.time()
|
||||||
rid = obj["rid"]
|
rid = obj["rid"]
|
||||||
expid = obj["expid"]
|
expid = obj["expid"]
|
||||||
|
if "file" in expid:
|
||||||
if obj["wd"] is not None:
|
if obj["wd"] is not None:
|
||||||
# Using repository
|
# Using repository
|
||||||
experiment_file = os.path.join(obj["wd"], expid["file"])
|
experiment_file = os.path.join(obj["wd"], expid["file"])
|
||||||
|
@ -253,7 +284,9 @@ def main():
|
||||||
else:
|
else:
|
||||||
experiment_file = expid["file"]
|
experiment_file = expid["file"]
|
||||||
repository_path = None
|
repository_path = None
|
||||||
exp = get_experiment(experiment_file, expid["class_name"])
|
exp = get_experiment_from_file(experiment_file, expid["class_name"])
|
||||||
|
else:
|
||||||
|
exp = get_experiment_from_content(expid["content"], expid["class_name"])
|
||||||
device_mgr.virtual_devices["scheduler"].set_run_info(
|
device_mgr.virtual_devices["scheduler"].set_run_info(
|
||||||
rid, obj["pipeline_name"], expid, obj["priority"])
|
rid, obj["pipeline_name"], expid, obj["priority"])
|
||||||
start_local_time = time.localtime(start_time)
|
start_local_time = time.localtime(start_time)
|
||||||
|
|
|
@ -9,7 +9,7 @@ class TestFrontends(unittest.TestCase):
|
||||||
"""Test --help as a simple smoke test against catastrophic breakage."""
|
"""Test --help as a simple smoke test against catastrophic breakage."""
|
||||||
commands = {
|
commands = {
|
||||||
"aqctl": [
|
"aqctl": [
|
||||||
"corelog"
|
"corelog", "moninj_proxy"
|
||||||
],
|
],
|
||||||
"artiq": [
|
"artiq": [
|
||||||
"client", "compile", "coreanalyzer", "coremgmt",
|
"client", "compile", "coreanalyzer", "coremgmt",
|
||||||
|
|
|
@ -10,7 +10,9 @@ Default network ports
|
||||||
+---------------------------------+--------------+
|
+---------------------------------+--------------+
|
||||||
| Core device (analyzer) | 1382 |
|
| Core device (analyzer) | 1382 |
|
||||||
+---------------------------------+--------------+
|
+---------------------------------+--------------+
|
||||||
| Core device (mon/inj) | 1383 |
|
| Moninj (core device or proxy) | 1383 |
|
||||||
|
+---------------------------------+--------------+
|
||||||
|
| Moninj (proxy control) | 1384 |
|
||||||
+---------------------------------+--------------+
|
+---------------------------------+--------------+
|
||||||
| Master (logging input) | 1066 |
|
| Master (logging input) | 1066 |
|
||||||
+---------------------------------+--------------+
|
+---------------------------------+--------------+
|
||||||
|
|
|
@ -109,6 +109,14 @@ Core device logging controller
|
||||||
:ref: artiq.frontend.aqctl_corelog.get_argparser
|
:ref: artiq.frontend.aqctl_corelog.get_argparser
|
||||||
:prog: aqctl_corelog
|
:prog: aqctl_corelog
|
||||||
|
|
||||||
|
Moninj proxy
|
||||||
|
------------
|
||||||
|
|
||||||
|
.. argparse::
|
||||||
|
:ref: artiq.frontend.aqctl_moninj_proxy.get_argparser
|
||||||
|
:prog: aqctl_moninj_proxy
|
||||||
|
|
||||||
|
|
||||||
.. _core-device-rtio-analyzer-tool:
|
.. _core-device-rtio-analyzer-tool:
|
||||||
|
|
||||||
Core device RTIO analyzer tool
|
Core device RTIO analyzer tool
|
||||||
|
|
|
@ -168,7 +168,7 @@
|
||||||
vivado = pkgs.buildFHSUserEnv {
|
vivado = pkgs.buildFHSUserEnv {
|
||||||
name = "vivado";
|
name = "vivado";
|
||||||
targetPkgs = vivadoDeps;
|
targetPkgs = vivadoDeps;
|
||||||
profile = "source /opt/Xilinx/Vivado/2021.1/settings64.sh";
|
profile = "set -e; source /opt/Xilinx/Vivado/2021.2/settings64.sh";
|
||||||
runScript = "vivado";
|
runScript = "vivado";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
1
setup.py
1
setup.py
|
@ -35,6 +35,7 @@ console_scripts = [
|
||||||
"artiq_run = artiq.frontend.artiq_run:main",
|
"artiq_run = artiq.frontend.artiq_run:main",
|
||||||
"artiq_flash = artiq.frontend.artiq_flash:main",
|
"artiq_flash = artiq.frontend.artiq_flash:main",
|
||||||
"aqctl_corelog = artiq.frontend.aqctl_corelog:main",
|
"aqctl_corelog = artiq.frontend.aqctl_corelog:main",
|
||||||
|
"aqctl_moninj_proxy = artiq.frontend.aqctl_moninj_proxy:main",
|
||||||
"afws_client = artiq.frontend.afws_client:main",
|
"afws_client = artiq.frontend.afws_client:main",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue