From b3dc199e6af9f6cf4a624082364665acad785697 Mon Sep 17 00:00:00 2001 From: sven-oxionics Date: Thu, 13 Jul 2023 14:05:26 +0100 Subject: [PATCH 01/66] Fix panic when receiving empty strings in rpc calls Receiving an empty string in an RPC call currently panics. When `length` is zero, a call to the `alloc` function (as implemented in `artiq/firmware/runtime/session.rs`) returns a null pointer. Constructing a `CMutSlice` from a null pointer panics. A `CMutSlice` consists of a pointer and the length. Rust's documentation of the `core::ptr` module states: "The canonical way to obtain a pointer that is valid for zero-sized accesses is `NonNull::dangling`." This commits adds a check for the length of a string received in an RPC call. Only for lengths greater than zero a memory allocation is performed. For zero-length strings, a dangling pointer is used. Test plan: Invoke the following experiment, which returns an empty string over RPC: ``` class ReturnEmptyString(artiq.experiment.EnvExperiment): def build(self): self.core: Core = self.get_device("core") @kernel def run(self): x = self.do_rpc() print(x) @rpc def do_rpc(self) -> TStr: return "" ``` Signed-off-by: Sven Over (Oxford Ionics) --- artiq/firmware/libproto_artiq/rpc_proto.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/artiq/firmware/libproto_artiq/rpc_proto.rs b/artiq/firmware/libproto_artiq/rpc_proto.rs index 49b78e633..cc567d7fb 100644 --- a/artiq/firmware/libproto_artiq/rpc_proto.rs +++ b/artiq/firmware/libproto_artiq/rpc_proto.rs @@ -112,8 +112,12 @@ unsafe fn recv_value(reader: &mut R, tag: Tag, data: &mut *mut (), Tag::String | Tag::Bytes | Tag::ByteArray => { consume_value!(CMutSlice, |ptr| { let length = reader.read_u32()? as usize; - *ptr = CMutSlice::new(alloc(length)? as *mut u8, length); - reader.read_exact((*ptr).as_mut())?; + if length > 0 { + *ptr = CMutSlice::new(alloc(length)? as *mut u8, length); + reader.read_exact((*ptr).as_mut())?; + } else { + *ptr = CMutSlice::new(core::ptr::NonNull::::dangling().as_ptr(), 0); + } Ok(()) }) } From 0befadee96ef774f6867f775832f46a1a1150e32 Mon Sep 17 00:00:00 2001 From: Simon Renblad Date: Wed, 5 Jul 2023 15:48:32 +0800 Subject: [PATCH 02/66] environment: rename ndecimals to precision --- artiq/language/environment.py | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/artiq/language/environment.py b/artiq/language/environment.py index bc0229998..ba42b2791 100644 --- a/artiq/language/environment.py +++ b/artiq/language/environment.py @@ -100,7 +100,7 @@ class NumberValue(_SimpleArgProcessor): """An argument that can take a numerical value. If ``type=="auto"``, the result will be a ``float`` unless - ndecimals = 0, scale = 1 and step is an integer. Setting ``type`` to + precision = 0, scale = 1 and step is an integer. Setting ``type`` to ``int`` will also result in an error unless these conditions are met. When ``scale`` is not specified, and the unit is a common one (i.e. @@ -123,14 +123,17 @@ class NumberValue(_SimpleArgProcessor): buttons in a UI. The default is the scale divided by 10. :param min: The minimum value of the argument. :param max: The maximum value of the argument. - :param ndecimals: The number of decimals a UI should use. + :param precision: The maximum number of decimals a UI should use. :param type: Type of this number. Accepts ``"float"``, ``"int"`` or ``"auto"``. Defaults to ``"auto"``. """ valid_types = ["auto", "float", "int"] - def __init__(self, default=NoDefault, unit="", scale=None, - step=None, min=None, max=None, ndecimals=2, type="auto"): + def __init__(self, default=NoDefault, unit="", *, scale=None, + step=None, min=None, max=None, precision=2, type="auto", ndecimals=None): + if ndecimals is not None: + print("DeprecationWarning: 'ndecimals' is deprecated. Please use 'precision' instead.") + precision = ndecimals if scale is None: if unit == "": scale = 1.0 @@ -147,7 +150,7 @@ class NumberValue(_SimpleArgProcessor): self.step = step self.min = min self.max = max - self.ndecimals = ndecimals + self.precision = precision self.type = type if self.type not in NumberValue.valid_types: @@ -155,7 +158,7 @@ class NumberValue(_SimpleArgProcessor): if self.type == "int" and not self._is_int_compatible(): raise ValueError(("Value marked as integer but settings are " - "not compatible. Please set ndecimals = 0, " + "not compatible. Please set precision = 0, " "scale = 1 and step to an integer")) super().__init__(default) @@ -165,7 +168,7 @@ class NumberValue(_SimpleArgProcessor): Are the settings other than `type` compatible with this being an integer? ''' - return (self.ndecimals == 0 + return (self.precision == 0 and int(self.step) == self.step and self.scale == 1) @@ -191,7 +194,7 @@ class NumberValue(_SimpleArgProcessor): d["step"] = self.step d["min"] = self.min d["max"] = self.max - d["ndecimals"] = self.ndecimals + d["precision"] = self.precision d["type"] = self.type return d From a0094aafbbd68820ffbdb1bc87e59180b6d67701 Mon Sep 17 00:00:00 2001 From: Simon Renblad Date: Tue, 18 Jul 2023 10:19:09 +0800 Subject: [PATCH 03/66] entries: rename ndecimals to precision --- artiq/gui/entries.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/artiq/gui/entries.py b/artiq/gui/entries.py index d3d0ff8fb..34468930e 100644 --- a/artiq/gui/entries.py +++ b/artiq/gui/entries.py @@ -120,7 +120,7 @@ class NumberEntryFloat(ScientificSpinBox): disable_scroll_wheel(self) procdesc = argument["desc"] scale = procdesc["scale"] - self.setDecimals(procdesc["ndecimals"]) + self.setDecimals(procdesc["precision"]) self.setPrecision() self.setSingleStep(procdesc["step"]/scale) self.setRelativeStep() @@ -159,7 +159,7 @@ class _NoScan(LayoutWidget): scale = procdesc["scale"] self.value = ScientificSpinBox() disable_scroll_wheel(self.value) - self.value.setDecimals(procdesc["ndecimals"]) + self.value.setDecimals(procdesc["precision"]) self.value.setPrecision() if procdesc["global_min"] is not None: self.value.setMinimum(procdesc["global_min"]/scale) @@ -202,7 +202,7 @@ class _RangeScan(LayoutWidget): scale = procdesc["scale"] def apply_properties(widget): - widget.setDecimals(procdesc["ndecimals"]) + widget.setDecimals(procdesc["precision"]) if procdesc["global_min"] is not None: widget.setMinimum(procdesc["global_min"]/scale) else: @@ -293,7 +293,7 @@ class _CenterScan(LayoutWidget): scale = procdesc["scale"] def apply_properties(widget): - widget.setDecimals(procdesc["ndecimals"]) + widget.setDecimals(procdesc["precision"]) if procdesc["global_min"] is not None: widget.setMinimum(procdesc["global_min"]/scale) else: @@ -469,7 +469,7 @@ class ScanEntry(LayoutWidget): def procdesc_to_entry(procdesc): ty = procdesc["ty"] if ty == "NumberValue": - is_int = (procdesc["ndecimals"] == 0 + is_int = (procdesc["precision"] == 0 and int(procdesc["step"]) == procdesc["step"] and procdesc["scale"] == 1) if is_int: From 94e076e976dda816d8f396daf01f44a55da9c2cd Mon Sep 17 00:00:00 2001 From: Simon Renblad Date: Tue, 18 Jul 2023 10:28:27 +0800 Subject: [PATCH 04/66] scan: rename ndecimals to precison --- artiq/language/scan.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/artiq/language/scan.py b/artiq/language/scan.py index 0454ef362..0f05d3756 100644 --- a/artiq/language/scan.py +++ b/artiq/language/scan.py @@ -191,11 +191,14 @@ class Scannable: :param unit: A string representing the unit of the scanned variable. :param scale: A numerical scaling factor by which the displayed values are multiplied when referenced in the experiment. - :param ndecimals: The number of decimals a UI should use. + :param precision: The maximum number of decimals a UI should use. """ - def __init__(self, default=NoDefault, unit="", scale=None, + def __init__(self, default=NoDefault, unit="", *, scale=None, global_step=None, global_min=None, global_max=None, - ndecimals=2): + precision=2, ndecimals=None): + if ndecimals is not None: + print("DeprecationWarning: 'ndecimals' is deprecated. Please use 'precision' instead.") + precision = ndecimals if scale is None: if unit == "": scale = 1.0 @@ -216,7 +219,7 @@ class Scannable: self.global_step = global_step self.global_min = global_min self.global_max = global_max - self.ndecimals = ndecimals + self.precision = precision def default(self): if not hasattr(self, "default_values"): @@ -240,7 +243,7 @@ class Scannable: d["global_step"] = self.global_step d["global_min"] = self.global_min d["global_max"] = self.global_max - d["ndecimals"] = self.ndecimals + d["precision"] = self.precision return d From 8589da07233efe6a26cf73dac00d7e0d9bc1b563 Mon Sep 17 00:00:00 2001 From: Simon Renblad Date: Wed, 5 Jul 2023 15:58:52 +0800 Subject: [PATCH 05/66] test_arguments: rename ndecimals to precision --- artiq/test/test_arguments.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/artiq/test/test_arguments.py b/artiq/test/test_arguments.py index 884c8982f..7cebf3c13 100644 --- a/artiq/test/test_arguments.py +++ b/artiq/test/test_arguments.py @@ -9,7 +9,7 @@ from artiq.language.environment import BooleanValue, EnumerationValue, \ class NumberValueCase(unittest.TestCase): def setUp(self): self.default_value = NumberValue() - self.int_value = NumberValue(42, step=1, ndecimals=0) + self.int_value = NumberValue(42, step=1, precision=0) self.float_value = NumberValue(42) def test_invalid_default(self): From 59ad8738314bc5c694d21b88963c335d3dc96765 Mon Sep 17 00:00:00 2001 From: Simon Renblad Date: Tue, 18 Jul 2023 10:42:54 +0800 Subject: [PATCH 06/66] examples: rename ndecimals to precision --- artiq/examples/kc705_nist_clock/repository/dds_setter.py | 2 +- .../kc705_nist_clock/repository/photon_histogram.py | 4 ++-- .../examples/kc705_nist_clock/repository/speed_benchmark.py | 2 +- artiq/examples/no_hardware/repository/arguments_demo.py | 6 +++--- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/artiq/examples/kc705_nist_clock/repository/dds_setter.py b/artiq/examples/kc705_nist_clock/repository/dds_setter.py index 7f4b1447e..29e391c9e 100644 --- a/artiq/examples/kc705_nist_clock/repository/dds_setter.py +++ b/artiq/examples/kc705_nist_clock/repository/dds_setter.py @@ -20,7 +20,7 @@ class DDSSetter(EnvExperiment): "driver": self.get_device(k), "frequency": self.get_argument( "{}_frequency".format(k), - NumberValue(100e6, scale=1e6, unit="MHz", ndecimals=6)) + NumberValue(100e6, scale=1e6, unit="MHz", precision=6)) } @kernel diff --git a/artiq/examples/kc705_nist_clock/repository/photon_histogram.py b/artiq/examples/kc705_nist_clock/repository/photon_histogram.py index 4a9166a8a..91c39d5fb 100644 --- a/artiq/examples/kc705_nist_clock/repository/photon_histogram.py +++ b/artiq/examples/kc705_nist_clock/repository/photon_histogram.py @@ -12,8 +12,8 @@ class PhotonHistogram(EnvExperiment): self.setattr_device("bdd_sw") self.setattr_device("pmt") - self.setattr_argument("nbins", NumberValue(100, ndecimals=0, step=1)) - self.setattr_argument("repeats", NumberValue(100, ndecimals=0, step=1)) + self.setattr_argument("nbins", NumberValue(100, precision=0, step=1)) + self.setattr_argument("repeats", NumberValue(100, precision=0, step=1)) self.setattr_dataset("cool_f", 230*MHz) self.setattr_dataset("detect_f", 220*MHz) diff --git a/artiq/examples/kc705_nist_clock/repository/speed_benchmark.py b/artiq/examples/kc705_nist_clock/repository/speed_benchmark.py index 63b807390..35ae16b4b 100644 --- a/artiq/examples/kc705_nist_clock/repository/speed_benchmark.py +++ b/artiq/examples/kc705_nist_clock/repository/speed_benchmark.py @@ -79,7 +79,7 @@ class SpeedBenchmark(EnvExperiment): "CoreSend1MB", "CorePrimes"])) self.setattr_argument("nruns", NumberValue(10, min=1, max=1000, - ndecimals=0, step=1)) + precision=0, step=1)) self.setattr_device("core") self.setattr_device("scheduler") diff --git a/artiq/examples/no_hardware/repository/arguments_demo.py b/artiq/examples/no_hardware/repository/arguments_demo.py index 9539a2d4e..b17c258ac 100644 --- a/artiq/examples/no_hardware/repository/arguments_demo.py +++ b/artiq/examples/no_hardware/repository/arguments_demo.py @@ -45,13 +45,13 @@ class ArgumentsDemo(EnvExperiment): PYONValue(self.get_dataset("foo", default=42))) self.setattr_argument("number", NumberValue(42e-6, unit="us", - ndecimals=4)) + precision=4)) self.setattr_argument("integer", NumberValue(42, - step=1, ndecimals=0)) + step=1, precision=0)) self.setattr_argument("string", StringValue("Hello World")) self.setattr_argument("scan", Scannable(global_max=400, default=NoScan(325), - ndecimals=6)) + precision=6)) self.setattr_argument("boolean", BooleanValue(True), "Group") self.setattr_argument("enum", EnumerationValue( ["foo", "bar", "quux"], "foo"), "Group") From 3d0feef61497f1fc580ed6631816a87843e22809 Mon Sep 17 00:00:00 2001 From: Simon Renblad Date: Wed, 5 Jul 2023 15:56:58 +0800 Subject: [PATCH 07/66] docs: rename ndecimals to precision --- doc/manual/getting_started_mgmt.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/manual/getting_started_mgmt.rst b/doc/manual/getting_started_mgmt.rst index 953a7fbd9..e9398c7bc 100644 --- a/doc/manual/getting_started_mgmt.rst +++ b/doc/manual/getting_started_mgmt.rst @@ -53,7 +53,7 @@ Experiments may have arguments whose values can be set in the dashboard and used def build(self): - self.setattr_argument("count", NumberValue(ndecimals=0, step=1)) + self.setattr_argument("count", NumberValue(precision=0, step=1)) def run(self): for i in range(self.count): From a2d62e6006a1c7ca988c973f9f1e44a8819a4520 Mon Sep 17 00:00:00 2001 From: Simon Renblad Date: Tue, 18 Jul 2023 10:40:43 +0800 Subject: [PATCH 08/66] RELEASE_NOTES: deprecated ndecimals --- RELEASE_NOTES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/RELEASE_NOTES.rst b/RELEASE_NOTES.rst index 242a917fa..0fd58fda3 100644 --- a/RELEASE_NOTES.rst +++ b/RELEASE_NOTES.rst @@ -68,6 +68,9 @@ Old syntax should be replaced with the form shown on the right. data[key][1] ==> value[key] data[key][2] ==> metadata[key] +* The ``ndecimals`` parameter in ``NumberValue`` and ``Scannable`` has been renamed to ``precision``. + Parameters after and including ``scale`` in both constructors are now keyword-only. + Refer to the updated ``no_hardware/arguments_demo.py`` example for current usage. ARTIQ-7 From 8f4c8387f9e73a3a1f728a7b5320779f24f52996 Mon Sep 17 00:00:00 2001 From: Simon Renblad Date: Wed, 19 Jul 2023 13:10:14 +0800 Subject: [PATCH 09/66] entries: rename setPrecision to setSigFigs --- artiq/gui/entries.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/artiq/gui/entries.py b/artiq/gui/entries.py index 34468930e..b86bc5c57 100644 --- a/artiq/gui/entries.py +++ b/artiq/gui/entries.py @@ -121,7 +121,7 @@ class NumberEntryFloat(ScientificSpinBox): procdesc = argument["desc"] scale = procdesc["scale"] self.setDecimals(procdesc["precision"]) - self.setPrecision() + self.setSigFigs() self.setSingleStep(procdesc["step"]/scale) self.setRelativeStep() if procdesc["min"] is not None: @@ -160,7 +160,7 @@ class _NoScan(LayoutWidget): self.value = ScientificSpinBox() disable_scroll_wheel(self.value) self.value.setDecimals(procdesc["precision"]) - self.value.setPrecision() + self.value.setSigFigs() if procdesc["global_min"] is not None: self.value.setMinimum(procdesc["global_min"]/scale) else: @@ -244,10 +244,10 @@ class _RangeScan(LayoutWidget): self.addWidget(randomize, 3, 1) apply_properties(start) - start.setPrecision() + start.setSigFigs() start.setRelativeStep() apply_properties(stop) - stop.setPrecision() + stop.setSigFigs() stop.setRelativeStep() apply_properties(scanner) @@ -310,7 +310,7 @@ class _CenterScan(LayoutWidget): center = ScientificSpinBox() disable_scroll_wheel(center) apply_properties(center) - center.setPrecision() + center.setSigFigs() center.setRelativeStep() center.setValue(state["center"]/scale) self.addWidget(center, 0, 1) @@ -319,7 +319,7 @@ class _CenterScan(LayoutWidget): span = ScientificSpinBox() disable_scroll_wheel(span) apply_properties(span) - span.setPrecision() + span.setSigFigs() span.setRelativeStep() span.setMinimum(0) span.setValue(state["span"]/scale) @@ -329,7 +329,7 @@ class _CenterScan(LayoutWidget): step = ScientificSpinBox() disable_scroll_wheel(step) apply_properties(step) - step.setPrecision() + step.setSigFigs() step.setRelativeStep() step.setMinimum(0) step.setValue(state["step"]/scale) From 4fbff1648c5e916263d0f2c541d96d2d795b0352 Mon Sep 17 00:00:00 2001 From: Simon Renblad Date: Wed, 19 Jul 2023 13:10:53 +0800 Subject: [PATCH 10/66] scientific_spinbox: rename precision to sig_figs --- artiq/gui/scientific_spinbox.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/artiq/gui/scientific_spinbox.py b/artiq/gui/scientific_spinbox.py index 27d415b02..fcbf48c29 100644 --- a/artiq/gui/scientific_spinbox.py +++ b/artiq/gui/scientific_spinbox.py @@ -20,21 +20,21 @@ class ScientificSpinBox(QtWidgets.QDoubleSpinBox): self.setCorrectionMode(self.CorrectToPreviousValue) # singleStep: resolution for step, buttons, accelerators # decimals: absolute rounding granularity - # precision: number of significant digits shown, %g precision - self.setPrecision() + # sigFigs: number of significant digits shown + self.setSigFigs() self.setRelativeStep() self.setRange(-inf, inf) self.setValue(0) # self.setKeyboardTracking(False) - def setPrecision(self, d=None): + def setSigFigs(self, d=None): if d is None: d = self.decimals() + 3 - self._precision = max(1, int(d)) - self._fmt = "{{:.{}g}}".format(self._precision) + self._sig_figs = max(1, int(d)) + self._fmt = "{{:.{}g}}".format(self._sig_figs) - def precision(self): - return self._precision + def sigFigs(self): + return self._sig_figs def setRelativeStep(self, s=None): if s is None: From a7569a0b2d080a759a4fc0fdee7b85699ca6efae Mon Sep 17 00:00:00 2001 From: Simon Renblad Date: Thu, 20 Jul 2023 12:26:43 +0800 Subject: [PATCH 11/66] tools: add scale_from_metadata helper func --- artiq/tools.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/artiq/tools.py b/artiq/tools.py index c0c49620e..61678f9bc 100644 --- a/artiq/tools.py +++ b/artiq/tools.py @@ -18,7 +18,8 @@ from artiq.language.environment import is_public_experiment from artiq.language import units -__all__ = ["parse_arguments", "elide", "short_format", "file_import", +__all__ = ["parse_arguments", "elide", "scale_from_metadata", + "short_format", "file_import", "get_experiment", "exc_to_warning", "asyncio_wait_or_cancel", "get_windows_drives", "get_user_config_dir"] @@ -54,12 +55,15 @@ def elide(s, maxlen): s += "..." return s +def scale_from_metadata(metadata): + unit = metadata.get("unit", "") + default_scale = getattr(units, unit, 1) + return metadata.get("scale", default_scale) def short_format(v, metadata={}): m = metadata unit = m.get("unit", "") - default_scale = getattr(units, unit, 1) - scale = m.get("scale", default_scale) + scale = scale_from_metadata(m) precision = m.get("precision", None) if v is None: return "None" From 2fb085f1a2870323b5cc0d2e19d128ac222a37dd Mon Sep 17 00:00:00 2001 From: Simon Renblad Date: Thu, 20 Jul 2023 12:29:42 +0800 Subject: [PATCH 12/66] datasets: change dataset value entry with units --- artiq/dashboard/datasets.py | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/artiq/dashboard/datasets.py b/artiq/dashboard/datasets.py index 2d08ce39c..c42f58659 100644 --- a/artiq/dashboard/datasets.py +++ b/artiq/dashboard/datasets.py @@ -5,7 +5,7 @@ import numpy as np from PyQt5 import QtCore, QtWidgets from sipyco import pyon -from artiq.tools import short_format, exc_to_warning +from artiq.tools import scale_from_metadata, short_format, exc_to_warning from artiq.gui.tools import LayoutWidget, QRecursiveFilterProxyModel from artiq.gui.models import DictSyncTreeSepModel from artiq.gui.scientific_spinbox import ScientificSpinBox @@ -77,6 +77,11 @@ class CreateEditDialog(QtWidgets.QDialog): self.value_widget.setText(value) if metadata is not None: + scale = scale_from_metadata(metadata) + value = pyon.decode(value) + t = type(value) + if np.issubdtype(t, np.number) or t is np.ndarray: + self.value_widget.setText(pyon.encode(value / scale)) self.unit_widget.setText(metadata.get('unit', '')) self.scale_widget.setText(str(metadata.get('scale', ''))) self.precision_widget.setText(str(metadata.get('precision', ''))) @@ -97,10 +102,15 @@ class CreateEditDialog(QtWidgets.QDialog): metadata['scale'] = float(scale) if precision != "": metadata['precision'] = int(precision) + scale = scale_from_metadata(metadata) + value = pyon.decode(value) + t = type(value) + if np.issubdtype(t, np.number) or t is np.ndarray: + value = value * scale if self.key and self.key != key: - asyncio.ensure_future(exc_to_warning(rename(self.key, key, pyon.decode(value), metadata, persist, self.dataset_ctl))) + asyncio.ensure_future(exc_to_warning(rename(self.key, key, value, metadata, persist, self.dataset_ctl))) else: - asyncio.ensure_future(exc_to_warning(self.dataset_ctl.set(key, pyon.decode(value), metadata=metadata, persist=persist))) + asyncio.ensure_future(exc_to_warning(self.dataset_ctl.set(key, value, metadata=metadata, persist=persist))) self.key = key QtWidgets.QDialog.accept(self) @@ -197,13 +207,7 @@ class DatasetsDock(QtWidgets.QDockWidget): key = self.table_model.index_to_key(idx) if key is not None: persist, value, metadata = self.table_model.backing_store[key] - t = type(value) - if np.issubdtype(t, np.number) or np.issubdtype(t, np.bool_): - value = str(value) - elif np.issubdtype(t, np.unicode_): - value = '"{}"'.format(str(value)) - else: - value = pyon.encode(value) + value = pyon.encode(value) CreateEditDialog(self, self.dataset_ctl, key, value, metadata, persist).open() def delete_clicked(self): From 6592b6ea1d69613b3b44b7bb49c3afc3083a757c Mon Sep 17 00:00:00 2001 From: Simon Renblad Date: Thu, 20 Jul 2023 13:30:29 +0800 Subject: [PATCH 13/66] artiq_client: change set_dataset with units --- artiq/frontend/artiq_client.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/artiq/frontend/artiq_client.py b/artiq/frontend/artiq_client.py index b3eb6438d..4e7667465 100755 --- a/artiq/frontend/artiq_client.py +++ b/artiq/frontend/artiq_client.py @@ -14,6 +14,7 @@ import sys import os from operator import itemgetter from dateutil.parser import parse as parse_date +import numpy as np from prettytable import PrettyTable @@ -22,7 +23,7 @@ from sipyco.sync_struct import Subscriber from sipyco.broadcast import Receiver from sipyco import common_args, pyon -from artiq.tools import short_format, parse_arguments +from artiq.tools import scale_from_metadata, short_format, parse_arguments from artiq import __version__ as artiq_version @@ -187,7 +188,12 @@ def _action_set_dataset(remote, args): metadata["scale"] = args.scale if args.precision is not None: metadata["precision"] = args.precision - remote.set(args.name, pyon.decode(args.value), persist, metadata) + scale = scale_from_metadata(metadata) + value = pyon.decode(args.value) + t = type(value) + if np.issubdtype(t, np.number) or t is np.ndarray: + value = value * scale + remote.set(args.name, value, persist, metadata) def _action_del_dataset(remote, args): From a384df17a47de54a3dd69f96860417c54649bb14 Mon Sep 17 00:00:00 2001 From: Simon Renblad Date: Thu, 20 Jul 2023 15:41:39 +0800 Subject: [PATCH 14/66] docs: add unit and precision explainer --- doc/manual/environment.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/manual/environment.rst b/doc/manual/environment.rst index cf2cf7308..8e52e3359 100644 --- a/doc/manual/environment.rst +++ b/doc/manual/environment.rst @@ -46,6 +46,8 @@ Datasets are values (possibly arrays) that are read and written by experiments a A dataset may be broadcasted, that is, distributed to all clients connected to the master. For example, the ARTIQ GUI may plot it while the experiment is in progress to give rapid feedback to the user. Broadcasted datasets live in a global key-value store; experiments should use distinctive real-time result names in order to avoid conflicts. Broadcasted datasets may be used to communicate values across experiments; for example, a periodic calibration experiment may update a dataset read by payload experiments. Broadcasted datasets are replaced when a new dataset with the same key (name) is produced. +Datasets can attach metadata for numerical formatting with the ``unit``, ``scale`` and ``precision`` parameters. In experiment code, values are assumed to be in the SI unit. In code, setting a dataset with a value of `1000` and the unit `kV` represents the quantity `1 kV`. It is recommended to use the globals defined by `artiq.language.units` and write `1*kV` instead of `1000` for the value. In dashboards and clients these globals are not available. There, setting a dataset with a value of `1` and the unit `kV` simply represents the quantity `1 kV`. ``precision`` refers to the max number of decimal places to display. This parameter does not affect the underlying value, and is only used for display purposes. + Broadcasted datasets may be persistent: the master stores them in a file typically called ``dataset_db.pyon`` so they are saved across master restarts. Datasets produced by an experiment run may be archived in the HDF5 output for that run. From 361088ae72f236233f8888007c93d52041d6f768 Mon Sep 17 00:00:00 2001 From: Simon Renblad Date: Fri, 21 Jul 2023 16:20:52 +0800 Subject: [PATCH 15/66] tools: add trim argument to format funcs --- artiq/tools.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/artiq/tools.py b/artiq/tools.py index 61678f9bc..8843bf5bb 100644 --- a/artiq/tools.py +++ b/artiq/tools.py @@ -72,6 +72,7 @@ def short_format(v, metadata={}): v_t = np.divide(v, scale) v_str = np.format_float_positional(v_t, precision=precision, + trim='-', unique=True) v_str += " " + unit if unit else "" return v_str @@ -84,6 +85,7 @@ def short_format(v, metadata={}): v_str = np.array2string(v_t, max_line_width=1000, precision=precision, + trim='-', suppress_small=True, separator=', ', threshold=4, From 07714be8a7e51359314423e695422b08cad529b3 Mon Sep 17 00:00:00 2001 From: Denis Date: Mon, 24 Jul 2023 16:32:13 +0800 Subject: [PATCH 16/66] jsonschema: add kasli_soc HW revision v1.1 --- artiq/coredevice/coredevice_generic.schema.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/artiq/coredevice/coredevice_generic.schema.json b/artiq/coredevice/coredevice_generic.schema.json index a695c3850..84f5f3505 100644 --- a/artiq/coredevice/coredevice_generic.schema.json +++ b/artiq/coredevice/coredevice_generic.schema.json @@ -130,7 +130,7 @@ }, "hw_rev": { "type": "string", - "enum": ["v1.0"] + "enum": ["v1.0", "v1.1"] } } } From 72a5231493599455f50b25d44e101d74e98762d0 Mon Sep 17 00:00:00 2001 From: linuswck <138766547+linuswck@users.noreply.github.com> Date: Tue, 25 Jul 2023 11:14:19 +0800 Subject: [PATCH 17/66] artiq_flash: add EEM FMC Carrier Board Support - The code is derived from PR #2134 936f24f6bdf47e577d3e7d73a330797542596ba8 --- artiq/frontend/artiq_flash.py | 34 ++++++++++++++++++++++++++++++---- 1 file changed, 30 insertions(+), 4 deletions(-) diff --git a/artiq/frontend/artiq_flash.py b/artiq/frontend/artiq_flash.py index 136b58c71..6c2037ca1 100755 --- a/artiq/frontend/artiq_flash.py +++ b/artiq/frontend/artiq_flash.py @@ -59,7 +59,7 @@ Prerequisites: help="SSH host to jump through") parser.add_argument("-t", "--target", default="kasli", help="target board, default: %(default)s, one of: " - "kasli kc705") + "kasli efc kc705") parser.add_argument("-I", "--preinit-command", default=[], action="append", help="add a pre-initialization OpenOCD command. " "Useful for selecting a board when several are connected.") @@ -215,9 +215,28 @@ class ProgrammerXC7(Programmer): Programmer.__init__(self, client, preinit_script) self._proxy = proxy - add_commands(self._board_script, - "source {boardfile}", - boardfile=self._transfer_script("board/{}.cfg".format(board))) + if board != "efc": + add_commands(self._board_script, + "source {boardfile}", + boardfile=self._transfer_script("board/{}.cfg".format(board))) + else: + add_commands(self._board_script, + # OpenOCD does not have the efc board file so custom script is included. + # To be used with Digilent-HS2 Programming Cable but the config in digilent-hs2.cfg is wrong + # See digilent_jtag_smt2_nc.cfg for details + "source [find interface/ftdi/digilent_jtag_smt2_nc.cfg]", + + "ftdi tdo_sample_edge falling", + + "reset_config none", + "transport select jtag", + "adapter speed 25000", + + "source [find cpld/xilinx-xc7.cfg]", + "source [find cpld/jtagspi.cfg]", + "source [find fpga/xilinx-xadc.cfg]", + "source [find fpga/xilinx-dna.cfg]" + ) self.add_flash_bank("spi0", "xc7", index=0) add_commands(self._script, "xadc_report xc7.tap") @@ -242,6 +261,13 @@ def main(): "storage": ("spi0", 0x440000), "firmware": ("spi0", 0x450000), }, + "efc": { + "programmer": partial(ProgrammerXC7, board="efc", proxy="bscan_spi_xc7a100t.bit"), + "gateware": ("spi0", 0x000000), + "bootloader": ("spi0", 0x400000), + "storage": ("spi0", 0x440000), + "firmware": ("spi0", 0x450000), + }, "kc705": { "programmer": partial(ProgrammerXC7, board="kc705", proxy="bscan_spi_xc7k325t.bit"), "gateware": ("spi0", 0x000000), From cb547c8a46de350417f2f43ffced71adf8dca97c Mon Sep 17 00:00:00 2001 From: linuswck Date: Tue, 1 Aug 2023 00:29:45 +0000 Subject: [PATCH 18/66] efc: turn on power of FMC peripheral - Add efc's io expander method - Enable VADJ, P3V3_FMC in satman main during startup --- artiq/firmware/libboard_misoc/io_expander.rs | 33 ++++++++++++++++++++ artiq/firmware/libboard_misoc/lib.rs | 2 +- artiq/firmware/satman/main.rs | 13 ++++++++ 3 files changed, 47 insertions(+), 1 deletion(-) diff --git a/artiq/firmware/libboard_misoc/io_expander.rs b/artiq/firmware/libboard_misoc/io_expander.rs index edf2ce666..7009e0476 100644 --- a/artiq/firmware/libboard_misoc/io_expander.rs +++ b/artiq/firmware/libboard_misoc/io_expander.rs @@ -81,6 +81,32 @@ impl IoExpander { Ok(io_expander) } + #[cfg(soc_platform = "efc")] + pub fn new() -> Result { + // TODO: Put Virtual User LEDs L0 L1 in gateware + const VIRTUAL_LED_MAPPING: [(u8, u8, u8); 2] = [(0, 0, 5), (1, 0, 6)]; + + let mut io_expander = IoExpander { + busno: 0, + port: 1, + address: 0x40, + virtual_led_mapping: &VIRTUAL_LED_MAPPING, + iodir: [0xff; 2], + out_current: [0; 2], + out_target: [0; 2], + registers: Registers { + iodira: 0x00, + iodirb: 0x01, + gpioa: 0x12, + gpiob: 0x13, + }, + }; + if !io_expander.check_ack()? { + return Err("MCP23017 io expander not found."); + }; + Ok(io_expander) + } + #[cfg(soc_platform = "kasli")] fn select(&self) -> Result<(), &'static str> { let mask: u16 = 1 << self.port; @@ -89,6 +115,13 @@ impl IoExpander { Ok(()) } + #[cfg(soc_platform = "efc")] + fn select(&self) -> Result<(), &'static str> { + let mask: u16 = 1 << self.port; + i2c::switch_select(self.busno, 0x70, mask as u8)?; + Ok(()) + } + fn write(&self, addr: u8, value: u8) -> Result<(), &'static str> { i2c::start(self.busno)?; i2c::write(self.busno, self.address)?; diff --git a/artiq/firmware/libboard_misoc/lib.rs b/artiq/firmware/libboard_misoc/lib.rs index 3189ccfcd..7ba835dcf 100644 --- a/artiq/firmware/libboard_misoc/lib.rs +++ b/artiq/firmware/libboard_misoc/lib.rs @@ -40,7 +40,7 @@ pub mod ethmac; pub mod i2c; #[cfg(soc_platform = "kasli")] pub mod i2c_eeprom; -#[cfg(all(soc_platform = "kasli", hw_rev = "v2.0"))] +#[cfg(any(all(soc_platform = "kasli", hw_rev = "v2.0"), soc_platform = "efc"))] pub mod io_expander; #[cfg(all(has_ethmac, feature = "smoltcp"))] pub mod net_settings; diff --git a/artiq/firmware/satman/main.rs b/artiq/firmware/satman/main.rs index 4e9f8fa33..cd101d28c 100644 --- a/artiq/firmware/satman/main.rs +++ b/artiq/firmware/satman/main.rs @@ -529,6 +529,19 @@ pub extern fn main() -> i32 { sysclk_setup(); + #[cfg(soc_platform = "efc")] + { + let mut io_expander = board_misoc::io_expander::IoExpander::new().unwrap(); + // Enable VADJ and P3V3_FMC + io_expander.set_oe(1, 1 << 0 | 1 << 1).unwrap(); + + io_expander.set(1, 0, true); + io_expander.set(1, 1, true); + + io_expander.service().unwrap(); + } + + #[cfg(not(soc_platform = "efc"))] unsafe { csr::drtio_transceiver::txenable_write(0xffffffffu32 as _); } From c206e92f295c496bae9d001b7518f9afa146787a Mon Sep 17 00:00:00 2001 From: linuswck Date: Tue, 1 Aug 2023 16:43:23 +0800 Subject: [PATCH 19/66] Bootloader: Remove kusddrphy support for SDRAM - Delete all the kusddrphy cfg flags and related code --- artiq/firmware/libboard_misoc/sdram.rs | 33 -------------------------- 1 file changed, 33 deletions(-) diff --git a/artiq/firmware/libboard_misoc/sdram.rs b/artiq/firmware/libboard_misoc/sdram.rs index 1227b411d..8130416bd 100644 --- a/artiq/firmware/libboard_misoc/sdram.rs +++ b/artiq/firmware/libboard_misoc/sdram.rs @@ -8,9 +8,6 @@ mod ddr { DFII_COMMAND_WRDATA, DFII_COMMAND_RDDATA}; use sdram_phy::{DFII_NPHASES, DFII_PIX_DATA_SIZE, DFII_PIX_WRDATA_ADDR, DFII_PIX_RDDATA_ADDR}; - #[cfg(kusddrphy)] - const DDRPHY_MAX_DELAY: u16 = 512; - #[cfg(not(kusddrphy))] const DDRPHY_MAX_DELAY: u16 = 32; const DQS_SIGNAL_COUNT: usize = DFII_PIX_DATA_SIZE / 2; @@ -35,17 +32,12 @@ mod ddr { #[cfg(ddrphy_wlevel)] unsafe fn write_level_scan(logger: &mut Option<&mut dyn fmt::Write>) { - #[cfg(kusddrphy)] - log!(logger, "DQS initial delay: {} taps\n", ddrphy::wdly_dqs_taps_read()); log!(logger, "Write leveling scan:\n"); enable_write_leveling(true); spin_cycles(100); - #[cfg(not(kusddrphy))] let ddrphy_max_delay : u16 = DDRPHY_MAX_DELAY; - #[cfg(kusddrphy)] - let ddrphy_max_delay : u16 = DDRPHY_MAX_DELAY - ddrphy::wdly_dqs_taps_read(); for n in 0..DQS_SIGNAL_COUNT { let dq_addr = dfii::PI0_RDDATA_ADDR @@ -57,10 +49,6 @@ mod ddr { ddrphy::wdly_dq_rst_write(1); ddrphy::wdly_dqs_rst_write(1); - #[cfg(kusddrphy)] - for _ in 0..ddrphy::wdly_dqs_taps_read() { - ddrphy::wdly_dqs_inc_write(1); - } let mut dq; for _ in 0..ddrphy_max_delay { @@ -88,17 +76,12 @@ mod ddr { unsafe fn write_level(logger: &mut Option<&mut dyn fmt::Write>, delay: &mut [u16; DQS_SIGNAL_COUNT], high_skew: &mut [bool; DQS_SIGNAL_COUNT]) -> bool { - #[cfg(kusddrphy)] - log!(logger, "DQS initial delay: {} taps\n", ddrphy::wdly_dqs_taps_read()); log!(logger, "Write leveling: "); enable_write_leveling(true); spin_cycles(100); - #[cfg(not(kusddrphy))] let ddrphy_max_delay : u16 = DDRPHY_MAX_DELAY; - #[cfg(kusddrphy)] - let ddrphy_max_delay : u16 = DDRPHY_MAX_DELAY - ddrphy::wdly_dqs_taps_read(); let mut failed = false; for n in 0..DQS_SIGNAL_COUNT { @@ -112,10 +95,6 @@ mod ddr { ddrphy::wdly_dq_rst_write(1); ddrphy::wdly_dqs_rst_write(1); - #[cfg(kusddrphy)] - for _ in 0..ddrphy::wdly_dqs_taps_read() { - ddrphy::wdly_dqs_inc_write(1); - } ddrphy::wlevel_strobe_write(1); spin_cycles(10); @@ -146,11 +125,6 @@ mod ddr { dq = ptr::read_volatile(dq_addr); } - // Get a bit further into the 0 zone - #[cfg(kusddrphy)] - for _ in 0..32 { - incr_delay(); - } } while dq == 0 { @@ -191,9 +165,6 @@ mod ddr { if delay[n] > threshold { ddrphy::dly_sel_write(1 << n); - #[cfg(kusddrphy)] - ddrphy::rdly_dq_bitslip_write(1); - #[cfg(not(kusddrphy))] for _ in 0..3 { ddrphy::rdly_dq_bitslip_write(1); } @@ -451,13 +422,9 @@ pub unsafe fn init(mut _logger: Option<&mut dyn fmt::Write>) -> bool { #[cfg(has_ddrphy)] { - #[cfg(kusddrphy)] - csr::ddrphy::en_vtc_write(0); if !ddr::level(&mut _logger) { return false } - #[cfg(kusddrphy)] - csr::ddrphy::en_vtc_write(1); } csr::dfii::control_write(sdram_phy::DFII_CONTROL_SEL); From 758b97426a7db79ee7c4a4db276dec636a684050 Mon Sep 17 00:00:00 2001 From: linuswck Date: Tue, 1 Aug 2023 17:39:21 +0800 Subject: [PATCH 20/66] Bootloader: SDRAM patch for EFC - Modification of the CFG flag ensure EFC to initialize DDRPHY correctly Note that Kasli and EFC share the same model of SDRAM --- artiq/firmware/libboard_misoc/sdram.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/artiq/firmware/libboard_misoc/sdram.rs b/artiq/firmware/libboard_misoc/sdram.rs index 8130416bd..b3839eaf6 100644 --- a/artiq/firmware/libboard_misoc/sdram.rs +++ b/artiq/firmware/libboard_misoc/sdram.rs @@ -217,7 +217,7 @@ mod ddr { ddrphy::dly_sel_write(1 << (DQS_SIGNAL_COUNT - n - 1)); ddrphy::rdly_dq_rst_write(1); - #[cfg(soc_platform = "kasli")] + #[cfg(any(soc_platform = "kasli", soc_platform = "efc"))] { for _ in 0..3 { ddrphy::rdly_dq_bitslip_write(1); @@ -308,7 +308,7 @@ mod ddr { let mut max_seen_valid = 0; ddrphy::rdly_dq_rst_write(1); - #[cfg(soc_platform = "kasli")] + #[cfg(any(soc_platform = "kasli", soc_platform = "efc"))] { for _ in 0..3 { ddrphy::rdly_dq_bitslip_write(1); @@ -371,7 +371,7 @@ mod ddr { // Set delay to the middle ddrphy::rdly_dq_rst_write(1); - #[cfg(soc_platform = "kasli")] + #[cfg(any(soc_platform = "kasli", soc_platform = "efc"))] { for _ in 0..3 { ddrphy::rdly_dq_bitslip_write(1); From c46ac6f87da0371fcd65b28aafb0307474334776 Mon Sep 17 00:00:00 2001 From: mwojcik Date: Fri, 4 Aug 2023 17:14:37 +0800 Subject: [PATCH 21/66] spi2: update set_config_mu doc --- artiq/coredevice/spi2.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/artiq/coredevice/spi2.py b/artiq/coredevice/spi2.py index 1a788d88f..3a7eb6ad5 100644 --- a/artiq/coredevice/spi2.py +++ b/artiq/coredevice/spi2.py @@ -277,9 +277,8 @@ class NRTSPIMaster: def set_config_mu(self, flags=0, length=8, div=6, cs=1): """Set the ``config`` register. - Note that the non-realtime SPI cores are usually clocked by the system - clock and not the RTIO clock. In many cases, the SPI configuration is - already set by the firmware and you do not need to call this method. + In many cases, the SPI configuration is already set by the firmware + and you do not need to call this method. """ spi_set_config(self.busno, flags, length, div, cs) From c4259dab18026905ef0002dfad4a203298ee7fbe Mon Sep 17 00:00:00 2001 From: Simon Renblad Date: Sat, 5 Aug 2023 11:38:07 +0800 Subject: [PATCH 22/66] applets.simple: add kwargs to AppletControlRPC (#2155) Co-authored-by: Simon Renblad --- artiq/applets/simple.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/artiq/applets/simple.py b/artiq/applets/simple.py index c7c7c81df..363314a9e 100644 --- a/artiq/applets/simple.py +++ b/artiq/applets/simple.py @@ -44,8 +44,8 @@ class AppletControlRPC: self.dataset_ctl = dataset_ctl self.background_tasks = set() - def _background(self, coro, *args): - task = self.loop.create_task(coro(*args)) + def _background(self, coro, *args, **kwargs): + task = self.loop.create_task(coro(*args, **kwargs)) self.background_tasks.add(task) task.add_done_callback(self.background_tasks.discard) From 3ed7e0ed0612fc3cc1068953b81c762573932ec0 Mon Sep 17 00:00:00 2001 From: Sebastien Bourdeauducq Date: Mon, 7 Aug 2023 17:52:42 +0800 Subject: [PATCH 23/66] flake: update dependencies --- flake.lock | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/flake.lock b/flake.lock index e64b7c60a..43a135f35 100644 --- a/flake.lock +++ b/flake.lock @@ -42,11 +42,11 @@ "mozilla-overlay": { "flake": false, "locked": { - "lastModified": 1687771476, - "narHash": "sha256-TSpqz6qYVRoqkEdOCawEQ4/cWt/4pracmvw17HK1tgE=", + "lastModified": 1690536331, + "narHash": "sha256-aRIf2FB2GTdfF7gl13WyETmiV/J7EhBGkSWXfZvlxcA=", "owner": "mozilla", "repo": "nixpkgs-mozilla", - "rev": "3a44b8783514e7d6db4b63df96071b6c2b014b07", + "rev": "db89c8707edcffefcd8e738459d511543a339ff5", "type": "github" }, "original": { @@ -57,11 +57,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1688868408, - "narHash": "sha256-RR9N5XTAxSBhK8MCvLq9uxfdkd7etC//seVXldy0k48=", + "lastModified": 1691328192, + "narHash": "sha256-w59N1zyDQ7SupfMJLFvtms/SIVbdryqlw5AS4+DiH+Y=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "510d721ce097150ae3b80f84b04b13b039186571", + "rev": "61676e4dcfeeb058f255294bcb08ea7f3bc3ce56", "type": "github" }, "original": { @@ -89,11 +89,11 @@ ] }, "locked": { - "lastModified": 1685540542, - "narHash": "sha256-wQJwL3xc6QVQbiJrt71/Z9tp4Eq1yqdGddcEiv7sPCw=", + "lastModified": 1691095538, + "narHash": "sha256-JX1Re8wzqg4odcv/QxPtEvO8Z4RO3ZbRKGQ+ZpDdtWc=", "owner": "m-labs", "repo": "sipyco", - "rev": "f5bf2ba875340a31a135aea14ad184575ca800ac", + "rev": "c4f18adb658e3a546319425750cebeb8c88a016a", "type": "github" }, "original": { @@ -121,11 +121,11 @@ "src-misoc": { "flake": false, "locked": { - "lastModified": 1685415268, - "narHash": "sha256-g4+yeSV+HtWjcllM5wk4vNBUVCXtDOzUSKhxXPT7Fyc=", + "lastModified": 1691370054, + "narHash": "sha256-ZI+H7gcQzx//TF5Jaf5iejd8PtPwiDAQ4redmb2WsDA=", "ref": "refs/heads/master", - "rev": "6d48ce77b6746d3226a682790fbc95b90340986e", - "revCount": 2440, + "rev": "1b871635d768d6c5efbab846a1e07da12877aed9", + "revCount": 2443, "submodules": true, "type": "git", "url": "https://github.com/m-labs/misoc.git" From 4d636ea593d1bc2d532da28944b8d3186266bd97 Mon Sep 17 00:00:00 2001 From: David Nadlinger Date: Sun, 6 Aug 2023 08:20:12 +0800 Subject: [PATCH 24/66] Upgrade to LLD 14 Previous linker versions had inserted some zero padding bytes between the ELF headers and the first section, but LLD 14 does not anymore. Hard-coding the offset of the first section in ksupport.elf manually isn't ideal; we should probably parse the ELF program headers instead when first setting up the kernel CPU. --- artiq/firmware/ksupport/glue.c | 2 +- artiq/firmware/libproto_artiq/kernel_proto.rs | 5 ++++- flake.nix | 14 +++++++------- 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/artiq/firmware/ksupport/glue.c b/artiq/firmware/ksupport/glue.c index a15491689..d43388769 100644 --- a/artiq/firmware/ksupport/glue.c +++ b/artiq/firmware/ksupport/glue.c @@ -17,7 +17,7 @@ void send_to_rtio_log(struct slice data); #define KERNELCPU_EXEC_ADDRESS 0x45000000 #define KERNELCPU_PAYLOAD_ADDRESS 0x45060000 #define KERNELCPU_LAST_ADDRESS 0x4fffffff -#define KSUPPORT_HEADER_SIZE 0x80 +#define KSUPPORT_HEADER_SIZE 0x74 FILE *stderr; diff --git a/artiq/firmware/libproto_artiq/kernel_proto.rs b/artiq/firmware/libproto_artiq/kernel_proto.rs index cc17147cd..2ecb39c3b 100644 --- a/artiq/firmware/libproto_artiq/kernel_proto.rs +++ b/artiq/firmware/libproto_artiq/kernel_proto.rs @@ -5,7 +5,10 @@ use dyld; pub const KERNELCPU_EXEC_ADDRESS: usize = 0x45000000; pub const KERNELCPU_PAYLOAD_ADDRESS: usize = 0x45060000; pub const KERNELCPU_LAST_ADDRESS: usize = 0x4fffffff; -pub const KSUPPORT_HEADER_SIZE: usize = 0x80; + +// Must match the offset of the first (starting at KERNELCPU_EXEC_ADDRESS) +// section in ksupport.elf. +pub const KSUPPORT_HEADER_SIZE: usize = 0x74; #[derive(Debug)] pub enum Message<'a> { diff --git a/flake.nix b/flake.nix index 33b5df443..edcee7540 100644 --- a/flake.nix +++ b/flake.nix @@ -124,7 +124,7 @@ nativeBuildInputs = [ pkgs.qt5.wrapQtAppsHook ]; # keep llvm_x and lld_x in sync with llvmlite - propagatedBuildInputs = [ pkgs.llvm_11 pkgs.lld_11 sipyco.packages.x86_64-linux.sipyco pythonparser pkgs.qt5.qtsvg artiq-comtools.packages.x86_64-linux.artiq-comtools ] + propagatedBuildInputs = [ pkgs.llvm_11 pkgs.lld_14 sipyco.packages.x86_64-linux.sipyco pythonparser pkgs.qt5.qtsvg artiq-comtools.packages.x86_64-linux.artiq-comtools ] ++ (with pkgs.python3Packages; [ llvmlite pyqtgraph pygit2 numpy dateutil scipy prettytable pyserial levenshtein h5py pyqt5 qasync tqdm lmdb jsonschema ]); dontWrapQtApps = true; @@ -147,10 +147,10 @@ "--set FONTCONFIG_FILE ${pkgs.fontconfig.out}/etc/fonts/fonts.conf" ]; - # FIXME: automatically propagate lld_11 llvm_11 dependencies + # FIXME: automatically propagate lld_14 llvm_11 dependencies # cacert is required in the check stage only, as certificates are to be # obtained from system elsewhere - nativeCheckInputs = [ pkgs.lld_11 pkgs.llvm_11 libartiq-support pkgs.lit outputcheck pkgs.cacert ]; + nativeCheckInputs = [ pkgs.lld_14 pkgs.llvm_11 libartiq-support pkgs.lit outputcheck pkgs.cacert ]; checkPhase = '' python -m unittest discover -v artiq.test @@ -229,7 +229,7 @@ pkgs.cargo-xbuild pkgs.llvmPackages_11.clang-unwrapped pkgs.llvm_11 - pkgs.lld_11 + pkgs.lld_14 vivado rustPlatform.cargoSetupHook ]; @@ -392,7 +392,7 @@ pkgs.cargo-xbuild pkgs.llvmPackages_11.clang-unwrapped pkgs.llvm_11 - pkgs.lld_11 + pkgs.lld_14 # To manually run compiler tests: pkgs.lit outputcheck @@ -420,7 +420,7 @@ pkgs.cargo-xbuild pkgs.llvmPackages_11.clang-unwrapped pkgs.llvm_11 - pkgs.lld_11 + pkgs.lld_14 packages.x86_64-linux.vivado packages.x86_64-linux.openocd-bscanspi ]; @@ -454,7 +454,7 @@ buildInputs = [ (pkgs.python3.withPackages(ps: with packages.x86_64-linux; [ artiq ps.paramiko ])) pkgs.llvm_11 - pkgs.lld_11 + pkgs.lld_14 pkgs.openssh packages.x86_64-linux.openocd-bscanspi # for the bscanspi bitstreams ]; From 8acfa8258615cf6a54fb9c7b60fbb7f4dc3bdf4f Mon Sep 17 00:00:00 2001 From: David Nadlinger Date: Sun, 6 Aug 2023 08:33:01 +0800 Subject: [PATCH 25/66] ksupport: Remove unused sections from linker script [nfc] We no longer build ksupport.ld in a position-independent fashion, and the reference to the ld.bfd _GLOBAL_OFFSET_TABLE issue was just a distraction --- artiq/firmware/ksupport/ksupport.ld | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/artiq/firmware/ksupport/ksupport.ld b/artiq/firmware/ksupport/ksupport.ld index 66b159f13..24c4d2524 100644 --- a/artiq/firmware/ksupport/ksupport.ld +++ b/artiq/firmware/ksupport/ksupport.ld @@ -35,16 +35,6 @@ SECTIONS *(.text .text.*) } :text - /* https://sourceware.org/bugzilla/show_bug.cgi?id=20475 */ - .got : { - PROVIDE(_GLOBAL_OFFSET_TABLE_ = .); - *(.got) - } :text - - .got.plt : { - *(.got.plt) - } :text - .rodata : { *(.rodata .rodata.*) From efbae51f9d845e5ecc982d82eadee95672a4310d Mon Sep 17 00:00:00 2001 From: David Nadlinger Date: Mon, 7 Aug 2023 05:51:32 +0800 Subject: [PATCH 26/66] runtime: Validate ksupport ELF against hard-coded address ranges MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This would have caught the reduction in header padding with LLD 14. In theory, we could just get rid of the hard-coded kernel CPU address ranges altogether and use ksupport.elf as the one source of truth; the code already exists in dyld. The actual base address of the file would still need to be forwarded to the kernel-side libunwind glue, though, as there doesn't seem to be a clean way to get the equivalent of KSUPPORT_HEADER_SIZE through the linker script. I have left this as-is with the hard-coded KERNELCPU_… constants for now. --- artiq/firmware/Cargo.lock | 1 + artiq/firmware/libdyld/lib.rs | 40 ++++++++++++++-------- artiq/firmware/runtime/Cargo.toml | 1 + artiq/firmware/runtime/kernel.rs | 57 +++++++++++++++++++++++++++---- artiq/firmware/runtime/main.rs | 1 + 5 files changed, 78 insertions(+), 22 deletions(-) diff --git a/artiq/firmware/Cargo.lock b/artiq/firmware/Cargo.lock index 23ded9aa2..f4bd9b88a 100644 --- a/artiq/firmware/Cargo.lock +++ b/artiq/firmware/Cargo.lock @@ -320,6 +320,7 @@ dependencies = [ "build_misoc", "byteorder", "cslice", + "dyld", "eh", "failure", "failure_derive", diff --git a/artiq/firmware/libdyld/lib.rs b/artiq/firmware/libdyld/lib.rs index 15bbe2548..64bfbdf15 100644 --- a/artiq/firmware/libdyld/lib.rs +++ b/artiq/firmware/libdyld/lib.rs @@ -5,7 +5,7 @@ use elf::*; pub mod elf; -fn read_unaligned(data: &[u8], offset: usize) -> Result { +pub fn read_unaligned(data: &[u8], offset: usize) -> Result { if data.len() < offset + mem::size_of::() { Err(()) } else { @@ -75,6 +75,25 @@ impl<'a> fmt::Display for Error<'a> { } } +pub fn is_elf_for_current_arch(ehdr: &Elf32_Ehdr, e_type: u16) -> bool { + const IDENT: [u8; EI_NIDENT] = [ + ELFMAG0, ELFMAG1, ELFMAG2, ELFMAG3, + ELFCLASS32, ELFDATA2LSB, EV_CURRENT, ELFOSABI_NONE, + /* ABI version */ 0, /* padding */ 0, 0, 0, 0, 0, 0, 0 + ]; + if ehdr.e_ident != IDENT { return false; } + + if ehdr.e_type != e_type { return false; } + + #[cfg(target_arch = "riscv32")] + const ARCH: u16 = EM_RISCV; + #[cfg(not(target_arch = "riscv32"))] + const ARCH: u16 = EM_NONE; + if ehdr.e_machine != ARCH { return false; } + + true +} + #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum Arch { RiscV, @@ -268,25 +287,16 @@ impl<'a> Library<'a> { let ehdr = read_unaligned::(data, 0) .map_err(|()| "cannot read ELF header")?; - const IDENT: [u8; EI_NIDENT] = [ - ELFMAG0, ELFMAG1, ELFMAG2, ELFMAG3, - ELFCLASS32, ELFDATA2LSB, EV_CURRENT, ELFOSABI_NONE, - /* ABI version */ 0, /* padding */ 0, 0, 0, 0, 0, 0, 0 - ]; - - #[cfg(target_arch = "riscv32")] - const ARCH: u16 = EM_RISCV; - #[cfg(not(target_arch = "riscv32"))] - const ARCH: u16 = EM_NONE; + if !is_elf_for_current_arch(&ehdr, ET_DYN) { + return Err("not a shared library for current architecture")? + } #[cfg(all(target_feature = "f", target_feature = "d"))] const FLAGS: u32 = EF_RISCV_FLOAT_ABI_DOUBLE; - #[cfg(not(all(target_feature = "f", target_feature = "d")))] const FLAGS: u32 = EF_RISCV_FLOAT_ABI_SOFT; - - if ehdr.e_ident != IDENT || ehdr.e_type != ET_DYN || ehdr.e_machine != ARCH || ehdr.e_flags != FLAGS { - return Err("not a shared library for current architecture")? + if ehdr.e_flags != FLAGS { + return Err("unexpected flags for shared library (wrong floating point ABI?)")? } let mut dyn_off = None; diff --git a/artiq/firmware/runtime/Cargo.toml b/artiq/firmware/runtime/Cargo.toml index 85d05768c..86db5ed08 100644 --- a/artiq/firmware/runtime/Cargo.toml +++ b/artiq/firmware/runtime/Cargo.toml @@ -19,6 +19,7 @@ byteorder = { version = "1.0", default-features = false } cslice = { version = "0.3" } log = { version = "0.4", default-features = false } managed = { version = "^0.7.1", default-features = false, features = ["alloc", "map"] } +dyld = { path = "../libdyld" } eh = { path = "../libeh" } unwind_backtrace = { path = "../libunwind_backtrace" } io = { path = "../libio", features = ["byteorder"] } diff --git a/artiq/firmware/runtime/kernel.rs b/artiq/firmware/runtime/kernel.rs index 1a67af556..42c1f2f05 100644 --- a/artiq/firmware/runtime/kernel.rs +++ b/artiq/firmware/runtime/kernel.rs @@ -1,5 +1,5 @@ -use core::ptr; use board_misoc::csr; +use core::{ptr, slice}; use mailbox; use rpc_queue; @@ -13,15 +13,20 @@ pub unsafe fn start() { stop(); - extern { + extern "C" { static _binary____ksupport_ksupport_elf_start: u8; static _binary____ksupport_ksupport_elf_end: u8; } - let ksupport_start = &_binary____ksupport_ksupport_elf_start as *const _; - let ksupport_end = &_binary____ksupport_ksupport_elf_end as *const _; - ptr::copy_nonoverlapping(ksupport_start, - (KERNELCPU_EXEC_ADDRESS - KSUPPORT_HEADER_SIZE) as *mut u8, - ksupport_end as usize - ksupport_start as usize); + let ksupport_elf_start = &_binary____ksupport_ksupport_elf_start as *const u8; + let ksupport_elf_end = &_binary____ksupport_ksupport_elf_end as *const u8; + let ksupport_elf = slice::from_raw_parts( + ksupport_elf_start, + ksupport_elf_end as usize - ksupport_elf_start as usize, + ); + + if let Err(msg) = load_image(&ksupport_elf) { + panic!("failed to load kernel CPU image (ksupport.elf): {}", msg); + } csr::kernel_cpu::reset_write(0); @@ -41,6 +46,44 @@ pub unsafe fn stop() { rpc_queue::init(); } +/// Loads the given image for execution on the kernel CPU. +/// +/// The entire image including the headers is copied into memory for later use by libunwind, but +/// placed such that the text section ends up at the right location in memory. Currently, we just +/// hard-code the address range, but at least verify that this matches the ELF program header given +/// in the image (avoids loading the – non-relocatable – code at the wrong address on toolchain/… +/// changes). +unsafe fn load_image(image: &[u8]) -> Result<(), &'static str> { + use dyld::elf::*; + use dyld::{is_elf_for_current_arch, read_unaligned}; + + let ehdr = read_unaligned::(image, 0).map_err(|()| "could not read ELF header")?; + + // The check assumes the two CPUs share the same architecture. This is just to avoid inscrutable + // errors; we do not functionally rely on this. + if !is_elf_for_current_arch(&ehdr, ET_EXEC) { + return Err("not an executable for kernel CPU architecture"); + } + + // First program header should be the main text/… LOAD (see ksupport.ld). + let phdr = read_unaligned::(image, ehdr.e_phoff as usize) + .map_err(|()| "could not read program header")?; + if phdr.p_type != PT_LOAD { + return Err("unexpected program header type"); + } + if phdr.p_vaddr + phdr.p_memsz > KERNELCPU_LAST_ADDRESS as u32 { + // This is a weak sanity check only; we also need to fit in the stack, etc. + return Err("too large for kernel CPU address range"); + } + const TARGET_ADDRESS: u32 = (KERNELCPU_EXEC_ADDRESS - KSUPPORT_HEADER_SIZE) as _; + if phdr.p_vaddr - phdr.p_offset != TARGET_ADDRESS { + return Err("unexpected load address/offset"); + } + + ptr::copy_nonoverlapping(image.as_ptr(), TARGET_ADDRESS as *mut u8, image.len()); + Ok(()) +} + pub fn validate(ptr: usize) -> bool { ptr >= KERNELCPU_EXEC_ADDRESS && ptr <= KERNELCPU_LAST_ADDRESS } diff --git a/artiq/firmware/runtime/main.rs b/artiq/firmware/runtime/main.rs index 285768e8e..4809efa46 100644 --- a/artiq/firmware/runtime/main.rs +++ b/artiq/firmware/runtime/main.rs @@ -1,6 +1,7 @@ #![feature(lang_items, panic_info_message, const_btree_new, iter_advance_by)] #![no_std] +extern crate dyld; extern crate eh; #[macro_use] extern crate alloc; From 9b94a09477e36953208058d0a01074c8f63620af Mon Sep 17 00:00:00 2001 From: Sebastien Bourdeauducq Date: Mon, 7 Aug 2023 18:28:44 +0800 Subject: [PATCH 27/66] flake: update to LLVM 14 --- flake.nix | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/flake.nix b/flake.nix index edcee7540..859a0b0b7 100644 --- a/flake.nix +++ b/flake.nix @@ -124,7 +124,7 @@ nativeBuildInputs = [ pkgs.qt5.wrapQtAppsHook ]; # keep llvm_x and lld_x in sync with llvmlite - propagatedBuildInputs = [ pkgs.llvm_11 pkgs.lld_14 sipyco.packages.x86_64-linux.sipyco pythonparser pkgs.qt5.qtsvg artiq-comtools.packages.x86_64-linux.artiq-comtools ] + propagatedBuildInputs = [ pkgs.llvm_14 pkgs.lld_14 sipyco.packages.x86_64-linux.sipyco pythonparser pkgs.qt5.qtsvg artiq-comtools.packages.x86_64-linux.artiq-comtools ] ++ (with pkgs.python3Packages; [ llvmlite pyqtgraph pygit2 numpy dateutil scipy prettytable pyserial levenshtein h5py pyqt5 qasync tqdm lmdb jsonschema ]); dontWrapQtApps = true; @@ -147,10 +147,10 @@ "--set FONTCONFIG_FILE ${pkgs.fontconfig.out}/etc/fonts/fonts.conf" ]; - # FIXME: automatically propagate lld_14 llvm_11 dependencies + # FIXME: automatically propagate lld_14 llvm_14 dependencies # cacert is required in the check stage only, as certificates are to be # obtained from system elsewhere - nativeCheckInputs = [ pkgs.lld_14 pkgs.llvm_11 libartiq-support pkgs.lit outputcheck pkgs.cacert ]; + nativeCheckInputs = [ pkgs.lld_14 pkgs.llvm_14 libartiq-support pkgs.lit outputcheck pkgs.cacert ]; checkPhase = '' python -m unittest discover -v artiq.test @@ -228,7 +228,7 @@ rust pkgs.cargo-xbuild pkgs.llvmPackages_11.clang-unwrapped - pkgs.llvm_11 + pkgs.llvm_14 pkgs.lld_14 vivado rustPlatform.cargoSetupHook @@ -391,7 +391,7 @@ rust pkgs.cargo-xbuild pkgs.llvmPackages_11.clang-unwrapped - pkgs.llvm_11 + pkgs.llvm_14 pkgs.lld_14 # To manually run compiler tests: pkgs.lit @@ -419,7 +419,7 @@ rust pkgs.cargo-xbuild pkgs.llvmPackages_11.clang-unwrapped - pkgs.llvm_11 + pkgs.llvm_14 pkgs.lld_14 packages.x86_64-linux.vivado packages.x86_64-linux.openocd-bscanspi @@ -453,7 +453,7 @@ buildInputs = [ (pkgs.python3.withPackages(ps: with packages.x86_64-linux; [ artiq ps.paramiko ])) - pkgs.llvm_11 + pkgs.llvm_14 pkgs.lld_14 pkgs.openssh packages.x86_64-linux.openocd-bscanspi # for the bscanspi bitstreams From d2ac6aceb3b6d314a11e18875f12717b937464ba Mon Sep 17 00:00:00 2001 From: Sebastien Bourdeauducq Date: Mon, 7 Aug 2023 18:45:13 +0800 Subject: [PATCH 28/66] flake: update to Clang 14 --- flake.nix | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/flake.nix b/flake.nix index 859a0b0b7..d0bd6ff23 100644 --- a/flake.nix +++ b/flake.nix @@ -227,7 +227,7 @@ (pkgs.python3.withPackages(ps: [ migen misoc (artiq.withExperimentalFeatures experimentalFeatures) ps.packaging ])) rust pkgs.cargo-xbuild - pkgs.llvmPackages_11.clang-unwrapped + pkgs.llvmPackages_14.clang-unwrapped pkgs.llvm_14 pkgs.lld_14 vivado @@ -390,7 +390,7 @@ (pkgs.python3.withPackages(ps: with packages.x86_64-linux; [ migen misoc ps.paramiko microscope ps.packaging ] ++ artiq.propagatedBuildInputs )) rust pkgs.cargo-xbuild - pkgs.llvmPackages_11.clang-unwrapped + pkgs.llvmPackages_14.clang-unwrapped pkgs.llvm_14 pkgs.lld_14 # To manually run compiler tests: @@ -418,7 +418,7 @@ (pkgs.python3.withPackages(ps: with packages.x86_64-linux; [ migen misoc artiq ps.packaging ])) rust pkgs.cargo-xbuild - pkgs.llvmPackages_11.clang-unwrapped + pkgs.llvmPackages_14.clang-unwrapped pkgs.llvm_14 pkgs.lld_14 packages.x86_64-linux.vivado From df662c4262ea3d0b4cdf4d6ab3184ecbb1f3aafa Mon Sep 17 00:00:00 2001 From: Sebastien Bourdeauducq Date: Mon, 7 Aug 2023 23:02:23 +0800 Subject: [PATCH 29/66] flake: update llvmlite --- artiq/test/lit/embedding/syscall_arg_attrs.py | 4 +-- artiq/test/lit/embedding/syscall_flags.py | 2 +- flake.nix | 26 +++++++++++++++++-- 3 files changed, 27 insertions(+), 5 deletions(-) diff --git a/artiq/test/lit/embedding/syscall_arg_attrs.py b/artiq/test/lit/embedding/syscall_arg_attrs.py index 67207dc32..a20d90bed 100644 --- a/artiq/test/lit/embedding/syscall_arg_attrs.py +++ b/artiq/test/lit/embedding/syscall_arg_attrs.py @@ -18,13 +18,13 @@ def entrypoint(): return_str() -# CHECK: declare void @accept_str\({ i8\*, i32 }\* byval\) +# CHECK: declare void @accept_str\({ i8\*, i32 }\* byval\({ i8\*, i32 }\)\) @syscall def accept_str(name: TStr) -> TNone: pass -# CHECK: declare void @return_str\({ i8\*, i32 }\* sret\) +# CHECK: declare void @return_str\({ i8\*, i32 }\* sret\({ i8\*, i32 }\)\) @syscall def return_str() -> TStr: pass diff --git a/artiq/test/lit/embedding/syscall_flags.py b/artiq/test/lit/embedding/syscall_flags.py index cc78f843b..b2e27d6a0 100644 --- a/artiq/test/lit/embedding/syscall_flags.py +++ b/artiq/test/lit/embedding/syscall_flags.py @@ -15,7 +15,7 @@ def foo() -> TNone: # sret nowrite functions shouldn't be marked inaccessiblememonly. # CHECK-L: ; Function Attrs: nounwind -# CHECK-NEXT-L: declare void @bar({ i32, i64 }* sret) +# CHECK-NEXT-L: declare void @bar({ i32, i64 }* sret({ i32, i64 })) @syscall(flags={"nounwind", "nowrite"}) def bar() -> TTuple([TInt32, TInt64]): pass diff --git a/flake.nix b/flake.nix index d0bd6ff23..e4b4f6460 100644 --- a/flake.nix +++ b/flake.nix @@ -111,6 +111,28 @@ ''; }; + llvmlite-new = pkgs.python3Packages.buildPythonPackage rec { + pname = "llvmlite"; + version = "0.40.1"; + src = pkgs.fetchFromGitHub { + owner = "numba"; + repo = "llvmlite"; + rev = "v${version}"; + sha256 = "sha256-gPEda9cMEsruvBt8I2VFfsTKZaPsNDgqx2Y9n0MSc4Y="; + }; + nativeBuildInputs = [ pkgs.llvm_14 ]; + # Disable static linking + # https://github.com/numba/llvmlite/issues/93 + postPatch = '' + substituteInPlace ffi/Makefile.linux --replace "-static-libstdc++" "" + substituteInPlace llvmlite/tests/test_binding.py --replace "test_linux" "nope" + ''; + # Set directory containing llvm-config binary + preConfigure = '' + export LLVM_CONFIG=${pkgs.llvm_14.dev}/bin/llvm-config + ''; + }; + artiq-upstream = pkgs.python3Packages.buildPythonPackage rec { pname = "artiq"; version = artiqVersion; @@ -124,8 +146,8 @@ nativeBuildInputs = [ pkgs.qt5.wrapQtAppsHook ]; # keep llvm_x and lld_x in sync with llvmlite - propagatedBuildInputs = [ pkgs.llvm_14 pkgs.lld_14 sipyco.packages.x86_64-linux.sipyco pythonparser pkgs.qt5.qtsvg artiq-comtools.packages.x86_64-linux.artiq-comtools ] - ++ (with pkgs.python3Packages; [ llvmlite pyqtgraph pygit2 numpy dateutil scipy prettytable pyserial levenshtein h5py pyqt5 qasync tqdm lmdb jsonschema ]); + propagatedBuildInputs = [ pkgs.llvm_14 pkgs.lld_14 sipyco.packages.x86_64-linux.sipyco pythonparser llvmlite-new pkgs.qt5.qtsvg artiq-comtools.packages.x86_64-linux.artiq-comtools ] + ++ (with pkgs.python3Packages; [ pyqtgraph pygit2 numpy dateutil scipy prettytable pyserial levenshtein h5py pyqt5 qasync tqdm lmdb jsonschema ]); dontWrapQtApps = true; postFixup = '' From 64d3f867a0026ce62efc2e8a7c83fb1a337baf55 Mon Sep 17 00:00:00 2001 From: occheung <54844539+occheung@users.noreply.github.com> Date: Wed, 9 Aug 2023 16:59:40 -0700 Subject: [PATCH 30/66] add DRTIO-over-EEM PHY for EFC and perhaps Phaser --- artiq/firmware/libboard_artiq/Cargo.toml | 1 + artiq/firmware/libboard_artiq/drtio_eem.rs | 219 ++++++++ artiq/firmware/libboard_artiq/lib.rs | 5 + artiq/firmware/runtime/Cargo.toml | 2 +- artiq/firmware/runtime/main.rs | 5 + artiq/firmware/satman/Cargo.toml | 2 +- artiq/firmware/satman/main.rs | 6 +- .../gateware/drtio/transceiver/eem_serdes.py | 481 ++++++++++++++++++ artiq/gateware/eem.py | 28 + 9 files changed, 746 insertions(+), 3 deletions(-) create mode 100644 artiq/firmware/libboard_artiq/drtio_eem.rs create mode 100644 artiq/gateware/drtio/transceiver/eem_serdes.py diff --git a/artiq/firmware/libboard_artiq/Cargo.toml b/artiq/firmware/libboard_artiq/Cargo.toml index 0f13ca2de..8405ee892 100644 --- a/artiq/firmware/libboard_artiq/Cargo.toml +++ b/artiq/firmware/libboard_artiq/Cargo.toml @@ -24,3 +24,4 @@ proto_artiq = { path = "../libproto_artiq" } [features] uart_console = [] +alloc = [] diff --git a/artiq/firmware/libboard_artiq/drtio_eem.rs b/artiq/firmware/libboard_artiq/drtio_eem.rs new file mode 100644 index 000000000..4434f8cb1 --- /dev/null +++ b/artiq/firmware/libboard_artiq/drtio_eem.rs @@ -0,0 +1,219 @@ +use board_misoc::{csr, clock, config}; +#[cfg(feature = "alloc")] +use alloc::format; + + +struct SerdesConfig { + pub delay: [u8; 4], +} + +impl SerdesConfig { + pub fn as_bytes(&self) -> &[u8] { + unsafe { + core::slice::from_raw_parts( + (self as *const SerdesConfig) as *const u8, + core::mem::size_of::(), + ) + } + } +} + +fn select_lane(lane_no: u8) { + unsafe { + csr::eem_transceiver::lane_sel_write(lane_no); + } +} + +fn apply_delay(tap: u8) { + unsafe { + csr::eem_transceiver::dly_cnt_in_write(tap); + csr::eem_transceiver::dly_ld_write(1); + clock::spin_us(1); + assert!(tap as u8 == csr::eem_transceiver::dly_cnt_out_read()); + } +} + +fn apply_config(config: &SerdesConfig) { + for lane_no in 0..4 { + select_lane(lane_no as u8); + apply_delay(config.delay[lane_no]); + } +} + +unsafe fn assign_delay() -> SerdesConfig { + // Select an appropriate delay for lane 0 + select_lane(0); + + let read_align = |dly: u8| -> f32 { + apply_delay(dly); + csr::eem_transceiver::counter_reset_write(1); + + csr::eem_transceiver::counter_enable_write(1); + clock::spin_us(2000); + csr::eem_transceiver::counter_enable_write(0); + + let (high, low) = ( + csr::eem_transceiver::counter_high_count_read(), + csr::eem_transceiver::counter_low_count_read(), + ); + if csr::eem_transceiver::counter_overflow_read() == 1 { + panic!("Unexpected phase detector counter overflow"); + } + + low as f32 / (low + high) as f32 + }; + + let mut best_dly = None; + + loop { + let mut prev = None; + for curr_dly in 0..32 { + let curr_low_rate = read_align(curr_dly); + + if let Some(prev_low_rate) = prev { + // This is potentially a crossover position + if prev_low_rate <= curr_low_rate && curr_low_rate >= 0.5 { + let prev_dev = 0.5 - prev_low_rate; + let curr_dev = curr_low_rate - 0.5; + let selected_idx = if prev_dev < curr_dev { + curr_dly - 1 + } else { + curr_dly + }; + + // The setup setup/hold calibration timing (even with + // tolerance) might be invalid in other lanes due to skew. + // 5 taps is very conservative, generally it is 1 or 2 + if selected_idx < 5 { + prev = None; + continue; + } else { + best_dly = Some(selected_idx); + break; + } + } + } + + // Only rising slope from <= 0.5 can result in a rising low rate + // crossover at 50%. + if curr_low_rate <= 0.5 { + prev = Some(curr_low_rate); + } + } + + if best_dly.is_none() { + error!("setup/hold timing calibration failed, retry in 1s..."); + clock::spin_us(1_000_000); + } else { + break; + } + } + + let best_dly = best_dly.unwrap(); + + apply_delay(best_dly); + let mut delay_list = [best_dly; 4]; + + // Assign delay for other lanes + for lane_no in 1..=3 { + select_lane(lane_no as u8); + + let mut min_deviation = 0.5; + let mut min_idx = 0; + for dly_delta in -3..=3 { + let index = (best_dly as isize + dly_delta) as u8; + let low_rate = read_align(index); + // abs() from f32 is not available in core library + let deviation = if low_rate < 0.5 { + 0.5 - low_rate + } else { + low_rate - 0.5 + }; + + if deviation < min_deviation { + min_deviation = deviation; + min_idx = index; + } + } + + apply_delay(min_idx); + delay_list[lane_no] = min_idx; + } + + debug!("setup/hold timing calibration: {:?}", delay_list); + + SerdesConfig { + delay: delay_list, + } +} + +unsafe fn align_comma() { + loop { + for slip in 1..=10 { + // The soft transceiver has 2 8b10b decoders, which receives lane + // 0/1 and lane 2/3 respectively. The decoder are time-multiplexed + // to decode exactly 1 lane each sysclk cycle. + // + // The decoder decodes lane 0/2 data on odd sysclk cycles, buffer + // on even cycles, and vice versa for lane 1/3. Data/Clock latency + // could change timing. The extend bit flips the decoding timing, + // so lane 0/2 data are decoded on even cycles, and lane 1/3 data + // are decoded on odd cycles. + // + // This is needed because transmitting/receiving a 8b10b character + // takes 2 sysclk cycles. Adjusting bitslip only via ISERDES + // limits the range to 1 cycle. The wordslip bit extends the range + // to 2 sysclk cycles. + csr::eem_transceiver::wordslip_write((slip > 5) as u8); + + // Apply a double bitslip since the ISERDES is 2x oversampled. + // Bitslip is used for comma alignment purposes once setup/hold + // timing is met. + csr::eem_transceiver::bitslip_write(1); + csr::eem_transceiver::bitslip_write(1); + clock::spin_us(1); + + csr::eem_transceiver::comma_align_reset_write(1); + clock::spin_us(100); + + if csr::eem_transceiver::comma_read() == 1 { + debug!("comma alignment completed after {} bitslips", slip); + return; + } + } + + error!("comma alignment failed, retrying in 1s..."); + clock::spin_us(1_000_000); + } +} + +pub fn init() { + for trx_no in 0..csr::CONFIG_EEM_TRANSCEIVERS { + unsafe { + csr::eem_transceiver::transceiver_sel_write(trx_no as u8); + } + + let key = format!("eem_drtio_delay{}", trx_no); + config::read(&key, |r| { + match r { + Ok(record) => { + info!("loading calibrated timing values from flash"); + unsafe { + apply_config(&*(record.as_ptr() as *const SerdesConfig)); + } + }, + + Err(_) => { + info!("calibrating..."); + let config = unsafe { assign_delay() }; + config::write(&key, config.as_bytes()).unwrap(); + } + } + }); + + unsafe { + align_comma(); + csr::eem_transceiver::rx_ready_write(1); + } + } +} diff --git a/artiq/firmware/libboard_artiq/lib.rs b/artiq/firmware/libboard_artiq/lib.rs index 209da3048..9579acaaa 100644 --- a/artiq/firmware/libboard_artiq/lib.rs +++ b/artiq/firmware/libboard_artiq/lib.rs @@ -12,6 +12,8 @@ extern crate log; extern crate io; extern crate board_misoc; extern crate proto_artiq; +#[cfg(feature = "alloc")] +extern crate alloc; pub mod spi; @@ -29,3 +31,6 @@ pub mod grabber; #[cfg(has_drtio)] pub mod drtioaux; pub mod drtio_routing; + +#[cfg(all(has_drtio_eem, feature = "alloc"))] +pub mod drtio_eem; diff --git a/artiq/firmware/runtime/Cargo.toml b/artiq/firmware/runtime/Cargo.toml index 86db5ed08..16477707b 100644 --- a/artiq/firmware/runtime/Cargo.toml +++ b/artiq/firmware/runtime/Cargo.toml @@ -26,7 +26,7 @@ io = { path = "../libio", features = ["byteorder"] } alloc_list = { path = "../liballoc_list" } board_misoc = { path = "../libboard_misoc", features = ["uart_console", "smoltcp"] } logger_artiq = { path = "../liblogger_artiq" } -board_artiq = { path = "../libboard_artiq" } +board_artiq = { path = "../libboard_artiq", features = ["alloc"] } proto_artiq = { path = "../libproto_artiq", features = ["log", "alloc"] } riscv = { version = "0.6.0", features = ["inline-asm"] } diff --git a/artiq/firmware/runtime/main.rs b/artiq/firmware/runtime/main.rs index 4809efa46..94cfd23c5 100644 --- a/artiq/firmware/runtime/main.rs +++ b/artiq/firmware/runtime/main.rs @@ -40,6 +40,8 @@ use board_artiq::drtioaux; use board_artiq::drtio_routing; use board_artiq::{mailbox, rpc_queue}; use proto_artiq::{mgmt_proto, moninj_proto, rpc_proto, session_proto, kernel_proto}; +#[cfg(has_drtio_eem)] +use board_artiq::drtio_eem; #[cfg(has_rtio_analyzer)] use proto_artiq::analyzer_proto; @@ -126,6 +128,9 @@ fn startup() { } rtio_clocking::init(); + #[cfg(has_drtio_eem)] + drtio_eem::init(); + let mut net_device = unsafe { ethmac::EthernetDevice::new() }; net_device.reset_phy_if_any(); diff --git a/artiq/firmware/satman/Cargo.toml b/artiq/firmware/satman/Cargo.toml index 0b219c15f..20dec311f 100644 --- a/artiq/firmware/satman/Cargo.toml +++ b/artiq/firmware/satman/Cargo.toml @@ -15,7 +15,7 @@ build_misoc = { path = "../libbuild_misoc" } [dependencies] log = { version = "0.4", default-features = false } board_misoc = { path = "../libboard_misoc", features = ["uart_console", "log"] } -board_artiq = { path = "../libboard_artiq" } +board_artiq = { path = "../libboard_artiq", features = ["alloc"] } alloc_list = { path = "../liballoc_list" } riscv = { version = "0.6.0", features = ["inline-asm"] } proto_artiq = { path = "../libproto_artiq", features = ["log", "alloc"] } diff --git a/artiq/firmware/satman/main.rs b/artiq/firmware/satman/main.rs index cd101d28c..553b2b3fd 100644 --- a/artiq/firmware/satman/main.rs +++ b/artiq/firmware/satman/main.rs @@ -17,6 +17,7 @@ use board_artiq::si5324; use board_artiq::{spi, drtioaux}; use board_artiq::drtio_routing; use proto_artiq::drtioaux_proto::ANALYZER_MAX_SIZE; +use board_artiq::drtio_eem; use riscv::register::{mcause, mepc, mtval}; use dma::Manager as DmaManager; use analyzer::Analyzer; @@ -541,13 +542,16 @@ pub extern fn main() -> i32 { io_expander.service().unwrap(); } - #[cfg(not(soc_platform = "efc"))] + #[cfg(not(has_drtio_eem))] unsafe { csr::drtio_transceiver::txenable_write(0xffffffffu32 as _); } init_rtio_crg(); + #[cfg(has_drtio_eem)] + drtio_eem::init(); + #[cfg(has_drtio_routing)] let mut repeaters = [repeater::Repeater::default(); csr::DRTIOREP.len()]; #[cfg(not(has_drtio_routing))] diff --git a/artiq/gateware/drtio/transceiver/eem_serdes.py b/artiq/gateware/drtio/transceiver/eem_serdes.py new file mode 100644 index 000000000..60fadc065 --- /dev/null +++ b/artiq/gateware/drtio/transceiver/eem_serdes.py @@ -0,0 +1,481 @@ +from migen import * +from misoc.interconnect.csr import * +from misoc.cores.code_8b10b import SingleEncoder, Decoder +from artiq.gateware.drtio.core import TransceiverInterface, ChannelInterface + + +class RXSerdes(Module): + def __init__(self, i_pads): + self.rxdata = [ Signal(10) for _ in range(4) ] + self.ld = [ Signal() for _ in range(4) ] + self.cnt_in = [ Signal(5) for _ in range(4) ] + self.cnt_out = [ Signal(5) for _ in range(4) ] + self.bitslip = [ Signal() for _ in range(4) ] + + ser_in_no_dly = [ Signal() for _ in range(4) ] + ser_in = [ Signal() for _ in range(4) ] + shifts = [ Signal(2) for _ in range(4) ] + + for i in range(4): + self.specials += [ + # Master deserializer + Instance("ISERDESE2", + p_DATA_RATE="DDR", + p_DATA_WIDTH=10, + p_INTERFACE_TYPE="NETWORKING", + p_NUM_CE=1, + p_SERDES_MODE="MASTER", + p_IOBDELAY="IFD", + o_Q1=self.rxdata[i][9], + o_Q2=self.rxdata[i][8], + o_Q3=self.rxdata[i][7], + o_Q4=self.rxdata[i][6], + o_Q5=self.rxdata[i][5], + o_Q6=self.rxdata[i][4], + o_Q7=self.rxdata[i][3], + o_Q8=self.rxdata[i][2], + o_SHIFTOUT1=shifts[i][0], + o_SHIFTOUT2=shifts[i][1], + i_DDLY=ser_in[i], + i_BITSLIP=self.bitslip[i], + i_CLK=ClockSignal("sys5x"), + i_CLKB=~ClockSignal("sys5x"), + i_CE1=1, + i_RST=ResetSignal(), + i_CLKDIV=ClockSignal()), + + # Slave deserializer + Instance("ISERDESE2", + p_DATA_RATE="DDR", + p_DATA_WIDTH=10, + p_INTERFACE_TYPE="NETWORKING", + p_NUM_CE=1, + p_SERDES_MODE="SLAVE", + p_IOBDELAY="IFD", + o_Q3=self.rxdata[i][1], + o_Q4=self.rxdata[i][0], + i_BITSLIP=self.bitslip[i], + i_CLK=ClockSignal("sys5x"), + i_CLKB=~ClockSignal("sys5x"), + i_CE1=1, + i_RST=ResetSignal(), + i_CLKDIV=ClockSignal(), + i_SHIFTIN1=shifts[i][0], + i_SHIFTIN2=shifts[i][1]), + + # Tunable delay + # IDELAYCTRL is with the clocking + Instance("IDELAYE2", + p_DELAY_SRC="IDATAIN", + p_SIGNAL_PATTERN="DATA", + p_CINVCTRL_SEL="FALSE", + p_HIGH_PERFORMANCE_MODE="TRUE", + # REFCLK refers to the clock source of IDELAYCTRL + p_REFCLK_FREQUENCY=200.0, + p_PIPE_SEL="FALSE", + p_IDELAY_TYPE="VAR_LOAD", + p_IDELAY_VALUE=0, + + i_C=ClockSignal(), + i_LD=self.ld[i], + i_CE=0, + i_LDPIPEEN=0, + i_INC=1, # Always increment + + # Set the optimal delay tap via the aligner + i_CNTVALUEIN=self.cnt_in[i], + # Allow the aligner to check the tap value + o_CNTVALUEOUT=self.cnt_out[i], + + i_IDATAIN=ser_in_no_dly[i], + o_DATAOUT=ser_in[i] + ), + + # IOB + Instance("IBUFDS", + p_DIFF_TERM="TRUE", + i_I=i_pads.p[i], + i_IB=i_pads.n[i], + o_O=ser_in_no_dly[i], + ) + ] + + +class TXSerdes(Module): + def __init__(self, o_pads): + self.txdata = [ Signal(5) for _ in range(4) ] + ser_out = [ Signal() for _ in range(4) ] + t_out = [ Signal() for _ in range(4) ] + + for i in range(4): + self.specials += [ + # Serializer + Instance("OSERDESE2", + p_DATA_RATE_OQ="SDR", p_DATA_RATE_TQ="BUF", + p_DATA_WIDTH=5, p_TRISTATE_WIDTH=1, + p_INIT_OQ=0b00000, + o_OQ=ser_out[i], + o_TQ=t_out[i], + i_RST=ResetSignal(), + i_CLK=ClockSignal("sys5x"), + i_CLKDIV=ClockSignal(), + i_D1=self.txdata[i][0], + i_D2=self.txdata[i][1], + i_D3=self.txdata[i][2], + i_D4=self.txdata[i][3], + i_D5=self.txdata[i][4], + i_TCE=1, i_OCE=1, + i_T1=0 + ), + + # IOB + Instance("OBUFTDS", + i_I=ser_out[i], + o_O=o_pads.p[i], + o_OB=o_pads.n[i], + i_T=t_out[i], + ) + ] + + +# This module owns 2 8b10b encoders, each encoder route codewords to 2 lanes, +# through time multiplexing. The scheduler releases 2 bytes every clock cycle, +# and the encoders each encode 1 byte. +# +# Since each lane only transmits 5 bits per sysclk cycle, the encoder selects +# a lane to first transmit the least significant word (LSW, 5 bits), and send +# the rest in the next cycle using the same lane. It takes advantage of the +# arrival sequence of bytes from the scrambler to achieve the transmission +# pattern shown in the MultiDecoder module. +class MultiEncoder(Module): + def __init__(self): + # Keep the link layer interface identical to standard encoders + self.d = [ Signal(8) for _ in range(2) ] + self.k = [ Signal() for _ in range(2) ] + + # Output interface + self.output = [ [ Signal(5) for _ in range(2) ] for _ in range(2) ] + + # Clock enable signal + # Alternate between sending encoded character to EEM 0/2 and EEM 1/3 + # every cycle + self.clk_div2 = Signal() + + # Intermediate registers for output and disparity + # More significant bits are buffered due to channel geometry + # Disparity bit is delayed. The same encoder is shared by 2 SERDES + output_bufs = [ Signal(5) for _ in range(2) ] + disp_bufs = [ Signal() for _ in range(2) ] + + encoders = [ SingleEncoder() for _ in range(2) ] + self.submodules += encoders + + # Encoded characters are routed to the EEM pairs: + # The first character goes through EEM 0/2 + # The second character goes through EEM 1/3, and repeat... + # Lower order bits go first, so higher order bits are buffered and + # transmitted in the next cycle. + for d, k, output, output_buf, disp_buf, encoder in \ + zip(self.d, self.k, self.output, output_bufs, disp_bufs, encoders): + self.comb += [ + encoder.d.eq(d), + encoder.k.eq(k), + + If(self.clk_div2, + output[0].eq(encoder.output[0:5]), + output[1].eq(output_buf), + ).Else( + output[0].eq(output_buf), + output[1].eq(encoder.output[0:5]), + ), + ] + # Handle intermediate registers + self.sync += [ + disp_buf.eq(encoder.disp_out), + encoder.disp_in.eq(disp_buf), + output_buf.eq(encoder.output[5:10]), + ] + + +# Owns 2 8b10b decoders, each decodes data from lane 0/1 and lane 2/3class +# respectively. The decoders are time multiplexed among the 2 lanes, and +# each decoder decodes exactly 1 lane per sysclk cycle. +# +# The transmitter could send the following data pattern over the 4 lanes. +# Capital letters denote the most significant word (MSW); The lowercase denote +# the least significant word (LSW) of the same 8b10b character. +# +# Cycle \ Lane 0 1 2 3 +# 0 a Y b Z +# 1 A c B d +# 2 a' C b' D +# 3 A' c' B' d' +# +# Lane 0/2 and lane 1/3 transmit word of different significance by design (see +# MultiEncoder). +# +# This module buffers the LSW, and immediately send the whole 8b10b character +# to the coresponding decoder once the MSW is also received. +class MultiDecoder(Module): + def __init__(self): + self.raw_input = [ Signal(5) for _ in range(2) ] + self.d = Signal(8) + self.k = Signal() + + # Clock enable signal + # Alternate between decoding encoded character from EEM 0/2 and + # EEM 1/3 every cycle + self.clk_div2 = Signal() + + # Extended bitslip mechanism. ISERDESE2 bitslip can only adjust bit + # position by 5 bits (1 cycle). However, an encoded character takes 2 + # cycles to transmit/receive. The module needs to correctly reassemble + # the 8b10b character. This is useful received waveform is the 1-cycle + # delayed version of the above waveform. The same scheme would + # incorrectly buffer words and create wrong symbols. + # + # Hence, wordslip put LSW as MSW and vice versa, effectively injects + # an additional 5 bit positions worth of bitslips. + self.wordslip = Signal() + + # Intermediate register for input + buffer = Signal(5) + + self.submodules.decoder = Decoder() + + # The decoder does the following actions: + # - Process received characters from EEM 0/2 + # - Same, but from EEM 1/3 + # + # Wordslipping is equivalent to swapping task between clock cycles. + # (i.e. Swap processing target. Instead of processing EEM 0/2, process + # EEM 1/3, and vice versa on the next cycle.) This effectively shifts + # the processing time of any encoded character by 1 clock cycle (5 + # bitslip equivalent without considering oversampling, 10 otherwise). + self.sync += [ + If(self.clk_div2 ^ self.wordslip, + buffer.eq(self.raw_input[1]) + ).Else( + buffer.eq(self.raw_input[0]) + ) + ] + + self.comb += [ + If(self.clk_div2 ^ self.wordslip, + self.decoder.input.eq(Cat(buffer, self.raw_input[0])) + ).Else( + self.decoder.input.eq(Cat(buffer, self.raw_input[1])) + ) + ] + + self.comb += [ + self.d.eq(self.decoder.d), + self.k.eq(self.decoder.k), + ] + + +class BangBangPhaseDetector(Module): + def __init__(self): + self.s = Signal(3) + + self.high = Signal() + self.low = Signal() + + self.comb += If(~self.s[0] & self.s[2], + self.high.eq(self.s[1]), + self.low.eq(~self.s[1]), + ).Else( + self.high.eq(0), + self.low.eq(0), + ) + + +class PhaseErrorCounter(Module, AutoCSR): + def __init__(self): + self.high_count = CSRStatus(18) + self.low_count = CSRStatus(18) + + # Odd indices are always oversampled bits + self.rxdata = Signal(10) + + # Measure setup/hold timing, count phase error in the following + self.submodules.detector = BangBangPhaseDetector() + self.comb += self.detector.s.eq(self.rxdata[:3]) + + self.reset = CSR() + self.enable = CSRStorage() + + self.overflow = CSRStatus() + high_carry = Signal() + low_carry = Signal() + + self.sync += [ + If(self.reset.re, + self.high_count.status.eq(0), + self.low_count.status.eq(0), + high_carry.eq(0), + low_carry.eq(0), + self.overflow.status.eq(0), + ).Elif(self.enable.storage, + Cat(self.high_count.status, high_carry).eq( + self.high_count.status + self.detector.high), + Cat(self.low_count.status, low_carry).eq( + self.low_count.status + self.detector.low), + If(high_carry | low_carry, self.overflow.status.eq(1)), + ) + ] + + +class SerdesSingle(Module): + def __init__(self, i_pads, o_pads): + # Serdes modules + self.submodules.rx_serdes = RXSerdes(i_pads) + self.submodules.tx_serdes = TXSerdes(o_pads) + + self.lane_sel = Signal(2) + + self.bitslip = Signal() + + for i in range(4): + self.comb += self.rx_serdes.bitslip[i].eq(self.bitslip) + + self.dly_cnt_in = Signal(5) + self.dly_ld = Signal() + + for i in range(4): + self.comb += [ + self.rx_serdes.cnt_in[i].eq(self.dly_cnt_in), + self.rx_serdes.ld[i].eq((self.lane_sel == i) & self.dly_ld), + ] + + self.dly_cnt_out = Signal(5) + + self.comb += Case(self.lane_sel, { + idx: self.dly_cnt_out.eq(self.rx_serdes.cnt_out[idx]) for idx in range(4) + }) + + self.wordslip = Signal() + + # Encoder/Decoder interfaces + self.submodules.encoder = MultiEncoder() + self.submodules.decoders = decoders = Array(MultiDecoder() for _ in range(2)) + + self.comb += [ + decoders[0].wordslip.eq(self.wordslip), + decoders[1].wordslip.eq(self.wordslip), + ] + + # Route encoded symbols to TXSerdes, decoded symbols from RXSerdes + for i in range(4): + self.comb += [ + self.tx_serdes.txdata[i].eq(self.encoder.output[i//2][i%2]), + decoders[i//2].raw_input[i%2].eq(self.rx_serdes.rxdata[i][0::2]), + ] + + self.clk_div2 = Signal() + self.comb += [ + self.encoder.clk_div2.eq(self.clk_div2), + self.decoders[0].clk_div2.eq(self.clk_div2), + self.decoders[1].clk_div2.eq(self.clk_div2), + ] + + # Monitor lane 0 decoder output for bitslip alignment + self.comma_align_reset = Signal() + self.comma = Signal() + + self.sync += If(self.comma_align_reset, + self.comma.eq(0), + ).Elif(~self.comma, + self.comma.eq( + ((decoders[0].d == 0x3C) | (decoders[0].d == 0xBC)) + & decoders[0].k)) + + +class EEMSerdes(Module, TransceiverInterface, AutoCSR): + def __init__(self, platform, data_pads): + self.rx_ready = CSRStorage() + + self.transceiver_sel = CSRStorage(max(1, log2_int(len(data_pads)))) + self.lane_sel = CSRStorage(2) + + self.bitslip = CSR() + + self.dly_cnt_in = CSRStorage(5) + self.dly_ld = CSR() + self.dly_cnt_out = CSRStatus(5) + + # Slide a word back/forward by 1 cycle, shared by all lanes of the + # same transceiver. This is to determine if this cycle should decode + # lane 0/2 or lane 1/3. See MultiEncoder/MultiDecoder for the full + # scheme & timing. + self.wordslip = CSRStorage() + + # Monitor lane 0 decoder output for bitslip alignment + self.comma_align_reset = CSR() + self.comma = CSRStatus() + + clk_div2 = Signal() + self.sync += clk_div2.eq(~clk_div2) + + channel_interfaces = [] + serdes_list = [] + for i_pads, o_pads in data_pads: + serdes = SerdesSingle(i_pads, o_pads) + self.comb += serdes.clk_div2.eq(clk_div2) + serdes_list.append(serdes) + + chan_if = ChannelInterface(serdes.encoder, serdes.decoders) + self.comb += chan_if.rx_ready.eq(self.rx_ready.storage) + channel_interfaces.append(chan_if) + + # Route CSR signals using transceiver_sel + self.comb += Case(self.transceiver_sel.storage, { + trx_no: [ + serdes.bitslip.eq(self.bitslip.re), + serdes.dly_ld.eq(self.dly_ld.re), + + self.dly_cnt_out.status.eq(serdes.dly_cnt_out), + self.comma.status.eq(serdes.comma), + ] for trx_no, serdes in enumerate(serdes_list) + }) + + # Wordslip needs to be latched. It needs to hold when calibrating + # other transceivers and/or after calibration. + self.sync += If(self.wordslip.re, + Case(self.transceiver_sel.storage, { + trx_no: [ + serdes.wordslip.eq(self.wordslip.storage) + ] for trx_no, serdes in enumerate(serdes_list) + }) + ) + + for serdes in serdes_list: + self.comb += [ + # Delay counter write only comes into effect after dly_ld + # So, just MUX dly_ld instead. + serdes.dly_cnt_in.eq(self.dly_cnt_in.storage), + + # Comma align reset & lane selection can be broadcasted + # without MUXing. Transceivers are aligned one-by-one + serdes.lane_sel.eq(self.lane_sel.storage), + serdes.comma_align_reset.eq(self.comma_align_reset.re), + ] + + # Setup/hold timing calibration module + self.submodules.counter = PhaseErrorCounter() + self.comb += Case(self.transceiver_sel.storage, { + trx_no: Case(self.lane_sel.storage, { + lane_idx: self.counter.rxdata.eq(serdes.rx_serdes.rxdata[lane_idx]) + for lane_idx in range(4) + }) for trx_no, serdes in enumerate(serdes_list) + }) + + self.submodules += serdes_list + + TransceiverInterface.__init__(self, channel_interfaces) + + for i in range(len(serdes_list)): + self.comb += [ + getattr(self, "cd_rtio_rx" + str(i)).clk.eq(ClockSignal()), + getattr(self, "cd_rtio_rx" + str(i)).rst.eq(ResetSignal()) + ] diff --git a/artiq/gateware/eem.py b/artiq/gateware/eem.py index bcb3db7be..8b611e803 100644 --- a/artiq/gateware/eem.py +++ b/artiq/gateware/eem.py @@ -757,3 +757,31 @@ class HVAmp(_EEM): phy = ttl_out_cls(pads.p, pads.n) target.submodules += phy target.rtio_channels.append(rtio.Channel.from_phy(phy)) + + +class EFC(_EEM): + @staticmethod + def io(eem, iostandard=default_iostandard): + # Master: Pair 0~3 data IN, 4~7 OUT + data_in = ("efc{}_drtio_rx".format(eem), 0, + Subsignal("p", Pins("{} {} {} {}".format(*[ + _eem_pin(eem, i, "p") for i in range(4) + ]))), + Subsignal("n", Pins("{} {} {} {}".format(*[ + _eem_pin(eem, i, "n") for i in range(4) + ]))), + iostandard(eem), + Misc("DIFF_TERM=TRUE"), + ) + + data_out = ("efc{}_drtio_tx".format(eem), 0, + Subsignal("p", Pins("{} {} {} {}".format(*[ + _eem_pin(eem, i, "p") for i in range(4, 8) + ]))), + Subsignal("n", Pins("{} {} {} {}".format(*[ + _eem_pin(eem, i, "n") for i in range(4, 8) + ]))), + iostandard(eem), + ) + + return [data_in, data_out] From 68dd0e029fcd3b53834e32bb532e959fbd2d88de Mon Sep 17 00:00:00 2001 From: occheung Date: Wed, 9 Aug 2023 14:24:41 -0700 Subject: [PATCH 31/66] targets: add efc target --- artiq/gateware/targets/efc.py | 159 ++++++++++++++++++++++++++++++++++ 1 file changed, 159 insertions(+) create mode 100644 artiq/gateware/targets/efc.py diff --git a/artiq/gateware/targets/efc.py b/artiq/gateware/targets/efc.py new file mode 100644 index 000000000..3297e525f --- /dev/null +++ b/artiq/gateware/targets/efc.py @@ -0,0 +1,159 @@ +#!/usr/bin/env python3 + +import argparse + +from migen import * +from migen.build.generic_platform import * + +from misoc.cores import gpio +from misoc.targets.efc import BaseSoC +from misoc.integration.builder import builder_args, builder_argdict + +from artiq.gateware.amp import AMPSoC +from artiq.gateware import rtio +from artiq.gateware.rtio.xilinx_clocking import fix_serdes_timing_path +from artiq.gateware.drtio.transceiver import eem_serdes +from artiq.gateware.drtio.rx_synchronizer import XilinxRXSynchronizer +from artiq.gateware.drtio import * +from artiq.build_soc import * + + +class Satellite(BaseSoC, AMPSoC): + mem_map = { + "rtio": 0x20000000, + "drtioaux": 0x50000000, + "mailbox": 0x70000000 + } + mem_map.update(BaseSoC.mem_map) + + def __init__(self, gateware_identifier_str=None, **kwargs): + BaseSoC.__init__(self, + cpu_type="vexriscv", + cpu_bus_width=64, + sdram_controller_type="minicon", + l2_size=128*1024, + clk_freq=125e6, + **kwargs) + AMPSoC.__init__(self) + add_identifier(self, gateware_identifier_str=gateware_identifier_str) + + platform = self.platform + + drtio_eem_io = [ + ("drtio_tx", 0, + Subsignal("p", Pins("eem0:d0_cc_p eem0:d1_p eem0:d2_p eem0:d3_p")), + Subsignal("n", Pins("eem0:d0_cc_n eem0:d1_n eem0:d2_n eem0:d3_n")), + IOStandard("LVDS_25"), + ), + ("drtio_rx", 0, + Subsignal("p", Pins("eem0:d4_p eem0:d5_p eem0:d6_p eem0:d7_p")), + Subsignal("n", Pins("eem0:d4_n eem0:d5_n eem0:d6_n eem0:d7_n")), + IOStandard("LVDS_25"), Misc("DIFF_TERM=TRUE"), + ), + ] + + platform.add_extension(drtio_eem_io) + data_pads = [ + (platform.request("drtio_rx"), platform.request("drtio_tx")) + ] + + # Disable SERVMOD, hardwire it to ground to enable EEM 0 + servmod = self.platform.request("servmod") + self.comb += servmod.eq(0) + + self.submodules.eem_transceiver = eem_serdes.EEMSerdes(self.platform, data_pads) + self.csr_devices.append("eem_transceiver") + self.config["HAS_DRTIO_EEM"] = None + self.config["EEM_TRANSCEIVERS"] = 1 + + self.submodules.rtio_tsc = rtio.TSC(glbl_fine_ts_width=3) + + cdr = ClockDomainsRenamer({"rtio_rx": "rtio_rx0"}) + self.submodules.rx_synchronizer = cdr(XilinxRXSynchronizer()) + core = cdr(DRTIOSatellite( + self.rtio_tsc, self.eem_transceiver.channels[0], + self.rx_synchronizer)) + self.submodules.drtiosat = core + self.csr_devices.append("drtiosat") + + self.submodules.drtioaux0 = cdr(DRTIOAuxController( + core.link_layer, self.cpu_dw)) + self.csr_devices.append("drtioaux0") + + memory_address = self.mem_map["drtioaux"] + self.add_wb_slave(memory_address, 0x800, self.drtioaux0.bus) + self.add_memory_region("drtioaux0_mem", memory_address | self.shadow_base, 0x800) + + self.config["HAS_DRTIO"] = None + self.add_csr_group("drtioaux", ["drtioaux0"]) + self.add_memory_group("drtioaux_mem", ["drtioaux0_mem"]) + + i2c = self.platform.request("fpga_i2c") + self.submodules.i2c = gpio.GPIOTristate([i2c.scl, i2c.sda]) + self.csr_devices.append("i2c") + self.config["I2C_BUS_COUNT"] = 1 + + # Enable I2C + i2c_reset = self.platform.request("i2c_mux_rst_n") + self.comb += i2c_reset.eq(1) + + fix_serdes_timing_path(platform) + + self.config["DRTIO_ROLE"] = "satellite" + self.config["RTIO_FREQUENCY"] = "125.0" + + self.rtio_channels = [] + + self.config["HAS_RTIO_LOG"] = None + self.config["RTIO_LOG_CHANNEL"] = len(self.rtio_channels) + self.rtio_channels.append(rtio.LogChannel()) + + self.add_rtio(self.rtio_channels) + + def add_rtio(self, rtio_channels, sed_lanes=8): + # Only add MonInj core if there is anything to monitor + if any([len(c.probes) for c in rtio_channels]): + self.submodules.rtio_moninj = rtio.MonInj(rtio_channels) + self.csr_devices.append("rtio_moninj") + + # satellite (master-controlled) RTIO + self.submodules.local_io = SyncRTIO(self.rtio_tsc, rtio_channels, lane_count=sed_lanes) + self.comb += self.drtiosat.async_errors.eq(self.local_io.async_errors) + + # subkernel RTIO + self.submodules.rtio = rtio.KernelInitiator(self.rtio_tsc) + self.register_kernel_cpu_csrdevice("rtio") + + self.submodules.rtio_dma = rtio.DMA(self.get_native_sdram_if(), self.cpu_dw) + self.csr_devices.append("rtio_dma") + self.submodules.cri_con = rtio.CRIInterconnectShared( + [self.drtiosat.cri, self.rtio_dma.cri], + [self.local_io.cri], + enable_routing=True) + self.csr_devices.append("cri_con") + self.submodules.routing_table = rtio.RoutingTableAccess(self.cri_con) + self.csr_devices.append("routing_table") + + self.submodules.rtio_analyzer = rtio.Analyzer(self.rtio_tsc, self.local_io.cri, + self.get_native_sdram_if(), cpu_dw=self.cpu_dw) + self.csr_devices.append("rtio_analyzer") + + +def main(): + parser = argparse.ArgumentParser( + description="ARTIQ device binary builder for EEM FMC Carrier systems") + builder_args(parser) + parser.set_defaults(output_dir="artiq_efc") + parser.add_argument("--gateware-identifier-str", default=None, + help="Override ROM identifier") + args = parser.parse_args() + + argdict = dict() + argdict["gateware_identifier_str"] = args.gateware_identifier_str + + soc = Satellite(**argdict) + build_artiq_soc(soc, builder_argdict(args)) + + +if __name__ == "__main__": + main() From e8818c812c84ad232e5e899fd43df97138d1dea4 Mon Sep 17 00:00:00 2001 From: mwojcik Date: Tue, 22 Aug 2023 16:27:37 +0800 Subject: [PATCH 32/66] satman: fix non-eem satellites failing to build --- artiq/firmware/satman/main.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/artiq/firmware/satman/main.rs b/artiq/firmware/satman/main.rs index 553b2b3fd..ffe3491d2 100644 --- a/artiq/firmware/satman/main.rs +++ b/artiq/firmware/satman/main.rs @@ -17,6 +17,7 @@ use board_artiq::si5324; use board_artiq::{spi, drtioaux}; use board_artiq::drtio_routing; use proto_artiq::drtioaux_proto::ANALYZER_MAX_SIZE; +#[cfg(has_drtio_eem)] use board_artiq::drtio_eem; use riscv::register::{mcause, mepc, mtval}; use dma::Manager as DmaManager; From b7bac8c9d8efeb75881ccfa91fe56123209fec68 Mon Sep 17 00:00:00 2001 From: linuswck Date: Mon, 21 Aug 2023 11:01:34 +0800 Subject: [PATCH 33/66] EFC: Add SPI Gateware for Shuttler DAC - Verified by a functional test reading back the rev register --- artiq/gateware/targets/efc.py | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/artiq/gateware/targets/efc.py b/artiq/gateware/targets/efc.py index 3297e525f..26d034a7d 100644 --- a/artiq/gateware/targets/efc.py +++ b/artiq/gateware/targets/efc.py @@ -5,7 +5,7 @@ import argparse from migen import * from migen.build.generic_platform import * -from misoc.cores import gpio +from misoc.cores import gpio, spi2 from misoc.targets.efc import BaseSoC from misoc.integration.builder import builder_args, builder_argdict @@ -102,6 +102,24 @@ class Satellite(BaseSoC, AMPSoC): self.config["DRTIO_ROLE"] = "satellite" self.config["RTIO_FREQUENCY"] = "125.0" + shuttler_io = [ + ('dac_spi', 0, + Subsignal('clk', Pins('fmc0:HB16_N')), + Subsignal('mosi', Pins('fmc0:HB06_CC_N')), + Subsignal('cs_n', Pins('fmc0:LA31_N fmc0:LA31_P fmc0:HB19_P fmc0:LA30_P')), + IOStandard("LVCMOS18")), + ('dac_rst', 0, Pins('fmc0:HB16_P'), IOStandard("LVCMOS18")), + ] + + platform.add_extension(shuttler_io) + + self.submodules.converter_spi = spi2.SPIMaster(spi2.SPIInterface(self.platform.request("dac_spi", 0))) + self.csr_devices.append("converter_spi") + self.config["HAS_CONVERTER_SPI"] = None + + dac_rst = self.platform.request('dac_rst') + self.comb += dac_rst.eq(0) + self.rtio_channels = [] self.config["HAS_RTIO_LOG"] = None From cd22e42cb4802d9fac9833ab747110340fc39644 Mon Sep 17 00:00:00 2001 From: linuswck Date: Wed, 23 Aug 2023 06:21:14 +0000 Subject: [PATCH 34/66] efc: add DRTIO virtual LEDs - EFC Gateware: Add virtual_leds to rtio - EFC Firmware: io_expander is kept being serviced to update virtual_leds after init --- artiq/firmware/libboard_misoc/io_expander.rs | 1 - artiq/firmware/satman/main.rs | 12 +++++++++++- artiq/gateware/targets/efc.py | 6 ++++++ 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/artiq/firmware/libboard_misoc/io_expander.rs b/artiq/firmware/libboard_misoc/io_expander.rs index 7009e0476..e9f0a48b4 100644 --- a/artiq/firmware/libboard_misoc/io_expander.rs +++ b/artiq/firmware/libboard_misoc/io_expander.rs @@ -83,7 +83,6 @@ impl IoExpander { #[cfg(soc_platform = "efc")] pub fn new() -> Result { - // TODO: Put Virtual User LEDs L0 L1 in gateware const VIRTUAL_LED_MAPPING: [(u8, u8, u8); 2] = [(0, 0, 5), (1, 0, 6)]; let mut io_expander = IoExpander { diff --git a/artiq/firmware/satman/main.rs b/artiq/firmware/satman/main.rs index ffe3491d2..b1553000a 100644 --- a/artiq/firmware/satman/main.rs +++ b/artiq/firmware/satman/main.rs @@ -531,9 +531,15 @@ pub extern fn main() -> i32 { sysclk_setup(); + #[cfg(soc_platform = "efc")] + let mut io_expander; #[cfg(soc_platform = "efc")] { - let mut io_expander = board_misoc::io_expander::IoExpander::new().unwrap(); + io_expander = board_misoc::io_expander::IoExpander::new().unwrap(); + + // Enable LEDs + io_expander.set_oe(0, 1 << 5 | 1 << 6 | 1 << 7).unwrap(); + // Enable VADJ and P3V3_FMC io_expander.set_oe(1, 1 << 0 | 1 << 1).unwrap(); @@ -576,6 +582,8 @@ pub extern fn main() -> i32 { io_expander0.service().expect("I2C I/O expander #0 service failed"); io_expander1.service().expect("I2C I/O expander #1 service failed"); } + #[cfg(soc_platform = "efc")] + io_expander.service().expect("I2C I/O expander service failed"); hardware_tick(&mut hardware_tick_ts); } @@ -609,6 +617,8 @@ pub extern fn main() -> i32 { io_expander0.service().expect("I2C I/O expander #0 service failed"); io_expander1.service().expect("I2C I/O expander #1 service failed"); } + #[cfg(soc_platform = "efc")] + io_expander.service().expect("I2C I/O expander service failed"); hardware_tick(&mut hardware_tick_ts); if drtiosat_tsc_loaded() { info!("TSC loaded from uplink"); diff --git a/artiq/gateware/targets/efc.py b/artiq/gateware/targets/efc.py index 26d034a7d..6bf123442 100644 --- a/artiq/gateware/targets/efc.py +++ b/artiq/gateware/targets/efc.py @@ -12,6 +12,7 @@ from misoc.integration.builder import builder_args, builder_argdict from artiq.gateware.amp import AMPSoC from artiq.gateware import rtio from artiq.gateware.rtio.xilinx_clocking import fix_serdes_timing_path +from artiq.gateware.rtio.phy import ttl_simple from artiq.gateware.drtio.transceiver import eem_serdes from artiq.gateware.drtio.rx_synchronizer import XilinxRXSynchronizer from artiq.gateware.drtio import * @@ -122,6 +123,11 @@ class Satellite(BaseSoC, AMPSoC): self.rtio_channels = [] + for i in range(2): + phy = ttl_simple.Output(self.virtual_leds.get(i)) + self.submodules += phy + self.rtio_channels.append(rtio.Channel.from_phy(phy)) + self.config["HAS_RTIO_LOG"] = None self.config["RTIO_LOG_CHANNEL"] = len(self.rtio_channels) self.rtio_channels.append(rtio.LogChannel()) From 9bee4b9697e899718fa45eb00d6a1b17aab4cf73 Mon Sep 17 00:00:00 2001 From: Sebastien Bourdeauducq Date: Fri, 25 Aug 2023 11:13:33 +0800 Subject: [PATCH 35/66] flake: update dependencies --- flake.lock | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/flake.lock b/flake.lock index 43a135f35..40693dd4b 100644 --- a/flake.lock +++ b/flake.lock @@ -57,11 +57,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1691328192, - "narHash": "sha256-w59N1zyDQ7SupfMJLFvtms/SIVbdryqlw5AS4+DiH+Y=", + "lastModified": 1692794066, + "narHash": "sha256-H0aG8r16dj0x/Wz6wQhQxc9V7AsObOiHPaKxQgH6Y08=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "61676e4dcfeeb058f255294bcb08ea7f3bc3ce56", + "rev": "fc944919f743bb22379dddf18dcb72db6cff84aa", "type": "github" }, "original": { @@ -121,11 +121,11 @@ "src-misoc": { "flake": false, "locked": { - "lastModified": 1691370054, - "narHash": "sha256-ZI+H7gcQzx//TF5Jaf5iejd8PtPwiDAQ4redmb2WsDA=", + "lastModified": 1692355160, + "narHash": "sha256-d6eUHeTtedv9TCMlP8pR/Cgds5FAaPJyYIyF74exd8o=", "ref": "refs/heads/master", - "rev": "1b871635d768d6c5efbab846a1e07da12877aed9", - "revCount": 2443, + "rev": "2f6201a01a9eedfbdc4e7dbde63a83e27013aecb", + "revCount": 2444, "submodules": true, "type": "git", "url": "https://github.com/m-labs/misoc.git" From 5d38db19d0f6c81cae1c98f30015352efadb7eaa Mon Sep 17 00:00:00 2001 From: Sebastien Bourdeauducq Date: Fri, 25 Aug 2023 11:32:28 +0800 Subject: [PATCH 36/66] drtio-eem: remove unnecessary rtio_rx clock domain --- artiq/gateware/drtio/core.py | 9 +++++---- artiq/gateware/drtio/rx_synchronizer.py | 6 ++++++ artiq/gateware/drtio/transceiver/eem_serdes.py | 8 +------- artiq/gateware/targets/efc.py | 7 +++---- 4 files changed, 15 insertions(+), 15 deletions(-) diff --git a/artiq/gateware/drtio/core.py b/artiq/gateware/drtio/core.py index 1af7fa7b4..b2a108237 100644 --- a/artiq/gateware/drtio/core.py +++ b/artiq/gateware/drtio/core.py @@ -27,12 +27,13 @@ class ChannelInterface: class TransceiverInterface(AutoCSR): - def __init__(self, channel_interfaces): + def __init__(self, channel_interfaces, *, async_rx=True): self.stable_clkin = CSRStorage() self.txenable = CSRStorage(len(channel_interfaces)) - for i in range(len(channel_interfaces)): - name = "rtio_rx" + str(i) - setattr(self.clock_domains, "cd_"+name, ClockDomain(name=name)) + if async_rx: + for i in range(len(channel_interfaces)): + name = "rtio_rx" + str(i) + setattr(self.clock_domains, "cd_"+name, ClockDomain(name=name)) self.channels = channel_interfaces diff --git a/artiq/gateware/drtio/rx_synchronizer.py b/artiq/gateware/drtio/rx_synchronizer.py index 055d1d70b..2630aaebe 100644 --- a/artiq/gateware/drtio/rx_synchronizer.py +++ b/artiq/gateware/drtio/rx_synchronizer.py @@ -2,6 +2,12 @@ from migen import * from migen.genlib.cdc import ElasticBuffer +class NoRXSynchronizer: + """To be used when RX is already synchronous (e.g. IOSERDES based PHY).""" + def resync(self, signal): + return signal + + class GenericRXSynchronizer(Module): """Simple RX synchronizer based on the portable Migen elastic buffer. diff --git a/artiq/gateware/drtio/transceiver/eem_serdes.py b/artiq/gateware/drtio/transceiver/eem_serdes.py index 60fadc065..73b373f80 100644 --- a/artiq/gateware/drtio/transceiver/eem_serdes.py +++ b/artiq/gateware/drtio/transceiver/eem_serdes.py @@ -472,10 +472,4 @@ class EEMSerdes(Module, TransceiverInterface, AutoCSR): self.submodules += serdes_list - TransceiverInterface.__init__(self, channel_interfaces) - - for i in range(len(serdes_list)): - self.comb += [ - getattr(self, "cd_rtio_rx" + str(i)).clk.eq(ClockSignal()), - getattr(self, "cd_rtio_rx" + str(i)).rst.eq(ResetSignal()) - ] + TransceiverInterface.__init__(self, channel_interfaces, async_rx=False) diff --git a/artiq/gateware/targets/efc.py b/artiq/gateware/targets/efc.py index 6bf123442..1623b338e 100644 --- a/artiq/gateware/targets/efc.py +++ b/artiq/gateware/targets/efc.py @@ -14,7 +14,7 @@ from artiq.gateware import rtio from artiq.gateware.rtio.xilinx_clocking import fix_serdes_timing_path from artiq.gateware.rtio.phy import ttl_simple from artiq.gateware.drtio.transceiver import eem_serdes -from artiq.gateware.drtio.rx_synchronizer import XilinxRXSynchronizer +from artiq.gateware.drtio.rx_synchronizer import NoRXSynchronizer from artiq.gateware.drtio import * from artiq.build_soc import * @@ -69,11 +69,10 @@ class Satellite(BaseSoC, AMPSoC): self.submodules.rtio_tsc = rtio.TSC(glbl_fine_ts_width=3) - cdr = ClockDomainsRenamer({"rtio_rx": "rtio_rx0"}) - self.submodules.rx_synchronizer = cdr(XilinxRXSynchronizer()) + cdr = ClockDomainsRenamer({"rtio_rx": "sys"}) core = cdr(DRTIOSatellite( self.rtio_tsc, self.eem_transceiver.channels[0], - self.rx_synchronizer)) + NoRXSynchronizer())) self.submodules.drtiosat = core self.csr_devices.append("drtiosat") From dc97d3aee69361750280650505f2608c985fe1c6 Mon Sep 17 00:00:00 2001 From: linuswck Date: Fri, 25 Aug 2023 11:49:20 +0800 Subject: [PATCH 37/66] drtio-eem: CONFIG_EEM_TRANSCEIVERS -> CONFIG_EEM_DRTIO_COUNT --- artiq/firmware/libboard_artiq/drtio_eem.rs | 2 +- artiq/gateware/targets/efc.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/artiq/firmware/libboard_artiq/drtio_eem.rs b/artiq/firmware/libboard_artiq/drtio_eem.rs index 4434f8cb1..f1cd6afc8 100644 --- a/artiq/firmware/libboard_artiq/drtio_eem.rs +++ b/artiq/firmware/libboard_artiq/drtio_eem.rs @@ -188,7 +188,7 @@ unsafe fn align_comma() { } pub fn init() { - for trx_no in 0..csr::CONFIG_EEM_TRANSCEIVERS { + for trx_no in 0..csr::CONFIG_EEM_DRTIO_COUNT { unsafe { csr::eem_transceiver::transceiver_sel_write(trx_no as u8); } diff --git a/artiq/gateware/targets/efc.py b/artiq/gateware/targets/efc.py index 1623b338e..637844dc0 100644 --- a/artiq/gateware/targets/efc.py +++ b/artiq/gateware/targets/efc.py @@ -65,7 +65,7 @@ class Satellite(BaseSoC, AMPSoC): self.submodules.eem_transceiver = eem_serdes.EEMSerdes(self.platform, data_pads) self.csr_devices.append("eem_transceiver") self.config["HAS_DRTIO_EEM"] = None - self.config["EEM_TRANSCEIVERS"] = 1 + self.config["EEM_DRTIO_COUNT"] = 1 self.submodules.rtio_tsc = rtio.TSC(glbl_fine_ts_width=3) From 737ff79ae7b7bb7ff3248c6a83229eb257e5094c Mon Sep 17 00:00:00 2001 From: linuswck Date: Fri, 25 Aug 2023 12:01:04 +0800 Subject: [PATCH 38/66] eem: add efc --- .../coredevice/coredevice_generic.schema.json | 24 ++++++++++++++++++- artiq/gateware/eem.py | 15 +++++++++--- artiq/gateware/eem_7series.py | 10 ++++++++ 3 files changed, 45 insertions(+), 4 deletions(-) diff --git a/artiq/coredevice/coredevice_generic.schema.json b/artiq/coredevice/coredevice_generic.schema.json index 84f5f3505..9e1fb885b 100644 --- a/artiq/coredevice/coredevice_generic.schema.json +++ b/artiq/coredevice/coredevice_generic.schema.json @@ -142,7 +142,7 @@ "properties": { "type": { "type": "string", - "enum": ["dio", "dio_spi", "urukul", "novogorny", "sampler", "suservo", "zotino", "grabber", "mirny", "fastino", "phaser", "hvamp"] + "enum": ["dio", "dio_spi", "urukul", "novogorny", "sampler", "suservo", "zotino", "grabber", "mirny", "fastino", "phaser", "hvamp", "efc"] }, "board": { "type": "string" @@ -610,6 +610,28 @@ }, "required": ["ports"] } + },{ + "title": "EFC", + "if": { + "properties": { + "type": { + "const": "efc" + } + } + }, + "then": { + "properties": { + "ports": { + "type": "array", + "items": { + "type": "integer" + }, + "minItems": 1, + "maxItems": 2 + } + }, + "required": ["ports"] + } }] } } diff --git a/artiq/gateware/eem.py b/artiq/gateware/eem.py index 8b611e803..112cd880f 100644 --- a/artiq/gateware/eem.py +++ b/artiq/gateware/eem.py @@ -25,11 +25,15 @@ def default_iostandard(eem): class _EEM: @classmethod - def add_extension(cls, target, eem, *args, **kwargs): + def add_extension(cls, target, eem, *args, is_drtio_over_eem=False, **kwargs): name = cls.__name__ target.platform.add_extension(cls.io(eem, *args, **kwargs)) - print("{} (EEM{}) starting at RTIO channel 0x{:06x}" - .format(name, eem, len(target.rtio_channels))) + if is_drtio_over_eem: + print("{} (EEM{}) starting at DRTIO channel 0x{:06x}" + .format(name, eem, (len(target.drtio_transceiver.channels) + len(target.eem_drtio_channels) + 1) << 16)) + else: + print("{} (EEM{}) starting at RTIO channel 0x{:06x}" + .format(name, eem, len(target.rtio_channels))) class DIO(_EEM): @@ -785,3 +789,8 @@ class EFC(_EEM): ) return [data_in, data_out] + + @classmethod + def add_std(cls, target, eem, eem_aux, iostandard=default_iostandard): + cls.add_extension(target, eem, is_drtio_over_eem=True, iostandard=iostandard) + target.eem_drtio_channels.append((target.platform.request("efc{}_drtio_rx".format(eem), 0), target.platform.request("efc{}_drtio_tx".format(eem), 0))) diff --git a/artiq/gateware/eem_7series.py b/artiq/gateware/eem_7series.py index 84276ad79..8c7f16c21 100644 --- a/artiq/gateware/eem_7series.py +++ b/artiq/gateware/eem_7series.py @@ -133,6 +133,15 @@ def peripheral_hvamp(module, peripheral, **kwargs): eem.HVAmp.add_std(module, peripheral["ports"][0], ttl_simple.Output, **kwargs) +def peripheral_efc(module, peripheral, **kwargs): + if len(peripheral["ports"]) == 1: + port = peripheral["ports"][0] + port_aux = None + elif len(peripheral["ports"]) == 2: + port, port_aux = peripheral["ports"] + else: + raise ValueError("wrong number of ports") + eem.EFC.add_std(module, port, port_aux) peripheral_processors = { "dio": peripheral_dio, @@ -147,6 +156,7 @@ peripheral_processors = { "fastino": peripheral_fastino, "phaser": peripheral_phaser, "hvamp": peripheral_hvamp, + "efc": peripheral_efc, } From f5cbca9c29eea005596aba0f7786940ad5d4e9db Mon Sep 17 00:00:00 2001 From: Sebastien Bourdeauducq Date: Fri, 25 Aug 2023 12:47:33 +0800 Subject: [PATCH 39/66] kasli: implement DRTIO-over-EEM --- artiq/gateware/targets/kasli.py | 52 ++++++++++++++++++++----- artiq/gateware/targets/kasli_generic.py | 11 ++++++ 2 files changed, 53 insertions(+), 10 deletions(-) diff --git a/artiq/gateware/targets/kasli.py b/artiq/gateware/targets/kasli.py index 55baa6a03..a9d5f5d2b 100755 --- a/artiq/gateware/targets/kasli.py +++ b/artiq/gateware/targets/kasli.py @@ -19,7 +19,7 @@ from artiq.gateware import rtio from artiq.gateware.rtio.phy import ttl_simple, ttl_serdes_7series, edge_counter from artiq.gateware.rtio.xilinx_clocking import fix_serdes_timing_path from artiq.gateware import eem -from artiq.gateware.drtio.transceiver import gtp_7series +from artiq.gateware.drtio.transceiver import gtp_7series, eem_serdes from artiq.gateware.drtio.siphaser import SiPhaser7Series from artiq.gateware.drtio.rx_synchronizer import XilinxRXSynchronizer from artiq.gateware.drtio import * @@ -288,17 +288,17 @@ class MasterBase(MiniSoC, AMPSoC): self.submodules.rtio_tsc = rtio.TSC(glbl_fine_ts_width=3) - drtio_csr_group = [] - drtioaux_csr_group = [] - drtioaux_memory_group = [] + self.drtio_csr_group = [] + self.drtioaux_csr_group = [] + self.drtioaux_memory_group = [] self.drtio_cri = [] for i in range(len(self.drtio_transceiver.channels)): core_name = "drtio" + str(i) coreaux_name = "drtioaux" + str(i) memory_name = "drtioaux" + str(i) + "_mem" - drtio_csr_group.append(core_name) - drtioaux_csr_group.append(coreaux_name) - drtioaux_memory_group.append(memory_name) + self.drtio_csr_group.append(core_name) + self.drtioaux_csr_group.append(coreaux_name) + self.drtioaux_memory_group.append(memory_name) cdr = ClockDomainsRenamer({"rtio_rx": "rtio_rx" + str(i)}) @@ -317,9 +317,6 @@ class MasterBase(MiniSoC, AMPSoC): self.add_memory_region(memory_name, memory_address | self.shadow_base, 0x800) self.config["HAS_DRTIO"] = None self.config["HAS_DRTIO_ROUTING"] = None - self.add_csr_group("drtio", drtio_csr_group) - self.add_csr_group("drtioaux", drtioaux_csr_group) - self.add_memory_group("drtioaux_mem", drtioaux_memory_group) rtio_clk_period = 1e9/rtio_clk_freq gtp = self.drtio_transceiver.gtps[0] @@ -367,6 +364,41 @@ class MasterBase(MiniSoC, AMPSoC): self.get_native_sdram_if(), cpu_dw=self.cpu_dw) self.csr_devices.append("rtio_analyzer") + def add_eem_drtio(self, eem_drtio_channels): + self.submodules.eem_transceiver = eem_serdes.EEMSerdes(self.platform, eem_drtio_channels) + self.csr_devices.append("eem_transceiver") + self.config["HAS_DRTIO_EEM"] = None + self.config["EEM_DRTIO_COUNT"] = len(eem_drtio_channels) + + cdr = ClockDomainsRenamer({"rtio_rx": "sys"}) + for i in range(len(self.eem_transceiver.channels)): + channel = i + len(self.drtio_transceiver.channels) + core_name = "drtio" + str(channel) + coreaux_name = "drtioaux" + str(channel) + memory_name = "drtioaux" + str(channel) + "_mem" + self.drtio_csr_group.append(core_name) + self.drtioaux_csr_group.append(coreaux_name) + self.drtioaux_memory_group.append(memory_name) + + core = cdr(DRTIOMaster(self.rtio_tsc, self.eem_transceiver.channels[i])) + setattr(self.submodules, core_name, core) + self.drtio_cri.append(core.cri) + self.csr_devices.append(core_name) + + coreaux = cdr(DRTIOAuxController(core.link_layer, self.cpu_dw)) + setattr(self.submodules, coreaux_name, coreaux) + self.csr_devices.append(coreaux_name) + + memory_address = self.mem_map["drtioaux"] + 0x800*channel + self.add_wb_slave(memory_address, 0x800, + coreaux.bus) + self.add_memory_region(memory_name, memory_address | self.shadow_base, 0x800) + + def add_drtio_cpuif_groups(self): + self.add_csr_group("drtio", self.drtio_csr_group) + self.add_csr_group("drtioaux", self.drtioaux_csr_group) + self.add_memory_group("drtioaux_mem", self.drtioaux_memory_group) + # Never running out of stupid features, GTs on A7 make you pack # unrelated transceiver PLLs into one GTPE2_COMMON yourself. def create_qpll(self): diff --git a/artiq/gateware/targets/kasli_generic.py b/artiq/gateware/targets/kasli_generic.py index e6df0b293..072a22b3e 100755 --- a/artiq/gateware/targets/kasli_generic.py +++ b/artiq/gateware/targets/kasli_generic.py @@ -71,10 +71,12 @@ class GenericMaster(MasterBase): if hw_rev is None: hw_rev = description["hw_rev"] self.class_name_override = description["variant"] + has_drtio_over_eem = any(peripheral["type"] == "efc" for peripheral in description["peripherals"]) MasterBase.__init__(self, hw_rev=hw_rev, rtio_clk_freq=description["rtio_frequency"], enable_sata=description["enable_sata_drtio"], + enable_sys5x=has_drtio_over_eem, **kwargs) self.config["DRTIO_ROLE"] = description["drtio_role"] if "ext_ref_frequency" in description: @@ -85,6 +87,8 @@ class GenericMaster(MasterBase): # EEM clock fan-out from Si5324, not MMCX self.comb += self.platform.request("clk_sel").eq(1) + if has_drtio_over_eem: + self.eem_drtio_channels = [] has_grabber = any(peripheral["type"] == "grabber" for peripheral in description["peripherals"]) if has_grabber: self.grabber_csr_group = [] @@ -96,6 +100,9 @@ class GenericMaster(MasterBase): self.rtio_channels.append(rtio.LogChannel()) self.add_rtio(self.rtio_channels, sed_lanes=description["sed_lanes"]) + if has_drtio_over_eem: + self.add_eem_drtio(self.eem_drtio_channels) + self.add_drtio_cpuif_groups() if has_grabber: self.config["HAS_GRABBER"] = None self.add_csr_group("grabber", self.grabber_csr_group) @@ -168,6 +175,10 @@ def main(): else: raise ValueError("Invalid DRTIO role") + has_efc = any(peripheral["type"] == "efc" for peripheral in description["peripherals"]) + if has_efc and (description["drtio_role"] == "standalone"): + raise ValueError("EFC requires DRTIO, please switch role to master") + soc = cls(description, gateware_identifier_str=args.gateware_identifier_str, **soc_kasli_argdict(args)) args.variant = description["variant"] build_artiq_soc(soc, builder_argdict(args)) From 72b0a175428bdacaaa1697c5433621d03335352c Mon Sep 17 00:00:00 2001 From: Sebastien Bourdeauducq Date: Fri, 25 Aug 2023 13:25:22 +0800 Subject: [PATCH 40/66] flake: register firmware outputs as hydra build products --- flake.nix | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/flake.nix b/flake.nix index e4b4f6460..713898697 100644 --- a/flake.nix +++ b/flake.nix @@ -275,16 +275,20 @@ ''; installPhase = '' - TARGET_DIR=$out - mkdir -p $TARGET_DIR - cp artiq_${target}/${variant}/gateware/top.bit $TARGET_DIR + mkdir $out + cp artiq_${target}/${variant}/gateware/top.bit $out if [ -e artiq_${target}/${variant}/software/bootloader/bootloader.bin ] - then cp artiq_${target}/${variant}/software/bootloader/bootloader.bin $TARGET_DIR + then cp artiq_${target}/${variant}/software/bootloader/bootloader.bin $out fi if [ -e artiq_${target}/${variant}/software/runtime ] - then cp artiq_${target}/${variant}/software/runtime/runtime.{elf,fbi} $TARGET_DIR - else cp artiq_${target}/${variant}/software/satman/satman.{elf,fbi} $TARGET_DIR + then cp artiq_${target}/${variant}/software/runtime/runtime.{elf,fbi} $out + else cp artiq_${target}/${variant}/software/satman/satman.{elf,fbi} $out fi + + mkdir $out/nix-support + for i in $out/*.*; do + echo file binary-dist $i >> $out/nix-support/hydra-build-products + done ''; # don't mangle ELF files as they are not for NixOS dontFixup = true; From 3620358f1280aaa4890e27fc272dc88e9e32f2cf Mon Sep 17 00:00:00 2001 From: Sebastien Bourdeauducq Date: Fri, 25 Aug 2023 13:34:56 +0800 Subject: [PATCH 41/66] flake: build efc firmware --- flake.nix | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/flake.nix b/flake.nix index 713898697..5ea21b24f 100644 --- a/flake.nix +++ b/flake.nix @@ -358,6 +358,11 @@ target = "kc705"; variant = "nist_clock"; }; + artiq-board-efc = makeArtiqBoardPackage { + target = "efc"; + variant = "shuttler"; + buildCommand = "python -m artiq.gateware.targets.efc"; + }; inherit sphinxcontrib-wavedrom latex-artiq-manual; artiq-manual-html = pkgs.stdenvNoCC.mkDerivation rec { name = "artiq-manual-html-${version}"; @@ -457,7 +462,7 @@ }; hydraJobs = { - inherit (packages.x86_64-linux) artiq artiq-board-kc705-nist_clock openocd-bscanspi; + inherit (packages.x86_64-linux) artiq artiq-board-kc705-nist_clock artiq-board-efc openocd-bscanspi; gateware-sim = pkgs.stdenvNoCC.mkDerivation { name = "gateware-sim"; buildInputs = [ From a2a780a3f26a6714c15013a09caec33a76ac9518 Mon Sep 17 00:00:00 2001 From: Sebastien Bourdeauducq Date: Fri, 25 Aug 2023 15:06:02 +0800 Subject: [PATCH 42/66] firmware: fix compilation warning --- artiq/firmware/libboard_misoc/io_expander.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/artiq/firmware/libboard_misoc/io_expander.rs b/artiq/firmware/libboard_misoc/io_expander.rs index e9f0a48b4..b86f4bd80 100644 --- a/artiq/firmware/libboard_misoc/io_expander.rs +++ b/artiq/firmware/libboard_misoc/io_expander.rs @@ -85,7 +85,7 @@ impl IoExpander { pub fn new() -> Result { const VIRTUAL_LED_MAPPING: [(u8, u8, u8); 2] = [(0, 0, 5), (1, 0, 6)]; - let mut io_expander = IoExpander { + let io_expander = IoExpander { busno: 0, port: 1, address: 0x40, From 2139456f806bc3b7ac4ed9ebb6fc416f57d5fa62 Mon Sep 17 00:00:00 2001 From: Sebastien Bourdeauducq Date: Fri, 25 Aug 2023 15:06:42 +0800 Subject: [PATCH 43/66] firmware: skip clock switch for efc --- artiq/firmware/satman/main.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/artiq/firmware/satman/main.rs b/artiq/firmware/satman/main.rs index b1553000a..e4452575f 100644 --- a/artiq/firmware/satman/main.rs +++ b/artiq/firmware/satman/main.rs @@ -463,6 +463,7 @@ const SI5324_SETTINGS: si5324::FrequencySettings crystal_as_ckin2: true }; +#[cfg(not(soc_platform = "efc"))] fn sysclk_setup() { let switched = unsafe { csr::crg::switch_done_read() @@ -529,6 +530,7 @@ pub extern fn main() -> i32 { io_expander1.service().unwrap(); } + #[cfg(not(soc_platform = "efc"))] sysclk_setup(); #[cfg(soc_platform = "efc")] From 3452d0c423c10943b7497f3792b3027fab4646e8 Mon Sep 17 00:00:00 2001 From: Sebastien Bourdeauducq Date: Fri, 25 Aug 2023 15:52:40 +0800 Subject: [PATCH 44/66] efc: use variant (expected everywhere else) --- artiq/gateware/targets/efc.py | 1 + flake.nix | 5 ++--- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/artiq/gateware/targets/efc.py b/artiq/gateware/targets/efc.py index 637844dc0..92cd2e771 100644 --- a/artiq/gateware/targets/efc.py +++ b/artiq/gateware/targets/efc.py @@ -167,6 +167,7 @@ def main(): description="ARTIQ device binary builder for EEM FMC Carrier systems") builder_args(parser) parser.set_defaults(output_dir="artiq_efc") + parser.add_argument("-V", "--variant", default="shuttler") parser.add_argument("--gateware-identifier-str", default=None, help="Override ROM identifier") args = parser.parse_args() diff --git a/flake.nix b/flake.nix index 5ea21b24f..2acbdb65e 100644 --- a/flake.nix +++ b/flake.nix @@ -358,10 +358,9 @@ target = "kc705"; variant = "nist_clock"; }; - artiq-board-efc = makeArtiqBoardPackage { + artiq-board-efc-shuttler = makeArtiqBoardPackage { target = "efc"; variant = "shuttler"; - buildCommand = "python -m artiq.gateware.targets.efc"; }; inherit sphinxcontrib-wavedrom latex-artiq-manual; artiq-manual-html = pkgs.stdenvNoCC.mkDerivation rec { @@ -462,7 +461,7 @@ }; hydraJobs = { - inherit (packages.x86_64-linux) artiq artiq-board-kc705-nist_clock artiq-board-efc openocd-bscanspi; + inherit (packages.x86_64-linux) artiq artiq-board-kc705-nist_clock artiq-board-efc-shuttler openocd-bscanspi; gateware-sim = pkgs.stdenvNoCC.mkDerivation { name = "gateware-sim"; buildInputs = [ From b56f7e429a918d83d1b74177f4a0c651f42a51ea Mon Sep 17 00:00:00 2001 From: linuswck Date: Mon, 28 Aug 2023 04:50:46 +0000 Subject: [PATCH 45/66] drtio: rename drtio_transceiver to gt_drtio --- artiq/firmware/runtime/rtio_clocking.rs | 4 +-- artiq/firmware/satman/main.rs | 4 +-- artiq/gateware/eem.py | 2 +- artiq/gateware/targets/kasli.py | 36 ++++++++++++------------- artiq/gateware/targets/kasli_generic.py | 4 +-- artiq/gateware/targets/kc705.py | 36 ++++++++++++------------- 6 files changed, 43 insertions(+), 43 deletions(-) diff --git a/artiq/firmware/runtime/rtio_clocking.rs b/artiq/firmware/runtime/rtio_clocking.rs index cce5d3b7b..c93f4b044 100644 --- a/artiq/firmware/runtime/rtio_clocking.rs +++ b/artiq/firmware/runtime/rtio_clocking.rs @@ -259,14 +259,14 @@ pub fn init() { unsafe { // clock switch and reboot will begin after TX is initialized // and TX will be initialized after this - csr::drtio_transceiver::stable_clkin_write(1); + csr::gt_drtio::stable_clkin_write(1); } loop {} } else { // enable TX after the reboot, with stable clock unsafe { - csr::drtio_transceiver::txenable_write(0xffffffffu32 as _); + csr::gt_drtio::txenable_write(0xffffffffu32 as _); } } } diff --git a/artiq/firmware/satman/main.rs b/artiq/firmware/satman/main.rs index e4452575f..7ef70f553 100644 --- a/artiq/firmware/satman/main.rs +++ b/artiq/firmware/satman/main.rs @@ -479,7 +479,7 @@ fn sysclk_setup() { // delay for clean UART log, wait until UART FIFO is empty clock::spin_us(1300); unsafe { - csr::drtio_transceiver::stable_clkin_write(1); + csr::gt_drtio::stable_clkin_write(1); } loop {} } @@ -553,7 +553,7 @@ pub extern fn main() -> i32 { #[cfg(not(has_drtio_eem))] unsafe { - csr::drtio_transceiver::txenable_write(0xffffffffu32 as _); + csr::gt_drtio::txenable_write(0xffffffffu32 as _); } init_rtio_crg(); diff --git a/artiq/gateware/eem.py b/artiq/gateware/eem.py index 112cd880f..e683946c5 100644 --- a/artiq/gateware/eem.py +++ b/artiq/gateware/eem.py @@ -30,7 +30,7 @@ class _EEM: target.platform.add_extension(cls.io(eem, *args, **kwargs)) if is_drtio_over_eem: print("{} (EEM{}) starting at DRTIO channel 0x{:06x}" - .format(name, eem, (len(target.drtio_transceiver.channels) + len(target.eem_drtio_channels) + 1) << 16)) + .format(name, eem, (len(target.gt_drtio.channels) + len(target.eem_drtio_channels) + 1) << 16)) else: print("{} (EEM{}) starting at RTIO channel 0x{:06x}" .format(name, eem, len(target.rtio_channels))) diff --git a/artiq/gateware/targets/kasli.py b/artiq/gateware/targets/kasli.py index a9d5f5d2b..f9b94e26c 100755 --- a/artiq/gateware/targets/kasli.py +++ b/artiq/gateware/targets/kasli.py @@ -268,17 +268,17 @@ class MasterBase(MiniSoC, AMPSoC): sfp_ctls = [platform.request("sfp_ctl", i) for i in range(1, 3)] self.comb += [sc.tx_disable.eq(0) for sc in sfp_ctls] - self.submodules.drtio_transceiver = gtp_7series.GTP( + self.submodules.gt_drtio = gtp_7series.GTP( qpll_channel=self.drtio_qpll_channel, data_pads=drtio_data_pads, sys_clk_freq=self.clk_freq, rtio_clk_freq=rtio_clk_freq) - self.csr_devices.append("drtio_transceiver") + self.csr_devices.append("gt_drtio") if enable_sata: - sfp_channels = self.drtio_transceiver.channels[1:] + sfp_channels = self.gt_drtio.channels[1:] else: - sfp_channels = self.drtio_transceiver.channels + sfp_channels = self.gt_drtio.channels if self.platform.hw_rev in ("v1.0", "v1.1"): self.comb += [sfp_ctl.led.eq(channel.rx_ready) for sfp_ctl, channel in zip(sfp_ctls, sfp_channels)] @@ -292,7 +292,7 @@ class MasterBase(MiniSoC, AMPSoC): self.drtioaux_csr_group = [] self.drtioaux_memory_group = [] self.drtio_cri = [] - for i in range(len(self.drtio_transceiver.channels)): + for i in range(len(self.gt_drtio.channels)): core_name = "drtio" + str(i) coreaux_name = "drtioaux" + str(i) memory_name = "drtioaux" + str(i) + "_mem" @@ -302,7 +302,7 @@ class MasterBase(MiniSoC, AMPSoC): cdr = ClockDomainsRenamer({"rtio_rx": "rtio_rx" + str(i)}) - core = cdr(DRTIOMaster(self.rtio_tsc, self.drtio_transceiver.channels[i])) + core = cdr(DRTIOMaster(self.rtio_tsc, self.gt_drtio.channels[i])) setattr(self.submodules, core_name, core) self.drtio_cri.append(core.cri) self.csr_devices.append(core_name) @@ -319,7 +319,7 @@ class MasterBase(MiniSoC, AMPSoC): self.config["HAS_DRTIO_ROUTING"] = None rtio_clk_period = 1e9/rtio_clk_freq - gtp = self.drtio_transceiver.gtps[0] + gtp = self.gt_drtio.gtps[0] txout_buf = Signal() self.specials += Instance("BUFG", i_I=gtp.txoutclk, o_O=txout_buf) @@ -331,7 +331,7 @@ class MasterBase(MiniSoC, AMPSoC): platform.add_false_path_constraints( self.crg.cd_sys.clk, gtp.txoutclk, gtp.rxoutclk) - for gtp in self.drtio_transceiver.gtps[1:]: + for gtp in self.gt_drtio.gtps[1:]: platform.add_period_constraint(gtp.rxoutclk, rtio_clk_period) platform.add_false_path_constraints( self.crg.cd_sys.clk, gtp.rxoutclk) @@ -372,7 +372,7 @@ class MasterBase(MiniSoC, AMPSoC): cdr = ClockDomainsRenamer({"rtio_rx": "sys"}) for i in range(len(self.eem_transceiver.channels)): - channel = i + len(self.drtio_transceiver.channels) + channel = i + len(self.gt_drtio.channels) core_name = "drtio" + str(channel) coreaux_name = "drtioaux" + str(channel) memory_name = "drtioaux" + str(channel) + "_mem" @@ -504,17 +504,17 @@ class SatelliteBase(BaseSoC, AMPSoC): if self.platform.hw_rev in ("v1.0", "v1.1"): sfp_ctls = [platform.request("sfp_ctl", i) for i in range(3)] self.comb += [sc.tx_disable.eq(0) for sc in sfp_ctls] - self.submodules.drtio_transceiver = gtp_7series.GTP( + self.submodules.gt_drtio = gtp_7series.GTP( qpll_channel=qpll.channels[0], data_pads=drtio_data_pads, sys_clk_freq=self.clk_freq, rtio_clk_freq=rtio_clk_freq) - self.csr_devices.append("drtio_transceiver") + self.csr_devices.append("gt_drtio") if enable_sata: - sfp_channels = self.drtio_transceiver.channels[1:] + sfp_channels = self.gt_drtio.channels[1:] else: - sfp_channels = self.drtio_transceiver.channels + sfp_channels = self.gt_drtio.channels if self.platform.hw_rev in ("v1.0", "v1.1"): self.comb += [sfp_ctl.led.eq(channel.rx_ready) for sfp_ctl, channel in zip(sfp_ctls, sfp_channels)] @@ -528,7 +528,7 @@ class SatelliteBase(BaseSoC, AMPSoC): drtioaux_memory_group = [] drtiorep_csr_group = [] self.drtio_cri = [] - for i in range(len(self.drtio_transceiver.channels)): + for i in range(len(self.gt_drtio.channels)): coreaux_name = "drtioaux" + str(i) memory_name = "drtioaux" + str(i) + "_mem" drtioaux_csr_group.append(coreaux_name) @@ -539,7 +539,7 @@ class SatelliteBase(BaseSoC, AMPSoC): if i == 0: self.submodules.rx_synchronizer = cdr(XilinxRXSynchronizer()) core = cdr(DRTIOSatellite( - self.rtio_tsc, self.drtio_transceiver.channels[i], + self.rtio_tsc, self.gt_drtio.channels[i], self.rx_synchronizer)) self.submodules.drtiosat = core self.csr_devices.append("drtiosat") @@ -548,7 +548,7 @@ class SatelliteBase(BaseSoC, AMPSoC): drtiorep_csr_group.append(corerep_name) core = cdr(DRTIORepeater( - self.rtio_tsc, self.drtio_transceiver.channels[i])) + self.rtio_tsc, self.gt_drtio.channels[i])) setattr(self.submodules, corerep_name, core) self.drtio_cri.append(core.cri) self.csr_devices.append(corerep_name) @@ -587,7 +587,7 @@ class SatelliteBase(BaseSoC, AMPSoC): self.config["HAS_SI5324"] = None self.config["SI5324_SOFT_RESET"] = None - gtp = self.drtio_transceiver.gtps[0] + gtp = self.gt_drtio.gtps[0] txout_buf = Signal() self.specials += Instance("BUFG", i_I=gtp.txoutclk, o_O=txout_buf) self.crg.configure(txout_buf, clk_sw=gtp.tx_init.done) @@ -597,7 +597,7 @@ class SatelliteBase(BaseSoC, AMPSoC): platform.add_false_path_constraints( self.crg.cd_sys.clk, gtp.txoutclk, gtp.rxoutclk) - for gtp in self.drtio_transceiver.gtps[1:]: + for gtp in self.gt_drtio.gtps[1:]: platform.add_period_constraint(gtp.rxoutclk, rtio_clk_period) platform.add_false_path_constraints( self.crg.cd_sys.clk, gtp.rxoutclk) diff --git a/artiq/gateware/targets/kasli_generic.py b/artiq/gateware/targets/kasli_generic.py index 072a22b3e..da92af8ef 100755 --- a/artiq/gateware/targets/kasli_generic.py +++ b/artiq/gateware/targets/kasli_generic.py @@ -108,7 +108,7 @@ class GenericMaster(MasterBase): self.add_csr_group("grabber", self.grabber_csr_group) for grabber in self.grabber_csr_group: self.platform.add_false_path_constraints( - self.drtio_transceiver.gtps[0].txoutclk, getattr(self, grabber).deserializer.cd_cl.clk) + self.gt_drtio.gtps[0].txoutclk, getattr(self, grabber).deserializer.cd_cl.clk) class GenericSatellite(SatelliteBase): @@ -142,7 +142,7 @@ class GenericSatellite(SatelliteBase): self.add_csr_group("grabber", self.grabber_csr_group) for grabber in self.grabber_csr_group: self.platform.add_false_path_constraints( - self.drtio_transceiver.gtps[0].txoutclk, getattr(self, grabber).deserializer.cd_cl.clk) + self.gt_drtio.gtps[0].txoutclk, getattr(self, grabber).deserializer.cd_cl.clk) def main(): diff --git a/artiq/gateware/targets/kc705.py b/artiq/gateware/targets/kc705.py index 884586f07..1c62476e5 100755 --- a/artiq/gateware/targets/kc705.py +++ b/artiq/gateware/targets/kc705.py @@ -215,11 +215,11 @@ class _MasterBase(MiniSoC, AMPSoC): ] # 1000BASE_BX10 Ethernet compatible, 100/125MHz RTIO clock - self.submodules.drtio_transceiver = gtx_7series.GTX( + self.submodules.gt_drtio = gtx_7series.GTX( clock_pads=platform.request("si5324_clkout"), pads=data_pads, clk_freq=self.clk_freq) - self.csr_devices.append("drtio_transceiver") + self.csr_devices.append("gt_drtio") self.submodules.rtio_tsc = rtio.TSC(glbl_fine_ts_width=3) @@ -227,7 +227,7 @@ class _MasterBase(MiniSoC, AMPSoC): drtioaux_csr_group = [] drtioaux_memory_group = [] self.drtio_cri = [] - for i in range(len(self.drtio_transceiver.channels)): + for i in range(len(self.gt_drtio.channels)): core_name = "drtio" + str(i) coreaux_name = "drtioaux" + str(i) memory_name = "drtioaux" + str(i) + "_mem" @@ -238,7 +238,7 @@ class _MasterBase(MiniSoC, AMPSoC): cdr = ClockDomainsRenamer({"rtio_rx": "rtio_rx" + str(i)}) core = cdr(DRTIOMaster( - self.rtio_tsc, self.drtio_transceiver.channels[i])) + self.rtio_tsc, self.gt_drtio.channels[i])) setattr(self.submodules, core_name, core) self.drtio_cri.append(core.cri) self.csr_devices.append(core_name) @@ -257,7 +257,7 @@ class _MasterBase(MiniSoC, AMPSoC): self.add_csr_group("drtioaux", drtioaux_csr_group) self.add_memory_group("drtioaux_mem", drtioaux_memory_group) - self.config["RTIO_FREQUENCY"] = str(self.drtio_transceiver.rtio_clk_freq/1e6) + self.config["RTIO_FREQUENCY"] = str(self.gt_drtio.rtio_clk_freq/1e6) self.submodules.si5324_rst_n = gpio.GPIOOut(platform.request("si5324_33").rst_n, reset_out=1) self.csr_devices.append("si5324_rst_n") i2c = self.platform.request("i2c") @@ -266,10 +266,10 @@ class _MasterBase(MiniSoC, AMPSoC): self.config["I2C_BUS_COUNT"] = 1 self.config["HAS_SI5324"] = None - rtio_clk_period = 1e9/self.drtio_transceiver.rtio_clk_freq + rtio_clk_period = 1e9/self.gt_drtio.rtio_clk_freq # Constrain TX & RX timing for the first transceiver channel # (First channel acts as master for phase alignment for all channels' TX) - gtx0 = self.drtio_transceiver.gtxs[0] + gtx0 = self.gt_drtio.gtxs[0] txout_buf = Signal() self.specials += Instance("BUFG", i_I=gtx0.txoutclk, o_O=txout_buf) @@ -286,7 +286,7 @@ class _MasterBase(MiniSoC, AMPSoC): self.crg.cd_sys.clk, gtx0.rxoutclk) # Constrain RX timing for the each transceiver channel # (Each channel performs single-lane phase alignment for RX) - for gtx in self.drtio_transceiver.gtxs[1:]: + for gtx in self.gt_drtio.gtxs[1:]: platform.add_period_constraint(gtx.rxoutclk, rtio_clk_period) platform.add_false_path_constraints( self.crg.cd_sys.clk, gtx.rxoutclk) @@ -363,11 +363,11 @@ class _SatelliteBase(BaseSoC, AMPSoC): rtio_clk_freq = clk_freq # 1000BASE_BX10 Ethernet compatible, 100/125MHz RTIO clock - self.submodules.drtio_transceiver = gtx_7series.GTX( + self.submodules.gt_drtio = gtx_7series.GTX( clock_pads=platform.request("si5324_clkout"), pads=data_pads, clk_freq=self.clk_freq) - self.csr_devices.append("drtio_transceiver") + self.csr_devices.append("gt_drtio") self.submodules.rtio_tsc = rtio.TSC(glbl_fine_ts_width=3) @@ -375,7 +375,7 @@ class _SatelliteBase(BaseSoC, AMPSoC): drtioaux_memory_group = [] drtiorep_csr_group = [] self.drtio_cri = [] - for i in range(len(self.drtio_transceiver.channels)): + for i in range(len(self.gt_drtio.channels)): coreaux_name = "drtioaux" + str(i) memory_name = "drtioaux" + str(i) + "_mem" drtioaux_csr_group.append(coreaux_name) @@ -387,7 +387,7 @@ class _SatelliteBase(BaseSoC, AMPSoC): if i == 0: self.submodules.rx_synchronizer = cdr(XilinxRXSynchronizer()) core = cdr(DRTIOSatellite( - self.rtio_tsc, self.drtio_transceiver.channels[0], self.rx_synchronizer)) + self.rtio_tsc, self.gt_drtio.channels[0], self.rx_synchronizer)) self.submodules.drtiosat = core self.csr_devices.append("drtiosat") # Repeaters @@ -395,7 +395,7 @@ class _SatelliteBase(BaseSoC, AMPSoC): corerep_name = "drtiorep" + str(i-1) drtiorep_csr_group.append(corerep_name) core = cdr(DRTIORepeater( - self.rtio_tsc, self.drtio_transceiver.channels[i])) + self.rtio_tsc, self.gt_drtio.channels[i])) setattr(self.submodules, corerep_name, core) self.drtio_cri.append(core.cri) self.csr_devices.append(corerep_name) @@ -414,14 +414,14 @@ class _SatelliteBase(BaseSoC, AMPSoC): self.add_memory_group("drtioaux_mem", drtioaux_memory_group) self.add_csr_group("drtiorep", drtiorep_csr_group) - self.config["RTIO_FREQUENCY"] = str(self.drtio_transceiver.rtio_clk_freq/1e6) + self.config["RTIO_FREQUENCY"] = str(self.gt_drtio.rtio_clk_freq/1e6) # Si5324 Phaser self.submodules.siphaser = SiPhaser7Series( si5324_clkin=platform.request("si5324_clkin_33"), rx_synchronizer=self.rx_synchronizer, ref_clk=ClockSignal("bootstrap"), ultrascale=False, - rtio_clk_freq=self.drtio_transceiver.rtio_clk_freq) + rtio_clk_freq=self.gt_drtio.rtio_clk_freq) platform.add_false_path_constraints( self.crg.cd_sys.clk, self.siphaser.mmcm_freerun_output) self.csr_devices.append("siphaser") @@ -433,10 +433,10 @@ class _SatelliteBase(BaseSoC, AMPSoC): self.config["I2C_BUS_COUNT"] = 1 self.config["HAS_SI5324"] = None - rtio_clk_period = 1e9/self.drtio_transceiver.rtio_clk_freq + rtio_clk_period = 1e9/self.gt_drtio.rtio_clk_freq # Constrain TX & RX timing for the first transceiver channel # (First channel acts as master for phase alignment for all channels' TX) - gtx0 = self.drtio_transceiver.gtxs[0] + gtx0 = self.gt_drtio.gtxs[0] txout_buf = Signal() self.specials += Instance("BUFG", i_I=gtx0.txoutclk, o_O=txout_buf) @@ -451,7 +451,7 @@ class _SatelliteBase(BaseSoC, AMPSoC): platform.add_period_constraint(gtx0.rxoutclk, rtio_clk_period) # Constrain RX timing for the each transceiver channel # (Each channel performs single-lane phase alignment for RX) - for gtx in self.drtio_transceiver.gtxs[1:]: + for gtx in self.gt_drtio.gtxs[1:]: platform.add_period_constraint(gtx.rxoutclk, rtio_clk_period) platform.add_false_path_constraints( self.crg.cd_sys.clk, gtx.rxoutclk) From ddb2b5e3a192ecac8329ae94dff31adb382add22 Mon Sep 17 00:00:00 2001 From: linuswck Date: Wed, 30 Aug 2023 10:06:47 +0800 Subject: [PATCH 46/66] efc: add shuttler DAC parallel data interface pads --- artiq/gateware/targets/efc.py | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/artiq/gateware/targets/efc.py b/artiq/gateware/targets/efc.py index 92cd2e771..91ad4a022 100644 --- a/artiq/gateware/targets/efc.py +++ b/artiq/gateware/targets/efc.py @@ -109,6 +109,38 @@ class Satellite(BaseSoC, AMPSoC): Subsignal('cs_n', Pins('fmc0:LA31_N fmc0:LA31_P fmc0:HB19_P fmc0:LA30_P')), IOStandard("LVCMOS18")), ('dac_rst', 0, Pins('fmc0:HB16_P'), IOStandard("LVCMOS18")), + ('dac_din', 0, + Subsignal('data', Pins('fmc0:HA06_N fmc0:HA06_P fmc0:HA07_N fmc0:HA02_N fmc0:HA07_P fmc0:HA02_P fmc0:HA03_N fmc0:HA03_P fmc0:HA04_N fmc0:HA04_P fmc0:HA05_N fmc0:HA05_P fmc0:HA00_CC_N fmc0:HA01_CC_N')), + Subsignal('clk', Pins('fmc0:HA00_CC_P')), + IOStandard('LVCMOS18')), + ('dac_din', 1, + Subsignal('data', Pins('fmc0:LA09_P fmc0:LA09_N fmc0:LA07_N fmc0:LA08_N fmc0:LA07_P fmc0:LA08_P fmc0:LA05_N fmc0:LA04_N fmc0:LA05_P fmc0:LA06_N fmc0:LA04_P fmc0:LA03_N fmc0:LA03_P fmc0:LA06_P')), + Subsignal('clk', Pins('fmc0:LA00_CC_P')), + IOStandard('LVCMOS18')), + ('dac_din', 2, + Subsignal('data', Pins('fmc0:HA14_N fmc0:HA14_P fmc0:HA12_N fmc0:HA12_P fmc0:HA13_N fmc0:HA10_N fmc0:HA10_P fmc0:HA11_N fmc0:HA11_P fmc0:HA13_P fmc0:HA08_N fmc0:HA08_P fmc0:HA09_N fmc0:HA09_P')), + Subsignal('clk', Pins('fmc0:HA01_CC_P')), + IOStandard('LVCMOS18')), + ('dac_din', 3, + Subsignal('data', Pins('fmc0:LA14_N fmc0:LA15_N fmc0:LA16_N fmc0:LA15_P fmc0:LA14_P fmc0:LA13_N fmc0:LA16_P fmc0:LA13_P fmc0:LA11_N fmc0:LA12_N fmc0:LA11_P fmc0:LA12_P fmc0:LA10_N fmc0:LA10_P')), + Subsignal('clk', Pins('fmc0:LA01_CC_P')), + IOStandard('LVCMOS18')), + ('dac_din', 4, + Subsignal('data', Pins('fmc0:HA22_N fmc0:HA19_N fmc0:HA22_P fmc0:HA21_N fmc0:HA21_P fmc0:HA19_P fmc0:HA18_CC_N fmc0:HA20_N fmc0:HA20_P fmc0:HA18_CC_P fmc0:HA15_N fmc0:HA15_P fmc0:HA16_N fmc0:HA16_P')), + Subsignal('clk', Pins('fmc0:HA17_CC_P')), + IOStandard('LVCMOS18')), + ('dac_din', 5, + Subsignal('data', Pins('fmc0:LA24_N fmc0:LA25_N fmc0:LA24_P fmc0:LA25_P fmc0:LA21_N fmc0:LA21_P fmc0:LA22_N fmc0:LA22_P fmc0:LA23_N fmc0:LA23_P fmc0:LA19_N fmc0:LA19_P fmc0:LA20_N fmc0:LA20_P')), + Subsignal('clk', Pins('fmc0:LA17_CC_P')), + IOStandard('LVCMOS18')), + ('dac_din', 6, + Subsignal('data', Pins('fmc0:HB08_N fmc0:HB08_P fmc0:HB07_N fmc0:HB07_P fmc0:HB04_N fmc0:HB04_P fmc0:HB01_N fmc0:HB05_N fmc0:HB01_P fmc0:HB05_P fmc0:HB02_N fmc0:HB02_P fmc0:HB03_N fmc0:HB03_P')), + Subsignal('clk', Pins('fmc0:HB00_CC_P')), + IOStandard('LVCMOS18')), + ('dac_din', 7, + Subsignal('data', Pins('fmc0:HB13_N fmc0:HB12_N fmc0:HB13_P fmc0:HB12_P fmc0:HB15_N fmc0:HB15_P fmc0:HB11_N fmc0:HB09_N fmc0:HB09_P fmc0:HB14_N fmc0:HB14_P fmc0:HB10_N fmc0:HB10_P fmc0:HB11_P')), + Subsignal('clk', Pins('fmc0:HB06_CC_P')), + IOStandard('LVCMOS18')), ] platform.add_extension(shuttler_io) From 1f58cd505c9ad872dd8907d4cc3d8c0be308cf30 Mon Sep 17 00:00:00 2001 From: Sebastien Bourdeauducq Date: Wed, 30 Aug 2023 15:39:46 +0800 Subject: [PATCH 47/66] flake: update dependencies --- flake.lock | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/flake.lock b/flake.lock index 40693dd4b..b1479b7c4 100644 --- a/flake.lock +++ b/flake.lock @@ -57,11 +57,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1692794066, - "narHash": "sha256-H0aG8r16dj0x/Wz6wQhQxc9V7AsObOiHPaKxQgH6Y08=", + "lastModified": 1693231525, + "narHash": "sha256-Zmh8m0HHcgGBDth6jdJPmc4UAAP0L4jQmqIztywF1Iw=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "fc944919f743bb22379dddf18dcb72db6cff84aa", + "rev": "c540061ac8d72d6e6d99345bd2d590c82b2f58c1", "type": "github" }, "original": { @@ -121,11 +121,11 @@ "src-misoc": { "flake": false, "locked": { - "lastModified": 1692355160, - "narHash": "sha256-d6eUHeTtedv9TCMlP8pR/Cgds5FAaPJyYIyF74exd8o=", + "lastModified": 1693380903, + "narHash": "sha256-bcxJX85i+QNBe+AaypTv8PB3hvNoGOVn+t7y3Yojf1s=", "ref": "refs/heads/master", - "rev": "2f6201a01a9eedfbdc4e7dbde63a83e27013aecb", - "revCount": 2444, + "rev": "a8cacd566445f502adb1ddf06aff3b6abd3c8b69", + "revCount": 2446, "submodules": true, "type": "git", "url": "https://github.com/m-labs/misoc.git" From df99450faaee1da687644711b5238823c17aec99 Mon Sep 17 00:00:00 2001 From: occheung <54844539+occheung@users.noreply.github.com> Date: Wed, 30 Aug 2023 08:38:39 -0700 Subject: [PATCH 48/66] shuttler: add pdq-based waveform generator --- artiq/coredevice/shuttler.py | 91 ++++++++++++++ artiq/gateware/shuttler.py | 224 ++++++++++++++++++++++++++++++++++ artiq/gateware/targets/efc.py | 4 + 3 files changed, 319 insertions(+) create mode 100644 artiq/coredevice/shuttler.py create mode 100644 artiq/gateware/shuttler.py diff --git a/artiq/coredevice/shuttler.py b/artiq/coredevice/shuttler.py new file mode 100644 index 000000000..71b3050f5 --- /dev/null +++ b/artiq/coredevice/shuttler.py @@ -0,0 +1,91 @@ +import numpy + +from artiq.language.core import * +from artiq.language.types import * +from artiq.coredevice.rtio import rtio_output + + +class Config: + kernel_invariants = {"core", "channel", "target_o"} + + def __init__(self, dmgr, channel, core_device="core"): + self.core = dmgr.get(core_device) + self.channel = channel + self.target_o = channel << 8 + + @kernel + def set_config(self, config): + rtio_output(self.target_o, config) + + +class Volt: + kernel_invariants = {"core", "channel", "target_o"} + + def __init__(self, dmgr, channel, core_device="core"): + self.core = dmgr.get(core_device) + self.channel = channel + self.target_o = channel << 8 + + @kernel + def set_waveform(self, a0: TInt32, a1: TInt32, a2: TInt64, a3: TInt64): + pdq_words = [ + a0, + a1, + a1 >> 16, + a2 & 0xFFFF, + (a2 >> 16) & 0xFFFF, + (a2 >> 32) & 0xFFFF, + a3 & 0xFFFF, + (a3 >> 16) & 0xFFFF, + (a3 >> 32) & 0xFFFF, + ] + + for i in range(len(pdq_words)): + rtio_output(self.target_o | i, pdq_words[i]) + delay_mu(int64(self.core.ref_multiplier)) + + +class Dds: + kernel_invariants = {"core", "channel", "target_o"} + + def __init__(self, dmgr, channel, core_device="core"): + self.core = dmgr.get(core_device) + self.channel = channel + self.target_o = channel << 8 + + @kernel + def set_waveform(self, b0: TInt32, b1: TInt32, b2: TInt64, b3: TInt64, + c0: TInt32, c1: TInt32, c2: TInt32): + pdq_words = [ + b0, + b1, + b1 >> 16, + b2 & 0xFFFF, + (b2 >> 16) & 0xFFFF, + (b2 >> 32) & 0xFFFF, + b3 & 0xFFFF, + (b3 >> 16) & 0xFFFF, + (b3 >> 32) & 0xFFFF, + c0, + c1, + c1 >> 16, + c2, + c2 >> 16, + ] + + for i in range(len(pdq_words)): + rtio_output(self.target_o | i, pdq_words[i]) + delay_mu(int64(self.core.ref_multiplier)) + + +class Trigger: + kernel_invariants = {"core", "channel", "target_o"} + + def __init__(self, dmgr, channel, core_device="core"): + self.core = dmgr.get(core_device) + self.channel = channel + self.target_o = channel << 8 + + @kernel + def trigger(self, trig_out): + rtio_output(self.target_o, trig_out) diff --git a/artiq/gateware/shuttler.py b/artiq/gateware/shuttler.py new file mode 100644 index 000000000..a736a442f --- /dev/null +++ b/artiq/gateware/shuttler.py @@ -0,0 +1,224 @@ +# Copyright 2013-2017 Robert Jordens +# +# pdq is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# pdq is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with pdq. If not, see . + +from collections import namedtuple +from operator import add + +from migen import * +from misoc.interconnect.stream import Endpoint +from misoc.cores.cordic import Cordic +from artiq.gateware.rtio import rtlink + + +class Dac(Module): + """Output module. + + Holds the two output line executors. + + Attributes: + data (Signal[16]): Output value to be send to the DAC. + clear (Signal): Clear accumulated phase offset when loading a new + waveform. Input. + i (Endpoint[]): Coefficients of the output lines. + """ + def __init__(self): + self.clear = Signal() + self.data = Signal(16) + + ### + + subs = [ + Volt(), + Dds(self.clear), + ] + + self.sync.rio += [ + self.data.eq(reduce(add, [sub.data for sub in subs])), + ] + + self.i = [ sub.i for sub in subs ] + self.submodules += subs + + +class Volt(Module): + """DC bias spline interpolator. + + The line data is interpreted as a concatenation of: + + * 16 bit amplitude offset + * 32 bit amplitude first order derivative + * 48 bit amplitude second order derivative + * 48 bit amplitude third order derivative + + Attributes: + data (Signal[16]): Output data from this spline. + i (Endpoint): Coefficients of the DC bias spline, along with its + latency compensation. + """ + def __init__(self): + self.data = Signal(16) + self.i = Endpoint([("data", 144)]) + self.i.latency = 17 + + ### + + v = [Signal(48) for i in range(4)] # amp, damp, ddamp, dddamp + + # Increase latency of stb by 17 cycles to compensate CORDIC latency + stb_r = [ Signal() for _ in range(17) ] + self.sync.rio += [ + stb_r[0].eq(self.i.stb), + ] + for idx in range(16): + self.sync.rio += stb_r[idx+1].eq(stb_r[idx]) + + self.sync.rio += [ + v[0].eq(v[0] + v[1]), + v[1].eq(v[1] + v[2]), + v[2].eq(v[2] + v[3]), + If(stb_r[16], + v[0].eq(0), + v[1].eq(0), + Cat(v[0][32:], v[1][16:], v[2], v[3]).eq(self.i.payload.raw_bits()), + ) + ] + self.comb += self.data.eq(v[0][32:]) + + +class Dds(Module): + """DDS spline interpolator. + + The line data is interpreted as: + + * 16 bit amplitude offset + * 32 bit amplitude first order derivative + * 48 bit amplitude second order derivative + * 48 bit amplitude third order derivative + * 16 bit phase offset + * 32 bit frequency word + * 32 bit chirp + + Args: + line (Record[line_layout]): Next line to be executed. Input. + clear (Signal): Clear accumulated phase offset when loading a new + waveform. Input. + + Attributes: + data (Signal[16]): Output data from this spline. + i (Endpoint): Coefficients of the DDS spline, along with its latency + compensation. + """ + def __init__(self, clear): + self.data = Signal(16) + self.i = Endpoint([("data", 224)]) + + ### + + self.submodules.cordic = Cordic(width=16, eval_mode="pipelined", + guard=None) + + za = Signal(32) + z = [Signal(32) for i in range(3)] # phase, dphase, ddphase + x = [Signal(48) for i in range(4)] # amp, damp, ddamp, dddamp + self.comb += [ + self.cordic.xi.eq(x[0][32:]), + self.cordic.yi.eq(0), + self.cordic.zi.eq(za[16:] + z[0][16:]), + self.data.eq(self.cordic.xo), + ] + + self.sync.rio += [ + za.eq(za + z[1]), + x[0].eq(x[0] + x[1]), + x[1].eq(x[1] + x[2]), + x[2].eq(x[2] + x[3]), + z[1].eq(z[1] + z[2]), + If(self.i.stb, + x[0].eq(0), + x[1].eq(0), + Cat(x[0][32:], x[1][16:], x[2], x[3], z[0][16:], z[1], z[2] + ).eq(self.i.payload.raw_bits()), + If(clear, + za.eq(0), + ) + ) + ] + + +class Config(Module): + def __init__(self): + self.clr = Signal(16, reset=0xFFFF) + self.i = Endpoint([("data", 16)]) + + # This introduces 1 extra latency to everything in config + # See the latency/delay attributes in Volt & DDS Endpoints/rtlinks + self.sync.rio += If(self.i.stb, self.clr.eq(self.i.data)) + + +Phy = namedtuple("Phy", "rtlink probes overrides") + + +class Shuttler(Module): + """Shuttler module. + + Used both in functional simulation and final gateware. + + Holds the DACs and the configuration register. The DAC and Config are + collected and adapted into RTIO interface. + + Attributes: + phys (list): List of Endpoints. + """ + def __init__(self): + NUM_OF_DACS = 16 + + self.phys = [] + + self.submodules.cfg = Config() + cfg_rtl_iface = rtlink.Interface(rtlink.OInterface( + data_width=len(self.cfg.i.data), + enable_replace=False)) + + self.comb += [ + self.cfg.i.stb.eq(cfg_rtl_iface.o.stb), + self.cfg.i.data.eq(cfg_rtl_iface.o.data), + ] + self.phys.append(Phy(cfg_rtl_iface, [], [])) + + trigger_iface = rtlink.Interface(rtlink.OInterface( + data_width=NUM_OF_DACS, + enable_replace=False)) + self.phys.append(Phy(trigger_iface, [], [])) + + for idx in range(NUM_OF_DACS): + dac = Dac() + self.comb += dac.clear.eq(self.cfg.clr[idx]), + + for i in dac.i: + delay = getattr(i, "latency", 0) + rtl_iface = rtlink.Interface(rtlink.OInterface( + data_width=16, address_width=4, delay=delay)) + array = Array(i.data[wi: wi+16] for wi in range(0, len(i.data), 16)) + + self.sync.rio += [ + i.stb.eq(trigger_iface.o.data[idx] & trigger_iface.o.stb), + If(rtl_iface.o.stb, + array[rtl_iface.o.address].eq(rtl_iface.o.data), + ), + ] + + self.phys.append(Phy(rtl_iface, [], [])) + + self.submodules += dac diff --git a/artiq/gateware/targets/efc.py b/artiq/gateware/targets/efc.py index 91ad4a022..156464c75 100644 --- a/artiq/gateware/targets/efc.py +++ b/artiq/gateware/targets/efc.py @@ -16,6 +16,7 @@ from artiq.gateware.rtio.phy import ttl_simple from artiq.gateware.drtio.transceiver import eem_serdes from artiq.gateware.drtio.rx_synchronizer import NoRXSynchronizer from artiq.gateware.drtio import * +from artiq.gateware.shuttler import Shuttler from artiq.build_soc import * @@ -159,6 +160,9 @@ class Satellite(BaseSoC, AMPSoC): self.submodules += phy self.rtio_channels.append(rtio.Channel.from_phy(phy)) + self.submodules.shuttler = Shuttler() + self.rtio_channels.extend(rtio.Channel.from_phy(phy) for phy in self.shuttler.phys) + self.config["HAS_RTIO_LOG"] = None self.config["RTIO_LOG_CHANNEL"] = len(self.rtio_channels) self.rtio_channels.append(rtio.LogChannel()) From 9556ca53de0b69cb6aa0c8bec4a29abc68a52965 Mon Sep 17 00:00:00 2001 From: Sebastien Bourdeauducq Date: Thu, 31 Aug 2023 17:42:20 +0800 Subject: [PATCH 49/66] flake: update dependencies --- flake.lock | 42 ++++++++++++++++++++++++++++++------------ 1 file changed, 30 insertions(+), 12 deletions(-) diff --git a/flake.lock b/flake.lock index b1479b7c4..a6c50f46f 100644 --- a/flake.lock +++ b/flake.lock @@ -11,11 +11,11 @@ ] }, "locked": { - "lastModified": 1664405593, - "narHash": "sha256-yP441NerlLGig7n+9xHsx8yCtZ+Ggd0VqfBSzc20E04=", + "lastModified": 1693473687, + "narHash": "sha256-BdLddCWbvoEyakcGwhph9b5dIU1iA0hCQV7KYgU8nos=", "owner": "m-labs", "repo": "artiq-comtools", - "rev": "15ddac62813ef623a076ccf982b3bc63d314e651", + "rev": "f522ef3dbc65961f17b2d3d41e927409d970fd79", "type": "github" }, "original": { @@ -25,12 +25,15 @@ } }, "flake-utils": { + "inputs": { + "systems": "systems" + }, "locked": { - "lastModified": 1659877975, - "narHash": "sha256-zllb8aq3YO3h8B/U0/J1WBgAL8EX5yWf5pMj3G0NAmc=", + "lastModified": 1692799911, + "narHash": "sha256-3eihraek4qL744EvQXsK1Ha6C3CR7nnT8X2qWap4RNk=", "owner": "numtide", "repo": "flake-utils", - "rev": "c0e246b9b83f637f4681389ecabcb2681b4f3af0", + "rev": "f9e7cf818399d17d347f847525c5a5a8032e4e44", "type": "github" }, "original": { @@ -57,11 +60,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1693231525, - "narHash": "sha256-Zmh8m0HHcgGBDth6jdJPmc4UAAP0L4jQmqIztywF1Iw=", + "lastModified": 1693341273, + "narHash": "sha256-wrsPjsIx2767909MPGhSIOmkpGELM9eufqLQOPxmZQg=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "c540061ac8d72d6e6d99345bd2d590c82b2f58c1", + "rev": "2ab91c8d65c00fd22a441c69bbf1bc9b420d5ea1", "type": "github" }, "original": { @@ -89,11 +92,11 @@ ] }, "locked": { - "lastModified": 1691095538, - "narHash": "sha256-JX1Re8wzqg4odcv/QxPtEvO8Z4RO3ZbRKGQ+ZpDdtWc=", + "lastModified": 1693473454, + "narHash": "sha256-kr8Ur6JNW/xVRHdPn3ou980IAxg/n+f3ZQBHuJ1uaC4=", "owner": "m-labs", "repo": "sipyco", - "rev": "c4f18adb658e3a546319425750cebeb8c88a016a", + "rev": "5467dcf9738673ab9a49e6f2377bda7c551b5f90", "type": "github" }, "original": { @@ -151,6 +154,21 @@ "repo": "pythonparser", "type": "github" } + }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } } }, "root": "root", From 01d777c977083c9166570eb26bc43112cecfde0a Mon Sep 17 00:00:00 2001 From: Simon Renblad Date: Fri, 1 Sep 2023 13:59:17 +0800 Subject: [PATCH 50/66] dashboard/datasets: fix CreateEditDialog datatype cast (#2176) --- artiq/dashboard/datasets.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/artiq/dashboard/datasets.py b/artiq/dashboard/datasets.py index c42f58659..9219058af 100644 --- a/artiq/dashboard/datasets.py +++ b/artiq/dashboard/datasets.py @@ -78,10 +78,11 @@ class CreateEditDialog(QtWidgets.QDialog): if metadata is not None: scale = scale_from_metadata(metadata) - value = pyon.decode(value) - t = type(value) - if np.issubdtype(t, np.number) or t is np.ndarray: - self.value_widget.setText(pyon.encode(value / scale)) + decoded_value = pyon.decode(value) + if scale == 1: + self.value_widget.setText(value) + else: + self.value_widget.setText(pyon.encode(decoded_value / scale)) self.unit_widget.setText(metadata.get('unit', '')) self.scale_widget.setText(str(metadata.get('scale', ''))) self.precision_widget.setText(str(metadata.get('precision', ''))) @@ -104,8 +105,9 @@ class CreateEditDialog(QtWidgets.QDialog): metadata['precision'] = int(precision) scale = scale_from_metadata(metadata) value = pyon.decode(value) - t = type(value) - if np.issubdtype(t, np.number) or t is np.ndarray: + t = value.dtype if value is np.ndarray else type(value) + is_floating = scale != 1 or np.issubdtype(t, np.floating) + if is_floating: value = value * scale if self.key and self.key != key: asyncio.ensure_future(exc_to_warning(rename(self.key, key, value, metadata, persist, self.dataset_ctl))) From 904afe1632a9c0ae8511883c7a1165bd459b5454 Mon Sep 17 00:00:00 2001 From: Simon Renblad Date: Fri, 1 Sep 2023 13:36:18 +0800 Subject: [PATCH 51/66] tools: remove trim param --- artiq/tools.py | 1 - 1 file changed, 1 deletion(-) diff --git a/artiq/tools.py b/artiq/tools.py index 8843bf5bb..dec615b51 100644 --- a/artiq/tools.py +++ b/artiq/tools.py @@ -85,7 +85,6 @@ def short_format(v, metadata={}): v_str = np.array2string(v_t, max_line_width=1000, precision=precision, - trim='-', suppress_small=True, separator=', ', threshold=4, From 838cc80922d75dc6f8c9817dd81f473eceb96d5f Mon Sep 17 00:00:00 2001 From: occheung <54844539+occheung@users.noreply.github.com> Date: Sat, 2 Sep 2023 19:25:08 -0700 Subject: [PATCH 52/66] EFC: Implement OOB reset --- .../gateware/drtio/transceiver/eem_serdes.py | 56 +++++++++++++++++++ artiq/gateware/targets/efc.py | 3 + 2 files changed, 59 insertions(+) diff --git a/artiq/gateware/drtio/transceiver/eem_serdes.py b/artiq/gateware/drtio/transceiver/eem_serdes.py index 73b373f80..f5ff1e847 100644 --- a/artiq/gateware/drtio/transceiver/eem_serdes.py +++ b/artiq/gateware/drtio/transceiver/eem_serdes.py @@ -11,6 +11,7 @@ class RXSerdes(Module): self.cnt_in = [ Signal(5) for _ in range(4) ] self.cnt_out = [ Signal(5) for _ in range(4) ] self.bitslip = [ Signal() for _ in range(4) ] + self.o = [ Signal() for _ in range(4) ] ser_in_no_dly = [ Signal() for _ in range(4) ] ser_in = [ Signal() for _ in range(4) ] @@ -34,6 +35,7 @@ class RXSerdes(Module): o_Q6=self.rxdata[i][4], o_Q7=self.rxdata[i][3], o_Q8=self.rxdata[i][2], + o_O=self.o[i], o_SHIFTOUT1=shifts[i][0], o_SHIFTOUT2=shifts[i][1], i_DDLY=ser_in[i], @@ -391,6 +393,56 @@ class SerdesSingle(Module): & decoders[0].k)) +class OOBReset(Module): + def __init__(self, iserdes_o): + ce_counter = Signal(13) + activity_ce = Signal() + transition_ce = Signal() + + self.sync.clk200 += Cat(ce_counter, activity_ce).eq(ce_counter + 1) + self.comb += transition_ce.eq(ce_counter[0]) + + idle_low_meta = Signal() + idle_high_meta = Signal() + idle_low = Signal() + idle_high = Signal() + + idle = Signal() + self.rst = Signal(reset=1) + + # Detect the lack of transitions (idle) within 2 clk200 cycles + self.specials += [ + Instance("FDCE", p_INIT=1, i_D=1, i_CLR=iserdes_o, + i_CE=transition_ce, i_C=ClockSignal("clk200"), o_Q=idle_low_meta, + attr={"async_reg", "ars_ff1"}), + Instance("FDCE", p_INIT=1, i_D=idle_low_meta, i_CLR=0, + i_CE=transition_ce, i_C=ClockSignal("clk200"), o_Q=idle_low, + attr={"async_reg", "ars_ff2"}), + + Instance("FDCE", p_INIT=1, i_D=1, i_CLR=~iserdes_o, + i_CE=transition_ce, i_C=ClockSignal("clk200"), o_Q=idle_high_meta, + attr={"async_reg", "ars_ff1"}), + Instance("FDCE", p_INIT=1, i_D=idle_high_meta, i_CLR=0, + i_CE=transition_ce, i_C=ClockSignal("clk200"), o_Q=idle_high, + attr={"async_reg", "ars_ff2"}), + ] + + # Detect activity for the last 2**13 clk200 cycles + # The 2**13 cycles are fully partitioned into 2**12 time segments of 2 + # cycles in duration. If there exists 2-cycle time segment without + # signal level transition, rst is asserted. + self.sync.clk200 += [ + If(activity_ce, + idle.eq(0), + self.rst.eq(idle), + ), + If(idle_low | idle_high, + idle.eq(1), + self.rst.eq(1), + ), + ] + + class EEMSerdes(Module, TransceiverInterface, AutoCSR): def __init__(self, platform, data_pads): self.rx_ready = CSRStorage() @@ -472,4 +524,8 @@ class EEMSerdes(Module, TransceiverInterface, AutoCSR): self.submodules += serdes_list + self.submodules.oob_reset = OOBReset(serdes_list[0].rx_serdes.o[0]) + self.rst = self.oob_reset.rst + self.rst.attr.add("no_retiming") + TransceiverInterface.__init__(self, channel_interfaces, async_rx=False) diff --git a/artiq/gateware/targets/efc.py b/artiq/gateware/targets/efc.py index 156464c75..3b8b280e6 100644 --- a/artiq/gateware/targets/efc.py +++ b/artiq/gateware/targets/efc.py @@ -89,6 +89,9 @@ class Satellite(BaseSoC, AMPSoC): self.add_csr_group("drtioaux", ["drtioaux0"]) self.add_memory_group("drtioaux_mem", ["drtioaux0_mem"]) + # Async reset gateware if data lane is idle + self.comb += self.crg.reset.eq(self.eem_transceiver.rst) + i2c = self.platform.request("fpga_i2c") self.submodules.i2c = gpio.GPIOTristate([i2c.scl, i2c.sda]) self.csr_devices.append("i2c") From 73ac4149124e805a7deb012579da7d90692defa5 Mon Sep 17 00:00:00 2001 From: Sebastien Bourdeauducq Date: Sun, 3 Sep 2023 10:59:52 +0800 Subject: [PATCH 53/66] flake: update dependencies --- flake.lock | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/flake.lock b/flake.lock index a6c50f46f..2762c79b7 100644 --- a/flake.lock +++ b/flake.lock @@ -60,11 +60,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1693341273, - "narHash": "sha256-wrsPjsIx2767909MPGhSIOmkpGELM9eufqLQOPxmZQg=", + "lastModified": 1693636127, + "narHash": "sha256-ZlS/lFGzK7BJXX2YVGnP3yZi3T9OLOEtBCyMJsb91U8=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "2ab91c8d65c00fd22a441c69bbf1bc9b420d5ea1", + "rev": "9075cba53e86dc318d159aee55dc9a7c9a4829c1", "type": "github" }, "original": { @@ -124,11 +124,11 @@ "src-misoc": { "flake": false, "locked": { - "lastModified": 1693380903, - "narHash": "sha256-bcxJX85i+QNBe+AaypTv8PB3hvNoGOVn+t7y3Yojf1s=", + "lastModified": 1693709836, + "narHash": "sha256-YiCk05RYLzZu1CYkQ2r7XtjwVEqkUGTQn388uOls9tI=", "ref": "refs/heads/master", - "rev": "a8cacd566445f502adb1ddf06aff3b6abd3c8b69", - "revCount": 2446, + "rev": "58dc4ee60d165ce9145cf3d904241fc154b6407f", + "revCount": 2448, "submodules": true, "type": "git", "url": "https://github.com/m-labs/misoc.git" From 98904ef4c38baec0651f7c8f49e2dfcf41835834 Mon Sep 17 00:00:00 2001 From: occheung Date: Sun, 3 Sep 2023 20:29:42 -0700 Subject: [PATCH 54/66] kasli: construct DRTIO-EEM modules before adding RTIO --- artiq/gateware/targets/kasli_generic.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/artiq/gateware/targets/kasli_generic.py b/artiq/gateware/targets/kasli_generic.py index da92af8ef..3a855933c 100755 --- a/artiq/gateware/targets/kasli_generic.py +++ b/artiq/gateware/targets/kasli_generic.py @@ -99,10 +99,12 @@ class GenericMaster(MasterBase): self.config["RTIO_LOG_CHANNEL"] = len(self.rtio_channels) self.rtio_channels.append(rtio.LogChannel()) - self.add_rtio(self.rtio_channels, sed_lanes=description["sed_lanes"]) if has_drtio_over_eem: self.add_eem_drtio(self.eem_drtio_channels) self.add_drtio_cpuif_groups() + + self.add_rtio(self.rtio_channels, sed_lanes=description["sed_lanes"]) + if has_grabber: self.config["HAS_GRABBER"] = None self.add_csr_group("grabber", self.grabber_csr_group) From 5e5d671f4c3bc26cd9f3bb9fa5f2049a3f1cc2f1 Mon Sep 17 00:00:00 2001 From: occheung Date: Sun, 3 Sep 2023 20:30:37 -0700 Subject: [PATCH 55/66] kasli: add invoke order comments --- artiq/gateware/targets/kasli.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/artiq/gateware/targets/kasli.py b/artiq/gateware/targets/kasli.py index f9b94e26c..6312170c9 100755 --- a/artiq/gateware/targets/kasli.py +++ b/artiq/gateware/targets/kasli.py @@ -365,6 +365,8 @@ class MasterBase(MiniSoC, AMPSoC): self.csr_devices.append("rtio_analyzer") def add_eem_drtio(self, eem_drtio_channels): + # Must be called before invoking add_rtio() to construct the CRI + # interconnect properly self.submodules.eem_transceiver = eem_serdes.EEMSerdes(self.platform, eem_drtio_channels) self.csr_devices.append("eem_transceiver") self.config["HAS_DRTIO_EEM"] = None From 7f63bb322da5453941f479625e81277de24a9ae0 Mon Sep 17 00:00:00 2001 From: occheung Date: Tue, 5 Sep 2023 16:58:28 +0800 Subject: [PATCH 56/66] disable DRTIO-over-EEM OSERDES until clock is stable This asserts OOB reset on EFC. --- artiq/firmware/runtime/rtio_clocking.rs | 3 +++ artiq/firmware/satman/main.rs | 5 +++++ artiq/gateware/drtio/transceiver/eem_serdes.py | 7 ++++++- 3 files changed, 14 insertions(+), 1 deletion(-) diff --git a/artiq/firmware/runtime/rtio_clocking.rs b/artiq/firmware/runtime/rtio_clocking.rs index c93f4b044..edb1b74e8 100644 --- a/artiq/firmware/runtime/rtio_clocking.rs +++ b/artiq/firmware/runtime/rtio_clocking.rs @@ -267,6 +267,9 @@ pub fn init() { // enable TX after the reboot, with stable clock unsafe { csr::gt_drtio::txenable_write(0xffffffffu32 as _); + + #[cfg(has_drtio_eem)] + csr::eem_transceiver::txenable_write(0xffffffffu32 as _); } } } diff --git a/artiq/firmware/satman/main.rs b/artiq/firmware/satman/main.rs index 7ef70f553..7c28df457 100644 --- a/artiq/firmware/satman/main.rs +++ b/artiq/firmware/satman/main.rs @@ -556,6 +556,11 @@ pub extern fn main() -> i32 { csr::gt_drtio::txenable_write(0xffffffffu32 as _); } + #[cfg(has_drtio_eem)] + unsafe { + csr::eem_transceiver::txenable_write(0xffffffffu32 as _); + } + init_rtio_crg(); #[cfg(has_drtio_eem)] diff --git a/artiq/gateware/drtio/transceiver/eem_serdes.py b/artiq/gateware/drtio/transceiver/eem_serdes.py index f5ff1e847..4092cb202 100644 --- a/artiq/gateware/drtio/transceiver/eem_serdes.py +++ b/artiq/gateware/drtio/transceiver/eem_serdes.py @@ -109,6 +109,8 @@ class TXSerdes(Module): ser_out = [ Signal() for _ in range(4) ] t_out = [ Signal() for _ in range(4) ] + self.ext_rst = Signal() + for i in range(4): self.specials += [ # Serializer @@ -118,7 +120,7 @@ class TXSerdes(Module): p_INIT_OQ=0b00000, o_OQ=ser_out[i], o_TQ=t_out[i], - i_RST=ResetSignal(), + i_RST=ResetSignal() | self.ext_rst, i_CLK=ClockSignal("sys5x"), i_CLKDIV=ClockSignal(), i_D1=self.txdata[i][0], @@ -529,3 +531,6 @@ class EEMSerdes(Module, TransceiverInterface, AutoCSR): self.rst.attr.add("no_retiming") TransceiverInterface.__init__(self, channel_interfaces, async_rx=False) + + for tx_en, serdes in zip(self.txenable.storage, serdes_list): + self.comb += serdes.tx_serdes.ext_rst.eq(~tx_en) From 4e770509dbbb30fb8a69a64d3fe45430a6f417a4 Mon Sep 17 00:00:00 2001 From: Florian Agbuya Date: Mon, 4 Sep 2023 16:06:33 +0800 Subject: [PATCH 57/66] almazny: fix doc formatting --- artiq/coredevice/almazny.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/artiq/coredevice/almazny.py b/artiq/coredevice/almazny.py index a78992b28..f7dacc94a 100644 --- a/artiq/coredevice/almazny.py +++ b/artiq/coredevice/almazny.py @@ -21,7 +21,7 @@ class AlmaznyLegacy: This applies to Almazny hardware v1.1 and earlier. Use :class:`artiq.coredevice.almazny.AlmaznyChannel` for Almazny v1.2 and later. - :param host_mirny - Mirny device Almazny is connected to + :param host_mirny: Mirny device Almazny is connected to """ def __init__(self, dmgr, host_mirny): @@ -61,8 +61,9 @@ class AlmaznyLegacy: def set_att(self, channel, att, rf_switch=True): """ Sets attenuators on chosen shift register (channel). + :param channel - index of the register [0-3] - :param att_mu - attenuation setting in dBm [0-31.5] + :param att - attenuation setting in dBm [0-31.5] :param rf_switch - rf switch (bool) """ self.set_att_mu(channel, self.att_to_mu(att), rf_switch) @@ -71,6 +72,7 @@ class AlmaznyLegacy: def set_att_mu(self, channel, att_mu, rf_switch=True): """ Sets attenuators on chosen shift register (channel). + :param channel - index of the register [0-3] :param att_mu - attenuation setting in machine units [0-63] :param rf_switch - rf switch (bool) @@ -83,6 +85,7 @@ class AlmaznyLegacy: def output_toggle(self, oe): """ Toggles output on all shift registers on or off. + :param oe - toggle output enable (bool) """ self.output_enable = oe @@ -141,6 +144,7 @@ class AlmaznyChannel: """ Convert an attenuation in dB, RF switch state and LED state to machine units. + :param att: attenuator setting in dB (0-31.5) :param enable: RF switch state (bool) :param led: LED state (bool) @@ -162,6 +166,7 @@ class AlmaznyChannel: def set_mu(self, mu): """ Set channel state (machine units). + :param mu: channel state in machine units. """ self.mirny_cpld.write_ext( @@ -171,6 +176,7 @@ class AlmaznyChannel: def set(self, att, enable, led=False): """ Set attenuation, RF switch, and LED state (SI units). + :param att: attenuator setting in dB (0-31.5) :param enable: RF switch state (bool) :param led: LED state (bool) From 0188f31f3af619b98399e4c0c1685df44d6f59a5 Mon Sep 17 00:00:00 2001 From: Florian Agbuya Date: Tue, 5 Sep 2023 10:41:40 +0800 Subject: [PATCH 58/66] i2c: fix doc formatting --- artiq/coredevice/i2c.py | 1 + 1 file changed, 1 insertion(+) diff --git a/artiq/coredevice/i2c.py b/artiq/coredevice/i2c.py index 2b3be2bc7..13c309527 100644 --- a/artiq/coredevice/i2c.py +++ b/artiq/coredevice/i2c.py @@ -161,6 +161,7 @@ class I2CSwitch: @kernel def set(self, channel): """Enable one channel. + :param channel: channel number (0-7) """ i2c_switch_select(self.busno, self.address >> 1, 1 << channel) From a619c9f3c26c89c7f43b95d5551b60b53915334b Mon Sep 17 00:00:00 2001 From: Florian Agbuya Date: Wed, 6 Sep 2023 14:05:41 +0800 Subject: [PATCH 59/66] almazny: fix minor doc formatting --- artiq/coredevice/almazny.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/artiq/coredevice/almazny.py b/artiq/coredevice/almazny.py index f7dacc94a..33aaf6b02 100644 --- a/artiq/coredevice/almazny.py +++ b/artiq/coredevice/almazny.py @@ -62,9 +62,9 @@ class AlmaznyLegacy: """ Sets attenuators on chosen shift register (channel). - :param channel - index of the register [0-3] - :param att - attenuation setting in dBm [0-31.5] - :param rf_switch - rf switch (bool) + :param channel: index of the register [0-3] + :param att: attenuation setting in dBm [0-31.5] + :param rf_switch: rf switch (bool) """ self.set_att_mu(channel, self.att_to_mu(att), rf_switch) @@ -73,9 +73,9 @@ class AlmaznyLegacy: """ Sets attenuators on chosen shift register (channel). - :param channel - index of the register [0-3] - :param att_mu - attenuation setting in machine units [0-63] - :param rf_switch - rf switch (bool) + :param channel: index of the register [0-3] + :param att_mu: attenuation setting in machine units [0-63] + :param rf_switch: rf switch (bool) """ self.channel_sw[channel] = 1 if rf_switch else 0 self.att_mu[channel] = att_mu @@ -86,7 +86,7 @@ class AlmaznyLegacy: """ Toggles output on all shift registers on or off. - :param oe - toggle output enable (bool) + :param oe: toggle output enable (bool) """ self.output_enable = oe cfg_reg = self.mirny_cpld.read_reg(1) @@ -131,6 +131,7 @@ class AlmaznyChannel: This driver requires Almazny hardware revision v1.2 or later and Mirny CPLD gateware v0.3 or later. Use :class:`artiq.coredevice.almazny.AlmaznyLegacy` for Almazny hardware v1.1 and earlier. + :param host_mirny: Mirny CPLD device name :param channel: channel index (0-3) """ From b49fb841ced196ed7e64bfe0cd8d6450711c0d01 Mon Sep 17 00:00:00 2001 From: linuswck Date: Tue, 5 Sep 2023 14:46:31 +0800 Subject: [PATCH 60/66] Firmware: EFC enables error led when going panic --- artiq/firmware/satman/main.rs | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/artiq/firmware/satman/main.rs b/artiq/firmware/satman/main.rs index 7c28df457..35d2922ee 100644 --- a/artiq/firmware/satman/main.rs +++ b/artiq/firmware/satman/main.rs @@ -656,6 +656,24 @@ pub extern fn main() -> i32 { } } +#[cfg(soc_platform = "efc")] +fn enable_error_led() { + let mut io_expander = board_misoc::io_expander::IoExpander::new().unwrap(); + + // Keep LEDs enabled + io_expander.set_oe(0, 1 << 5 | 1 << 6 | 1 << 7).unwrap(); + // Enable Error LED + io_expander.set(0, 7, true); + + // Keep VADJ and P3V3_FMC enabled + io_expander.set_oe(1, 1 << 0 | 1 << 1).unwrap(); + + io_expander.set(1, 0, true); + io_expander.set(1, 1, true); + + io_expander.service().unwrap(); +} + #[no_mangle] pub extern fn exception(_regs: *const u32) { let pc = mepc::read(); @@ -695,8 +713,16 @@ pub fn panic_fmt(info: &core::panic::PanicInfo) -> ! { if let Some(location) = info.location() { print!("panic at {}:{}:{}", location.file(), location.line(), location.column()); + #[cfg(soc_platform = "efc")] + { + if location.file() != "libboard_misoc/io_expander.rs" { + enable_error_led(); + } + } } else { print!("panic at unknown location"); + #[cfg(soc_platform = "efc")] + enable_error_led(); } if let Some(message) = info.message() { println!(": {}", message); From 1ec1ab05029548f11eaddebf6ba325181a2b101b Mon Sep 17 00:00:00 2001 From: Sebastien Bourdeauducq Date: Wed, 6 Sep 2023 18:28:08 +0800 Subject: [PATCH 61/66] flake: update dependencies --- flake.lock | 12 ++++++------ flake.nix | 2 ++ 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/flake.lock b/flake.lock index 2762c79b7..4acbe9c75 100644 --- a/flake.lock +++ b/flake.lock @@ -60,11 +60,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1693636127, - "narHash": "sha256-ZlS/lFGzK7BJXX2YVGnP3yZi3T9OLOEtBCyMJsb91U8=", + "lastModified": 1693771906, + "narHash": "sha256-32EnPCaVjOiEERZ+o/2Ir7JH9pkfwJZJ27SKHNvt4yk=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "9075cba53e86dc318d159aee55dc9a7c9a4829c1", + "rev": "da5adce0ffaff10f6d0fee72a02a5ed9d01b52fc", "type": "github" }, "original": { @@ -108,11 +108,11 @@ "src-migen": { "flake": false, "locked": { - "lastModified": 1674045327, - "narHash": "sha256-oYdeY0MbTReKbAwmSznnqw0wNawdInJoFJVWW3tesFA=", + "lastModified": 1693990700, + "narHash": "sha256-qJLA03QcZ5S9DrqrseuzIQBTWS7rjAbYJxLYZEQ8rxA=", "owner": "m-labs", "repo": "migen", - "rev": "ccaee68e14d3636e1d8fb2e0864dd89b1b1f7384", + "rev": "2cfee3e0db6fdca9b5918686ea77c93252e7cebd", "type": "github" }, "original": { diff --git a/flake.nix b/flake.nix index 2acbdb65e..a8e740fb8 100644 --- a/flake.nix +++ b/flake.nix @@ -190,6 +190,8 @@ migen = pkgs.python3Packages.buildPythonPackage rec { name = "migen"; src = src-migen; + format = "pyproject"; + nativeBuildInputs = [ pkgs.python3Packages.setuptools ]; propagatedBuildInputs = [ pkgs.python3Packages.colorama ]; }; From 2f3329181c2579ed77334e5875e8698a804be91d Mon Sep 17 00:00:00 2001 From: Florian Agbuya Date: Wed, 6 Sep 2023 18:55:24 +0800 Subject: [PATCH 62/66] flake: fix deprecated 'U' mode in outputcheck for python 3.11 --- flake.nix | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/flake.nix b/flake.nix index a8e740fb8..ae95ee250 100644 --- a/flake.nix +++ b/flake.nix @@ -91,6 +91,10 @@ sha256 = "1y27vz6jq6sywas07kz3v01sqjd0sga9yv9w2cksqac3v7wmf2a0"; }; prePatch = "echo ${version} > RELEASE-VERSION"; + postPatch = '' + substituteInPlace OutputCheck/Driver.py \ + --replace "argparse.FileType('rU')" "argparse.FileType('r')" + ''; }; libartiq-support = pkgs.stdenv.mkDerivation { From 7c8073c1ce100655f72dee65bcc5f49e668318ac Mon Sep 17 00:00:00 2001 From: linuswck Date: Mon, 4 Sep 2023 17:23:24 +0800 Subject: [PATCH 63/66] Shuttler: Add DAC Data Interface Gateware - Add Parallel DDR Data Interface for DAC - Add MMCM to generate phase shifted DDR Clk(45 degree phase shift by default) - Connect dac_interface to Shuttler Module --- artiq/gateware/shuttler.py | 80 +++++++++++++++++++++++++++++++++-- artiq/gateware/targets/efc.py | 7 +-- 2 files changed, 81 insertions(+), 6 deletions(-) diff --git a/artiq/gateware/shuttler.py b/artiq/gateware/shuttler.py index a736a442f..90e4eff68 100644 --- a/artiq/gateware/shuttler.py +++ b/artiq/gateware/shuttler.py @@ -18,9 +18,78 @@ from operator import add from migen import * from misoc.interconnect.stream import Endpoint +from misoc.interconnect.csr import * from misoc.cores.cordic import Cordic from artiq.gateware.rtio import rtlink +class DacInterface(Module, AutoCSR): + def __init__(self, pads): + bit_width = len(pads[0].data) + + self.data = [[Signal(bit_width) for _ in range(2)] for _ in range(8)] + + self.ddr_clk_phase_shift = CSR() + self.ddr_clk_phase_shift_done = CSRStatus(reset=1) + + mmcm_ps_fb = Signal() + mmcm_ps_output = Signal() + mmcm_ps_psdone = Signal() + ddr_clk = Signal() + + # Generate DAC DDR CLK + # 125MHz to 125MHz with controllable phase shift, + # VCO @ 1000MHz. + # Phase is shifted by 45 degree by default + self.specials += \ + Instance("MMCME2_ADV", + p_CLKIN1_PERIOD=8.0, + i_CLKIN1=ClockSignal(), + i_RST=ResetSignal(), + i_CLKINSEL=1, + + p_CLKFBOUT_MULT_F=8.0, + p_CLKOUT0_DIVIDE_F=8.0, + p_DIVCLK_DIVIDE=1, + p_CLKOUT0_PHASE=45.0, + + o_CLKFBOUT=mmcm_ps_fb, i_CLKFBIN=mmcm_ps_fb, + + p_CLKOUT0_USE_FINE_PS="TRUE", + o_CLKOUT0=mmcm_ps_output, + + i_PSCLK=ClockSignal(), + i_PSEN=self.ddr_clk_phase_shift.re, + i_PSINCDEC=self.ddr_clk_phase_shift.r, + o_PSDONE=mmcm_ps_psdone, + ) + + self.sync += [ + If(self.ddr_clk_phase_shift.re, self.ddr_clk_phase_shift_done.status.eq(0)), + If(mmcm_ps_psdone, self.ddr_clk_phase_shift_done.status.eq(1)) + ] + + # din.clk pads locate at multiple clock regions/IO banks + self.specials += [ + Instance("BUFG", i_I=mmcm_ps_output, o_O=ddr_clk), + ] + + for i, din in enumerate(pads): + self.specials += Instance("ODDR", + i_C=ddr_clk, + i_CE=1, + i_D1=1, + i_D2=0, + o_Q=din.clk, + p_DDR_CLK_EDGE="SAME_EDGE") + self.specials += [ + Instance("ODDR", + i_C=ClockSignal(), + i_CE=1, + i_D1=self.data[i][0][bit], # DDR CLK Rising Edge + i_D2=self.data[i][1][bit], # DDR CLK Falling Edge + o_Q=din.data[bit], + p_DDR_CLK_EDGE="SAME_EDGE") + for bit in range(bit_width)] class Dac(Module): """Output module. @@ -170,7 +239,7 @@ class Config(Module): Phy = namedtuple("Phy", "rtlink probes overrides") -class Shuttler(Module): +class Shuttler(Module, AutoCSR): """Shuttler module. Used both in functional simulation and final gateware. @@ -181,9 +250,11 @@ class Shuttler(Module): Attributes: phys (list): List of Endpoints. """ - def __init__(self): + def __init__(self, pads): NUM_OF_DACS = 16 + self.submodules.dac_interface = DacInterface(pads) + self.phys = [] self.submodules.cfg = Config() @@ -204,7 +275,10 @@ class Shuttler(Module): for idx in range(NUM_OF_DACS): dac = Dac() - self.comb += dac.clear.eq(self.cfg.clr[idx]), + self.comb += [ + dac.clear.eq(self.cfg.clr[idx]), + self.dac_interface.data[idx // 2][idx % 2].eq(dac.data) + ] for i in dac.i: delay = getattr(i, "latency", 0) diff --git a/artiq/gateware/targets/efc.py b/artiq/gateware/targets/efc.py index 3b8b280e6..b88f3f2b5 100644 --- a/artiq/gateware/targets/efc.py +++ b/artiq/gateware/targets/efc.py @@ -153,8 +153,8 @@ class Satellite(BaseSoC, AMPSoC): self.csr_devices.append("converter_spi") self.config["HAS_CONVERTER_SPI"] = None - dac_rst = self.platform.request('dac_rst') - self.comb += dac_rst.eq(0) + self.submodules.dac_rst = gpio.GPIOOut(self.platform.request("dac_rst")) + self.csr_devices.append("dac_rst") self.rtio_channels = [] @@ -163,7 +163,8 @@ class Satellite(BaseSoC, AMPSoC): self.submodules += phy self.rtio_channels.append(rtio.Channel.from_phy(phy)) - self.submodules.shuttler = Shuttler() + self.submodules.shuttler = Shuttler([platform.request("dac_din", i) for i in range(8)]) + self.csr_devices.append("shuttler") self.rtio_channels.extend(rtio.Channel.from_phy(phy) for phy in self.shuttler.phys) self.config["HAS_RTIO_LOG"] = None From ccb140a9298e2ff7eb7727f34e6492a71256d151 Mon Sep 17 00:00:00 2001 From: linuswck Date: Mon, 4 Sep 2023 16:50:00 +0800 Subject: [PATCH 64/66] Firmware: Add AD9117 DAC Startup Seq for shuttler --- artiq/firmware/libboard_artiq/ad9117.rs | 70 +++++++++++++++++++++++++ artiq/firmware/libboard_artiq/lib.rs | 3 ++ artiq/firmware/libboard_artiq/spi.rs | 14 ++--- artiq/firmware/satman/main.rs | 5 ++ 4 files changed, 86 insertions(+), 6 deletions(-) create mode 100644 artiq/firmware/libboard_artiq/ad9117.rs diff --git a/artiq/firmware/libboard_artiq/ad9117.rs b/artiq/firmware/libboard_artiq/ad9117.rs new file mode 100644 index 000000000..4c152e976 --- /dev/null +++ b/artiq/firmware/libboard_artiq/ad9117.rs @@ -0,0 +1,70 @@ +use spi; +use board_misoc::{csr, clock}; + +const DATA_CTRL_REG : u8 = 0x02; +const IRCML_REG : u8 = 0x05; +const QRCML_REG : u8 = 0x08; +const CLKMODE_REG : u8 = 0x14; +const VERSION_REG : u8 = 0x1F; + +fn hard_reset() { + unsafe { + // Min Pulse Width: 50ns + csr::dac_rst::out_write(1); + clock::spin_us(1); + csr::dac_rst::out_write(0); + } +} + +fn spi_setup(dac_sel: u8, half_duplex: bool, end: bool) -> Result<(), &'static str> { + // Clear the cs_polarity and cs config + spi::set_config(0, 0, 8, 64, 0b1111)?; + spi::set_config(0, 1 << 3, 8, 64, (7 - dac_sel) << 1)?; + spi::set_config(0, (half_duplex as u8) << 7 | (end as u8) << 1, 8, 64, 0b0001)?; + Ok(()) +} + +fn write(dac_sel: u8, reg: u8, val: u8) -> Result<(), &'static str> { + spi_setup(dac_sel, false, false)?; + spi::write(0, (reg as u32) << 24)?; + spi_setup(dac_sel, false, true)?; + spi::write(0, (val as u32) << 24)?; + + Ok(()) +} + +fn read(dac_sel: u8, reg: u8) -> Result { + spi_setup(dac_sel, false, false)?; + spi::write(0, ((reg | 1 << 7) as u32) << 24)?; + spi_setup(dac_sel, true, true)?; + spi::write(0, 0)?; + + Ok(spi::read(0)? as u8) +} + +pub fn init() -> Result<(), &'static str> { + hard_reset(); + + for channel in 0..8 { + let reg = read(channel, VERSION_REG)?; + if reg != 0x0A { + debug!("DAC AD9117 Channel {} has incorrect hardware version. VERSION reg: {:02x}", channel, reg); + return Err("DAC AD9117 hardware version is not equal to 0x0A"); + } + let reg = read(channel, CLKMODE_REG)?; + if reg >> 4 & 1 != 0 { + debug!("DAC AD9117 Channel {} retiming fails. CLKMODE reg: {:02x}", channel, reg); + return Err("DAC AD9117 retiming failure"); + } + + // Set the DACs input data format to be twos complement + // Set IFIRST and IRISING to True + write(channel, DATA_CTRL_REG, 1 << 7 | 1 << 5 | 1 << 4)?; + + // Enable internal common mode resistors of both channels + write(channel, IRCML_REG, 1 << 7)?; + write(channel, QRCML_REG, 1 << 7)?; + } + + Ok(()) +} diff --git a/artiq/firmware/libboard_artiq/lib.rs b/artiq/firmware/libboard_artiq/lib.rs index 9579acaaa..f564c98e4 100644 --- a/artiq/firmware/libboard_artiq/lib.rs +++ b/artiq/firmware/libboard_artiq/lib.rs @@ -34,3 +34,6 @@ pub mod drtio_routing; #[cfg(all(has_drtio_eem, feature = "alloc"))] pub mod drtio_eem; + +#[cfg(soc_platform = "efc")] +pub mod ad9117; diff --git a/artiq/firmware/libboard_artiq/spi.rs b/artiq/firmware/libboard_artiq/spi.rs index 78556b24b..3464580a3 100644 --- a/artiq/firmware/libboard_artiq/spi.rs +++ b/artiq/firmware/libboard_artiq/spi.rs @@ -2,9 +2,11 @@ mod imp { use board_misoc::csr; - pub fn set_config(busno: u8, flags: u8, length: u8, div: u8, cs: u8) -> Result<(), ()> { + const INVALID_BUS: &'static str = "Invalid SPI bus"; + + pub fn set_config(busno: u8, flags: u8, length: u8, div: u8, cs: u8) -> Result<(), &'static str> { if busno != 0 { - return Err(()) + return Err(INVALID_BUS) } unsafe { while csr::converter_spi::writable_read() == 0 {} @@ -31,9 +33,9 @@ mod imp { Ok(()) } - pub fn write(busno: u8, data: u32) -> Result<(), ()> { + pub fn write(busno: u8, data: u32) -> Result<(), &'static str> { if busno != 0 { - return Err(()) + return Err(INVALID_BUS) } unsafe { while csr::converter_spi::writable_read() == 0 {} @@ -42,9 +44,9 @@ mod imp { Ok(()) } - pub fn read(busno: u8) -> Result { + pub fn read(busno: u8) -> Result { if busno != 0 { - return Err(()) + return Err(INVALID_BUS) } Ok(unsafe { while csr::converter_spi::writable_read() == 0 {} diff --git a/artiq/firmware/satman/main.rs b/artiq/firmware/satman/main.rs index 35d2922ee..6eb1c7d9d 100644 --- a/artiq/firmware/satman/main.rs +++ b/artiq/firmware/satman/main.rs @@ -15,6 +15,8 @@ use board_misoc::{csr, ident, clock, uart_logger, i2c, pmp}; #[cfg(has_si5324)] use board_artiq::si5324; use board_artiq::{spi, drtioaux}; +#[cfg(soc_platform = "efc")] +use board_artiq::ad9117; use board_artiq::drtio_routing; use proto_artiq::drtioaux_proto::ANALYZER_MAX_SIZE; #[cfg(has_drtio_eem)] @@ -578,6 +580,9 @@ pub extern fn main() -> i32 { let mut hardware_tick_ts = 0; + #[cfg(soc_platform = "efc")] + ad9117::init().expect("AD9117 initialization failed"); + loop { while !drtiosat_link_rx_up() { drtiosat_process_errors(); From 5c85cef0c208588189061c36d46477008b4af26b Mon Sep 17 00:00:00 2001 From: Jonathan Coates Date: Tue, 12 Sep 2023 14:43:38 +0100 Subject: [PATCH 65/66] Allow indexing tuples in kernel code MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This only allows for indexing with a constant value (e.g. x[0]). While slices would be possible to implement, it's not clear how to preserve type inference here. The current typing rule is: Γ ⊢ x : τ Γ ⊢ a : Int Γ ⊢ b : Int ------------------------------------ Γ ⊢ x[a:b] : τ However, tuples would require a different typing rule, and so we'd need to defer type inference if τ is a tyvar. I'm not confident that this won't change behaviour, so we leave as-is for now. Signed-off-by: Jonathan Coates --- .../compiler/transforms/artiq_ir_generator.py | 22 +++++++++++++++- artiq/compiler/transforms/inferencer.py | 26 ++++++++++++++++++- .../lit/embedding/error_tuple_index_assign.py | 15 +++++++++++ artiq/test/lit/embedding/index_tuple.py | 16 ++++++++++++ .../test/lit/inferencer/error_tuple_index.py | 14 ++++++++++ artiq/test/lit/integration/tuple_index.py | 22 ++++++++++++++++ 6 files changed, 113 insertions(+), 2 deletions(-) create mode 100644 artiq/test/lit/embedding/error_tuple_index_assign.py create mode 100644 artiq/test/lit/embedding/index_tuple.py create mode 100644 artiq/test/lit/inferencer/error_tuple_index.py create mode 100644 artiq/test/lit/integration/tuple_index.py diff --git a/artiq/compiler/transforms/artiq_ir_generator.py b/artiq/compiler/transforms/artiq_ir_generator.py index 4a3d9fbbc..7ede45531 100644 --- a/artiq/compiler/transforms/artiq_ir_generator.py +++ b/artiq/compiler/transforms/artiq_ir_generator.py @@ -1198,7 +1198,27 @@ class ARTIQIRGenerator(algorithm.Visitor): finally: self.current_assign = old_assign - if isinstance(node.slice, ast.Index): + if types.is_tuple(node.value.type): + assert isinstance(node.slice, ast.Index), \ + "Internal compiler error: tuple index should be an Index" + assert isinstance(node.slice.value, ast.Num), \ + "Internal compiler error: tuple index should be a constant" + + if self.current_assign is not None: + diag = diagnostic.Diagnostic("error", + "cannot assign to a tuple element", + {}, node.loc) + self.engine.process(diag) + + index = node.slice.value.n + indexed = self.append( + ir.GetAttr(value, index, name="{}.e{}".format(value.name, index)), + loc=node.loc + ) + + return indexed + + elif isinstance(node.slice, ast.Index): try: old_assign, self.current_assign = self.current_assign, None index = self.visit(node.slice.value) diff --git a/artiq/compiler/transforms/inferencer.py b/artiq/compiler/transforms/inferencer.py index fdc268d4f..57bbedf82 100644 --- a/artiq/compiler/transforms/inferencer.py +++ b/artiq/compiler/transforms/inferencer.py @@ -259,7 +259,31 @@ class Inferencer(algorithm.Visitor): def visit_SubscriptT(self, node): self.generic_visit(node) - if isinstance(node.slice, ast.Index): + + if types.is_tuple(node.value.type): + if (not isinstance(node.slice, ast.Index) or + not isinstance(node.slice.value, ast.Num)): + diag = diagnostic.Diagnostic( + "error", "tuples can only be indexed by a constant", {}, + node.slice.loc, [] + ) + self.engine.process(diag) + return + + tuple_type = node.value.type.find() + index = node.slice.value.n + if index < 0 or index >= len(tuple_type.elts): + diag = diagnostic.Diagnostic( + "error", + "index {index} is out of range for tuple of size {size}", + {"index": index, "size": len(tuple_type.elts)}, + node.slice.loc, [] + ) + self.engine.process(diag) + return + + self._unify(node.type, tuple_type.elts[index], node.loc, node.value.loc) + elif isinstance(node.slice, ast.Index): if types.is_tuple(node.slice.value.type): if types.is_var(node.value.type): return diff --git a/artiq/test/lit/embedding/error_tuple_index_assign.py b/artiq/test/lit/embedding/error_tuple_index_assign.py new file mode 100644 index 000000000..ffa68a6b6 --- /dev/null +++ b/artiq/test/lit/embedding/error_tuple_index_assign.py @@ -0,0 +1,15 @@ +# RUN: %python -m artiq.compiler.testbench.embedding +diag %s 2>%t +# RUN: OutputCheck %s --file-to-check=%t + +from artiq.language.core import * +from artiq.language.types import * + +@kernel +def modify(x): + # CHECK-L: ${LINE:+1}: error: cannot assign to a tuple element + x[0] = 2 + +@kernel +def entrypoint(): + modify((1, "foo", True)) + modify((2, "bar", False)) diff --git a/artiq/test/lit/embedding/index_tuple.py b/artiq/test/lit/embedding/index_tuple.py new file mode 100644 index 000000000..fced90201 --- /dev/null +++ b/artiq/test/lit/embedding/index_tuple.py @@ -0,0 +1,16 @@ +# RUN: env ARTIQ_DUMP_LLVM=%t %python -m artiq.compiler.testbench.embedding +compile %s +# RUN: OutputCheck %s --file-to-check=%t.ll + +from artiq.language.core import * +from artiq.language.types import * + +# CHECK-L: void @_Z16testbench.unpackzz({ i32, { i8*, i32 }, i1 } %ARG.x) + +@kernel +def unpack(x): + print(x[0]) + +@kernel +def entrypoint(): + unpack((1, "foo", True)) + unpack((2, "bar", False)) diff --git a/artiq/test/lit/inferencer/error_tuple_index.py b/artiq/test/lit/inferencer/error_tuple_index.py new file mode 100644 index 000000000..dd6f2dca9 --- /dev/null +++ b/artiq/test/lit/inferencer/error_tuple_index.py @@ -0,0 +1,14 @@ +# RUN: %python -m artiq.compiler.testbench.inferencer +diag %s >%t +# RUN: OutputCheck %s --file-to-check=%t + +i = 0 +x = (1, "foo", True) + +# CHECK-L: ${LINE:+1}: error: tuples can only be indexed by a constant +x[i] + +# CHECK-L: ${LINE:+1}: error: tuples can only be indexed by a constant +x[0:2] + +# CHECK-L: ${LINE:+1}: error: index 3 is out of range for tuple of size 3 +x[3] diff --git a/artiq/test/lit/integration/tuple_index.py b/artiq/test/lit/integration/tuple_index.py new file mode 100644 index 000000000..46ebb1428 --- /dev/null +++ b/artiq/test/lit/integration/tuple_index.py @@ -0,0 +1,22 @@ +# RUN: %python -m artiq.compiler.testbench.jit %s +# RUN: %python %s + +# Basic indexing +a = (1, "xyz", True) + +assert a[0] == 1 +assert a[1] == "xyz" +assert a[2] == True + +# Nested indexing +b = (a, 2, (3, "abc", a)) + +assert b[0][0] == 1 +assert b[1] == 2 +assert b[2][2][0] == 1 + +# Usage on the LHS of an assignment +c = (1, 2, [1, 2, 3]) + +c[2][0] = 456 +assert c[2][0] == 456 From 6df85478e42a94e9f2c5303e6e5be47ac7b9a04d Mon Sep 17 00:00:00 2001 From: Florian Agbuya Date: Wed, 13 Sep 2023 12:16:13 +0800 Subject: [PATCH 66/66] scan: fix deprecated shuffle parameter in python 3.11 --- artiq/language/scan.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/artiq/language/scan.py b/artiq/language/scan.py index 0f05d3756..ee06b2413 100644 --- a/artiq/language/scan.py +++ b/artiq/language/scan.py @@ -83,8 +83,7 @@ class RangeScan(ScanObject): self.sequence = [i*dx + start for i in range(npoints)] if randomize: - rng = random.Random(seed) - random.shuffle(self.sequence, rng.random) + random.Random(seed).shuffle(self.sequence) def __iter__(self): return iter(self.sequence) @@ -120,8 +119,7 @@ class CenterScan(ScanObject): for i in range(n) for sign in [-1, 1]][1:] if randomize: - rng = random.Random(seed) - random.shuffle(self.sequence, rng.random) + random.Random(seed).shuffle(self.sequence) def __iter__(self): return iter(self.sequence)