From c440f9fe1bc7275802124794c36d58d7418871cf Mon Sep 17 00:00:00 2001 From: Sebastien Bourdeauducq Date: Wed, 4 May 2022 08:28:55 +0800 Subject: [PATCH 01/10] flake: update dependencies --- flake.lock | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/flake.lock b/flake.lock index 7c7414949..82c75c34c 100644 --- a/flake.lock +++ b/flake.lock @@ -26,11 +26,11 @@ "mozilla-overlay": { "flake": false, "locked": { - "lastModified": 1645464064, - "narHash": "sha256-YeN4bpPvHkVOpQzb8APTAfE7/R+MFMwJUMkqmfvytSk=", + "lastModified": 1650459918, + "narHash": "sha256-sroCK+QJTmoXtcRkwZyKOP9iAYOPID2Bwdxn4GkG16w=", "owner": "mozilla", "repo": "nixpkgs-mozilla", - "rev": "15b7a05f20aab51c4ffbefddb1b448e862dccb7d", + "rev": "e1f7540fc0a8b989fb8cf701dc4fd7fc76bcf168", "type": "github" }, "original": { @@ -41,11 +41,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1650244918, - "narHash": "sha256-DsS5nxjTpnoUC4pNXJI1rit7TnDTij8vQDa5PtcDCD0=", + "lastModified": 1651514317, + "narHash": "sha256-1dVfhWpFPpBcaDKVoEvsiqBeozjGZLNfNY/u4jipbS4=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "7b38b03d76ab71bdc8dc325e3f6338d984cc35ca", + "rev": "9e49886b3d83d18ca59f66ec7b388315fa3d2e60", "type": "github" }, "original": { @@ -73,11 +73,11 @@ ] }, "locked": { - "lastModified": 1649724535, - "narHash": "sha256-15Nun4EY35j+6SPZkjzZtyH/ncxLS60KuGJjFh5kSTc=", + "lastModified": 1651624017, + "narHash": "sha256-engReD8l4xzQQJP8BJ7gILUyA1KH5d60lexDVfnoiRM=", "owner": "m-labs", "repo": "sipyco", - "rev": "939f84f9b5eef7efbf7423c735d1834783b6140e", + "rev": "efda015e9994faf6cafc19b239d8e69ca4d26226", "type": "github" }, "original": { From 35f30ddf054ba123c923efcd3a0ddf28c758ef36 Mon Sep 17 00:00:00 2001 From: Spaqin Date: Fri, 6 May 2022 13:33:42 +0800 Subject: [PATCH 02/10] Expose TTLClockGen for Kasli JSONs (#1886) --- RELEASE_NOTES.rst | 1 + artiq/coredevice/coredevice_generic.schema.json | 4 ++-- artiq/frontend/artiq_ddb_template.py | 3 ++- artiq/gateware/eem.py | 4 +--- artiq/gateware/eem_7series.py | 3 ++- artiq/gateware/rtio/phy/ttl_simple.py | 9 +++++++-- 6 files changed, 15 insertions(+), 9 deletions(-) diff --git a/RELEASE_NOTES.rst b/RELEASE_NOTES.rst index 0bd400186..19a97a206 100644 --- a/RELEASE_NOTES.rst +++ b/RELEASE_NOTES.rst @@ -12,6 +12,7 @@ Highlights: - Kasli-SoC, a new EEM carrier based on a Zynq SoC, enabling much faster kernel execution. - HVAMP_8CH 8 channel HV amplifier for Fastino / Zotinos - Almazny mezzanine board for Mirny +* TTL device output can be now configured to work as a clock generator. * 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. diff --git a/artiq/coredevice/coredevice_generic.schema.json b/artiq/coredevice/coredevice_generic.schema.json index 800f0863e..79527cf31 100644 --- a/artiq/coredevice/coredevice_generic.schema.json +++ b/artiq/coredevice/coredevice_generic.schema.json @@ -170,11 +170,11 @@ }, "bank_direction_low": { "type": "string", - "enum": ["input", "output"] + "enum": ["input", "output", "clkgen"] }, "bank_direction_high": { "type": "string", - "enum": ["input", "output"] + "enum": ["input", "output", "clkgen"] } }, "required": ["ports", "bank_direction_low", "bank_direction_high"] diff --git a/artiq/frontend/artiq_ddb_template.py b/artiq/frontend/artiq_ddb_template.py index 4fd3a543f..b6d9294a3 100755 --- a/artiq/frontend/artiq_ddb_template.py +++ b/artiq/frontend/artiq_ddb_template.py @@ -94,7 +94,8 @@ class PeripheralManager: def process_dio(self, rtio_offset, peripheral, num_channels=8): class_names = { "input": "TTLInOut", - "output": "TTLOut" + "output": "TTLOut", + "clkgen": "TTLClockGen" } classes = [ class_names[peripheral["bank_direction_low"]], diff --git a/artiq/gateware/eem.py b/artiq/gateware/eem.py index 6c588b476..23a85eca8 100644 --- a/artiq/gateware/eem.py +++ b/artiq/gateware/eem.py @@ -231,10 +231,8 @@ class Urukul(_EEM): target.rtio_channels.append(rtio.Channel.from_phy(phy, ififo_depth=4)) pads = target.platform.request("urukul{}_dds_reset_sync_in".format(eem)) - pad = Signal(reset=0) - target.specials += DifferentialOutput(pad, pads.p, pads.n) if sync_gen_cls is not None: # AD9910 variant and SYNC_IN from EEM - phy = sync_gen_cls(pad, ftw_width=4) + phy = sync_gen_cls(pad=pads.p, pad_n=pads.n, ftw_width=4) target.submodules += phy target.rtio_channels.append(rtio.Channel.from_phy(phy)) diff --git a/artiq/gateware/eem_7series.py b/artiq/gateware/eem_7series.py index 06a2d9b1d..1f5f770c2 100644 --- a/artiq/gateware/eem_7series.py +++ b/artiq/gateware/eem_7series.py @@ -5,7 +5,8 @@ from artiq.gateware.rtio.phy import ttl_simple, ttl_serdes_7series, edge_counter def peripheral_dio(module, peripheral, **kwargs): ttl_classes = { "input": ttl_serdes_7series.InOut_8X, - "output": ttl_serdes_7series.Output_8X + "output": ttl_serdes_7series.Output_8X, + "clkgen": ttl_simple.ClockGen } if len(peripheral["ports"]) != 1: raise ValueError("wrong number of ports") diff --git a/artiq/gateware/rtio/phy/ttl_simple.py b/artiq/gateware/rtio/phy/ttl_simple.py index 4484ce3af..192165dc9 100644 --- a/artiq/gateware/rtio/phy/ttl_simple.py +++ b/artiq/gateware/rtio/phy/ttl_simple.py @@ -145,11 +145,16 @@ class InOut(Module): class ClockGen(Module): - def __init__(self, pad, ftw_width=24): + def __init__(self, pad, pad_n=None, ftw_width=24, dci=False): self.rtlink = rtlink.Interface(rtlink.OInterface(ftw_width)) # # # + pad_o = Signal() + if pad_n is None: + self.comb += pad.eq(pad_o) + else: + self.specials += DifferentialOutput(pad_o, pad, pad_n) ftw = Signal(ftw_width) acc = Signal(ftw_width) self.sync.rio += If(self.rtlink.o.stb, ftw.eq(self.rtlink.o.data)) @@ -165,5 +170,5 @@ class ClockGen(Module): acc.eq(0) ) ), - pad.eq(acc[-1]) + pad_o.eq(acc[-1]) ] From 8a7af3f75c353c9809dabe16714af70cb9215567 Mon Sep 17 00:00:00 2001 From: David Nadlinger Date: Sat, 7 May 2022 21:21:12 +0100 Subject: [PATCH 03/10] compiler: Fix "nowrite" miscompilation for sret functions This affected e.g. rtio_input_timestamped_data(). --- artiq/compiler/transforms/llvm_ir_generator.py | 5 ++++- artiq/test/lit/embedding/syscall_flags.py | 8 ++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/artiq/compiler/transforms/llvm_ir_generator.py b/artiq/compiler/transforms/llvm_ir_generator.py index 50ce33f61..881a9e717 100644 --- a/artiq/compiler/transforms/llvm_ir_generator.py +++ b/artiq/compiler/transforms/llvm_ir_generator.py @@ -1409,7 +1409,10 @@ class LLVMIRGenerator: llfun.args[idx].add_attribute(attr) if 'nounwind' in insn.target_function().type.flags: llfun.attributes.add('nounwind') - if 'nowrite' in insn.target_function().type.flags: + if 'nowrite' in insn.target_function().type.flags and not is_sret: + # Even if "nowrite" is correct from the user's perspective (doesn't + # access any other memory observable to ARTIQ Python), this isn't + # true on the LLVM IR level for sret return values. llfun.attributes.add('inaccessiblememonly') return llfun, list(llargs), llarg_attrs, llcallstackptr diff --git a/artiq/test/lit/embedding/syscall_flags.py b/artiq/test/lit/embedding/syscall_flags.py index f8c618c3f..cc78f843b 100644 --- a/artiq/test/lit/embedding/syscall_flags.py +++ b/artiq/test/lit/embedding/syscall_flags.py @@ -13,6 +13,14 @@ from artiq.language.types import * def foo() -> TNone: pass +# sret nowrite functions shouldn't be marked inaccessiblememonly. +# CHECK-L: ; Function Attrs: nounwind +# CHECK-NEXT-L: declare void @bar({ i32, i64 }* sret) +@syscall(flags={"nounwind", "nowrite"}) +def bar() -> TTuple([TInt32, TInt64]): + pass + @kernel def entrypoint(): foo() + bar() From 16393efa7c439932095ee007ca4d3ac5c1cb8df6 Mon Sep 17 00:00:00 2001 From: Laurent Stephenson Date: Thu, 12 May 2022 15:13:46 -0600 Subject: [PATCH 04/10] fix issue #1890: make dashboard use moninj port from device_db Signed-off-by: Laurent Stephenson --- artiq/dashboard/moninj.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/artiq/dashboard/moninj.py b/artiq/dashboard/moninj.py index a9adc5678..c624529af 100644 --- a/artiq/dashboard/moninj.py +++ b/artiq/dashboard/moninj.py @@ -203,6 +203,7 @@ _WidgetDesc = namedtuple("_WidgetDesc", "uid comment cls arguments") def setup_from_ddb(ddb): mi_addr = None + mi_port = None dds_sysclk = None description = set() @@ -235,14 +236,16 @@ def setup_from_ddb(ddb): description.add(widget) elif v["type"] == "controller" and k == "core_moninj": mi_addr = v["host"] + mi_port = v.get("port_proxy", 1383) except KeyError: pass - return mi_addr, dds_sysclk, description + return mi_addr, mi_port, dds_sysclk, description class _DeviceManager: def __init__(self): self.mi_addr = None + self.mi_port = None self.reconnect_mi = asyncio.Event() self.mi_connection = None self.mi_connector_task = asyncio.ensure_future(self.mi_connector()) @@ -264,10 +267,11 @@ class _DeviceManager: return ddb def notify(self, mod): - mi_addr, dds_sysclk, description = setup_from_ddb(self.ddb) + mi_addr, mi_port, dds_sysclk, description = setup_from_ddb(self.ddb) - if mi_addr != self.mi_addr: + if (mi_addr, mi_port) != (self.mi_addr, self.mi_port): self.mi_addr = mi_addr + self.mi_port = mi_port self.reconnect_mi.set() self.dds_sysclk = dds_sysclk @@ -397,7 +401,7 @@ class _DeviceManager: new_mi_connection = CommMonInj(self.monitor_cb, self.injection_status_cb, self.disconnect_cb) try: - await new_mi_connection.connect(self.mi_addr, 1383) + await new_mi_connection.connect(self.mi_addr, self.mi_port) except asyncio.CancelledError: logger.info("cancelled connection to moninj") break From 70503bee6fe26a9e5af22074a6fb1eaf936bd5fe Mon Sep 17 00:00:00 2001 From: kk1050 <103404672+kk1050@users.noreply.github.com> Date: Wed, 18 May 2022 17:07:43 +0800 Subject: [PATCH 05/10] dashboard: add dataset rename feature (#1893) Co-authored-by: kk105 --- artiq/dashboard/datasets.py | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/artiq/dashboard/datasets.py b/artiq/dashboard/datasets.py index 10e51e906..674e653e9 100644 --- a/artiq/dashboard/datasets.py +++ b/artiq/dashboard/datasets.py @@ -14,6 +14,12 @@ from artiq.gui.scientific_spinbox import ScientificSpinBox logger = logging.getLogger(__name__) +async def rename(key, newkey, value, dataset_ctl): + if key != newkey: + await dataset_ctl.delete(key) + await dataset_ctl.set(newkey, value) + + class Editor(QtWidgets.QDialog): def __init__(self, parent, dataset_ctl, key, value): QtWidgets.QDialog.__init__(self, parent=parent) @@ -26,7 +32,11 @@ class Editor(QtWidgets.QDialog): self.setLayout(grid) grid.addWidget(QtWidgets.QLabel("Name:"), 0, 0) - grid.addWidget(QtWidgets.QLabel(key), 0, 1) + + self.name_widget = QtWidgets.QLineEdit() + self.name_widget.setText(key) + + grid.addWidget(self.name_widget, 0, 1) grid.addWidget(QtWidgets.QLabel("Value:"), 1, 0) grid.addWidget(self.get_edit_widget(value), 1, 1) @@ -39,8 +49,9 @@ class Editor(QtWidgets.QDialog): buttons.rejected.connect(self.reject) def accept(self): + newkey = self.name_widget.text() value = self.initial_type(self.get_edit_widget_value()) - asyncio.ensure_future(self.dataset_ctl.set(self.key, value)) + asyncio.ensure_future(rename(self.key, newkey, value, self.dataset_ctl)) QtWidgets.QDialog.accept(self) def get_edit_widget(self, initial_value): From 9896d78e07b93e04214d75029ae94202ecdda1fb Mon Sep 17 00:00:00 2001 From: Sebastien Bourdeauducq Date: Wed, 18 May 2022 19:04:13 +0800 Subject: [PATCH 06/10] afws_client: update --- artiq/frontend/afws_client.py | 62 +++++++++++++++++++++++++++++++---- flake.nix | 2 +- 2 files changed, 57 insertions(+), 7 deletions(-) diff --git a/artiq/frontend/afws_client.py b/artiq/frontend/afws_client.py index 7d9a78265..25ddfc1f9 100755 --- a/artiq/frontend/afws_client.py +++ b/artiq/frontend/afws_client.py @@ -7,7 +7,10 @@ import socket import ssl import io import zipfile +import json +from prettytable import PrettyTable from getpass import getpass +from tqdm import tqdm def get_artiq_cert(): @@ -54,20 +57,42 @@ class Client: self.fsocket.write((" ".join(command) + "\n").encode()) self.fsocket.flush() + def read_line(self): + return self.fsocket.readline().decode("ascii") + def read_reply(self): return self.fsocket.readline().decode("ascii").split() + def read_json(self): + return json.loads(self.fsocket.readline().decode("ascii")) + def login(self, username, password): self.send_command("LOGIN", username, password) return self.read_reply() == ["HELLO"] - def build(self, rev, variant): - self.send_command("BUILD", rev, variant) + def build(self, rev, variant, log): + if not variant: + variants = self.get_variants() + if len(variants) != 1: + raise ValueError("User can build more than 1 variant - need to specify") + variant = variants[0][0] + print("Building variant: {}".format(variant)) + if log: + self.send_command("BUILD", rev, variant, "LOG_ENABLE") + else: + self.send_command("BUILD", rev, variant) reply = self.read_reply()[0] if reply != "BUILDING": return reply, None print("Build in progress. This may take 10-15 minutes.") - reply, status = self.read_reply() + if log: + line = self.read_line() + while line != "" and line.startswith("LOG"): + print(line[4:], end="") + line = self.read_line() + reply, status = line.split() + else: + reply, status = self.read_reply() if reply != "DONE": raise ValueError("Unexpected server reply: expected 'DONE', got '{}'".format(reply)) if status != "done": @@ -76,13 +101,28 @@ class Client: reply, length = self.read_reply() if reply != "PRODUCT": raise ValueError("Unexpected server reply: expected 'PRODUCT', got '{}'".format(reply)) - contents = self.fsocket.read(int(length)) + length = int(length) + contents = bytearray() + with tqdm(total=length, unit="iB", unit_scale=True, unit_divisor=1024) as progress_bar: + total = 0 + while total != length: + chunk_len = min(4096, length-total) + contents += self.fsocket.read(chunk_len) + total += chunk_len + progress_bar.update(chunk_len) print("Download completed.") return "OK", contents def passwd(self, password): self.send_command("PASSWD", password) return self.read_reply() == ["OK"] + + def get_variants(self): + self.send_command("GET_VARIANTS") + reply = self.read_reply()[0] + if reply != "OK": + raise ValueError("Unexpected server reply: expected 'OK', got '{}'".format(reply)) + return self.read_json() def main(): @@ -95,9 +135,11 @@ def main(): action.required = True act_build = action.add_parser("build", help="build and download firmware") act_build.add_argument("--rev", default=None, help="revision to build (default: currently installed ARTIQ revision)") - act_build.add_argument("variant", help="variant to build") + act_build.add_argument("--log", action="store_true", help="Display the build log") act_build.add_argument("directory", help="output directory") + act_build.add_argument("variant", nargs="?", default=None, help="variant to build (can be omitted if user is authorised to build only one)") act_passwd = action.add_parser("passwd", help="change password") + act_get_variants = action.add_parser("get_variants", help="get available variants and expiry dates") args = parser.parse_args() cert = args.cert @@ -146,14 +188,22 @@ def main(): if rev is None: print("Unable to determine currently installed ARTIQ revision. Specify manually using --rev.") sys.exit(1) - result, contents = client.build(rev, args.variant) + result, contents = client.build(rev, args.variant, args.log) if result != "OK": if result == "UNAUTHORIZED": print("You are not authorized to build this variant. Your firmware subscription may have expired. Contact helpdesk\x40m-labs.hk.") + elif result == "TOOMANY": + print("Too many builds in a queue. Please wait for others to finish.") else: print("Build failed: {}".format(result)) sys.exit(1) zip_unarchive(contents, args.directory) + elif args.action == "get_variants": + data = client.get_variants() + table = PrettyTable() + table.field_names = ["Variant", "Expiry date"] + table.add_rows(data) + print(table) else: raise ValueError finally: diff --git a/flake.nix b/flake.nix index ed0cad534..c025214a9 100644 --- a/flake.nix +++ b/flake.nix @@ -147,7 +147,7 @@ nativeBuildInputs = [ pkgs.qt5.wrapQtAppsHook ]; # keep llvm_x and lld_x in sync with llvmlite propagatedBuildInputs = [ pkgs.llvm_11 pkgs.lld_11 llvmlite-new sipyco.packages.x86_64-linux.sipyco pythonparser artiq-comtools.packages.x86_64-linux.artiq-comtools ] - ++ (with pkgs.python3Packages; [ pyqtgraph pygit2 numpy dateutil scipy prettytable pyserial python-Levenshtein h5py pyqt5 qasync ]); + ++ (with pkgs.python3Packages; [ pyqtgraph pygit2 numpy dateutil scipy prettytable pyserial python-Levenshtein h5py pyqt5 qasync tqdm ]); dontWrapQtApps = true; postFixup = '' From 8599be55505c7d91a4ce183d1bc8053effcd3c72 Mon Sep 17 00:00:00 2001 From: Sebastien Bourdeauducq Date: Wed, 18 May 2022 19:04:52 +0800 Subject: [PATCH 07/10] flake: update nixpkgs --- flake.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/flake.lock b/flake.lock index 82c75c34c..56bcfea39 100644 --- a/flake.lock +++ b/flake.lock @@ -41,11 +41,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1651514317, - "narHash": "sha256-1dVfhWpFPpBcaDKVoEvsiqBeozjGZLNfNY/u4jipbS4=", + "lastModified": 1652559422, + "narHash": "sha256-jPVTNImBTUIFdtur+d4IVot6eXmsvtOcBm0TzxmhWPk=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "9e49886b3d83d18ca59f66ec7b388315fa3d2e60", + "rev": "8b3398bc7587ebb79f93dfeea1b8c574d3c6dba1", "type": "github" }, "original": { From 4bdb4c8e115a84203c8d978816cc1f2a45124b2f Mon Sep 17 00:00:00 2001 From: mwojcik Date: Thu, 19 May 2022 15:25:01 +0800 Subject: [PATCH 08/10] config: error instead of empty value if key not found --- artiq/firmware/libboard_misoc/config.rs | 7 ++++++- artiq/firmware/runtime/session.rs | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/artiq/firmware/libboard_misoc/config.rs b/artiq/firmware/libboard_misoc/config.rs index 9a737e303..51f71ac33 100644 --- a/artiq/firmware/libboard_misoc/config.rs +++ b/artiq/firmware/libboard_misoc/config.rs @@ -9,6 +9,7 @@ pub enum Error { MissingSeparator { offset: usize }, Utf8Error(str::Utf8Error), NoFlash, + KeyNotFound } impl fmt::Display for Error { @@ -28,6 +29,8 @@ impl fmt::Display for Error { write!(f, "{}", err), &Error::NoFlash => write!(f, "flash memory is not present"), + &Error::KeyNotFound => + write!(f, "key not found") } } } @@ -156,14 +159,16 @@ mod imp { f(Lock::take().and_then(|lock| { let mut iter = Iter::new(lock.data()); let mut value = &[][..]; + let mut found = false; while let Some(result) = iter.next() { let (record_key, record_value) = result?; if key.as_bytes() == record_key { + found = true; // last write wins value = record_value } } - Ok(value) + if found { Ok(value) } else { Err(Error::KeyNotFound) } })) } diff --git a/artiq/firmware/runtime/session.rs b/artiq/firmware/runtime/session.rs index bd1562269..c6d745545 100644 --- a/artiq/firmware/runtime/session.rs +++ b/artiq/firmware/runtime/session.rs @@ -529,7 +529,7 @@ fn flash_kernel_worker(io: &Io, aux_mutex: &Mutex, config::read(config_key, |result| { match result { - Ok(kernel) if kernel.len() > 0 => unsafe { + Ok(kernel) => unsafe { // kernel CPU cannot access the SPI flash address space directly, // so make a copy. kern_load(io, &mut session, Vec::from(kernel).as_ref()) From d24a36a02a994b4a5755e6bc55065bac94c8971a Mon Sep 17 00:00:00 2001 From: mwojcik Date: Thu, 19 May 2022 15:25:20 +0800 Subject: [PATCH 09/10] comm_mgmt: fix read_expect --- artiq/coredevice/comm_mgmt.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/artiq/coredevice/comm_mgmt.py b/artiq/coredevice/comm_mgmt.py index 539643751..9833d7520 100644 --- a/artiq/coredevice/comm_mgmt.py +++ b/artiq/coredevice/comm_mgmt.py @@ -110,9 +110,10 @@ class CommMgmt: return ty def _read_expect(self, ty): - if self._read_header() != ty: + header = self._read_header() + if header != ty: raise IOError("Incorrect reply from device: {} (expected {})". - format(self._read_type, ty)) + format(header, ty)) def _read_int32(self): (value, ) = struct.unpack(self.endian + "l", self._read(4)) From cf8e5838474ed1c6f9252ad0caa0f2773c7e0565 Mon Sep 17 00:00:00 2001 From: mwojcik Date: Thu, 19 May 2022 15:54:05 +0800 Subject: [PATCH 10/10] comm_mgmt: expect error on config_read --- artiq/coredevice/comm_mgmt.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/artiq/coredevice/comm_mgmt.py b/artiq/coredevice/comm_mgmt.py index 9833d7520..870e2759d 100644 --- a/artiq/coredevice/comm_mgmt.py +++ b/artiq/coredevice/comm_mgmt.py @@ -160,7 +160,12 @@ class CommMgmt: def config_read(self, key): self._write_header(Request.ConfigRead) self._write_string(key) - self._read_expect(Reply.ConfigData) + ty = self._read_header() + if ty == Reply.Error: + raise IOError("Device failed to read config. The key may not exist.") + elif ty != Reply.ConfigData: + raise IOError("Incorrect reply from device: {} (expected {})". + format(ty, Reply.ConfigData)) return self._read_string() def config_write(self, key, value):