diff --git a/README.rst b/README.rst index cef2fb286..9ec774741 100644 --- a/README.rst +++ b/README.rst @@ -29,7 +29,7 @@ Website: https://m-labs.hk/artiq License ======= -Copyright (C) 2014-2022 M-Labs Limited. +Copyright (C) 2014-2023 M-Labs Limited. ARTIQ is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by diff --git a/RELEASE_NOTES.rst b/RELEASE_NOTES.rst index 28e06bdd6..f9922a5cf 100644 --- a/RELEASE_NOTES.rst +++ b/RELEASE_NOTES.rst @@ -8,10 +8,21 @@ ARTIQ-8 (Unreleased) Highlights: -* Implemented Phaser-servo. This requires recent gateware on Phaser. -* Implemented Phaser-MIQRO support. This requires the Phaser MIQRO gateware - variant. -* MSYS2 packaging for Windows. +* Hardware support: + - Implemented Phaser-servo. This requires recent gateware on Phaser. + - Implemented Phaser-MIQRO support. This requires the Phaser MIQRO gateware + variant. + - Sampler: fixed ADC MU to Volt conversion factor for Sampler v2.2+. + For earlier hardware versions, specify the hardware version in the device + database file (e.g. ``"hw_rev": "v2.1"``) to use the correct conversion factor. + - Metlino and Sayma support has been dropped due to complications with synchronous RTIO clocking. +* CPU (on softcore platforms) and AXI bus (on Zynq) are now clocked synchronously with the RTIO + clock, to facilitate implementation of local processing on DRTIO satellites, and to slightly + reduce RTIO latency. +* MSYS2 packaging for Windows, which replaces Conda. Conda packages are still available to + support legacy installations, but may be removed in a future release. +* Added channel names to RTIO errors. +* Full Python 3.10 support. ARTIQ-7 ------- @@ -27,7 +38,7 @@ Highlights: - Almazny mezzanine board for Mirny - Phaser: improved documentation, exposed the DAC coarse mixer and ``sif_sync``, exposed upconverter calibration and enabling/disabling of upconverter LO & RF outputs, added helpers to align Phaser updates to the - RTIO timeline (``get_next_frame_mu()`` + RTIO timeline (``get_next_frame_mu()``). - Urukul: ``get()``, ``get_mu()``, ``get_att()``, and ``get_att_mu()`` functions added for AD9910 and AD9912. * Softcore targets now use the RISC-V architecture (VexRiscv) instead of OR1K (mor1kx). * Gateware FPU is supported on KC705 and Kasli 2.0. @@ -77,9 +88,9 @@ Breaking changes: generated for some configurations. * Phaser: fixed coarse mixer frequency configuration * Mirny: Added extra delays in ``ADF5356.sync()``. This avoids the need of an extra delay before - calling `ADF5356.init()`. + calling ``ADF5356.init()``. * The deprecated ``set_dataset(..., save=...)`` is no longer supported. -* The ``PCA9548`` I2C switch class was renamed to ``I2CSwitch``, to accomodate support for PCA9547, +* The ``PCA9548`` I2C switch class was renamed to ``I2CSwitch``, to accommodate support for PCA9547, and possibly other switches in future. Readback has been removed, and now only one channel per switch is supported. diff --git a/artiq/browser/experiments.py b/artiq/browser/experiments.py index 6b580a7ed..a67b93736 100644 --- a/artiq/browser/experiments.py +++ b/artiq/browser/experiments.py @@ -406,7 +406,7 @@ class ExperimentsArea(QtWidgets.QMdiArea): self.worker_handlers = { "get_device_db": lambda: {}, - "get_device": lambda k: {"type": "dummy"}, + "get_device": lambda key, resolve_alias=False: {"type": "dummy"}, "get_dataset": self._ddb.get, "update_dataset": self._ddb.update, } diff --git a/artiq/browser/files.py b/artiq/browser/files.py index 46bf10b3f..a714cb696 100644 --- a/artiq/browser/files.py +++ b/artiq/browser/files.py @@ -102,13 +102,14 @@ class Hdf5FileSystemModel(QtWidgets.QFileSystemModel): h5 = open_h5(info) if h5 is not None: try: - expid = pyon.decode(h5["expid"][()]) - start_time = datetime.fromtimestamp(h5["start_time"][()]) + expid = pyon.decode(h5["expid"][()]) if "expid" in h5 else dict() + start_time = datetime.fromtimestamp(h5["start_time"][()]) if "start_time" in h5 else "" v = ("artiq_version: {}\nrepo_rev: {}\nfile: {}\n" "class_name: {}\nrid: {}\nstart_time: {}").format( - h5["artiq_version"][()], expid["repo_rev"], - expid.get("file", ""), expid["class_name"], - h5["rid"][()], start_time) + h5["artiq_version"].asstr()[()] if "artiq_version" in h5 else "", + expid.get("repo_rev", ""), + expid.get("file", ""), expid.get("class_name", ""), + h5["rid"][()] if "rid" in h5 else "", start_time) return v except: logger.warning("unable to read metadata from %s", @@ -174,14 +175,14 @@ class FilesDock(QtWidgets.QDockWidget): logger.debug("loading datasets from %s", info.filePath()) with f: try: - expid = pyon.decode(f["expid"][()]) - start_time = datetime.fromtimestamp(f["start_time"][()]) + expid = pyon.decode(f["expid"][()]) if "expid" in f else dict() + start_time = datetime.fromtimestamp(f["start_time"][()]) if "start_time" in f else "" v = { - "artiq_version": f["artiq_version"][()], - "repo_rev": expid["repo_rev"], + "artiq_version": f["artiq_version"].asstr()[()] if "artiq_version" in f else "", + "repo_rev": expid.get("repo_rev", ""), "file": expid.get("file", ""), - "class_name": expid["class_name"], - "rid": f["rid"][()], + "class_name": expid.get("class_name", ""), + "rid": f["rid"][()] if "rid" in f else "", "start_time": start_time, } self.metadata_changed.emit(v) diff --git a/artiq/coredevice/ad9912.py b/artiq/coredevice/ad9912.py index 7f81ef400..44b2109cb 100644 --- a/artiq/coredevice/ad9912.py +++ b/artiq/coredevice/ad9912.py @@ -27,6 +27,8 @@ class AD9912: f_ref/clk_div*pll_n where f_ref is the reference frequency and clk_div is the reference clock divider (both set in the parent Urukul CPLD instance). + :param pll_en: PLL enable bit, set to False to bypass PLL (default: True). + Note that when bypassing the PLL the red front panel LED may remain on. """ core: KernelInvariant[Core] @@ -34,11 +36,12 @@ class AD9912: bus: KernelInvariant[SPIMaster] chip_select: KernelInvariant[int32] pll_n: KernelInvariant[int32] + pll_en: KernelInvariant[bool] ftw_per_hz: KernelInvariant[float] sw: KernelInvariant[Option[TTLOut]] def __init__(self, dmgr, chip_select, cpld_device, sw_device=None, - pll_n=10): + pll_n=10, pll_en=True): self.cpld = dmgr.get(cpld_device) self.core = self.cpld.core self.bus = self.cpld.bus @@ -48,8 +51,12 @@ class AD9912: self.sw = Some(dmgr.get(sw_device)) else: self.sw = none + self.pll_en = pll_en self.pll_n = pll_n - sysclk = self.cpld.refclk / [1, 1, 2, 4][self.cpld.clk_div] * pll_n + if pll_en: + sysclk = self.cpld.refclk / [1, 1, 2, 4][self.cpld.clk_div] * pll_n + else: + sysclk = self.cpld.refclk assert sysclk <= 1e9 self.ftw_per_hz = 1 / sysclk * (1 << 48) @@ -111,13 +118,15 @@ class AD9912: raise ValueError("Urukul AD9912 product id mismatch") self.core.delay(50. * us) # HSTL power down, CMOS power down - self.write(AD9912_PWRCNTRL1, 0x80, 1) - self.cpld.io_update.pulse(2. * us) - self.write(AD9912_N_DIV, self.pll_n // 2 - 2, 1) - self.cpld.io_update.pulse(2. * us) - # I_cp = 375 µA, VCO high range - self.write(AD9912_PLLCFG, 0b00000101, 1) + pwrcntrl1 = 0x80 | (int32(not self.pll_en) << 4) + self.write(AD9912_PWRCNTRL1, pwrcntrl1, 1) self.cpld.io_update.pulse(2. * us) + if self.pll_en: + self.write(AD9912_N_DIV, self.pll_n // 2 - 2, 1) + self.cpld.io_update.pulse(2. * us) + # I_cp = 375 µA, VCO high range + self.write(AD9912_PLLCFG, 0b00000101, 1) + self.cpld.io_update.pulse(2. * us) self.core.delay(1. * ms) @kernel diff --git a/artiq/coredevice/ad9914.py b/artiq/coredevice/ad9914.py index 092654675..4cff10844 100644 --- a/artiq/coredevice/ad9914.py +++ b/artiq/coredevice/ad9914.py @@ -91,6 +91,10 @@ class AD9914: self.set_x_duration_mu = 7 * self.write_duration_mu self.exit_x_duration_mu = 3 * self.write_duration_mu + @staticmethod + def get_rtio_channels(bus_channel, **kwargs): + return [(bus_channel, None)] + @kernel def write(self, addr: int32, data: int32): rtio_output((self.bus_channel << 8) | addr, data) diff --git a/artiq/coredevice/adf5356.py b/artiq/coredevice/adf5356.py index 683990bfa..84a15f556 100644 --- a/artiq/coredevice/adf5356.py +++ b/artiq/coredevice/adf5356.py @@ -83,6 +83,10 @@ class ADF5356: self._init_registers() + @staticmethod + def get_rtio_channels(channel, **kwargs): + return [(channel, None)] + @kernel def init(self, blind: bool = False): """ diff --git a/artiq/coredevice/coredevice_generic.schema.json b/artiq/coredevice/coredevice_generic.schema.json index fa687104f..12047d04a 100644 --- a/artiq/coredevice/coredevice_generic.schema.json +++ b/artiq/coredevice/coredevice_generic.schema.json @@ -332,6 +332,9 @@ "minItems": 2, "maxItems": 2 }, + "sampler_hw_rev": { + "type": "string" + }, "urukul0_ports": { "type": "array", "items": { diff --git a/artiq/coredevice/edge_counter.py b/artiq/coredevice/edge_counter.py index e560565fb..7b29ef4cb 100644 --- a/artiq/coredevice/edge_counter.py +++ b/artiq/coredevice/edge_counter.py @@ -97,6 +97,10 @@ class EdgeCounter: self.channel = channel self.counter_max = (1 << (gateware_width - 1)) - 1 + @staticmethod + def get_rtio_channels(channel, **kwargs): + return [(channel, None)] + @kernel def gate_rising(self, duration: float) -> int64: """Count rising edges for the given duration and request the total at diff --git a/artiq/coredevice/fastino.py b/artiq/coredevice/fastino.py index 995d20b6b..c5f297f74 100644 --- a/artiq/coredevice/fastino.py +++ b/artiq/coredevice/fastino.py @@ -22,7 +22,7 @@ class Fastino: DAC updates synchronized to a frame edge. The `log2_width=0` RTIO layout uses one DAC channel per RTIO address and a - dense RTIO address space. The RTIO words are narrow. (32 bit) and + dense RTIO address space. The RTIO words are narrow (32 bit) and few-channel updates are efficient. There is the least amount of DAC state tracking in kernels, at the cost of more DMA and RTIO data. The setting here and in the RTIO PHY (gateware) must match. @@ -57,6 +57,10 @@ class Fastino: assert self.core.ref_period == 1*ns self.t_frame = int64(14*7*4) + @staticmethod + def get_rtio_channels(channel, **kwargs): + return [(channel, None)] + @kernel def init(self): """Initialize the device. diff --git a/artiq/coredevice/grabber.py b/artiq/coredevice/grabber.py index c71414a6d..be713053c 100644 --- a/artiq/coredevice/grabber.py +++ b/artiq/coredevice/grabber.py @@ -29,6 +29,10 @@ class Grabber: # ROI engine outputs for one video frame. self.sentinel = int32(int64(2**count_width)) + @staticmethod + def get_rtio_channels(channel_base, **kwargs): + return [(channel_base, "ROI coordinates"), (channel_base + 1, "ROI mask")] + @kernel def setup_roi(self, n: int32, x0: int32, y0: int32, x1: int32, y1: int32): """ diff --git a/artiq/coredevice/phaser.py b/artiq/coredevice/phaser.py index 31d4ab78c..49279597d 100644 --- a/artiq/coredevice/phaser.py +++ b/artiq/coredevice/phaser.py @@ -241,7 +241,7 @@ class Phaser: channel: Kernel[list[PhaserChannel]] def __init__(self, dmgr, channel_base, miso_delay=1, tune_fifo_offset=True, - clk_sel=False, sync_dly=0, dac=None, trf0=None, trf1=None, + clk_sel=False, sync_dly=0, dac=None, trf0=None, trf1=None, gw_rev=PHASER_GW_BASE, core_device="core"): self.channel_base = channel_base self.core = dmgr.get(core_device) @@ -255,7 +255,7 @@ class Phaser: self.clk_sel = clk_sel self.tune_fifo_offset = tune_fifo_offset self.sync_dly = sync_dly - self.gw_rev = -1 # discovered in init() + self.gw_rev = gw_rev # verified in init() self.dac_mmap = DAC34H84(dac).get_mmap() self.dac_mmap = [int32(uint32(x)) for x in self.dac_mmap] # NAC3TODO https://git.m-labs.hk/M-Labs/nac3/issues/14 @@ -263,6 +263,18 @@ class Phaser: self.channel = [PhaserChannel(self, ch, trf) for ch, trf in enumerate([trf0, trf1])] + @staticmethod + def get_rtio_channels(channel_base, gw_rev=PHASER_GW_BASE, **kwargs): + if gw_rev == PHASER_GW_MIQRO: + return [(channel_base, "base"), (channel_base + 1, "ch0"), (channel_base + 2, "ch1")] + elif gw_rev == PHASER_GW_BASE: + return [(channel_base, "base"), + (channel_base + 1, "ch0 frequency"), + (channel_base + 2, "ch0 phase amplitude"), + (channel_base + 3, "ch1 frequency"), + (channel_base + 4, "ch1 phase amplitude")] + raise ValueError("invalid gw_rev `{}`".format(gw_rev)) + @kernel def init(self, debug: bool = False): """Initialize the board. @@ -280,10 +292,11 @@ class Phaser: self.core.delay(.1*ms) # slack is_baseband = hw_rev & PHASER_HW_REV_VARIANT != 0 - self.gw_rev = self.read8(PHASER_ADDR_GW_REV) + gw_rev = self.read8(PHASER_ADDR_GW_REV) if debug: print_rpc(("gw_rev:", self.gw_rev)) self.core.break_realtime() + assert gw_rev == self.gw_rev self.core.delay(.1*ms) # slack # allow a few errors during startup and alignment since boot diff --git a/artiq/coredevice/sampler.py b/artiq/coredevice/sampler.py index 686e92d62..c0a7329ff 100644 --- a/artiq/coredevice/sampler.py +++ b/artiq/coredevice/sampler.py @@ -19,25 +19,27 @@ SPI_CS_PGIA = 1 # separate SPI bus, CS used as RCLK @portable -def adc_mu_to_volt(data: int32, gain: int32 = 0) -> float: +def adc_mu_to_volt(data: int32, gain: int32 = 0, corrected_fs: bool = True) -> float: """Convert ADC data in machine units to Volts. :param data: 16 bit signed ADC word :param gain: PGIA gain setting (0: 1, ..., 3: 1000) + :param corrected_fs: use corrected ADC FS reference. + Should be True for Samplers' revisions after v2.1. False for v2.1 and earlier. :return: Voltage in Volts """ volt_per_lsb = 0. if gain == 0: - volt_per_lsb = 20./float(1 << 16) + volt_per_lsb = 20.48 / float(1 << 16) if corrected_fs else 20. / float(1 << 16) elif gain == 1: - volt_per_lsb = 2./float(1 << 16) + volt_per_lsb = 2.048 / float(1 << 16) if corrected_fs else 2. / float(1 << 16) elif gain == 2: - volt_per_lsb = .2/float(1 << 16) + volt_per_lsb = .2048 / float(1 << 16) if corrected_fs else .2 / float(1 << 16) elif gain == 3: - volt_per_lsb = .02/float(1 << 16) + volt_per_lsb = 0.02048 / float(1 << 16) if corrected_fs else .02 / float(1 << 16) else: raise ValueError("invalid gain") - return float(data)*volt_per_lsb + return float(data)* volt_per_lsb @nac3 @@ -54,6 +56,7 @@ class Sampler: :param gains: Initial value for PGIA gains shift register (default: 0x0000). Knowledge of this state is not transferred between experiments. + :param hw_rev: Sampler's hardware revision string (default 'v2.2') :param core_device: Core device name """ core: KernelInvariant[Core] @@ -62,9 +65,10 @@ class Sampler: cnv: KernelInvariant[TTLOut] div: KernelInvariant[int32] gains: Kernel[int32] + corrected_fs: KernelInvariant[bool] def __init__(self, dmgr, spi_adc_device, spi_pgia_device, cnv_device, - div=8, gains=0x0000, core_device="core"): + div=8, gains=0x0000, hw_rev="v2.2", core_device="core"): self.bus_adc = dmgr.get(spi_adc_device) self.bus_adc.update_xfer_duration_mu(div, 32) self.bus_pgia = dmgr.get(spi_pgia_device) @@ -73,6 +77,11 @@ class Sampler: self.cnv = dmgr.get(cnv_device) self.div = div self.gains = gains + self.corrected_fs = self.use_corrected_fs(hw_rev) + + @staticmethod + def use_corrected_fs(hw_rev): + return hw_rev != "v2.1" @kernel def init(self): @@ -155,4 +164,4 @@ class Sampler: for i in range(n): channel = i + 8 - len(data) gain = (self.gains >> (channel*2)) & 0b11 - data[i] = adc_mu_to_volt(adc_data[i], gain) + data[i] = adc_mu_to_volt(adc_data[i], gain, self.revision) diff --git a/artiq/coredevice/spi2.py b/artiq/coredevice/spi2.py index 4b07dc907..f3443e5d6 100644 --- a/artiq/coredevice/spi2.py +++ b/artiq/coredevice/spi2.py @@ -77,6 +77,10 @@ class SPIMaster: self.channel = channel self.update_xfer_duration_mu(div, length) + @staticmethod + def get_rtio_channels(channel, **kwargs): + return [(channel, None)] + @portable def frequency_to_div(self, f: float) -> int32: """Convert a SPI clock frequency to the closest SPI clock divider.""" diff --git a/artiq/coredevice/suservo.py b/artiq/coredevice/suservo.py index 07655ba43..1e0934385 100644 --- a/artiq/coredevice/suservo.py +++ b/artiq/coredevice/suservo.py @@ -28,12 +28,12 @@ def y_mu_to_full_scale(y: int32) -> float: @portable -def adc_mu_to_volts(x: int32, gain: int32) -> float: +def adc_mu_to_volts(x: int32, gain: int32, corrected_fs: bool = True) -> float: """Convert servo ADC data from machine units to Volt.""" val = (x >> 1) & 0xffff mask = 1 << 15 val = -(val & mask) + (val & ~mask) - return sampler_adc_mu_to_volt(val, gain) + return sampler_adc_mu_to_volt(val, gain, corrected_fs) @nac3 @@ -68,6 +68,7 @@ class SUServo: :param gains: Initial value for PGIA gains shift register (default: 0x0000). Knowledge of this state is not transferred between experiments. + :param sampler_hw_rev: Sampler's revision string :param core_device: Core device name """ @@ -78,11 +79,11 @@ class SUServo: channel: KernelInvariant[int32] gains: Kernel[int32] ref_period_mu: KernelInvariant[int64] - + corrected_fs: KernelInvariant[bool] def __init__(self, dmgr, channel, pgia_device, cpld_devices, dds_devices, - gains=0x0000, core_device="core"): + gains=0x0000, sampler_hw_rev="v2.2", core_device="core"): self.core = dmgr.get(core_device) self.pgia = dmgr.get(pgia_device) @@ -94,8 +95,13 @@ class SUServo: self.gains = gains self.ref_period_mu = self.core.seconds_to_mu( self.core.coarse_ref_period) + self.corrected_fs = sampler.Sampler.use_corrected_fs(sampler_hw_rev) assert self.ref_period_mu == self.core.ref_multiplier + @staticmethod + def get_rtio_channels(channel, **kwargs): + return [(channel, None)] + @kernel def init(self): """Initialize the servo, Sampler and both Urukuls. @@ -247,7 +253,7 @@ class SUServo: """ val = self.get_adc_mu(channel) gain = (self.gains >> (channel*2)) & 0b11 - return adc_mu_to_volts(val, gain) + return adc_mu_to_volts(val, gain, self.corrected_fs) @nac3 @@ -274,6 +280,10 @@ class Channel: self.servo.channel) self.dds = self.servo.ddses[self.servo_channel // 4] + @staticmethod + def get_rtio_channels(channel, **kwargs): + return [(channel, None)] + @kernel def set(self, en_out: bool, en_iir: bool = False, profile: int32 = 0): """Operate channel. diff --git a/artiq/coredevice/ttl.py b/artiq/coredevice/ttl.py index 795c0fbe0..6d2a51e1f 100644 --- a/artiq/coredevice/ttl.py +++ b/artiq/coredevice/ttl.py @@ -39,6 +39,10 @@ class TTLOut: self.channel = channel self.target_o = channel << 8 + @staticmethod + def get_rtio_channels(channel, **kwargs): + return [(channel, None)] + @kernel def output(self): pass @@ -137,6 +141,10 @@ class TTLInOut: self.target_sens = (channel << 8) + 2 self.target_sample = (channel << 8) + 3 + @staticmethod + def get_rtio_channels(channel, **kwargs): + return [(channel, None)] + @kernel def set_oe(self, oe: bool): rtio_output(self.target_oe, 1 if oe else 0) @@ -477,6 +485,10 @@ class TTLClockGen: self.target = channel << 8 self.acc_width = acc_width + @staticmethod + def get_rtio_channels(channel, **kwargs): + return [(channel, None)] + @portable def frequency_to_ftw(self, frequency: float) -> int32: """Returns the frequency tuning word corresponding to the given diff --git a/artiq/dashboard/datasets.py b/artiq/dashboard/datasets.py index 674e653e9..1052e03d1 100644 --- a/artiq/dashboard/datasets.py +++ b/artiq/dashboard/datasets.py @@ -14,92 +14,18 @@ from artiq.gui.scientific_spinbox import ScientificSpinBox logger = logging.getLogger(__name__) -async def rename(key, newkey, value, dataset_ctl): - if key != newkey: +async def rename(key, new_key, value, persist, dataset_ctl): + if key != new_key: await dataset_ctl.delete(key) - await dataset_ctl.set(newkey, value) + await dataset_ctl.set(new_key, value, persist) -class Editor(QtWidgets.QDialog): - def __init__(self, parent, dataset_ctl, key, value): - QtWidgets.QDialog.__init__(self, parent=parent) - self.dataset_ctl = dataset_ctl - self.key = key - self.initial_type = type(value) - - self.setWindowTitle("Edit dataset") - grid = QtWidgets.QGridLayout() - self.setLayout(grid) - - grid.addWidget(QtWidgets.QLabel("Name:"), 0, 0) - - self.name_widget = QtWidgets.QLineEdit() - self.name_widget.setText(key) - - grid.addWidget(self.name_widget, 0, 1) - - grid.addWidget(QtWidgets.QLabel("Value:"), 1, 0) - grid.addWidget(self.get_edit_widget(value), 1, 1) - - buttons = QtWidgets.QDialogButtonBox( - QtWidgets.QDialogButtonBox.Ok | QtWidgets.QDialogButtonBox.Cancel) - grid.setRowStretch(2, 1) - grid.addWidget(buttons, 3, 0, 1, 2) - buttons.accepted.connect(self.accept) - buttons.rejected.connect(self.reject) - - def accept(self): - newkey = self.name_widget.text() - value = self.initial_type(self.get_edit_widget_value()) - asyncio.ensure_future(rename(self.key, newkey, value, self.dataset_ctl)) - QtWidgets.QDialog.accept(self) - - def get_edit_widget(self, initial_value): - raise NotImplementedError - - def get_edit_widget_value(self): - raise NotImplementedError - - -class NumberEditor(Editor): - def get_edit_widget(self, initial_value): - self.edit_widget = ScientificSpinBox() - self.edit_widget.setDecimals(13) - self.edit_widget.setPrecision() - self.edit_widget.setRelativeStep() - self.edit_widget.setValue(float(initial_value)) - return self.edit_widget - - def get_edit_widget_value(self): - return self.edit_widget.value() - - -class BoolEditor(Editor): - def get_edit_widget(self, initial_value): - self.edit_widget = QtWidgets.QCheckBox() - self.edit_widget.setChecked(bool(initial_value)) - return self.edit_widget - - def get_edit_widget_value(self): - return self.edit_widget.isChecked() - - -class StringEditor(Editor): - def get_edit_widget(self, initial_value): - self.edit_widget = QtWidgets.QLineEdit() - self.edit_widget.setText(initial_value) - return self.edit_widget - - def get_edit_widget_value(self): - return self.edit_widget.text() - - -class Creator(QtWidgets.QDialog): - def __init__(self, parent, dataset_ctl): +class CreateEditDialog(QtWidgets.QDialog): + def __init__(self, parent, dataset_ctl, key=None, value=None, persist=False): QtWidgets.QDialog.__init__(self, parent=parent) self.dataset_ctl = dataset_ctl - self.setWindowTitle("Create dataset") + self.setWindowTitle("Create dataset" if key is None else "Edit dataset") grid = QtWidgets.QGridLayout() grid.setRowMinimumHeight(1, 40) grid.setColumnMinimumWidth(2, 60) @@ -130,16 +56,24 @@ class Creator(QtWidgets.QDialog): self.buttons.addButton( self.cancel, QtWidgets.QDialogButtonBox.RejectRole) grid.setRowStretch(3, 1) - grid.addWidget(self.buttons, 4, 0, 1, 3) + grid.addWidget(self.buttons, 4, 0, 1, 3, alignment=QtCore.Qt.AlignHCenter) self.buttons.accepted.connect(self.accept) self.buttons.rejected.connect(self.reject) + self.key = key + self.name_widget.setText(key) + self.value_widget.setText(value) + self.box_widget.setChecked(persist) + def accept(self): key = self.name_widget.text() value = self.value_widget.text() persist = self.box_widget.isChecked() - asyncio.ensure_future(exc_to_warning(self.dataset_ctl.set( - key, pyon.decode(value), persist))) + if self.key and self.key != key: + asyncio.ensure_future(exc_to_warning(rename(self.key, key, pyon.decode(value), persist, self.dataset_ctl))) + else: + asyncio.ensure_future(exc_to_warning(self.dataset_ctl.set(key, pyon.decode(value), persist))) + self.key = key QtWidgets.QDialog.accept(self) def dtype(self): @@ -226,7 +160,7 @@ class DatasetsDock(QtWidgets.QDockWidget): self.table.setModel(self.table_model_filter) def create_clicked(self): - Creator(self, self.dataset_ctl).open() + CreateEditDialog(self, self.dataset_ctl).open() def edit_clicked(self): idx = self.table.selectedIndexes() @@ -236,17 +170,13 @@ class DatasetsDock(QtWidgets.QDockWidget): if key is not None: persist, value = self.table_model.backing_store[key] t = type(value) - if np.issubdtype(t, np.number): - dialog_cls = NumberEditor - elif np.issubdtype(t, np.bool_): - dialog_cls = BoolEditor + if np.issubdtype(t, np.number) or np.issubdtype(t, np.bool_): + value = str(value) elif np.issubdtype(t, np.unicode_): - dialog_cls = StringEditor + value = '"{}"'.format(str(value)) else: - logger.error("Cannot edit dataset %s: " - "type %s is not supported", key, t) - return - dialog_cls(self, self.dataset_ctl, key, value).open() + value = pyon.encode(value) + CreateEditDialog(self, self.dataset_ctl, key, value, persist).open() def delete_clicked(self): idx = self.table.selectedIndexes() diff --git a/artiq/firmware/Cargo.lock b/artiq/firmware/Cargo.lock index 8e0c7f95a..c6f71926e 100644 --- a/artiq/firmware/Cargo.lock +++ b/artiq/firmware/Cargo.lock @@ -371,9 +371,9 @@ checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" [[package]] name = "smoltcp" -version = "0.8.0" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2308a1657c8db1f5b4993bab4e620bdbe5623bd81f254cf60326767bb243237" +checksum = "ee34c1e1bfc7e9206cc0fb8030a90129b4e319ab53856249bb27642cab914fb3" dependencies = [ "bitflags", "byteorder", diff --git a/artiq/firmware/bootloader/Cargo.toml b/artiq/firmware/bootloader/Cargo.toml index 9e5ba820a..ac0df1001 100644 --- a/artiq/firmware/bootloader/Cargo.toml +++ b/artiq/firmware/bootloader/Cargo.toml @@ -16,5 +16,5 @@ build_misoc = { path = "../libbuild_misoc" } byteorder = { version = "1.0", default-features = false } crc = { version = "1.7", default-features = false } board_misoc = { path = "../libboard_misoc", features = ["uart_console", "smoltcp"] } -smoltcp = { version = "0.8.0", default-features = false, features = ["medium-ethernet", "proto-ipv4", "proto-ipv6", "socket-tcp"] } +smoltcp = { version = "0.8.2", default-features = false, features = ["medium-ethernet", "proto-ipv4", "proto-ipv6", "socket-tcp"] } riscv = { version = "0.6.0", features = ["inline-asm"] } diff --git a/artiq/firmware/bootloader/main.rs b/artiq/firmware/bootloader/main.rs index c496a79b7..89d2002e1 100644 --- a/artiq/firmware/bootloader/main.rs +++ b/artiq/firmware/bootloader/main.rs @@ -497,7 +497,7 @@ pub extern fn main() -> i32 { println!(r"|_| |_|_|____/ \___/ \____|"); println!(""); println!("MiSoC Bootloader"); - println!("Copyright (c) 2017-2022 M-Labs Limited"); + println!("Copyright (c) 2017-2023 M-Labs Limited"); println!(""); #[cfg(has_ethmac)] diff --git a/artiq/firmware/ksupport/lib.rs b/artiq/firmware/ksupport/lib.rs index d9786ae3c..acb461f88 100644 --- a/artiq/firmware/ksupport/lib.rs +++ b/artiq/firmware/ksupport/lib.rs @@ -414,13 +414,13 @@ extern fn dma_playback(timestamp: i64, ptr: i32) { csr::rtio_dma::error_write(1); if error & 1 != 0 { raise!("RTIOUnderflow", - "RTIO underflow at {0} mu, channel {1}", - timestamp as i64, channel as i64, 0); + "RTIO underflow at channel {rtio_channel_info:0}, {1} mu", + channel as i64, timestamp as i64, 0); } if error & 2 != 0 { raise!("RTIODestinationUnreachable", - "RTIO destination unreachable, output, at {0} mu, channel {1}", - timestamp as i64, channel as i64, 0); + "RTIO destination unreachable, output, at channel {rtio_channel_info:0}, {1} mu", + channel as i64, timestamp as i64, 0); } } } @@ -471,6 +471,8 @@ unsafe fn attribute_writeback(typeinfo: *const ()) { } } +static mut STACK_GUARD_BASE: usize = 0x0; + #[no_mangle] pub unsafe fn main() { eh_artiq::reset_exception_buffer(KERNELCPU_PAYLOAD_ADDRESS); @@ -502,6 +504,7 @@ pub unsafe fn main() { ptr::write_bytes(__bss_start as *mut u8, 0, (_end - __bss_start) as usize); board_misoc::pmp::init_stack_guard(_sstack_guard as usize); + STACK_GUARD_BASE = _sstack_guard as usize; board_misoc::cache::flush_cpu_dcache(); board_misoc::cache::flush_cpu_icache(); @@ -531,10 +534,20 @@ pub unsafe fn main() { #[no_mangle] #[unwind(allowed)] -pub extern fn exception(_regs: *const u32) { +pub unsafe extern fn exception(_regs: *const u32) { let pc = mepc::read(); let cause = mcause::read().cause(); let mtval = mtval::read(); + if let mcause::Trap::Exception(mcause::Exception::LoadFault) + | mcause::Trap::Exception(mcause::Exception::StoreFault) = cause + { + if mtval >= STACK_GUARD_BASE + && mtval < (STACK_GUARD_BASE + board_misoc::pmp::STACK_GUARD_SIZE) + { + panic!("{:?} at PC {:#08x} in stack guard page ({:#08x}); stack overflow in user kernel code?", + cause, u32::try_from(pc).unwrap(), mtval); + } + } panic!("{:?} at PC {:#08x}, trap value {:#08x}", cause, u32::try_from(pc).unwrap(), mtval); } diff --git a/artiq/firmware/ksupport/rtio.rs b/artiq/firmware/ksupport/rtio.rs index d9f568f75..b0168ba03 100644 --- a/artiq/firmware/ksupport/rtio.rs +++ b/artiq/firmware/ksupport/rtio.rs @@ -67,13 +67,13 @@ mod imp { } if status & RTIO_O_STATUS_UNDERFLOW != 0 { raise!("RTIOUnderflow", - "RTIO underflow at {0} mu, channel {1}, slack {2} mu", - timestamp, channel as i64, timestamp - get_counter()); + "RTIO underflow at channel {rtio_channel_info:0}, {1} mu, slack {2} mu", + channel as i64, timestamp, timestamp - get_counter()); } if status & RTIO_O_STATUS_DESTINATION_UNREACHABLE != 0 { raise!("RTIODestinationUnreachable", - "RTIO destination unreachable, output, at {0} mu, channel {1}", - timestamp, channel as i64, 0); + "RTIO destination unreachable, output, at channel {rtio_channel_info:0}, {1} mu", + channel as i64, timestamp, 0); } } @@ -115,7 +115,7 @@ mod imp { if status & RTIO_I_STATUS_OVERFLOW != 0 { raise!("RTIOOverflow", - "RTIO input overflow on channel {0}", + "RTIO input overflow on channel {rtio_channel_info:0}", channel as i64, 0, 0); } if status & RTIO_I_STATUS_WAIT_EVENT != 0 { @@ -123,7 +123,7 @@ mod imp { } if status & RTIO_I_STATUS_DESTINATION_UNREACHABLE != 0 { raise!("RTIODestinationUnreachable", - "RTIO destination unreachable, input, on channel {0}", + "RTIO destination unreachable, input, on channel {rtio_channel_info:0}", channel as i64, 0, 0); } @@ -143,12 +143,12 @@ mod imp { if status & RTIO_I_STATUS_OVERFLOW != 0 { raise!("RTIOOverflow", - "RTIO input overflow on channel {0}", + "RTIO input overflow on channel {rtio_channel_info:0}", channel as i64, 0, 0); } if status & RTIO_I_STATUS_DESTINATION_UNREACHABLE != 0 { raise!("RTIODestinationUnreachable", - "RTIO destination unreachable, input, on channel {0}", + "RTIO destination unreachable, input, on channel {rtio_channel_info:0}", channel as i64, 0, 0); } @@ -168,7 +168,7 @@ mod imp { if status & RTIO_I_STATUS_OVERFLOW != 0 { raise!("RTIOOverflow", - "RTIO input overflow on channel {0}", + "RTIO input overflow on channel {rtio_channel_info:0}", channel as i64, 0, 0); } if status & RTIO_I_STATUS_WAIT_EVENT != 0 { @@ -176,7 +176,7 @@ mod imp { } if status & RTIO_I_STATUS_DESTINATION_UNREACHABLE != 0 { raise!("RTIODestinationUnreachable", - "RTIO destination unreachable, input, on channel {0}", + "RTIO destination unreachable, input, on channel {rtio_channel_info:0}", channel as i64, 0, 0); } diff --git a/artiq/firmware/libboard_artiq/lib.rs b/artiq/firmware/libboard_artiq/lib.rs index 7436fc8c5..209da3048 100644 --- a/artiq/firmware/libboard_artiq/lib.rs +++ b/artiq/firmware/libboard_artiq/lib.rs @@ -22,8 +22,6 @@ pub mod rpc_queue; #[cfg(has_si5324)] pub mod si5324; -#[cfg(has_wrpll)] -pub mod wrpll; #[cfg(has_grabber)] pub mod grabber; diff --git a/artiq/firmware/libboard_artiq/si5324.rs b/artiq/firmware/libboard_artiq/si5324.rs index 63e49bd5e..73491d84f 100644 --- a/artiq/firmware/libboard_artiq/si5324.rs +++ b/artiq/firmware/libboard_artiq/si5324.rs @@ -28,7 +28,7 @@ pub struct FrequencySettings { pub n31: u32, pub n32: u32, pub bwsel: u8, - pub crystal_ref: bool + pub crystal_as_ckin2: bool } pub enum Input { @@ -83,7 +83,7 @@ fn map_frequency_settings(settings: &FrequencySettings) -> Result Result<()> { pub fn setup(settings: &FrequencySettings, input: Input) -> Result<()> { let s = map_frequency_settings(settings)?; + let cksel_reg = match input { Input::Ckin1 => 0b00, Input::Ckin2 => 0b01, }; init()?; - if settings.crystal_ref { + if settings.crystal_as_ckin2 { write(0, read(0)? | 0x40)?; // FREE_RUN=1 } write(2, (read(2)? & 0x0f) | (s.bwsel << 4))?; diff --git a/artiq/firmware/libboard_artiq/wrpll.rs b/artiq/firmware/libboard_artiq/wrpll.rs deleted file mode 100644 index a9c9a702f..000000000 --- a/artiq/firmware/libboard_artiq/wrpll.rs +++ /dev/null @@ -1,536 +0,0 @@ -use board_misoc::{csr, clock}; - -mod i2c { - use board_misoc::{csr, clock}; - - #[derive(Debug, Clone, Copy)] - pub enum Dcxo { - Main, - Helper - } - - fn half_period() { clock::spin_us(1) } - const SDA_MASK: u8 = 2; - const SCL_MASK: u8 = 1; - - fn sda_i(dcxo: Dcxo) -> bool { - let reg = match dcxo { - Dcxo::Main => unsafe { csr::wrpll::main_dcxo_gpio_in_read() }, - Dcxo::Helper => unsafe { csr::wrpll::helper_dcxo_gpio_in_read() }, - }; - reg & SDA_MASK != 0 - } - - fn sda_oe(dcxo: Dcxo, oe: bool) { - let reg = match dcxo { - Dcxo::Main => unsafe { csr::wrpll::main_dcxo_gpio_oe_read() }, - Dcxo::Helper => unsafe { csr::wrpll::helper_dcxo_gpio_oe_read() }, - }; - let reg = if oe { reg | SDA_MASK } else { reg & !SDA_MASK }; - match dcxo { - Dcxo::Main => unsafe { csr::wrpll::main_dcxo_gpio_oe_write(reg) }, - Dcxo::Helper => unsafe { csr::wrpll::helper_dcxo_gpio_oe_write(reg) } - } - } - - fn sda_o(dcxo: Dcxo, o: bool) { - let reg = match dcxo { - Dcxo::Main => unsafe { csr::wrpll::main_dcxo_gpio_out_read() }, - Dcxo::Helper => unsafe { csr::wrpll::helper_dcxo_gpio_out_read() }, - }; - let reg = if o { reg | SDA_MASK } else { reg & !SDA_MASK }; - match dcxo { - Dcxo::Main => unsafe { csr::wrpll::main_dcxo_gpio_out_write(reg) }, - Dcxo::Helper => unsafe { csr::wrpll::helper_dcxo_gpio_out_write(reg) } - } - } - - fn scl_oe(dcxo: Dcxo, oe: bool) { - let reg = match dcxo { - Dcxo::Main => unsafe { csr::wrpll::main_dcxo_gpio_oe_read() }, - Dcxo::Helper => unsafe { csr::wrpll::helper_dcxo_gpio_oe_read() }, - }; - let reg = if oe { reg | SCL_MASK } else { reg & !SCL_MASK }; - match dcxo { - Dcxo::Main => unsafe { csr::wrpll::main_dcxo_gpio_oe_write(reg) }, - Dcxo::Helper => unsafe { csr::wrpll::helper_dcxo_gpio_oe_write(reg) } - } - } - - fn scl_o(dcxo: Dcxo, o: bool) { - let reg = match dcxo { - Dcxo::Main => unsafe { csr::wrpll::main_dcxo_gpio_out_read() }, - Dcxo::Helper => unsafe { csr::wrpll::helper_dcxo_gpio_out_read() }, - }; - let reg = if o { reg | SCL_MASK } else { reg & !SCL_MASK }; - match dcxo { - Dcxo::Main => unsafe { csr::wrpll::main_dcxo_gpio_out_write(reg) }, - Dcxo::Helper => unsafe { csr::wrpll::helper_dcxo_gpio_out_write(reg) } - } - } - - pub fn init(dcxo: Dcxo) -> Result<(), &'static str> { - // Set SCL as output, and high level - scl_o(dcxo, true); - scl_oe(dcxo, true); - // Prepare a zero level on SDA so that sda_oe pulls it down - sda_o(dcxo, false); - // Release SDA - sda_oe(dcxo, false); - - // Check the I2C bus is ready - half_period(); - half_period(); - if !sda_i(dcxo) { - // Try toggling SCL a few times - for _bit in 0..8 { - scl_o(dcxo, false); - half_period(); - scl_o(dcxo, true); - half_period(); - } - } - - if !sda_i(dcxo) { - return Err("SDA is stuck low and doesn't get unstuck"); - } - Ok(()) - } - - pub fn start(dcxo: Dcxo) { - // Set SCL high then SDA low - scl_o(dcxo, true); - half_period(); - sda_oe(dcxo, true); - half_period(); - } - - pub fn stop(dcxo: Dcxo) { - // First, make sure SCL is low, so that the target releases the SDA line - scl_o(dcxo, false); - half_period(); - // Set SCL high then SDA high - sda_oe(dcxo, true); - scl_o(dcxo, true); - half_period(); - sda_oe(dcxo, false); - half_period(); - } - - pub fn write(dcxo: Dcxo, data: u8) -> bool { - // MSB first - for bit in (0..8).rev() { - // Set SCL low and set our bit on SDA - scl_o(dcxo, false); - sda_oe(dcxo, data & (1 << bit) == 0); - half_period(); - // Set SCL high ; data is shifted on the rising edge of SCL - scl_o(dcxo, true); - half_period(); - } - // Check ack - // Set SCL low, then release SDA so that the I2C target can respond - scl_o(dcxo, false); - half_period(); - sda_oe(dcxo, false); - // Set SCL high and check for ack - scl_o(dcxo, true); - half_period(); - // returns true if acked (I2C target pulled SDA low) - !sda_i(dcxo) - } - - pub fn read(dcxo: Dcxo, ack: bool) -> u8 { - // Set SCL low first, otherwise setting SDA as input may cause a transition - // on SDA with SCL high which will be interpreted as START/STOP condition. - scl_o(dcxo, false); - half_period(); // make sure SCL has settled low - sda_oe(dcxo, false); - - let mut data: u8 = 0; - - // MSB first - for bit in (0..8).rev() { - scl_o(dcxo, false); - half_period(); - // Set SCL high and shift data - scl_o(dcxo, true); - half_period(); - if sda_i(dcxo) { data |= 1 << bit } - } - // Send ack - // Set SCL low and pull SDA low when acking - scl_o(dcxo, false); - if ack { sda_oe(dcxo, true) } - half_period(); - // then set SCL high - scl_o(dcxo, true); - half_period(); - - data - } -} - -mod si549 { - use board_misoc::clock; - use super::i2c; - - #[cfg(soc_platform = "kasli")] - pub const ADDRESS: u8 = 0x67; - - pub fn write(dcxo: i2c::Dcxo, reg: u8, val: u8) -> Result<(), &'static str> { - i2c::start(dcxo); - if !i2c::write(dcxo, ADDRESS << 1) { - return Err("Si549 failed to ack write address") - } - if !i2c::write(dcxo, reg) { - return Err("Si549 failed to ack register") - } - if !i2c::write(dcxo, val) { - return Err("Si549 failed to ack value") - } - i2c::stop(dcxo); - Ok(()) - } - - pub fn write_no_ack_value(dcxo: i2c::Dcxo, reg: u8, val: u8) -> Result<(), &'static str> { - i2c::start(dcxo); - if !i2c::write(dcxo, ADDRESS << 1) { - return Err("Si549 failed to ack write address") - } - if !i2c::write(dcxo, reg) { - return Err("Si549 failed to ack register") - } - i2c::write(dcxo, val); - i2c::stop(dcxo); - Ok(()) - } - - pub fn read(dcxo: i2c::Dcxo, reg: u8) -> Result { - i2c::start(dcxo); - if !i2c::write(dcxo, ADDRESS << 1) { - return Err("Si549 failed to ack write address") - } - if !i2c::write(dcxo, reg) { - return Err("Si549 failed to ack register") - } - i2c::stop(dcxo); - - i2c::start(dcxo); - if !i2c::write(dcxo, (ADDRESS << 1) | 1) { - return Err("Si549 failed to ack read address") - } - let val = i2c::read(dcxo, false); - i2c::stop(dcxo); - - Ok(val) - } - - pub fn program(dcxo: i2c::Dcxo, hsdiv: u16, lsdiv: u8, fbdiv: u64) -> Result<(), &'static str> { - i2c::init(dcxo)?; - - write(dcxo, 255, 0x00)?; // PAGE - write_no_ack_value(dcxo, 7, 0x80)?; // RESET - clock::spin_us(100_000); // required? not specified in datasheet. - - write(dcxo, 255, 0x00)?; // PAGE - write(dcxo, 69, 0x00)?; // Disable FCAL override. - // Note: Value 0x00 from Table 5.6 is inconsistent with Table 5.7, - // which shows bit 0 as reserved and =1. - write(dcxo, 17, 0x00)?; // Synchronously disable output - - // The Si549 has no ID register, so we check that it responds correctly - // by writing values to a RAM-like register and reading them back. - for test_value in 0..255 { - write(dcxo, 23, test_value)?; - let readback = read(dcxo, 23)?; - if readback != test_value { - return Err("Si549 detection failed"); - } - } - - write(dcxo, 23, hsdiv as u8)?; - write(dcxo, 24, (hsdiv >> 8) as u8 | (lsdiv << 4))?; - write(dcxo, 26, fbdiv as u8)?; - write(dcxo, 27, (fbdiv >> 8) as u8)?; - write(dcxo, 28, (fbdiv >> 16) as u8)?; - write(dcxo, 29, (fbdiv >> 24) as u8)?; - write(dcxo, 30, (fbdiv >> 32) as u8)?; - write(dcxo, 31, (fbdiv >> 40) as u8)?; - - write(dcxo, 7, 0x08)?; // Start FCAL - write(dcxo, 17, 0x01)?; // Synchronously enable output - - Ok(()) - } - - // Si549 digital frequency trim ("all-digital PLL" register) - // ∆ f_out = adpll * 0.0001164e-6 (0.1164 ppb/lsb) - // max trim range is +- 950 ppm - pub fn set_adpll(dcxo: i2c::Dcxo, adpll: i32) -> Result<(), &'static str> { - write(dcxo, 231, adpll as u8)?; - write(dcxo, 232, (adpll >> 8) as u8)?; - write(dcxo, 233, (adpll >> 16) as u8)?; - clock::spin_us(100); - Ok(()) - } - - pub fn get_adpll(dcxo: i2c::Dcxo) -> Result { - let b1 = read(dcxo, 231)? as i32; - let b2 = read(dcxo, 232)? as i32; - let b3 = read(dcxo, 233)? as i8 as i32; - Ok(b3 << 16 | b2 << 8 | b1) - } -} - -// to do: load from gateware config -const DDMTD_COUNTER_N: u32 = 15; -const DDMTD_COUNTER_M: u32 = (1 << DDMTD_COUNTER_N); -const F_SYS: f64 = csr::CONFIG_CLOCK_FREQUENCY as f64; - -const F_MAIN: f64 = 125.0e6; -const F_HELPER: f64 = F_MAIN * DDMTD_COUNTER_M as f64 / (DDMTD_COUNTER_M + 1) as f64; -const F_BEAT: f64 = F_MAIN - F_HELPER; -const TIME_STEP: f32 = 1./F_BEAT as f32; - -fn ddmtd_tag_to_s(mu: f32) -> f32 { - return (mu as f32)*TIME_STEP; -} - -fn get_frequencies() -> (u32, u32, u32) { - unsafe { - csr::wrpll::frequency_counter_update_en_write(1); - // wait for at least one full update cycle (> 2 timer periods) - clock::spin_us(200_000); - csr::wrpll::frequency_counter_update_en_write(0); - let helper = csr::wrpll::frequency_counter_counter_helper_read(); - let main = csr::wrpll::frequency_counter_counter_rtio_read(); - let cdr = csr::wrpll::frequency_counter_counter_rtio_rx0_read(); - (helper, main, cdr) - } -} - -fn log_frequencies() -> (u32, u32, u32) { - let (f_helper, f_main, f_cdr) = get_frequencies(); - let conv_khz = |f| 4*(f as u64)*(csr::CONFIG_CLOCK_FREQUENCY as u64)/(1000*(1 << 23)); - info!("helper clock frequency: {}kHz ({})", conv_khz(f_helper), f_helper); - info!("main clock frequency: {}kHz ({})", conv_khz(f_main), f_main); - info!("CDR clock frequency: {}kHz ({})", conv_khz(f_cdr), f_cdr); - (f_helper, f_main, f_cdr) -} - -fn get_tags() -> (i32, i32, u16, u16) { - unsafe { - csr::wrpll::tag_arm_write(1); - while csr::wrpll::tag_arm_read() != 0 {} - - let main_diff = csr::wrpll::main_diff_tag_read() as i32; - let helper_diff = csr::wrpll::helper_diff_tag_read() as i32; - let ref_tag = csr::wrpll::ref_tag_read(); - let main_tag = csr::wrpll::main_tag_read(); - (main_diff, helper_diff, ref_tag, main_tag) - } -} - -fn print_tags() { - const NUM_TAGS: usize = 30; - let mut main_diffs = [0; NUM_TAGS]; // input to main loop filter - let mut helper_diffs = [0; NUM_TAGS]; // input to helper loop filter - let mut ref_tags = [0; NUM_TAGS]; - let mut main_tags = [0; NUM_TAGS]; - let mut jitter = [0 as f32; NUM_TAGS]; - - for i in 0..NUM_TAGS { - let (main_diff, helper_diff, ref_tag, main_tag) = get_tags(); - main_diffs[i] = main_diff; - helper_diffs[i] = helper_diff; - ref_tags[i] = ref_tag; - main_tags[i] = main_tag; - } - info!("DDMTD ref tags: {:?}", ref_tags); - info!("DDMTD main tags: {:?}", main_tags); - info!("DDMTD main diffs: {:?}", main_diffs); - info!("DDMTD helper diffs: {:?}", helper_diffs); - - // look at the difference between the main DCXO and reference... - let t0 = main_diffs[0]; - main_diffs.iter_mut().for_each(|x| *x -= t0); - - // crude estimate of the max difference across our sample set (assumes no unwrapping issues...) - let delta = main_diffs[main_diffs.len()-1] as f32 / (main_diffs.len()-1) as f32; - info!("detla: {:?} tags", delta); - let delta_f: f32 = delta/DDMTD_COUNTER_M as f32 * F_BEAT as f32; - info!("MAIN <-> ref frequency difference: {:?} Hz ({:?} ppm)", delta_f, delta_f/F_HELPER as f32 * 1e6); - - jitter.iter_mut().enumerate().for_each(|(i, x)| *x = main_diffs[i] as f32 - delta*(i as f32)); - info!("jitter: {:?} tags", jitter); - - let var = jitter.iter().map(|x| x*x).fold(0 as f32, |acc, x| acc + x as f32) / NUM_TAGS as f32; - info!("variance: {:?} tags^2", var); -} - -pub fn init() { - info!("initializing WR PLL..."); - - unsafe { csr::wrpll::helper_reset_write(1); } - - unsafe { - csr::wrpll::helper_dcxo_i2c_address_write(si549::ADDRESS); - csr::wrpll::main_dcxo_i2c_address_write(si549::ADDRESS); - } - - #[cfg(rtio_frequency = "125.0")] - let (h_hsdiv, h_lsdiv, h_fbdiv) = (0x05c, 0, 0x04b5badb98a); - #[cfg(rtio_frequency = "125.0")] - let (m_hsdiv, m_lsdiv, m_fbdiv) = (0x05c, 0, 0x04b5c447213); - - si549::program(i2c::Dcxo::Main, m_hsdiv, m_lsdiv, m_fbdiv) - .expect("cannot initialize main Si549"); - si549::program(i2c::Dcxo::Helper, h_hsdiv, h_lsdiv, h_fbdiv) - .expect("cannot initialize helper Si549"); - // Si549 Settling Time for Large Frequency Change. - // Datasheet said 10ms but it lied. - clock::spin_us(50_000); - - unsafe { csr::wrpll::helper_reset_write(0); } - clock::spin_us(1); -} - -pub fn diagnostics() { - info!("WRPLL diagnostics..."); - info!("Untrimmed oscillator frequencies:"); - log_frequencies(); - - info!("Increase helper DCXO frequency by +10ppm (1.25kHz):"); - si549::set_adpll(i2c::Dcxo::Helper, 85911).expect("ADPLL write failed"); - // to do: add check on frequency? - log_frequencies(); -} - -fn trim_dcxos(f_helper: u32, f_main: u32, f_cdr: u32) -> Result<(i32, i32), &'static str> { - info!("Trimming oscillator frequencies..."); - const DCXO_STEP: i64 = (1.0e6/0.0001164) as i64; - const ADPLL_MAX: i64 = (950.0/0.0001164) as i64; - - const TIMER_WIDTH: u32 = 23; - const COUNTER_DIV: u32 = 2; - - // how many counts we expect to measure - const SYS_COUNTS: i64 = (1 << (TIMER_WIDTH - COUNTER_DIV)) as i64; - const EXP_MAIN_COUNTS: i64 = ((SYS_COUNTS as f64) * (F_MAIN/F_SYS)) as i64; - const EXP_HELPER_COUNTS: i64 = ((SYS_COUNTS as f64) * (F_HELPER/F_SYS)) as i64; - - // calibrate the SYS clock to the CDR clock and correct the measured counts - // assume frequency errors are small so we can make an additive correction - // positive error means sys clock is too fast - let sys_err: i64 = EXP_MAIN_COUNTS - (f_cdr as i64); - let main_err: i64 = EXP_MAIN_COUNTS - (f_main as i64) - sys_err; - let helper_err: i64 = EXP_HELPER_COUNTS - (f_helper as i64) - sys_err; - - info!("sys count err {}", sys_err); - info!("main counts err {}", main_err); - info!("helper counts err {}", helper_err); - - // calculate required adjustment to the ADPLL register see - // https://www.silabs.com/documents/public/data-sheets/si549-datasheet.pdf - // section 5.6 - let helper_adpll: i64 = helper_err*DCXO_STEP/EXP_HELPER_COUNTS; - let main_adpll: i64 = main_err*DCXO_STEP/EXP_MAIN_COUNTS; - if helper_adpll.abs() > ADPLL_MAX { - return Err("helper DCXO offset too large"); - } - if main_adpll.abs() > ADPLL_MAX { - return Err("main DCXO offset too large"); - } - - info!("ADPLL offsets: helper={} main={}", helper_adpll, main_adpll); - Ok((helper_adpll as i32, main_adpll as i32)) -} - -fn statistics(data: &[u16]) -> (f32, f32) { - let sum = data.iter().fold(0 as u32, |acc, x| acc + *x as u32); - let mean = sum as f32 / data.len() as f32; - - let squared_sum = data.iter().fold(0 as u32, |acc, x| acc + (*x as u32).pow(2)); - let variance = (squared_sum as f32 / data.len() as f32) - mean; - return (mean, variance) -} - -fn select_recovered_clock_int(rc: bool) -> Result<(), &'static str> { - info!("Untrimmed oscillator frequencies:"); - let (f_helper, f_main, f_cdr) = log_frequencies(); - if rc { - let (helper_adpll, main_adpll) = trim_dcxos(f_helper, f_main, f_cdr)?; - // to do: add assertion on max frequency shift here? - si549::set_adpll(i2c::Dcxo::Helper, helper_adpll).expect("ADPLL write failed"); - si549::set_adpll(i2c::Dcxo::Main, main_adpll).expect("ADPLL write failed"); - - log_frequencies(); - clock::spin_us(100_000); // TO DO: remove/reduce! - print_tags(); - - info!("increasing main DCXO by 1ppm (125Hz):"); - si549::set_adpll(i2c::Dcxo::Main, main_adpll + 8591).expect("ADPLL write failed"); - clock::spin_us(100_000); - print_tags(); - - si549::set_adpll(i2c::Dcxo::Main, main_adpll).expect("ADPLL write failed"); - - unsafe { - csr::wrpll::adpll_offset_helper_write(helper_adpll as u32); - csr::wrpll::adpll_offset_main_write(main_adpll as u32); - csr::wrpll::helper_dcxo_gpio_enable_write(0); - csr::wrpll::main_dcxo_gpio_enable_write(0); - csr::wrpll::helper_dcxo_errors_write(0xff); - csr::wrpll::main_dcxo_errors_write(0xff); - csr::wrpll::collector_reset_write(0); - } - clock::spin_us(1_000); // wait for the collector to produce meaningful output - unsafe { - csr::wrpll::filter_reset_write(0); - } - - clock::spin_us(100_000); - - print_tags(); -// let mut tags = [0; 10]; -// for i in 0..tags.len() { -// tags[i] = get_ddmtd_helper_tag(); -// } -// info!("DDMTD helper tags: {:?}", tags); - - unsafe { - csr::wrpll::filter_reset_write(1); - csr::wrpll::collector_reset_write(1); - } - clock::spin_us(50_000); - unsafe { - csr::wrpll::helper_dcxo_gpio_enable_write(1); - csr::wrpll::main_dcxo_gpio_enable_write(1); - } - unsafe { - info!("error {} {}", - csr::wrpll::helper_dcxo_errors_read(), - csr::wrpll::main_dcxo_errors_read()); - } - info!("new ADPLL: {} {}", - si549::get_adpll(i2c::Dcxo::Helper)?, - si549::get_adpll(i2c::Dcxo::Main)?); - } else { - si549::set_adpll(i2c::Dcxo::Helper, 0).expect("ADPLL write failed"); - si549::set_adpll(i2c::Dcxo::Main, 0).expect("ADPLL write failed"); - } - Ok(()) -} - -pub fn select_recovered_clock(rc: bool) { - if rc { - info!("switching to recovered clock"); - } else { - info!("switching to local XO clock"); - } - match select_recovered_clock_int(rc) { - Ok(()) => info!("clock transition completed"), - Err(e) => error!("clock transition failed: {}", e) - } -} diff --git a/artiq/firmware/libboard_misoc/Cargo.toml b/artiq/firmware/libboard_misoc/Cargo.toml index 69c1d46d3..7b4951ea7 100644 --- a/artiq/firmware/libboard_misoc/Cargo.toml +++ b/artiq/firmware/libboard_misoc/Cargo.toml @@ -15,7 +15,7 @@ build_misoc = { path = "../libbuild_misoc" } [dependencies] byteorder = { version = "1.0", default-features = false } log = { version = "0.4", default-features = false, optional = true } -smoltcp = { version = "0.8.0", default-features = false, optional = true } +smoltcp = { version = "0.8.2", default-features = false, optional = true } riscv = { version = "0.6.0", features = ["inline-asm"] } [features] diff --git a/artiq/firmware/libboard_misoc/io_expander.rs b/artiq/firmware/libboard_misoc/io_expander.rs index 19baf5364..edf2ce666 100644 --- a/artiq/firmware/libboard_misoc/io_expander.rs +++ b/artiq/firmware/libboard_misoc/io_expander.rs @@ -1,5 +1,14 @@ -use i2c; use csr; +use i2c; + +// Only the bare minimum registers. Bits/IO connections equivalent between IC types. +struct Registers { + // PCA9539 equivalent register names in comments + iodira: u8, // Configuration Port 0 + iodirb: u8, // Configuration Port 1 + gpioa: u8, // Output Port 0 + gpiob: u8, // Output Port 1 +} pub struct IoExpander { busno: u8, @@ -9,15 +18,17 @@ pub struct IoExpander { iodir: [u8; 2], out_current: [u8; 2], out_target: [u8; 2], + registers: Registers, } impl IoExpander { #[cfg(all(soc_platform = "kasli", hw_rev = "v2.0"))] - pub fn new(index: u8) -> Self { + pub fn new(index: u8) -> Result { const VIRTUAL_LED_MAPPING0: [(u8, u8, u8); 2] = [(0, 0, 6), (1, 1, 6)]; const VIRTUAL_LED_MAPPING1: [(u8, u8, u8); 2] = [(2, 0, 6), (3, 1, 6)]; + // Both expanders on SHARED I2C bus - match index { + let mut io_expander = match index { 0 => IoExpander { busno: 0, port: 11, @@ -26,6 +37,12 @@ impl IoExpander { iodir: [0xff; 2], out_current: [0; 2], out_target: [0; 2], + registers: Registers { + iodira: 0x00, + iodirb: 0x01, + gpioa: 0x12, + gpiob: 0x13, + }, }, 1 => IoExpander { busno: 0, @@ -35,9 +52,33 @@ impl IoExpander { iodir: [0xff; 2], out_current: [0; 2], out_target: [0; 2], + registers: Registers { + iodira: 0x00, + iodirb: 0x01, + gpioa: 0x12, + gpiob: 0x13, + }, }, - _ => panic!("incorrect I/O expander index"), + _ => return Err("incorrect I/O expander index"), + }; + if !io_expander.check_ack()? { + #[cfg(feature = "log")] + log::info!( + "MCP23017 io expander {} not found. Checking for PCA9539.", + index + ); + io_expander.address += 0xa8; // translate to PCA9539 addresses (see schematic) + io_expander.registers = Registers { + iodira: 0x06, + iodirb: 0x07, + gpioa: 0x02, + gpiob: 0x03, + }; + if !io_expander.check_ack()? { + return Err("Neither MCP23017 nor PCA9539 io expander found."); + }; } + Ok(io_expander) } #[cfg(soc_platform = "kasli")] @@ -57,9 +98,18 @@ impl IoExpander { Ok(()) } + fn check_ack(&self) -> Result { + // Check for ack from io expander + self.select()?; + i2c::start(self.busno)?; + let ack = i2c::write(self.busno, self.address)?; + i2c::stop(self.busno)?; + Ok(ack) + } + fn update_iodir(&self) -> Result<(), &'static str> { - self.write(0x00, self.iodir[0])?; - self.write(0x01, self.iodir[1])?; + self.write(self.registers.iodira, self.iodir[0])?; + self.write(self.registers.iodirb, self.iodir[1])?; Ok(()) } @@ -72,9 +122,9 @@ impl IoExpander { self.update_iodir()?; self.out_current[0] = 0x00; - self.write(0x12, 0x00)?; + self.write(self.registers.gpioa, 0x00)?; self.out_current[1] = 0x00; - self.write(0x13, 0x00)?; + self.write(self.registers.gpiob, 0x00)?; Ok(()) } @@ -94,20 +144,18 @@ impl IoExpander { pub fn service(&mut self) -> Result<(), &'static str> { for (led, port, bit) in self.virtual_led_mapping.iter() { - let level = unsafe { - (csr::virtual_leds::status_read() >> led) & 1 - }; + let level = unsafe { (csr::virtual_leds::status_read() >> led) & 1 }; self.set(*port, *bit, level != 0); } if self.out_target != self.out_current { self.select()?; if self.out_target[0] != self.out_current[0] { - self.write(0x12, self.out_target[0])?; + self.write(self.registers.gpioa, self.out_target[0])?; self.out_current[0] = self.out_target[0]; } if self.out_target[1] != self.out_current[1] { - self.write(0x13, self.out_target[1])?; + self.write(self.registers.gpiob, self.out_target[1])?; self.out_current[1] = self.out_target[1]; } } diff --git a/artiq/firmware/libboard_misoc/riscv32/pmp.rs b/artiq/firmware/libboard_misoc/riscv32/pmp.rs index 9ac80b9e3..976f257b1 100644 --- a/artiq/firmware/libboard_misoc/riscv32/pmp.rs +++ b/artiq/firmware/libboard_misoc/riscv32/pmp.rs @@ -9,6 +9,8 @@ const PMP_W : usize = 0b00000010; const PMP_R : usize = 0b00000001; const PMP_OFF : usize = 0b00000000; +pub const STACK_GUARD_SIZE: usize = 0x1000; + #[inline(always)] pub unsafe fn init_stack_guard(guard_base: usize) { pmpaddr2::write((guard_base >> 2) | ((0x1000 - 1) >> 3)); diff --git a/artiq/firmware/libproto_artiq/rpc_proto.rs b/artiq/firmware/libproto_artiq/rpc_proto.rs index 85fc902ba..49b78e633 100644 --- a/artiq/firmware/libproto_artiq/rpc_proto.rs +++ b/artiq/firmware/libproto_artiq/rpc_proto.rs @@ -429,8 +429,8 @@ mod tag { // the ptr/length(s) pair is basically CSlice Tag::Bytes | Tag::String | Tag::ByteArray | Tag::List(_) | Tag::Array(_, _) => core::mem::align_of::>(), - // will not be sent from the host - _ => unreachable!("unexpected tag from host") + Tag::Keyword(_) => unreachable!("Tag::Keyword should not appear in composite types"), + Tag::Object => core::mem::align_of::(), } } diff --git a/artiq/firmware/runtime/main.rs b/artiq/firmware/runtime/main.rs index f91f4d391..6738d7c55 100644 --- a/artiq/firmware/runtime/main.rs +++ b/artiq/firmware/runtime/main.rs @@ -1,4 +1,4 @@ -#![feature(lang_items, panic_info_message)] +#![feature(lang_items, panic_info_message, const_btree_new, iter_advance_by)] #![no_std] extern crate eh; @@ -104,8 +104,8 @@ fn startup() { let (mut io_expander0, mut io_expander1); #[cfg(all(soc_platform = "kasli", hw_rev = "v2.0"))] { - io_expander0 = board_misoc::io_expander::IoExpander::new(0); - io_expander1 = board_misoc::io_expander::IoExpander::new(1); + io_expander0 = board_misoc::io_expander::IoExpander::new(0).unwrap(); + io_expander1 = board_misoc::io_expander::IoExpander::new(1).unwrap(); io_expander0.init().expect("I2C I/O expander #0 initialization failed"); io_expander1.init().expect("I2C I/O expander #1 initialization failed"); diff --git a/artiq/firmware/runtime/rtio_clocking.rs b/artiq/firmware/runtime/rtio_clocking.rs index f5159ad2f..f094e5383 100644 --- a/artiq/firmware/runtime/rtio_clocking.rs +++ b/artiq/firmware/runtime/rtio_clocking.rs @@ -1,7 +1,5 @@ use board_misoc::config; -#[cfg(si5324_as_synthesizer)] use board_artiq::si5324; -#[cfg(has_drtio)] use board_misoc::{csr, clock}; #[derive(Debug, PartialEq)] @@ -10,7 +8,6 @@ pub enum RtioClock { Default, Int_125, Int_100, - Int_150, Ext0_Bypass, Ext0_Synth0_10to125, Ext0_Synth0_100to125, @@ -23,7 +20,6 @@ fn get_rtio_clock_cfg() -> RtioClock { let res = match result { Ok("int_125") => RtioClock::Int_125, Ok("int_100") => RtioClock::Int_100, - Ok("int_150") => RtioClock::Int_150, Ok("ext0_bypass") => RtioClock::Ext0_Bypass, Ok("ext0_bypass_125") => RtioClock::Ext0_Bypass, Ok("ext0_bypass_100") => RtioClock::Ext0_Bypass, @@ -54,9 +50,7 @@ fn get_rtio_clock_cfg() -> RtioClock { return RtioClock::Ext0_Synth0_125to125; #[cfg(all(rtio_frequency = "125.0", not(si5324_ext_ref)))] return RtioClock::Int_125; - #[cfg(all(rtio_frequency = "150.0", not(si5324_ext_ref)))] - return RtioClock::Int_150; - #[cfg(all(rtio_frequency = "100.0", not(si5324_ext_ref)))] + #[cfg(all(rtio_frequency = "100.0", not(si5324_ext_ref), not(soc_platform = "kasli")))] return RtioClock::Int_100; //in case nothing is set return RtioClock::Int_125; @@ -68,40 +62,12 @@ fn get_rtio_clock_cfg() -> RtioClock { #[cfg(has_rtio_crg)] pub mod crg { - #[cfg(has_rtio_clock_switch)] - use super::RtioClock; use board_misoc::{clock, csr}; pub fn check() -> bool { unsafe { csr::rtio_crg::pll_locked_read() != 0 } } - #[cfg(has_rtio_clock_switch)] - pub fn init(clk: RtioClock) -> bool { - let clk_sel: u8 = match clk { - RtioClock::Ext0_Bypass => { - info!("Using external clock"); - 1 - }, - RtioClock::Int_125 => { - info!("Using internal RTIO clock"); - 0 - }, - _ => { - warn!("rtio_clock setting '{:?}' is not supported. Using default internal RTIO clock instead", clk); - 0 - } - }; - unsafe { - csr::rtio_crg::pll_reset_write(1); - csr::rtio_crg::clock_sel_write(clk_sel); - csr::rtio_crg::pll_reset_write(0); - } - clock::spin_us(150); - return check() - } - - #[cfg(not(has_rtio_clock_switch))] pub fn init() -> bool { info!("Using internal RTIO clock"); unsafe { @@ -117,147 +83,177 @@ pub mod crg { pub fn check() -> bool { true } } -#[cfg(si5324_as_synthesizer)] -fn setup_si5324_as_synthesizer(cfg: RtioClock) { - let si5324_settings = match cfg { +// Si5324 input to select for locking to an external clock (as opposed to +// a recovered link clock in DRTIO satellites, which is handled elsewhere). +#[cfg(all(soc_platform = "kasli", hw_rev = "v2.0"))] +const SI5324_EXT_INPUT: si5324::Input = si5324::Input::Ckin1; +#[cfg(all(soc_platform = "kasli", not(hw_rev = "v2.0")))] +const SI5324_EXT_INPUT: si5324::Input = si5324::Input::Ckin2; +#[cfg(all(soc_platform = "kc705"))] +const SI5324_EXT_INPUT: si5324::Input = si5324::Input::Ckin2; + +fn setup_si5324_pll(cfg: RtioClock) { + let (si5324_settings, si5324_ref_input) = match cfg { RtioClock::Ext0_Synth0_10to125 => { // 125 MHz output from 10 MHz CLKINx reference, 504 Hz BW info!("using 10MHz reference to make 125MHz RTIO clock with PLL"); - si5324::FrequencySettings { - n1_hs : 10, - nc1_ls : 4, - n2_hs : 10, - n2_ls : 300, - n31 : 6, - n32 : 6, - bwsel : 4, - crystal_ref: false - } + ( + si5324::FrequencySettings { + n1_hs : 10, + nc1_ls : 4, + n2_hs : 10, + n2_ls : 300, + n31 : 6, + n32 : 6, + bwsel : 4, + crystal_as_ckin2: false + }, + SI5324_EXT_INPUT + ) }, RtioClock::Ext0_Synth0_100to125 => { // 125MHz output, from 100MHz CLKINx reference, 586 Hz loop bandwidth - info!("using 10MHz reference to make 125MHz RTIO clock with PLL"); - si5324::FrequencySettings { - n1_hs : 10, - nc1_ls : 4, - n2_hs : 10, - n2_ls : 260, - n31 : 52, - n32 : 52, - bwsel : 4, - crystal_ref: false - } + info!("using 100MHz reference to make 125MHz RTIO clock with PLL"); + ( + si5324::FrequencySettings { + n1_hs : 10, + nc1_ls : 4, + n2_hs : 10, + n2_ls : 260, + n31 : 52, + n32 : 52, + bwsel : 4, + crystal_as_ckin2: false + }, + SI5324_EXT_INPUT + ) }, RtioClock::Ext0_Synth0_125to125 => { // 125MHz output, from 125MHz CLKINx reference, 606 Hz loop bandwidth - info!("using 10MHz reference to make 125MHz RTIO clock with PLL"); - si5324::FrequencySettings { - n1_hs : 5, - nc1_ls : 8, - n2_hs : 7, - n2_ls : 360, - n31 : 63, - n32 : 63, - bwsel : 4, - crystal_ref: false - } - }, - RtioClock::Int_150 => { // 150MHz output, from crystal - info!("using internal 150MHz RTIO clock"); - si5324::FrequencySettings { - n1_hs : 9, - nc1_ls : 4, - n2_hs : 10, - n2_ls : 33732, - n31 : 7139, - n32 : 7139, - bwsel : 3, - crystal_ref: true - } + info!("using 125MHz reference to make 125MHz RTIO clock with PLL"); + ( + si5324::FrequencySettings { + n1_hs : 5, + nc1_ls : 8, + n2_hs : 7, + n2_ls : 360, + n31 : 63, + n32 : 63, + bwsel : 4, + crystal_as_ckin2: false + }, + SI5324_EXT_INPUT + ) }, RtioClock::Int_100 => { // 100MHz output, from crystal info!("using internal 100MHz RTIO clock"); - si5324::FrequencySettings { - n1_hs : 9, - nc1_ls : 6, - n2_hs : 10, - n2_ls : 33732, - n31 : 7139, - n32 : 7139, - bwsel : 3, - crystal_ref: true - } + ( + si5324::FrequencySettings { + n1_hs : 9, + nc1_ls : 6, + n2_hs : 10, + n2_ls : 33732, + n31 : 7139, + n32 : 7139, + bwsel : 3, + crystal_as_ckin2: true + }, + si5324::Input::Ckin2 + ) }, RtioClock::Int_125 => { // 125MHz output, from crystal, 7 Hz info!("using internal 125MHz RTIO clock"); - si5324::FrequencySettings { - n1_hs : 10, - nc1_ls : 4, - n2_hs : 10, - n2_ls : 19972, - n31 : 4565, - n32 : 4565, - bwsel : 4, - crystal_ref: true - } - } + ( + si5324::FrequencySettings { + n1_hs : 10, + nc1_ls : 4, + n2_hs : 10, + n2_ls : 19972, + n31 : 4565, + n32 : 4565, + bwsel : 4, + crystal_as_ckin2: true + }, + si5324::Input::Ckin2 + ) + }, _ => { // 125MHz output like above, default (if chosen option is not supported) warn!("rtio_clock setting '{:?}' is not supported. Falling back to default internal 125MHz RTIO clock.", cfg); - si5324::FrequencySettings { - n1_hs : 10, - nc1_ls : 4, - n2_hs : 10, - n2_ls : 19972, - n31 : 4565, - n32 : 4565, - bwsel : 4, - crystal_ref: true - } + ( + si5324::FrequencySettings { + n1_hs : 10, + nc1_ls : 4, + n2_hs : 10, + n2_ls : 19972, + n31 : 4565, + n32 : 4565, + bwsel : 4, + crystal_as_ckin2: true + }, + si5324::Input::Ckin2 + ) } }; - #[cfg(all(soc_platform = "kasli", hw_rev = "v2.0", not(si5324_ext_ref)))] - let si5324_ref_input = si5324::Input::Ckin2; - #[cfg(all(soc_platform = "kasli", hw_rev = "v2.0", si5324_ext_ref))] - let si5324_ref_input = si5324::Input::Ckin1; - #[cfg(all(soc_platform = "kasli", not(hw_rev = "v2.0")))] - let si5324_ref_input = si5324::Input::Ckin2; - #[cfg(soc_platform = "kc705")] - let si5324_ref_input = si5324::Input::Ckin2; si5324::setup(&si5324_settings, si5324_ref_input).expect("cannot initialize Si5324"); } -pub fn init() { - let clock_cfg = get_rtio_clock_cfg(); - #[cfg(si5324_as_synthesizer)] +fn setup_si5324(clock_cfg: RtioClock) { + let switched = unsafe { + csr::crg::switch_done_read() + }; + if switched == 1 { + info!("Clocking has already been set up."); + return; + } + match clock_cfg { + RtioClock::Ext0_Bypass => { + info!("using external RTIO clock with PLL bypass"); + si5324::bypass(SI5324_EXT_INPUT).expect("cannot bypass Si5324") + }, + _ => setup_si5324_pll(clock_cfg), + } + + // switch sysclk source to si5324 + #[cfg(not(has_drtio))] { - #[cfg(all(soc_platform = "kasli", hw_rev = "v2.0"))] - let si5324_ext_input = si5324::Input::Ckin1; - #[cfg(all(soc_platform = "kasli", not(hw_rev = "v2.0")))] - let si5324_ext_input = si5324::Input::Ckin2; - #[cfg(soc_platform = "kc705")] - let si5324_ext_input = si5324::Input::Ckin2; - match clock_cfg { - RtioClock::Ext0_Bypass => { - info!("using external RTIO clock with PLL bypass"); - si5324::bypass(si5324_ext_input).expect("cannot bypass Si5324") - }, - _ => setup_si5324_as_synthesizer(clock_cfg), + info!("Switching sys clock, rebooting..."); + // delay for clean UART log, wait until UART FIFO is empty + clock::spin_us(1300); + unsafe { + csr::crg::clock_sel_write(1); + loop {} } } +} + + +pub fn init() { + let clock_cfg = get_rtio_clock_cfg(); + setup_si5324(clock_cfg); #[cfg(has_drtio)] { - unsafe { - csr::drtio_transceiver::stable_clkin_write(1); + let switched = unsafe { + csr::crg::switch_done_read() + }; + if switched == 0 { + info!("Switching sys clock, rebooting..."); + clock::spin_us(500); // delay for clean UART log + 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); + } + loop {} } - clock::spin_us(1500); // wait for CPLL/QPLL lock - unsafe { - csr::drtio_transceiver::txenable_write(0xffffffffu32 as _); + else { + // enable TX after the reboot, with stable clock + unsafe { + csr::drtio_transceiver::txenable_write(0xffffffffu32 as _); + } } } + #[cfg(has_rtio_crg)] { - #[cfg(has_rtio_clock_switch)] - let result = crg::init(clock_cfg); - #[cfg(not(has_rtio_clock_switch))] let result = crg::init(); if !result { error!("RTIO clock failed"); diff --git a/artiq/firmware/runtime/rtio_mgt.rs b/artiq/firmware/runtime/rtio_mgt.rs index c4ca665d4..92aaaf66a 100644 --- a/artiq/firmware/runtime/rtio_mgt.rs +++ b/artiq/firmware/runtime/rtio_mgt.rs @@ -1,15 +1,20 @@ +use alloc::collections::BTreeMap; +use alloc::string::String; use core::cell::RefCell; use urc::Urc; -use board_misoc::csr; +use board_misoc::{csr, config}; #[cfg(has_drtio)] use board_misoc::clock; use board_artiq::drtio_routing; use sched::Io; use sched::Mutex; +use io::{Cursor, ProtoRead}; const ASYNC_ERROR_COLLISION: u8 = 1 << 0; const ASYNC_ERROR_BUSY: u8 = 1 << 1; const ASYNC_ERROR_SEQUENCE_ERROR: u8 = 1 << 2; +static mut RTIO_DEVICE_MAP: BTreeMap = BTreeMap::new(); + #[cfg(has_drtio)] pub mod drtio { use super::*; @@ -215,15 +220,15 @@ pub mod drtio { destination_set_up(routing_table, up_destinations, destination, false), Ok(drtioaux::Packet::DestinationOkReply) => (), Ok(drtioaux::Packet::DestinationSequenceErrorReply { channel }) => { - error!("[DEST#{}] RTIO sequence error involving channel 0x{:04x}", destination, channel); + error!("[DEST#{}] RTIO sequence error involving channel 0x{:04x}:{}", destination, channel, resolve_channel_name(channel as u32)); unsafe { SEEN_ASYNC_ERRORS |= ASYNC_ERROR_SEQUENCE_ERROR }; } Ok(drtioaux::Packet::DestinationCollisionReply { channel }) => { - error!("[DEST#{}] RTIO collision involving channel 0x{:04x}", destination, channel); + error!("[DEST#{}] RTIO collision involving channel 0x{:04x}:{}", destination, channel, resolve_channel_name(channel as u32)); unsafe { SEEN_ASYNC_ERRORS |= ASYNC_ERROR_COLLISION }; } Ok(drtioaux::Packet::DestinationBusyReply { channel }) => { - error!("[DEST#{}] RTIO busy error involving channel 0x{:04x}", destination, channel); + error!("[DEST#{}] RTIO busy error involving channel 0x{:04x}:{}", destination, channel, resolve_channel_name(channel as u32)); unsafe { SEEN_ASYNC_ERRORS |= ASYNC_ERROR_BUSY }; } Ok(packet) => error!("[DEST#{}] received unexpected aux packet: {:?}", destination, packet), @@ -349,16 +354,16 @@ fn async_error_thread(io: Io) { io.until(|| csr::rtio_core::async_error_read() != 0).unwrap(); let errors = csr::rtio_core::async_error_read(); if errors & ASYNC_ERROR_COLLISION != 0 { - error!("RTIO collision involving channel {}", - csr::rtio_core::collision_channel_read()); + let channel = csr::rtio_core::collision_channel_read(); + error!("RTIO collision involving channel 0x{:04x}:{}", channel, resolve_channel_name(channel as u32)); } if errors & ASYNC_ERROR_BUSY != 0 { - error!("RTIO busy error involving channel {}", - csr::rtio_core::busy_channel_read()); + let channel = csr::rtio_core::busy_channel_read(); + error!("RTIO busy error involving channel 0x{:04x}:{}", channel, resolve_channel_name(channel as u32)); } if errors & ASYNC_ERROR_SEQUENCE_ERROR != 0 { - error!("RTIO sequence error involving channel {}", - csr::rtio_core::sequence_error_channel_read()); + let channel = csr::rtio_core::sequence_error_channel_read(); + error!("RTIO sequence error involving channel 0x{:04x}:{}", channel, resolve_channel_name(channel as u32)); } SEEN_ASYNC_ERRORS = errors; csr::rtio_core::async_error_write(errors); @@ -366,9 +371,47 @@ fn async_error_thread(io: Io) { } } +fn read_device_map() -> BTreeMap { + let mut device_map: BTreeMap = BTreeMap::new(); + config::read("device_map", |value: Result<&[u8], config::Error>| { + let mut bytes = match value { + Ok(val) => if val.len() > 0 { Cursor::new(val) } else { + error!("read_device_map: `device_map` was not found in the config"); + return; + }, + Err(err) => { + error!("read_device_map: error reading `device_map` from config: {}", err); + return; + } + }; + let size = bytes.read_u32().unwrap(); + for _ in 0..size { + let channel = bytes.read_u32().unwrap(); + let device_name= bytes.read_string().unwrap(); + if let Some(old_entry) = device_map.insert(channel, device_name.clone()) { + error!("conflicting entries for channel {}: `{}` and `{}`", + channel, old_entry, device_name); + } + } + }); + device_map +} + +fn _resolve_channel_name(channel: u32, device_map: &BTreeMap) -> String { + match device_map.get(&channel) { + Some(val) => val.clone(), + None => String::from("unknown") + } +} + +pub fn resolve_channel_name(channel: u32) -> String { + _resolve_channel_name(channel, unsafe{&RTIO_DEVICE_MAP}) +} + pub fn startup(io: &Io, aux_mutex: &Mutex, routing_table: &Urc>, up_destinations: &Urc>) { + unsafe { RTIO_DEVICE_MAP = read_device_map(); } drtio::startup(io, aux_mutex, routing_table, up_destinations); unsafe { csr::rtio_core::reset_phy_write(1); diff --git a/artiq/firmware/runtime/session.rs b/artiq/firmware/runtime/session.rs index c6d745545..c0f009a96 100644 --- a/artiq/firmware/runtime/session.rs +++ b/artiq/firmware/runtime/session.rs @@ -1,4 +1,4 @@ -use core::{mem, str, cell::{Cell, RefCell}, fmt::Write as FmtWrite}; +use core::{mem, str, cell::{Cell, RefCell}, fmt::Write as FmtWrite, slice}; use alloc::{vec::Vec, string::String}; use byteorder::{ByteOrder, NativeEndian}; use cslice::CSlice; @@ -10,7 +10,7 @@ use urc::Urc; use sched::{ThreadHandle, Io, Mutex, TcpListener, TcpStream, Error as SchedError}; use rtio_clocking; use rtio_dma::Manager as DmaManager; -use rtio_mgt::get_async_errors; +use rtio_mgt::{get_async_errors, resolve_channel_name}; use cache::Cache; use kern_hwreq; use board_artiq::drtio_routing; @@ -449,6 +449,29 @@ fn process_kern_message(io: &Io, aux_mutex: &Mutex, session.kernel_state = KernelState::Absent; unsafe { session.congress.cache.unborrow() } + let exceptions_with_channel: Vec> = exceptions.iter() + .map(|exception| { + if let Some(exn) = exception { + if exn.message.len() == usize::MAX { // host string + Some(exn.clone()) + } else { + let msg = str::from_utf8(unsafe { slice::from_raw_parts(exn.message.as_ptr(), exn.message.len()) }) + .unwrap() + .replace("{rtio_channel_info:0}", &format!("0x{:04x}:{}", exn.param[0], resolve_channel_name(exn.param[0] as u32))); + Some(eh::eh_artiq::Exception { + id: exn.id, + file: exn.file, + line: exn.line, + column: exn.column, + function: exn.function, + message: unsafe { CSlice::new(msg.as_ptr(), msg.len()) }, + param: exn.param, + }) + } + } else { None } + }) + .collect(); + match stream { None => { error!("exception in flash kernel"); @@ -459,7 +482,7 @@ fn process_kern_message(io: &Io, aux_mutex: &Mutex, }, Some(ref mut stream) => { host_write(stream, host::Reply::KernelException { - exceptions: exceptions, + exceptions: &exceptions_with_channel, stack_pointers: stack_pointers, backtrace: backtrace, async_errors: unsafe { get_async_errors() } diff --git a/artiq/firmware/satman/main.rs b/artiq/firmware/satman/main.rs index e869cdc82..b4ca3386f 100644 --- a/artiq/firmware/satman/main.rs +++ b/artiq/firmware/satman/main.rs @@ -12,8 +12,6 @@ use core::convert::TryFrom; use board_misoc::{csr, ident, clock, uart_logger, i2c, pmp}; #[cfg(has_si5324)] use board_artiq::si5324; -#[cfg(has_wrpll)] -use board_artiq::wrpll; use board_artiq::{spi, drtioaux}; use board_artiq::drtio_routing; use riscv::register::{mcause, mepc, mtval}; @@ -383,19 +381,6 @@ fn hardware_tick(ts: &mut u64) { } } -#[cfg(all(has_si5324, rtio_frequency = "150.0"))] -const SI5324_SETTINGS: si5324::FrequencySettings - = si5324::FrequencySettings { - n1_hs : 6, - nc1_ls : 6, - n2_hs : 10, - n2_ls : 270, - n31 : 75, - n32 : 75, - bwsel : 4, - crystal_ref: true -}; - #[cfg(all(has_si5324, rtio_frequency = "125.0"))] const SI5324_SETTINGS: si5324::FrequencySettings = si5324::FrequencySettings { @@ -406,7 +391,7 @@ const SI5324_SETTINGS: si5324::FrequencySettings n31 : 63, n32 : 63, bwsel : 4, - crystal_ref: true + crystal_as_ckin2: true }; #[cfg(all(has_si5324, rtio_frequency = "100.0"))] @@ -419,9 +404,31 @@ const SI5324_SETTINGS: si5324::FrequencySettings n31 : 50, n32 : 50, bwsel : 4, - crystal_ref: true + crystal_as_ckin2: true }; +fn sysclk_setup() { + let switched = unsafe { + csr::crg::switch_done_read() + }; + if switched == 1 { + info!("Clocking has already been set up."); + return; + } + else { + #[cfg(has_si5324)] + si5324::setup(&SI5324_SETTINGS, si5324::Input::Ckin1).expect("cannot initialize Si5324"); + info!("Switching sys clock, rebooting..."); + // delay for clean UART log, wait until UART FIFO is empty + clock::spin_us(1300); + unsafe { + csr::drtio_transceiver::stable_clkin_write(1); + } + loop {} + } +} + + #[no_mangle] pub extern fn main() -> i32 { extern { @@ -445,21 +452,10 @@ pub extern fn main() -> i32 { let (mut io_expander0, mut io_expander1); #[cfg(all(soc_platform = "kasli", hw_rev = "v2.0"))] { - io_expander0 = board_misoc::io_expander::IoExpander::new(0); - io_expander1 = board_misoc::io_expander::IoExpander::new(1); + io_expander0 = board_misoc::io_expander::IoExpander::new(0).unwrap(); + io_expander1 = board_misoc::io_expander::IoExpander::new(1).unwrap(); io_expander0.init().expect("I2C I/O expander #0 initialization failed"); io_expander1.init().expect("I2C I/O expander #1 initialization failed"); - #[cfg(has_wrpll)] - { - io_expander0.set_oe(1, 1 << 7).unwrap(); - io_expander0.set(1, 7, true); - io_expander0.service().unwrap(); - io_expander1.set_oe(0, 1 << 7).unwrap(); - io_expander1.set_oe(1, 1 << 7).unwrap(); - io_expander1.set(0, 7, true); - io_expander1.set(1, 7, true); - io_expander1.service().unwrap(); - } // Actively drive TX_DISABLE to false on SFP0..3 io_expander0.set_oe(0, 1 << 1).unwrap(); @@ -474,21 +470,12 @@ pub extern fn main() -> i32 { io_expander1.service().unwrap(); } - #[cfg(has_si5324)] - si5324::setup(&SI5324_SETTINGS, si5324::Input::Ckin1).expect("cannot initialize Si5324"); - #[cfg(has_wrpll)] - wrpll::init(); + sysclk_setup(); - unsafe { - csr::drtio_transceiver::stable_clkin_write(1); - } - clock::spin_us(1500); // wait for CPLL/QPLL lock - #[cfg(not(has_jdcg))] unsafe { csr::drtio_transceiver::txenable_write(0xffffffffu32 as _); } - #[cfg(has_wrpll)] - wrpll::diagnostics(); + init_rtio_crg(); #[cfg(has_drtio_routing)] @@ -504,11 +491,6 @@ pub extern fn main() -> i32 { let mut hardware_tick_ts = 0; loop { - #[cfg(has_jdcg)] - unsafe { - // Hide from uplink until RTM is ready - csr::drtio_transceiver::txenable_write(0xfffffffeu32 as _); - } while !drtiosat_link_rx_up() { drtiosat_process_errors(); for rep in repeaters.iter_mut() { @@ -528,15 +510,11 @@ pub extern fn main() -> i32 { si5324::siphaser::select_recovered_clock(true).expect("failed to switch clocks"); si5324::siphaser::calibrate_skew().expect("failed to calibrate skew"); } - #[cfg(has_wrpll)] - wrpll::select_recovered_clock(true); drtioaux::reset(0); drtiosat_reset(false); drtiosat_reset_phy(false); - #[cfg(has_jdcg)] - let mut was_up = false; while drtiosat_link_rx_up() { drtiosat_process_errors(); process_aux_packets(&mut repeaters, &mut routing_table, &mut rank); @@ -551,18 +529,6 @@ pub extern fn main() -> i32 { hardware_tick(&mut hardware_tick_ts); if drtiosat_tsc_loaded() { info!("TSC loaded from uplink"); - #[cfg(has_jdcg)] - { - // We assume that the RTM on repeater0 is up. - // Uplink should not send a TSC load command unless the link is - // up, and we are hiding when the RTM is down. - if let Err(e) = jdcg::jesd204sync::sysref_rtio_align() { - error!("failed to align SYSREF with TSC ({})", e); - } - if let Err(e) = jdcg::jesd204sync::resync_dacs() { - error!("DAC resync failed after SYSREF/TSC realignment ({})", e); - } - } for rep in repeaters.iter() { if let Err(e) = rep.sync_tsc() { error!("failed to sync TSC ({})", e); @@ -572,46 +538,14 @@ pub extern fn main() -> i32 { error!("aux packet error: {}", e); } } - #[cfg(has_jdcg)] - { - let is_up = repeaters[0].is_up(); - if is_up && !was_up { - /* - * One side of the JESD204 elastic buffer is clocked by the jitter filter - * (Si5324 or WRPLL), the other by the RTM. - * The elastic buffer can operate only when those two clocks are derived from - * the same oscillator. - * This is the case when either of those conditions is true: - * (1) The DRTIO master and the RTM are clocked directly from a common external - * source, *and* the jitter filter has locked to the recovered clock. - * This clocking scheme may provide less noise and phase drift at the DACs. - * (2) The RTM clock is connected to the jitter filter output. - * To handle those cases, we simply keep the JESD204 core in reset unless the - * jitter filter is locked to the recovered clock. - */ - jdcg::jesd::reset(false); - let _ = jdcg::jdac::init(); - jdcg::jesd204sync::sysref_auto_align(); - jdcg::jdac::stpl(); - unsafe { - csr::drtio_transceiver::txenable_write(0xffffffffu32 as _); // unhide - } - } - was_up = is_up; - } } - #[cfg(has_jdcg)] - jdcg::jesd::reset(true); - drtiosat_reset_phy(true); drtiosat_reset(true); drtiosat_tsc_loaded(); info!("uplink is down, switching to local oscillator clock"); #[cfg(has_si5324)] si5324::siphaser::select_recovered_clock(false).expect("failed to switch clocks"); - #[cfg(has_wrpll)] - wrpll::select_recovered_clock(false); } } diff --git a/artiq/frontend/aqctl_corelog.py b/artiq/frontend/aqctl_corelog.py index 8c766ecf5..c5f5a8eee 100755 --- a/artiq/frontend/aqctl_corelog.py +++ b/artiq/frontend/aqctl_corelog.py @@ -96,14 +96,15 @@ def main(): signal_handler.setup() try: get_logs_task = asyncio.ensure_future( - get_logs_sim(args.core_addr) if args.simulation else get_logs(args.core_addr)) + get_logs_sim(args.core_addr) if args.simulation else get_logs(args.core_addr), + loop=loop) try: server = Server({"corelog": PingTarget()}, None, True) loop.run_until_complete(server.start(common_args.bind_address_from_args(args), args.port)) try: _, pending = loop.run_until_complete(asyncio.wait( - [signal_handler.wait_terminate(), - server.wait_terminate(), + [loop.create_task(signal_handler.wait_terminate()), + loop.create_task(server.wait_terminate()), get_logs_task], return_when=asyncio.FIRST_COMPLETED)) for task in pending: diff --git a/artiq/frontend/aqctl_moninj_proxy.py b/artiq/frontend/aqctl_moninj_proxy.py index fcf9e259d..d1e8774bd 100755 --- a/artiq/frontend/aqctl_moninj_proxy.py +++ b/artiq/frontend/aqctl_moninj_proxy.py @@ -219,8 +219,8 @@ def main(): loop.run_until_complete(server.start(bind_address, args.port_control)) try: _, pending = loop.run_until_complete(asyncio.wait( - [signal_handler.wait_terminate(), - server.wait_terminate(), + [loop.create_task(signal_handler.wait_terminate()), + loop.create_task(server.wait_terminate()), comm_moninj.wait_terminate()], return_when=asyncio.FIRST_COMPLETED)) for task in pending: diff --git a/artiq/frontend/artiq_browser.py b/artiq/frontend/artiq_browser.py index 204ac2382..398244b0e 100755 --- a/artiq/frontend/artiq_browser.py +++ b/artiq/frontend/artiq_browser.py @@ -49,7 +49,7 @@ def get_argparser(): class Browser(QtWidgets.QMainWindow): def __init__(self, smgr, datasets_sub, browse_root, - master_host, master_port): + master_host, master_port, *, loop=None): QtWidgets.QMainWindow.__init__(self) smgr.register(self) @@ -81,9 +81,9 @@ class Browser(QtWidgets.QMainWindow): self.files.dataset_changed.connect( self.experiments.dataset_changed) - self.applets = applets.AppletsDock(self, datasets_sub) + self.applets = applets.AppletsDock(self, datasets_sub, loop=loop) smgr.register(self.applets) - atexit_register_coroutine(self.applets.stop) + atexit_register_coroutine(self.applets.stop, loop=loop) self.datasets = datasets.DatasetsDock( datasets_sub, master_host, master_port) @@ -152,7 +152,7 @@ def main(): smgr = state.StateManager(args.db_file) browser = Browser(smgr, datasets_sub, args.browse_root, - args.server, args.port) + args.server, args.port, loop=loop) widget_log_handler.callback = browser.log.model.append if os.name == "nt": @@ -161,8 +161,8 @@ def main(): # QDockWidgets fail to be embedded. browser.show() smgr.load() - smgr.start() - atexit_register_coroutine(smgr.stop) + smgr.start(loop=loop) + atexit_register_coroutine(smgr.stop, loop=loop) if args.select is not None: browser.files.select(args.select) diff --git a/artiq/frontend/artiq_compile.py b/artiq/frontend/artiq_compile.py index 32218c9ec..c5836aa6a 100755 --- a/artiq/frontend/artiq_compile.py +++ b/artiq/frontend/artiq_compile.py @@ -60,6 +60,8 @@ def main(): arguments = parse_arguments(args.arguments) argument_mgr = ProcessArgumentManager(arguments) exp_inst = exp((device_mgr, dataset_mgr, argument_mgr, {})) + argument_mgr.check_unprocessed_arguments() + if not getattr(exp.run, "__artiq_kernel__", False): raise ValueError("Experiment entry point must be a kernel") diff --git a/artiq/frontend/artiq_dashboard.py b/artiq/frontend/artiq_dashboard.py index dfe1e82d6..033d58ee7 100755 --- a/artiq/frontend/artiq_dashboard.py +++ b/artiq/frontend/artiq_dashboard.py @@ -148,7 +148,7 @@ def main(): report_disconnect) loop.run_until_complete(subscriber.connect( args.server, args.port_notify)) - atexit_register_coroutine(subscriber.close) + atexit_register_coroutine(subscriber.close, loop=loop) sub_clients[notifier_name] = subscriber broadcast_clients = dict() @@ -156,7 +156,7 @@ def main(): client = Receiver(target, [], report_disconnect) loop.run_until_complete(client.connect( args.server, args.port_broadcast)) - atexit_register_coroutine(client.close) + atexit_register_coroutine(client.close, loop=loop) broadcast_clients[target] = client # initialize main window @@ -195,14 +195,15 @@ def main(): "server": args.server, "port_notify": args.port_notify, "port_control": args.port_control, - }) - atexit_register_coroutine(d_applets.stop) + }, + loop=loop) + atexit_register_coroutine(d_applets.stop, loop=loop) smgr.register(d_applets) broadcast_clients["ccb"].notify_cbs.append(d_applets.ccb_notify) d_ttl_dds = moninj.MonInj(rpc_clients["schedule"]) loop.run_until_complete(d_ttl_dds.start(args.server, args.port_notify)) - atexit_register_coroutine(d_ttl_dds.stop) + atexit_register_coroutine(d_ttl_dds.stop, loop=loop) d_schedule = schedule.ScheduleDock( rpc_clients["schedule"], sub_clients["schedule"]) @@ -231,8 +232,8 @@ def main(): # QDockWidgets fail to be embedded. main_window.show() smgr.load() - smgr.start() - atexit_register_coroutine(smgr.stop) + smgr.start(loop=loop) + atexit_register_coroutine(smgr.stop, loop=loop) # work around for https://github.com/m-labs/artiq/issues/1307 d_ttl_dds.ttl_dock.show() diff --git a/artiq/frontend/artiq_ddb_template.py b/artiq/frontend/artiq_ddb_template.py index ed36a16b5..bc3d25900 100755 --- a/artiq/frontend/artiq_ddb_template.py +++ b/artiq/frontend/artiq_ddb_template.py @@ -8,6 +8,7 @@ from itertools import count from artiq import __version__ as artiq_version from artiq.coredevice import jsondesc +from artiq.coredevice.phaser import PHASER_GW_MIQRO, PHASER_GW_BASE def process_header(output, description): @@ -382,10 +383,12 @@ class PeripheralManager: "arguments": {{ "spi_adc_device": "spi_{name}_adc", "spi_pgia_device": "spi_{name}_pgia", - "cnv_device": "ttl_{name}_cnv" + "cnv_device": "ttl_{name}_cnv", + "hw_rev": "{hw_rev}" }} }}""", name=self.get_name("sampler"), + hw_rev=peripheral.get("hw_rev", "v2.2"), adc_channel=rtio_offset, pgia_channel=rtio_offset + 1, cnv_channel=rtio_offset + 2) @@ -416,11 +419,13 @@ class PeripheralManager: "channel": 0x{suservo_channel:06x}, "pgia_device": "spi_{sampler_name}_pgia", "cpld_devices": {cpld_names_list}, - "dds_devices": {dds_names_list} + "dds_devices": {dds_names_list}, + "sampler_hw_rev": "{sampler_hw_rev}" }} }}""", suservo_name=suservo_name, sampler_name=sampler_name, + sampler_hw_rev=peripheral.get("sampler_hw_rev", "v2.2"), cpld_names_list=[urukul_name + "_cpld" for urukul_name in urukul_names], dds_names_list=[urukul_name + "_dds" for urukul_name in urukul_names], suservo_channel=rtio_offset+next(channel)) @@ -534,10 +539,10 @@ class PeripheralManager: def process_phaser(self, rtio_offset, peripheral): mode = peripheral.get("mode", "base") if mode == "miqro": - dac = ', "dac": {"pll_m": 16, "pll_n": 3, "interpolation": 2}' + dac = f', "dac": {{"pll_m": 16, "pll_n": 3, "interpolation": 2}}, "gw_rev"={PHASER_GW_MIQRO}' n_channels = 3 else: - dac = "" + dac = f', "gw_rev"={PHASER_GW_BASE}' n_channels = 5 self.gen(""" device_db["{name}"] = {{ diff --git a/artiq/frontend/artiq_master.py b/artiq/frontend/artiq_master.py index da57e6b3d..ced3c828c 100755 --- a/artiq/frontend/artiq_master.py +++ b/artiq/frontend/artiq_master.py @@ -86,7 +86,7 @@ def main(): server_broadcast = Broadcaster() loop.run_until_complete(server_broadcast.start( bind, args.port_broadcast)) - atexit_register_coroutine(server_broadcast.stop) + atexit_register_coroutine(server_broadcast.stop, loop=loop) log_forwarder.callback = (lambda msg: server_broadcast.broadcast("log", msg)) @@ -100,8 +100,8 @@ def main(): device_db = DeviceDB(args.device_db) dataset_db = DatasetDB(args.dataset_db) - dataset_db.start() - atexit_register_coroutine(dataset_db.stop) + dataset_db.start(loop=loop) + atexit_register_coroutine(dataset_db.stop, loop=loop) worker_handlers = dict() if args.git: @@ -113,8 +113,8 @@ def main(): atexit.register(experiment_db.close) scheduler = Scheduler(RIDCounter(), worker_handlers, experiment_db, args.log_submissions) - scheduler.start() - atexit_register_coroutine(scheduler.stop) + scheduler.start(loop=loop) + atexit_register_coroutine(scheduler.stop, loop=loop) config = MasterConfig(args.name) @@ -131,7 +131,7 @@ def main(): "scheduler_check_termination": scheduler.check_termination, "ccb_issue": ccb_issue, }) - experiment_db.scan_repository_async() + experiment_db.scan_repository_async(loop=loop) server_control = RPCServer({ "master_config": config, @@ -142,7 +142,7 @@ def main(): }, allow_parallel=True) loop.run_until_complete(server_control.start( bind, args.port_control)) - atexit_register_coroutine(server_control.stop) + atexit_register_coroutine(server_control.stop, loop=loop) server_notify = Publisher({ "schedule": scheduler.notifier, @@ -153,12 +153,12 @@ def main(): }) loop.run_until_complete(server_notify.start( bind, args.port_notify)) - atexit_register_coroutine(server_notify.stop) + atexit_register_coroutine(server_notify.stop, loop=loop) server_logging = LoggingServer() loop.run_until_complete(server_logging.start( bind, args.port_logging)) - atexit_register_coroutine(server_logging.stop) + atexit_register_coroutine(server_logging.stop, loop=loop) print("ARTIQ master is now ready.") loop.run_until_complete(signal_handler.wait_terminate()) diff --git a/artiq/frontend/artiq_rtiomap.py b/artiq/frontend/artiq_rtiomap.py new file mode 100755 index 000000000..e35b61156 --- /dev/null +++ b/artiq/frontend/artiq_rtiomap.py @@ -0,0 +1,89 @@ +#!/usr/bin/env python3 + +import argparse +import importlib +import struct + +from sipyco import common_args + +from artiq import __version__ as artiq_version +from artiq.master.databases import DeviceDB + + +def get_argparser(): + parser = argparse.ArgumentParser(description="ARTIQ RTIO channel name map encoder tool") + + parser.add_argument("--version", action="version", + version="ARTIQ v{}".format(artiq_version), + help="print the ARTIQ version number") + + common_args.verbosity_args(parser) + parser.add_argument("--device-db", default="device_db.py", + help="device database file (default: '%(default)s')") + parser.add_argument("file", metavar="FILE", default=None, + help="write the result into the specified file, or read from it to show the map (see `--show`)") + parser.add_argument("--show", default=False, action="store_true", + help="show the channel mapping from the specified file, instead of writing to it") + + return parser + + +def get_rtio_channels(desc): + if isinstance(desc, dict) and desc["type"] == "local": + module = importlib.import_module(desc["module"]) + device_class = getattr(module, desc["class"]) + return getattr(device_class, "get_rtio_channels", lambda **kwargs: [])(**desc.get("arguments", {})) + return [] + + +def get_channel_map(device_db): + reversed_map = {} + for dev_name, device in device_db.items(): + try: + channels = get_rtio_channels(device) + except Exception as e: + raise Exception(f"failed to process the device `{dev_name}`") from e + for chan, suffix in channels: + assert chan not in reversed_map + reversed_map[chan] = dev_name + (" " + suffix if suffix is not None else "") + + return reversed_map + + +def serialize_device_map(channel_map, outfile): + outfile.write(struct.pack(" {device}") + else: + ddb = DeviceDB(args.device_db) + chan_map = get_channel_map(ddb.get_device_db()) + + with open(args.file, "wb") as outfile: + serialize_device_map(chan_map, outfile) + + +if __name__ == "__main__": + main() diff --git a/artiq/frontend/artiq_run.py b/artiq/frontend/artiq_run.py index 89578ec9b..9c51b5cd0 100755 --- a/artiq/frontend/artiq_run.py +++ b/artiq/frontend/artiq_run.py @@ -133,7 +133,9 @@ def _build_experiment(device_mgr, dataset_mgr, args): "arguments": arguments } device_mgr.virtual_devices["scheduler"].expid = expid - return get_experiment(module, args.class_name)(managers) + exp_inst = get_experiment(module, args.class_name)(managers) + argument_mgr.check_unprocessed_arguments() + return exp_inst def run(with_file=False): diff --git a/artiq/frontend/artiq_sinara_tester.py b/artiq/frontend/artiq_sinara_tester.py index b6faf9518..d1888d177 100755 --- a/artiq/frontend/artiq_sinara_tester.py +++ b/artiq/frontend/artiq_sinara_tester.py @@ -451,7 +451,7 @@ class SinaraTester(EnvExperiment): print("Frequencies:") for card_n, channels in enumerate(chunker(self.mirnies, 4)): for channel_n, (channel_name, channel_dev) in enumerate(channels): - frequency = 1000.*(card_n + 1) + channel_n * 100 + frequency = float(1000 + 100 * (card_n + 1) + channel_n * 10) print("{}\t{}MHz".format(channel_name, frequency)) self.setup_mirny(channel_dev, frequency) print("{} info: {}".format(channel_name, channel_dev.info())) diff --git a/artiq/gateware/drtio/aux_controller.py b/artiq/gateware/drtio/aux_controller.py index 051a03a20..5f91b1f45 100644 --- a/artiq/gateware/drtio/aux_controller.py +++ b/artiq/gateware/drtio/aux_controller.py @@ -22,8 +22,7 @@ class Transmitter(Module, AutoCSR): self.aux_tx = CSR() self.specials.mem = Memory(mem_dw, max_packet//(mem_dw//8)) - converter = ClockDomainsRenamer("rtio")( - stream.Converter(mem_dw, ll_dw)) + converter = stream.Converter(mem_dw, ll_dw) self.submodules += converter # when continuously fed, the Converter outputs data continuously @@ -36,7 +35,7 @@ class Transmitter(Module, AutoCSR): seen_eop_rst = Signal() frame_r = Signal() seen_eop = Signal() - self.sync.rtio += [ + self.sync += [ If(link_layer.tx_aux_ack, frame_r.eq(link_layer.tx_aux_frame), If(frame_r & ~link_layer.tx_aux_frame, seen_eop.eq(1)) @@ -44,13 +43,9 @@ class Transmitter(Module, AutoCSR): If(seen_eop_rst, seen_eop.eq(0)) ] - mem_port = self.mem.get_port(clock_domain="rtio") + mem_port = self.mem.get_port() self.specials += mem_port - self.aux_tx_length.storage.attr.add("no_retiming") - tx_length = Signal(bits_for(max_packet)) - self.specials += MultiReg(self.aux_tx_length.storage, tx_length, "rtio") - frame_counter_nbits = bits_for(max_packet) - log2_int(mem_dw//8) frame_counter = Signal(frame_counter_nbits) frame_counter_next = Signal(frame_counter_nbits) @@ -66,35 +61,33 @@ class Transmitter(Module, AutoCSR): mem_port.adr.eq(frame_counter_next), converter.sink.data.eq(mem_port.dat_r) ] - self.sync.rtio += frame_counter.eq(frame_counter_next) - start_tx = PulseSynchronizer("sys", "rtio") - tx_done = PulseSynchronizer("rtio", "sys") - self.submodules += start_tx, tx_done - self.comb += start_tx.i.eq(self.aux_tx.re) + tx_done = Signal() self.sync += [ - If(tx_done.o, self.aux_tx.w.eq(0)), - If(self.aux_tx.re, self.aux_tx.w.eq(1)) + frame_counter.eq(frame_counter_next), + If(self.aux_tx.re, self.aux_tx.w.eq(1)), + If(tx_done, self.aux_tx.w.eq(0)) ] - fsm = ClockDomainsRenamer("rtio")(FSM(reset_state="IDLE")) + fsm = FSM(reset_state="IDLE") self.submodules += fsm fsm.act("IDLE", frame_counter_rst.eq(1), seen_eop_rst.eq(1), - If(start_tx.o, NextState("TRANSMIT")) + If(self.aux_tx.re, NextState("TRANSMIT")) ) fsm.act("TRANSMIT", converter.sink.stb.eq(1), If(converter.sink.ack, frame_counter_ce.eq(1) ), - If(frame_counter_next == tx_length, NextState("WAIT_INTERFRAME")) + If(frame_counter_next == self.aux_tx_length.storage, + NextState("WAIT_INTERFRAME")) ) fsm.act("WAIT_INTERFRAME", If(seen_eop, - tx_done.i.eq(1), + tx_done.eq(1), NextState("IDLE") ) ) diff --git a/artiq/gateware/drtio/core.py b/artiq/gateware/drtio/core.py index 6311456e4..1af7fa7b4 100644 --- a/artiq/gateware/drtio/core.py +++ b/artiq/gateware/drtio/core.py @@ -2,7 +2,6 @@ from types import SimpleNamespace from migen import * from migen.genlib.resetsync import AsyncResetSynchronizer -from migen.genlib.cdc import PulseSynchronizer from misoc.interconnect.csr import * from artiq.gateware.rtio import cri, rtlink @@ -31,7 +30,6 @@ class TransceiverInterface(AutoCSR): def __init__(self, channel_interfaces): self.stable_clkin = CSRStorage() self.txenable = CSRStorage(len(channel_interfaces)) - self.clock_domains.cd_rtio = ClockDomain() for i in range(len(channel_interfaces)): name = "rtio_rx" + str(i) setattr(self.clock_domains, "cd_"+name, ClockDomain(name=name)) @@ -60,15 +58,15 @@ class SyncRTIO(Module): assert tsc.glbl_fine_ts_width >= chan_fine_ts_width self.submodules.outputs = ClockDomainsRenamer("rio")( - SED(channels, tsc.glbl_fine_ts_width, "sync", + SED(channels, tsc.glbl_fine_ts_width, lane_count=lane_count, fifo_depth=fifo_depth, enable_spread=False, report_buffer_space=True, interface=self.cri)) self.comb += self.outputs.coarse_timestamp.eq(tsc.coarse_ts) - self.sync.rtio += self.outputs.minimum_coarse_timestamp.eq(tsc.coarse_ts + 16) + self.sync += self.outputs.minimum_coarse_timestamp.eq(tsc.coarse_ts + 16) self.submodules.inputs = ClockDomainsRenamer("rio")( - InputCollector(tsc, channels, "sync", interface=self.cri)) + InputCollector(tsc, channels, interface=self.cri)) for attr, _ in async_errors_layout: self.comb += getattr(self.async_errors, attr).eq(getattr(self.outputs, attr)) @@ -79,15 +77,15 @@ class DRTIOSatellite(Module): self.reset = CSRStorage(reset=1) self.reset_phy = CSRStorage(reset=1) self.tsc_loaded = CSR() - # master interface in the rtio domain + # master interface in the sys domain self.cri = cri.Interface() self.async_errors = Record(async_errors_layout) self.clock_domains.cd_rio = ClockDomain() self.clock_domains.cd_rio_phy = ClockDomain() self.comb += [ - self.cd_rio.clk.eq(ClockSignal("rtio")), - self.cd_rio_phy.clk.eq(ClockSignal("rtio")) + self.cd_rio.clk.eq(ClockSignal("sys")), + self.cd_rio_phy.clk.eq(ClockSignal("sys")) ] reset = Signal() reset_phy = Signal() @@ -125,9 +123,9 @@ class DRTIOSatellite(Module): rx_rt_frame_perm=rx_synchronizer.resync(self.link_layer.rx_rt_frame_perm), rx_rt_data=rx_synchronizer.resync(self.link_layer.rx_rt_data) ) - self.submodules.link_stats = link_layer.LinkLayerStats(link_layer_sync, "rtio") - self.submodules.rt_packet = ClockDomainsRenamer("rtio")( - rt_packet_satellite.RTPacketSatellite(link_layer_sync, interface=self.cri)) + self.submodules.link_stats = link_layer.LinkLayerStats(link_layer_sync, "sys") + self.submodules.rt_packet = rt_packet_satellite.RTPacketSatellite( + link_layer_sync, interface=self.cri) self.comb += self.rt_packet.reset.eq(self.cd_rio.rst) self.comb += [ @@ -135,12 +133,9 @@ class DRTIOSatellite(Module): tsc.load_value.eq(self.rt_packet.tsc_load_value) ] - ps_tsc_load = PulseSynchronizer("rtio", "sys") - self.submodules += ps_tsc_load - self.comb += ps_tsc_load.i.eq(self.rt_packet.tsc_load) self.sync += [ If(self.tsc_loaded.re, self.tsc_loaded.w.eq(0)), - If(ps_tsc_load.o, self.tsc_loaded.w.eq(1)) + If(self.rt_packet.tsc_load, self.tsc_loaded.w.eq(1)) ] self.submodules.rt_errors = rt_errors_satellite.RTErrorsSatellite( diff --git a/artiq/gateware/drtio/link_layer.py b/artiq/gateware/drtio/link_layer.py index a4779e912..367830067 100644 --- a/artiq/gateware/drtio/link_layer.py +++ b/artiq/gateware/drtio/link_layer.py @@ -232,7 +232,7 @@ class LinkLayer(Module, AutoCSR): # receiver locked, comma aligned, receiving valid 8b10b symbols self.rx_ready = Signal() - tx = ClockDomainsRenamer("rtio")(LinkLayerTX(encoder)) + tx = LinkLayerTX(encoder) rx = ClockDomainsRenamer("rtio_rx")(LinkLayerRX(decoders)) self.submodules += tx, rx @@ -256,31 +256,23 @@ class LinkLayer(Module, AutoCSR): rx_up = Signal() rx_up_r = Signal() - self.sync.rtio += rx_up_r.eq(rx_up) + self.sync += rx_up_r.eq(rx_up) rx_up_rx = Signal() rx_up_r.attr.add("no_retiming") self.specials += [ MultiReg(rx_up_r, rx_up_rx, "rtio_rx"), MultiReg(rx_up_r, self.rx_up.status)] - tx_force_aux_zero_rtio = Signal() - tx_force_rt_zero_rtio = Signal() - self.tx_force_aux_zero.storage.attr.add("no_retiming") - self.tx_force_rt_zero.storage.attr.add("no_retiming") - self.specials += [ - MultiReg(self.tx_force_aux_zero.storage, tx_force_aux_zero_rtio, "rtio"), - MultiReg(self.tx_force_rt_zero.storage, tx_force_rt_zero_rtio, "rtio")] - rx_disable_rx = Signal() self.rx_disable.storage.attr.add("no_retiming") self.specials += MultiReg(self.rx_disable.storage, rx_disable_rx, "rtio_rx") self.comb += [ - tx.aux_frame.eq(self.tx_aux_frame | tx_force_aux_zero_rtio), - tx.aux_data.eq(Mux(tx_force_aux_zero_rtio, 0, self.tx_aux_data)), + tx.aux_frame.eq(self.tx_aux_frame | self.tx_force_aux_zero.storage), + tx.aux_data.eq(Mux(self.tx_force_aux_zero.storage, 0, self.tx_aux_data)), self.tx_aux_ack.eq(tx.aux_ack), - tx.rt_frame.eq(self.tx_rt_frame | tx_force_rt_zero_rtio), - tx.rt_data.eq(Mux(tx_force_rt_zero_rtio, 0, self.tx_rt_data)) + tx.rt_frame.eq(self.tx_rt_frame | self.tx_force_rt_zero.storage), + tx.rt_data.eq(Mux(self.tx_force_rt_zero.storage, 0, self.tx_rt_data)) ] # we register those to improve timing margins, as the data may need # to be recaptured by RXSynchronizer. @@ -294,10 +286,10 @@ class LinkLayer(Module, AutoCSR): self.rx_rt_data.eq(rx.rt_data) ] - wait_scrambler = ClockDomainsRenamer("rtio")(WaitTimer(15)) + wait_scrambler = WaitTimer(15) self.submodules += wait_scrambler - fsm = ClockDomainsRenamer("rtio")(FSM(reset_state="WAIT_RX_READY")) + fsm = FSM(reset_state="WAIT_RX_READY") self.submodules += fsm fsm.act("WAIT_RX_READY", diff --git a/artiq/gateware/drtio/rt_controller_master.py b/artiq/gateware/drtio/rt_controller_master.py index fdd340be8..3ed22dbe7 100644 --- a/artiq/gateware/drtio/rt_controller_master.py +++ b/artiq/gateware/drtio/rt_controller_master.py @@ -108,7 +108,7 @@ class RTController(Module): cond_underflow = Signal() self.comb += cond_underflow.eq((self.cri.o_timestamp[tsc.glbl_fine_ts_width:] - - self.csrs.underflow_margin.storage[tsc.glbl_fine_ts_width:]) < tsc.coarse_ts_sys) + - self.csrs.underflow_margin.storage[tsc.glbl_fine_ts_width:]) < tsc.coarse_ts) # buffer space buffer_space = Memory(16, 256) diff --git a/artiq/gateware/drtio/rt_controller_repeater.py b/artiq/gateware/drtio/rt_controller_repeater.py index 0fe10d1de..53b2b1b07 100644 --- a/artiq/gateware/drtio/rt_controller_repeater.py +++ b/artiq/gateware/drtio/rt_controller_repeater.py @@ -15,15 +15,11 @@ class RTController(Module, AutoCSR): self.command_missed_chan_sel = CSRStatus(24) self.buffer_space_timeout_dest = CSRStatus(8) - self.specials += MultiReg(self.reset.storage, rt_packet.reset, "rtio") + self.sync += rt_packet.reset.eq(self.reset.storage) set_time_stb = Signal() - set_time_ack = Signal() - self.submodules += CrossDomainRequest("rtio", - set_time_stb, set_time_ack, None, - rt_packet.set_time_stb, rt_packet.set_time_ack, None) self.sync += [ - If(set_time_ack, set_time_stb.eq(0)), + If(rt_packet.set_time_stb, set_time_stb.eq(0)), If(self.set_time.re, set_time_stb.eq(1)) ] self.comb += self.set_time.w.eq(set_time_stb) @@ -31,10 +27,10 @@ class RTController(Module, AutoCSR): errors = [ (rt_packet.err_unknown_packet_type, "rtio_rx", None, None), (rt_packet.err_packet_truncated, "rtio_rx", None, None), - (rt_packet.err_command_missed, "rtio", + (rt_packet.err_command_missed, "sys", Cat(rt_packet.command_missed_cmd, rt_packet.command_missed_chan_sel), Cat(self.command_missed_cmd.status, self.command_missed_chan_sel.status)), - (rt_packet.err_buffer_space_timeout, "rtio", + (rt_packet.err_buffer_space_timeout, "sys", rt_packet.buffer_space_destination, self.buffer_space_timeout_dest.status) ] diff --git a/artiq/gateware/drtio/rt_packet_master.py b/artiq/gateware/drtio/rt_packet_master.py index 4fd26f85d..70d44ecaf 100644 --- a/artiq/gateware/drtio/rt_packet_master.py +++ b/artiq/gateware/drtio/rt_packet_master.py @@ -2,7 +2,7 @@ from migen import * from migen.genlib.fsm import * -from migen.genlib.fifo import AsyncFIFO +from migen.genlib.fifo import SyncFIFO from migen.genlib.cdc import BlindTransfer from artiq.gateware.rtio.cdc import GrayCodeTransfer @@ -76,8 +76,8 @@ class RTPacketMaster(Module): assert len(link_layer.tx_rt_data) % 8 == 0 ws = len(link_layer.tx_rt_data) tx_plm = get_m2s_layouts(ws) - tx_dp = ClockDomainsRenamer("rtio")(TransmitDatapath( - link_layer.tx_rt_frame, link_layer.tx_rt_data, tx_plm)) + tx_dp = TransmitDatapath( + link_layer.tx_rt_frame, link_layer.tx_rt_data, tx_plm) self.submodules += tx_dp rx_plm = get_s2m_layouts(ws) rx_dp = ClockDomainsRenamer("rtio_rx")(ReceiveDatapath( @@ -85,8 +85,7 @@ class RTPacketMaster(Module): self.submodules += rx_dp # Write FIFO and extra data count - sr_fifo = ClockDomainsRenamer({"write": "sys", "read": "rtio"})( - AsyncFIFO(1+64+24+8+512, sr_fifo_depth)) + sr_fifo = SyncFIFO(1+64+24+8+512, sr_fifo_depth) self.submodules += sr_fifo sr_notwrite_d = Signal() sr_timestamp_d = Signal(64) @@ -106,7 +105,7 @@ class RTPacketMaster(Module): sr_buf_re = Signal() self.comb += sr_fifo.re.eq(sr_fifo.readable & (~sr_buf_readable | sr_buf_re)) - self.sync.rtio += \ + self.sync += \ If(sr_fifo.re, sr_buf_readable.eq(1), ).Elif(sr_buf_re, @@ -120,7 +119,7 @@ class RTPacketMaster(Module): sr_extra_data_cnt = Signal(8) sr_data = Signal(512) - self.sync.rtio += If(sr_fifo.re, + self.sync += If(sr_fifo.re, sr_notwrite.eq(sr_notwrite_d), sr_timestamp.eq(sr_timestamp_d), sr_chan_sel.eq(sr_chan_sel_d), @@ -131,11 +130,11 @@ class RTPacketMaster(Module): sr_extra_data_d = Signal(512) self.comb += sr_extra_data_d.eq(sr_data_d[short_data_len:]) for i in range(512//ws): - self.sync.rtio += If(sr_fifo.re, + self.sync += If(sr_fifo.re, If(sr_extra_data_d[ws*i:ws*(i+1)] != 0, sr_extra_data_cnt.eq(i+1))) sr_extra_data = Signal(512) - self.sync.rtio += If(sr_fifo.re, sr_extra_data.eq(sr_extra_data_d)) + self.sync += If(sr_fifo.re, sr_extra_data.eq(sr_extra_data_d)) extra_data_ce = Signal() extra_data_last = Signal() @@ -146,7 +145,7 @@ class RTPacketMaster(Module): for i in range(512//ws)}), extra_data_last.eq(extra_data_counter == sr_extra_data_cnt) ] - self.sync.rtio += \ + self.sync += \ If(extra_data_ce, extra_data_counter.eq(extra_data_counter + 1), ).Else( @@ -160,18 +159,6 @@ class RTPacketMaster(Module): buffer_space_not, buffer_space, self.buffer_space_not, self.buffer_space_not_ack, self.buffer_space) - set_time_stb = Signal() - set_time_ack = Signal() - self.submodules += CrossDomainRequest("rtio", - self.set_time_stb, self.set_time_ack, None, - set_time_stb, set_time_ack, None) - - echo_stb = Signal() - echo_ack = Signal() - self.submodules += CrossDomainRequest("rtio", - self.echo_stb, self.echo_ack, None, - echo_stb, echo_ack, None) - read_not = Signal() read_no_event = Signal() read_is_overflow = Signal() @@ -199,14 +186,14 @@ class RTPacketMaster(Module): ] # TX FSM - tx_fsm = ClockDomainsRenamer("rtio")(FSM(reset_state="IDLE")) + tx_fsm = FSM(reset_state="IDLE") self.submodules += tx_fsm echo_sent_now = Signal() - self.sync.rtio += self.echo_sent_now.eq(echo_sent_now) + self.sync += self.echo_sent_now.eq(echo_sent_now) tsc_value = Signal(64) tsc_value_load = Signal() - self.sync.rtio += If(tsc_value_load, tsc_value.eq(self.tsc_value)) + self.sync += If(tsc_value_load, tsc_value.eq(self.tsc_value)) tx_fsm.act("IDLE", # Ensure 2 cycles between frames on the link. @@ -223,10 +210,10 @@ class RTPacketMaster(Module): NextState("WRITE") ) ).Else( - If(echo_stb, + If(self.echo_stb, echo_sent_now.eq(1), NextState("ECHO") - ).Elif(set_time_stb, + ).Elif(self.set_time_stb, tsc_value_load.eq(1), NextState("SET_TIME") ) @@ -273,14 +260,14 @@ class RTPacketMaster(Module): tx_fsm.act("ECHO", tx_dp.send("echo_request"), If(tx_dp.packet_last, - echo_ack.eq(1), + self.echo_ack.eq(1), NextState("IDLE") ) ) tx_fsm.act("SET_TIME", tx_dp.send("set_time", timestamp=tsc_value), If(tx_dp.packet_last, - set_time_ack.eq(1), + self.set_time_ack.eq(1), NextState("IDLE") ) ) @@ -334,16 +321,14 @@ class RTPacketMaster(Module): # packet counters tx_frame_r = Signal() packet_cnt_tx = Signal(32) - self.sync.rtio += [ + self.sync += [ tx_frame_r.eq(link_layer.tx_rt_frame), If(link_layer.tx_rt_frame & ~tx_frame_r, packet_cnt_tx.eq(packet_cnt_tx + 1)) ] - cdc_packet_cnt_tx = GrayCodeTransfer(32) - self.submodules += cdc_packet_cnt_tx + self.comb += [ - cdc_packet_cnt_tx.i.eq(packet_cnt_tx), - self.packet_cnt_tx.eq(cdc_packet_cnt_tx.o) + self.packet_cnt_tx.eq(packet_cnt_tx) ] rx_frame_r = Signal() @@ -353,7 +338,7 @@ class RTPacketMaster(Module): If(link_layer.rx_rt_frame & ~rx_frame_r, packet_cnt_rx.eq(packet_cnt_rx + 1)) ] - cdc_packet_cnt_rx = ClockDomainsRenamer({"rtio": "rtio_rx"})( + cdc_packet_cnt_rx = ClockDomainsRenamer({"rtio": "sys", "sys": "rtio_rx"})( GrayCodeTransfer(32)) self.submodules += cdc_packet_cnt_rx self.comb += [ diff --git a/artiq/gateware/drtio/rt_packet_repeater.py b/artiq/gateware/drtio/rt_packet_repeater.py index 9374d1a18..728c24ae8 100644 --- a/artiq/gateware/drtio/rt_packet_repeater.py +++ b/artiq/gateware/drtio/rt_packet_repeater.py @@ -38,8 +38,8 @@ class RTPacketRepeater(Module): assert len(link_layer.tx_rt_data) % 8 == 0 ws = len(link_layer.tx_rt_data) tx_plm = get_m2s_layouts(ws) - tx_dp = ClockDomainsRenamer("rtio")(TransmitDatapath( - link_layer.tx_rt_frame, link_layer.tx_rt_data, tx_plm)) + tx_dp = TransmitDatapath( + link_layer.tx_rt_frame, link_layer.tx_rt_data, tx_plm) self.submodules += tx_dp rx_plm = get_s2m_layouts(ws) rx_dp = ClockDomainsRenamer("rtio_rx")(ReceiveDatapath( @@ -49,7 +49,7 @@ class RTPacketRepeater(Module): # TSC sync tsc_value = Signal(64) tsc_value_load = Signal() - self.sync.rtio += If(tsc_value_load, tsc_value.eq(tsc.coarse_ts)) + self.sync += If(tsc_value_load, tsc_value.eq(tsc.coarse_ts)) # CRI buffer stage 1 cb0_loaded = Signal() @@ -60,7 +60,7 @@ class RTPacketRepeater(Module): cb0_chan_sel = Signal(24) cb0_o_address = Signal(8) cb0_o_data = Signal(512) - self.sync.rtio += [ + self.sync += [ If(self.reset | cb0_ack, cb0_loaded.eq(0), cb0_cmd.eq(cri.commands["nop"]) @@ -91,7 +91,7 @@ class RTPacketRepeater(Module): cb_chan_sel = Signal(24) cb_o_address = Signal(8) cb_o_data = Signal(512) - self.sync.rtio += [ + self.sync += [ If(self.reset | cb_ack, cb_loaded.eq(0), cb_cmd.eq(cri.commands["nop"]) @@ -112,11 +112,11 @@ class RTPacketRepeater(Module): wb_extra_data_a = Signal(512) self.comb += wb_extra_data_a.eq(self.cri.o_data[short_data_len:]) for i in range(512//ws): - self.sync.rtio += If(self.cri.cmd == cri.commands["write"], + self.sync += If(self.cri.cmd == cri.commands["write"], If(wb_extra_data_a[ws*i:ws*(i+1)] != 0, wb_extra_data_cnt.eq(i+1))) wb_extra_data = Signal(512) - self.sync.rtio += If(self.cri.cmd == cri.commands["write"], + self.sync += If(self.cri.cmd == cri.commands["write"], wb_extra_data.eq(wb_extra_data_a)) extra_data_ce = Signal() @@ -128,7 +128,7 @@ class RTPacketRepeater(Module): for i in range(512//ws)}), extra_data_last.eq(extra_data_counter == wb_extra_data_cnt) ] - self.sync.rtio += \ + self.sync += \ If(extra_data_ce, extra_data_counter.eq(extra_data_counter + 1), ).Else( @@ -136,19 +136,19 @@ class RTPacketRepeater(Module): ) # Buffer space - self.sync.rtio += If(self.cri.cmd == cri.commands["get_buffer_space"], + self.sync += If(self.cri.cmd == cri.commands["get_buffer_space"], self.buffer_space_destination.eq(self.cri.chan_sel[16:])) rx_buffer_space_not = Signal() rx_buffer_space = Signal(16) buffer_space_not = Signal() buffer_space_not_ack = Signal() - self.submodules += CrossDomainNotification("rtio_rx", "rtio", + self.submodules += CrossDomainNotification("rtio_rx", "sys", rx_buffer_space_not, rx_buffer_space, buffer_space_not, buffer_space_not_ack, self.cri.o_buffer_space) - timeout_counter = ClockDomainsRenamer("rtio")(WaitTimer(8191)) + timeout_counter = WaitTimer(8191) self.submodules += timeout_counter # Read @@ -163,7 +163,7 @@ class RTPacketRepeater(Module): rtio_read_is_overflow = Signal() rtio_read_data = Signal(32) rtio_read_timestamp = Signal(64) - self.submodules += CrossDomainNotification("rtio_rx", "rtio", + self.submodules += CrossDomainNotification("rtio_rx", "sys", read_not, Cat(read_no_event, read_is_overflow, read_data, read_timestamp), @@ -183,7 +183,7 @@ class RTPacketRepeater(Module): i_status_wait_event, i_status_overflow, cb0_loaded | cb_loaded)) load_read_reply = Signal() - self.sync.rtio += [ + self.sync += [ If(load_read_reply, i_status_wait_event.eq(0), i_status_overflow.eq(0), @@ -200,7 +200,7 @@ class RTPacketRepeater(Module): ] # TX and CRI FSM - tx_fsm = ClockDomainsRenamer("rtio")(FSM(reset_state="IDLE")) + tx_fsm = FSM(reset_state="IDLE") self.submodules += tx_fsm tx_fsm.act("IDLE", diff --git a/artiq/gateware/drtio/rx_synchronizer.py b/artiq/gateware/drtio/rx_synchronizer.py index 90fddb97c..055d1d70b 100644 --- a/artiq/gateware/drtio/rx_synchronizer.py +++ b/artiq/gateware/drtio/rx_synchronizer.py @@ -17,7 +17,7 @@ class GenericRXSynchronizer(Module): return synchronized def do_finalize(self): - eb = ElasticBuffer(sum(len(s[0]) for s in self.signals), 4, "rtio_rx", "rtio") + eb = ElasticBuffer(sum(len(s[0]) for s in self.signals), 4, "rtio_rx", "sys") self.submodules += eb self.comb += [ eb.din.eq(Cat(*[s[0] for s in self.signals])), @@ -57,6 +57,6 @@ class XilinxRXSynchronizer(Module): self.specials += [ Instance("FD", i_C=ClockSignal("rtio_rx"), i_D=din[i], o_Q=inter[i], attr={hu_set, ("RLOC", "X0Y{}".format(i))}), - Instance("FD", i_C=ClockSignal("rtio"), i_D=inter[i], o_Q=dout[i], + Instance("FD", i_C=ClockSignal("sys"), i_D=inter[i], o_Q=dout[i], attr={hu_set, ("RLOC", "X1Y{}".format(i))}) ] diff --git a/artiq/gateware/drtio/siphaser.py b/artiq/gateware/drtio/siphaser.py index 5237b7453..c4054c127 100644 --- a/artiq/gateware/drtio/siphaser.py +++ b/artiq/gateware/drtio/siphaser.py @@ -1,23 +1,23 @@ from migen import * -from migen.genlib.cdc import MultiReg, PulseSynchronizer +from migen.genlib.cdc import MultiReg from misoc.interconnect.csr import * -# This code assumes 125/62.5MHz reference clock and 100MHz, 125MHz or 150MHz RTIO +# This code assumes 125/62.5MHz reference clock and 100MHz or 125MHz RTIO # frequency. class SiPhaser7Series(Module, AutoCSR): def __init__(self, si5324_clkin, rx_synchronizer, - ref_clk=None, ref_div2=False, ultrascale=False, rtio_clk_freq=150e6): + ref_clk=None, ref_div2=False, ultrascale=False, rtio_clk_freq=125e6): self.switch_clocks = CSRStorage() self.phase_shift = CSR() self.phase_shift_done = CSRStatus(reset=1) self.error = CSR() - assert rtio_clk_freq in (100e6, 125e6, 150e6) + assert rtio_clk_freq in (100e6, 125e6) - # 125MHz/62.5MHz reference clock to 100MHz/125MHz/150MHz. VCO @ 750MHz. + # 125MHz/62.5MHz reference clock to 100MHz/125MHz. VCO @ 750MHz. # Used to provide a startup clock to the transceiver through the Si, # we do not use the crystal reference so that the PFD (f3) frequency # can be high. @@ -97,17 +97,14 @@ class SiPhaser7Series(Module, AutoCSR): toggle_out = rx_synchronizer.resync(toggle_in) toggle_out_expected = Signal() - self.sync.rtio += toggle_out_expected.eq(~toggle_out) + self.sync += toggle_out_expected.eq(~toggle_out) error = Signal() - error_clear = PulseSynchronizer("sys", "rtio") - self.submodules += error_clear - self.sync.rtio += [ + self.sync += [ If(toggle_out != toggle_out_expected, error.eq(1)), - If(error_clear.o, error.eq(0)) + If(self.error.re, error.eq(0)) ] self.specials += MultiReg(error, self.error.w) - self.comb += error_clear.i.eq(self.error.re) # expose MMCM outputs - used for clock constraints self.mmcm_freerun_output = mmcm_freerun_output diff --git a/artiq/gateware/drtio/transceiver/clock_aligner.py b/artiq/gateware/drtio/transceiver/clock_aligner.py index 9d17d2b77..b0649a148 100644 --- a/artiq/gateware/drtio/transceiver/clock_aligner.py +++ b/artiq/gateware/drtio/transceiver/clock_aligner.py @@ -33,7 +33,7 @@ class BruteforceClockAligner(Module): check_counter = Signal(max=check_max_val+1) check = Signal() reset_check_counter = Signal() - self.sync.rtio_tx += [ + self.sync += [ check.eq(0), If(reset_check_counter, check_counter.eq(check_max_val) @@ -47,7 +47,7 @@ class BruteforceClockAligner(Module): ) ] - checks_reset = PulseSynchronizer("rtio_tx", "rtio_rx") + checks_reset = PulseSynchronizer("sys", "rtio_rx") self.submodules += checks_reset comma_n = ~comma & 0b1111111111 @@ -76,7 +76,7 @@ class BruteforceClockAligner(Module): ) ] - fsm = ClockDomainsRenamer("rtio_tx")(FSM(reset_state="WAIT_COMMA")) + fsm = FSM(reset_state="WAIT_COMMA") self.submodules += fsm fsm.act("WAIT_COMMA", diff --git a/artiq/gateware/drtio/transceiver/gth_ultrascale.py b/artiq/gateware/drtio/transceiver/gth_ultrascale.py deleted file mode 100644 index ddc88037a..000000000 --- a/artiq/gateware/drtio/transceiver/gth_ultrascale.py +++ /dev/null @@ -1,704 +0,0 @@ -from functools import reduce -from operator import or_, and_ - -from migen import * -from migen.genlib.resetsync import AsyncResetSynchronizer - -from misoc.cores.code_8b10b import Encoder, Decoder - -from microscope import * - -from artiq.gateware.drtio.core import TransceiverInterface, ChannelInterface -from artiq.gateware.drtio.transceiver.clock_aligner import BruteforceClockAligner -from artiq.gateware.drtio.transceiver.gth_ultrascale_init import * - - -class GTHSingle(Module): - def __init__(self, refclk, pads, sys_clk_freq, rtio_clk_freq, rtiox_mul, dw, mode): - assert (dw == 20) or (dw == 40) - assert mode in ["single", "master", "slave"] - self.mode = mode - - # phase alignment - self.txsyncallin = Signal() - self.txphaligndone = Signal() - self.txsyncallin = Signal() - self.txsyncin = Signal() - self.txsyncout = Signal() - self.txdlysreset = Signal() - - # # # - - self.txenable = Signal() - nwords = dw//10 - self.submodules.encoder = encoder = ClockDomainsRenamer("rtio_tx")( - Encoder(nwords, True)) - self.submodules.decoders = decoders = [ClockDomainsRenamer("rtio_rx")( - (Decoder(True))) for _ in range(nwords)] - self.rx_ready = Signal() - - # transceiver direct clock outputs - # for OBUFDS_GTE3 - self.rxrecclkout = Signal() - # useful to specify clock constraints in a way palatable to Vivado - self.txoutclk = Signal() - self.rxoutclk = Signal() - - # # # - - # TX generates RTIO clock, init must be in system domain - self.submodules.tx_init = tx_init = GTHInit(sys_clk_freq, False, mode) - # RX receives restart commands from RTIO domain - rx_init = ClockDomainsRenamer("rtio_tx")(GTHInit(rtio_clk_freq, True)) - self.submodules += rx_init - - cpll_reset = Signal() - cpll_lock = Signal() - self.comb += [ - cpll_reset.eq(tx_init.pllreset), - tx_init.plllock.eq(cpll_lock), - rx_init.plllock.eq(cpll_lock) - ] - - txdata = Signal(dw) - rxdata = Signal(dw) - rxphaligndone = Signal() - gth_params = dict( - p_ACJTAG_DEBUG_MODE =0b0, - p_ACJTAG_MODE =0b0, - p_ACJTAG_RESET =0b0, - p_ADAPT_CFG0 =0b1111100000000000, - p_ADAPT_CFG1 =0b0000000000000000, - p_ALIGN_COMMA_DOUBLE ="FALSE", - p_ALIGN_COMMA_ENABLE =0b0000000000, - p_ALIGN_COMMA_WORD =1, - p_ALIGN_MCOMMA_DET ="FALSE", - p_ALIGN_MCOMMA_VALUE =0b1010000011, - p_ALIGN_PCOMMA_DET ="FALSE", - p_ALIGN_PCOMMA_VALUE =0b0101111100, - p_A_RXOSCALRESET =0b0, - p_A_RXPROGDIVRESET =0b0, - p_A_TXPROGDIVRESET =0b0, - p_CBCC_DATA_SOURCE_SEL ="ENCODED", - p_CDR_SWAP_MODE_EN =0b0, - p_CHAN_BOND_KEEP_ALIGN ="FALSE", - p_CHAN_BOND_MAX_SKEW =1, - p_CHAN_BOND_SEQ_1_1 =0b0000000000, - p_CHAN_BOND_SEQ_1_2 =0b0000000000, - p_CHAN_BOND_SEQ_1_3 =0b0000000000, - p_CHAN_BOND_SEQ_1_4 =0b0000000000, - p_CHAN_BOND_SEQ_1_ENABLE =0b1111, - p_CHAN_BOND_SEQ_2_1 =0b0000000000, - p_CHAN_BOND_SEQ_2_2 =0b0000000000, - p_CHAN_BOND_SEQ_2_3 =0b0000000000, - p_CHAN_BOND_SEQ_2_4 =0b0000000000, - p_CHAN_BOND_SEQ_2_ENABLE =0b1111, - p_CHAN_BOND_SEQ_2_USE ="FALSE", - p_CHAN_BOND_SEQ_LEN =1, - p_CLK_CORRECT_USE ="FALSE", - p_CLK_COR_KEEP_IDLE ="FALSE", - p_CLK_COR_MAX_LAT =20, - p_CLK_COR_MIN_LAT =18, - p_CLK_COR_PRECEDENCE ="TRUE", - p_CLK_COR_REPEAT_WAIT =0, - p_CLK_COR_SEQ_1_1 =0b0000000000, - p_CLK_COR_SEQ_1_2 =0b0000000000, - p_CLK_COR_SEQ_1_3 =0b0000000000, - p_CLK_COR_SEQ_1_4 =0b0000000000, - p_CLK_COR_SEQ_1_ENABLE =0b1111, - p_CLK_COR_SEQ_2_1 =0b0000000000, - p_CLK_COR_SEQ_2_2 =0b0000000000, - p_CLK_COR_SEQ_2_3 =0b0000000000, - p_CLK_COR_SEQ_2_4 =0b0000000000, - p_CLK_COR_SEQ_2_ENABLE =0b1111, - p_CLK_COR_SEQ_2_USE ="FALSE", - p_CLK_COR_SEQ_LEN =1, - p_CPLL_CFG0 =0b0110011111111000, - p_CPLL_CFG1 =0b1010010010101100, - p_CPLL_CFG2 =0b0000000000000111, - p_CPLL_CFG3 =0b000000, - p_CPLL_FBDIV =5, - p_CPLL_FBDIV_45 =4, - p_CPLL_INIT_CFG0 =0b0000001010110010, - p_CPLL_INIT_CFG1 =0b00000000, - p_CPLL_LOCK_CFG =0b0000000111101000, - p_CPLL_REFCLK_DIV =1, - p_DDI_CTRL =0b00, - p_DDI_REALIGN_WAIT =15, - p_DEC_MCOMMA_DETECT ="FALSE", - p_DEC_PCOMMA_DETECT ="FALSE", - p_DEC_VALID_COMMA_ONLY ="FALSE", - p_DFE_D_X_REL_POS =0b0, - p_DFE_VCM_COMP_EN =0b0, - p_DMONITOR_CFG0 =0b0000000000, - p_DMONITOR_CFG1 =0b00000000, - p_ES_CLK_PHASE_SEL =0b0, - p_ES_CONTROL =0b000000, - p_ES_ERRDET_EN ="FALSE", - p_ES_EYE_SCAN_EN ="FALSE", - p_ES_HORZ_OFFSET =0b000000000000, - p_ES_PMA_CFG =0b0000000000, - p_ES_PRESCALE =0b00000, - p_ES_QUALIFIER0 =0b0000000000000000, - p_ES_QUALIFIER1 =0b0000000000000000, - p_ES_QUALIFIER2 =0b0000000000000000, - p_ES_QUALIFIER3 =0b0000000000000000, - p_ES_QUALIFIER4 =0b0000000000000000, - p_ES_QUAL_MASK0 =0b0000000000000000, - p_ES_QUAL_MASK1 =0b0000000000000000, - p_ES_QUAL_MASK2 =0b0000000000000000, - p_ES_QUAL_MASK3 =0b0000000000000000, - p_ES_QUAL_MASK4 =0b0000000000000000, - p_ES_SDATA_MASK0 =0b0000000000000000, - p_ES_SDATA_MASK1 =0b0000000000000000, - p_ES_SDATA_MASK2 =0b0000000000000000, - p_ES_SDATA_MASK3 =0b0000000000000000, - p_ES_SDATA_MASK4 =0b0000000000000000, - p_EVODD_PHI_CFG =0b00000000000, - p_EYE_SCAN_SWAP_EN =0b0, - p_FTS_DESKEW_SEQ_ENABLE =0b1111, - p_FTS_LANE_DESKEW_CFG =0b1111, - p_FTS_LANE_DESKEW_EN ="FALSE", - p_GEARBOX_MODE =0b00000, - p_GM_BIAS_SELECT =0b0, - p_LOCAL_MASTER =0b1, - p_OOBDIVCTL =0b00, - p_OOB_PWRUP =0b0, - p_PCI3_AUTO_REALIGN ="OVR_1K_BLK", - p_PCI3_PIPE_RX_ELECIDLE =0b0, - p_PCI3_RX_ASYNC_EBUF_BYPASS =0b00, - p_PCI3_RX_ELECIDLE_EI2_ENABLE =0b0, - p_PCI3_RX_ELECIDLE_H2L_COUNT =0b000000, - p_PCI3_RX_ELECIDLE_H2L_DISABLE =0b000, - p_PCI3_RX_ELECIDLE_HI_COUNT =0b000000, - p_PCI3_RX_ELECIDLE_LP4_DISABLE =0b0, - p_PCI3_RX_FIFO_DISABLE =0b0, - p_PCIE_BUFG_DIV_CTRL =0b0001000000000000, - p_PCIE_RXPCS_CFG_GEN3 =0b0000001010100100, - p_PCIE_RXPMA_CFG =0b0000000000001010, - p_PCIE_TXPCS_CFG_GEN3 =0b0010010010100100, - p_PCIE_TXPMA_CFG =0b0000000000001010, - p_PCS_PCIE_EN ="FALSE", - p_PCS_RSVD0 =0b0000000000000000, - p_PCS_RSVD1 =0b000, - p_PD_TRANS_TIME_FROM_P2 =0b000000111100, - p_PD_TRANS_TIME_NONE_P2 =0b00011001, - p_PD_TRANS_TIME_TO_P2 =0b01100100, - p_PLL_SEL_MODE_GEN12 =0b00, - p_PLL_SEL_MODE_GEN3 =0b11, - p_PMA_RSV1 =0b1111000000000000, - p_PROCESS_PAR =0b010, - p_RATE_SW_USE_DRP =0b1, - p_RESET_POWERSAVE_DISABLE =0b0, - ) - gth_params.update( - p_RXBUFRESET_TIME =0b00011, - p_RXBUF_ADDR_MODE ="FAST", - p_RXBUF_EIDLE_HI_CNT =0b1000, - p_RXBUF_EIDLE_LO_CNT =0b0000, - p_RXBUF_EN ="FALSE", - p_RXBUF_RESET_ON_CB_CHANGE ="TRUE", - p_RXBUF_RESET_ON_COMMAALIGN ="FALSE", - p_RXBUF_RESET_ON_EIDLE ="FALSE", - p_RXBUF_RESET_ON_RATE_CHANGE ="TRUE", - p_RXBUF_THRESH_OVFLW =0, - p_RXBUF_THRESH_OVRD ="FALSE", - p_RXBUF_THRESH_UNDFLW =0, - p_RXCDRFREQRESET_TIME =0b00001, - p_RXCDRPHRESET_TIME =0b00001, - p_RXCDR_CFG0 =0b0000000000000000, - p_RXCDR_CFG0_GEN3 =0b0000000000000000, - p_RXCDR_CFG1 =0b0000000000000000, - p_RXCDR_CFG1_GEN3 =0b0000000000000000, - p_RXCDR_CFG2 =0b0000011111010110, - p_RXCDR_CFG2_GEN3 =0b0000011111100110, - p_RXCDR_CFG3 =0b0000000000000000, - p_RXCDR_CFG3_GEN3 =0b0000000000000000, - p_RXCDR_CFG4 =0b0000000000000000, - p_RXCDR_CFG4_GEN3 =0b0000000000000000, - p_RXCDR_CFG5 =0b0000000000000000, - p_RXCDR_CFG5_GEN3 =0b0000000000000000, - p_RXCDR_FR_RESET_ON_EIDLE =0b0, - p_RXCDR_HOLD_DURING_EIDLE =0b0, - p_RXCDR_LOCK_CFG0 =0b0100010010000000, - p_RXCDR_LOCK_CFG1 =0b0101111111111111, - p_RXCDR_LOCK_CFG2 =0b0111011111000011, - p_RXCDR_PH_RESET_ON_EIDLE =0b0, - p_RXCFOK_CFG0 =0b0100000000000000, - p_RXCFOK_CFG1 =0b0000000001100101, - p_RXCFOK_CFG2 =0b0000000000101110, - p_RXDFELPMRESET_TIME =0b0001111, - p_RXDFELPM_KL_CFG0 =0b0000000000000000, - p_RXDFELPM_KL_CFG1 =0b0000000000000010, - p_RXDFELPM_KL_CFG2 =0b0000000000000000, - p_RXDFE_CFG0 =0b0000101000000000, - p_RXDFE_CFG1 =0b0000000000000000, - p_RXDFE_GC_CFG0 =0b0000000000000000, - p_RXDFE_GC_CFG1 =0b0111100001110000, - p_RXDFE_GC_CFG2 =0b0000000000000000, - p_RXDFE_H2_CFG0 =0b0000000000000000, - p_RXDFE_H2_CFG1 =0b0000000000000000, - p_RXDFE_H3_CFG0 =0b0100000000000000, - p_RXDFE_H3_CFG1 =0b0000000000000000, - p_RXDFE_H4_CFG0 =0b0010000000000000, - p_RXDFE_H4_CFG1 =0b0000000000000011, - p_RXDFE_H5_CFG0 =0b0010000000000000, - p_RXDFE_H5_CFG1 =0b0000000000000011, - p_RXDFE_H6_CFG0 =0b0010000000000000, - p_RXDFE_H6_CFG1 =0b0000000000000000, - p_RXDFE_H7_CFG0 =0b0010000000000000, - p_RXDFE_H7_CFG1 =0b0000000000000000, - p_RXDFE_H8_CFG0 =0b0010000000000000, - p_RXDFE_H8_CFG1 =0b0000000000000000, - p_RXDFE_H9_CFG0 =0b0010000000000000, - p_RXDFE_H9_CFG1 =0b0000000000000000, - p_RXDFE_HA_CFG0 =0b0010000000000000, - p_RXDFE_HA_CFG1 =0b0000000000000000, - p_RXDFE_HB_CFG0 =0b0010000000000000, - p_RXDFE_HB_CFG1 =0b0000000000000000, - p_RXDFE_HC_CFG0 =0b0000000000000000, - p_RXDFE_HC_CFG1 =0b0000000000000000, - p_RXDFE_HD_CFG0 =0b0000000000000000, - p_RXDFE_HD_CFG1 =0b0000000000000000, - p_RXDFE_HE_CFG0 =0b0000000000000000, - p_RXDFE_HE_CFG1 =0b0000000000000000, - p_RXDFE_HF_CFG0 =0b0000000000000000, - p_RXDFE_HF_CFG1 =0b0000000000000000, - p_RXDFE_OS_CFG0 =0b1000000000000000, - p_RXDFE_OS_CFG1 =0b0000000000000000, - p_RXDFE_UT_CFG0 =0b1000000000000000, - p_RXDFE_UT_CFG1 =0b0000000000000011, - p_RXDFE_VP_CFG0 =0b1010101000000000, - p_RXDFE_VP_CFG1 =0b0000000000110011, - p_RXDLY_CFG =0b0000000000011111, - p_RXDLY_LCFG =0b0000000000110000, - p_RXELECIDLE_CFG ="SIGCFG_4", - p_RXGBOX_FIFO_INIT_RD_ADDR =4, - p_RXGEARBOX_EN ="FALSE", - p_RXISCANRESET_TIME =0b00001, - p_RXLPM_CFG =0b0000000000000000, - p_RXLPM_GC_CFG =0b0001000000000000, - p_RXLPM_KH_CFG0 =0b0000000000000000, - p_RXLPM_KH_CFG1 =0b0000000000000010, - p_RXLPM_OS_CFG0 =0b1000000000000000, - p_RXLPM_OS_CFG1 =0b0000000000000010, - p_RXOOB_CFG =0b000000110, - p_RXOOB_CLK_CFG ="PMA", - p_RXOSCALRESET_TIME =0b00011, - p_RXOUT_DIV =2, - p_RXPCSRESET_TIME =0b00011, - p_RXPHBEACON_CFG =0b0000000000000000, - p_RXPHDLY_CFG =0b0010000000100000, - p_RXPHSAMP_CFG =0b0010000100000000, - p_RXPHSLIP_CFG =0b0110011000100010, - p_RXPH_MONITOR_SEL =0b00000, - p_RXPI_CFG0 =0b00, - p_RXPI_CFG1 =0b00, - p_RXPI_CFG2 =0b00, - p_RXPI_CFG3 =0b00, - p_RXPI_CFG4 =0b1, - p_RXPI_CFG5 =0b1, - p_RXPI_CFG6 =0b000, - p_RXPI_LPM =0b0, - p_RXPI_VREFSEL =0b0, - p_RXPMACLK_SEL ="DATA", - p_RXPMARESET_TIME =0b00011, - p_RXPRBS_ERR_LOOPBACK =0b0, - p_RXPRBS_LINKACQ_CNT =15, - p_RXSLIDE_AUTO_WAIT =7, - p_RXSLIDE_MODE ="OFF", - p_RXSYNC_MULTILANE =0b0, - p_RXSYNC_OVRD =0b0, - p_RXSYNC_SKIP_DA =0b0, - p_RX_AFE_CM_EN =0b0, - p_RX_BIAS_CFG0 =0b0000101010110100, - p_RX_BUFFER_CFG =0b000000, - p_RX_CAPFF_SARC_ENB =0b0, - p_RX_CLK25_DIV =6, - p_RX_CLKMUX_EN =0b1, - p_RX_CLK_SLIP_OVRD =0b00000, - p_RX_CM_BUF_CFG =0b1010, - p_RX_CM_BUF_PD =0b0, - p_RX_CM_SEL =0b11, - p_RX_CM_TRIM =0b1010, - p_RX_CTLE3_LPF =0b00000001, - p_RX_DATA_WIDTH =dw, - p_RX_DDI_SEL =0b000000, - p_RX_DEFER_RESET_BUF_EN ="TRUE", - p_RX_DFELPM_CFG0 =0b0110, - p_RX_DFELPM_CFG1 =0b1, - p_RX_DFELPM_KLKH_AGC_STUP_EN =0b1, - p_RX_DFE_AGC_CFG0 =0b10, - p_RX_DFE_AGC_CFG1 =0b100, - p_RX_DFE_KL_LPM_KH_CFG0 =0b01, - p_RX_DFE_KL_LPM_KH_CFG1 =0b100, - p_RX_DFE_KL_LPM_KL_CFG0 =0b01, - p_RX_DFE_KL_LPM_KL_CFG1 =0b100, - p_RX_DFE_LPM_HOLD_DURING_EIDLE =0b0, - p_RX_DISPERR_SEQ_MATCH ="TRUE", - p_RX_DIVRESET_TIME =0b00001, - p_RX_EN_HI_LR =0b0, - p_RX_EYESCAN_VS_CODE =0b0000000, - p_RX_EYESCAN_VS_NEG_DIR =0b0, - p_RX_EYESCAN_VS_RANGE =0b00, - p_RX_EYESCAN_VS_UT_SIGN =0b0, - p_RX_FABINT_USRCLK_FLOP =0b0, - p_RX_INT_DATAWIDTH =dw==40, - p_RX_PMA_POWER_SAVE =0b0, - p_RX_PROGDIV_CFG =0.0, - p_RX_SAMPLE_PERIOD =0b111, - p_RX_SIG_VALID_DLY =11, - p_RX_SUM_DFETAPREP_EN =0b0, - p_RX_SUM_IREF_TUNE =0b0000, - p_RX_SUM_RES_CTRL =0b00, - p_RX_SUM_VCMTUNE =0b0000, - p_RX_SUM_VCM_OVWR =0b0, - p_RX_SUM_VREF_TUNE =0b000, - p_RX_TUNE_AFE_OS =0b10, - p_RX_WIDEMODE_CDR =0b0, - p_RX_XCLK_SEL ="RXUSR", - p_SAS_MAX_COM =64, - p_SAS_MIN_COM =36, - p_SATA_BURST_SEQ_LEN =0b1110, - p_SATA_CPLL_CFG ="VCO_3000MHZ", - p_SATA_MAX_BURST =8, - p_SATA_MAX_INIT =21, - p_SATA_MAX_WAKE =7, - p_SATA_MIN_BURST =4, - p_SATA_MIN_INIT =12, - p_SATA_MIN_WAKE =4, - p_SHOW_REALIGN_COMMA ="TRUE", - p_SIM_RECEIVER_DETECT_PASS ="TRUE", - p_SIM_RESET_SPEEDUP ="TRUE", - p_SIM_TX_EIDLE_DRIVE_LEVEL =0b0, - p_SIM_VERSION =2, - p_TAPDLY_SET_TX =0b00, - p_TEMPERATUR_PAR =0b0010, - p_TERM_RCAL_CFG =0b100001000010000, - p_TERM_RCAL_OVRD =0b000, - p_TRANS_TIME_RATE =0b00001110, - p_TST_RSV0 =0b00000000, - p_TST_RSV1 =0b00000000, - ) - gth_params.update( - p_TXBUF_EN ="FALSE", - p_TXBUF_RESET_ON_RATE_CHANGE ="TRUE", - p_TXDLY_CFG =0b0000000000001001, - p_TXDLY_LCFG =0b0000000001010000, - p_TXDRVBIAS_N =0b1010, - p_TXDRVBIAS_P =0b1010, - p_TXFIFO_ADDR_CFG ="LOW", - p_TXGBOX_FIFO_INIT_RD_ADDR =4, - p_TXGEARBOX_EN ="FALSE", - p_TXOUT_DIV =2, - p_TXPCSRESET_TIME =0b00011, - p_TXPHDLY_CFG0 =0b0010000000100000, - p_TXPHDLY_CFG1 =0b0000000001110101, - p_TXPH_CFG =0b0000100110000000, - p_TXPH_MONITOR_SEL =0b00000, - p_TXPI_CFG0 =0b00, - p_TXPI_CFG1 =0b00, - p_TXPI_CFG2 =0b00, - p_TXPI_CFG3 =0b1, - p_TXPI_CFG4 =0b1, - p_TXPI_CFG5 =0b000, - p_TXPI_GRAY_SEL =0b0, - p_TXPI_INVSTROBE_SEL =0b0, - p_TXPI_LPM =0b0, - p_TXPI_PPMCLK_SEL ="TXUSRCLK2", - p_TXPI_PPM_CFG =0b00000000, - p_TXPI_SYNFREQ_PPM =0b001, - p_TXPI_VREFSEL =0b0, - p_TXPMARESET_TIME =0b00011, - p_TXSYNC_MULTILANE =0 if mode == "single" else 1, - p_TXSYNC_OVRD =0b0, - p_TXSYNC_SKIP_DA =0b0, - p_TX_CLK25_DIV =6, - p_TX_CLKMUX_EN =0b1, - p_TX_DATA_WIDTH =dw, - p_TX_DCD_CFG =0b000010, - p_TX_DCD_EN =0b0, - p_TX_DEEMPH0 =0b000000, - p_TX_DEEMPH1 =0b000000, - p_TX_DIVRESET_TIME =0b00001, - p_TX_DRIVE_MODE ="DIRECT", - p_TX_EIDLE_ASSERT_DELAY =0b100, - p_TX_EIDLE_DEASSERT_DELAY =0b011, - p_TX_EML_PHI_TUNE =0b0, - p_TX_FABINT_USRCLK_FLOP =0b0, - p_TX_IDLE_DATA_ZERO =0b0, - p_TX_INT_DATAWIDTH =dw==40, - p_TX_LOOPBACK_DRIVE_HIZ ="FALSE", - p_TX_MAINCURSOR_SEL =0b0, - p_TX_MARGIN_FULL_0 =0b1001111, - p_TX_MARGIN_FULL_1 =0b1001110, - p_TX_MARGIN_FULL_2 =0b1001100, - p_TX_MARGIN_FULL_3 =0b1001010, - p_TX_MARGIN_FULL_4 =0b1001000, - p_TX_MARGIN_LOW_0 =0b1000110, - p_TX_MARGIN_LOW_1 =0b1000101, - p_TX_MARGIN_LOW_2 =0b1000011, - p_TX_MARGIN_LOW_3 =0b1000010, - p_TX_MARGIN_LOW_4 =0b1000000, - p_TX_MODE_SEL =0b000, - p_TX_PMADATA_OPT =0b0, - p_TX_PMA_POWER_SAVE =0b0, - p_TX_PROGCLK_SEL ="PREPI", - p_TX_PROGDIV_CFG =dw/rtiox_mul, - p_TX_QPI_STATUS_EN =0b0, - p_TX_RXDETECT_CFG =0b00000000110010, - p_TX_RXDETECT_REF =0b100, - p_TX_SAMPLE_PERIOD =0b111, - p_TX_SARC_LPBK_ENB =0b0, - p_TX_XCLK_SEL ="TXUSR", - p_USE_PCS_CLK_PHASE_SEL =0b0, - p_WB_MODE =0b00, - ) - gth_params.update( - # Reset modes - i_GTRESETSEL=0, - i_RESETOVRD=0, - - i_CPLLRESET=0, - i_CPLLPD=cpll_reset, - o_CPLLLOCK=cpll_lock, - i_CPLLLOCKEN=1, - i_CPLLREFCLKSEL=0b001, - i_TSTIN=2**20-1, - i_GTREFCLK0=refclk, - - # TX clock - o_TXOUTCLK=self.txoutclk, - i_TXSYSCLKSEL=0b00, - i_TXPLLCLKSEL=0b00, - i_TXOUTCLKSEL=0b101, - - # TX Startup/Reset - i_GTTXRESET=tx_init.gtXxreset, - o_TXRESETDONE=tx_init.Xxresetdone, - i_TXDLYSRESET=tx_init.Xxdlysreset if mode != "slave" else self.txdlysreset, - o_TXDLYSRESETDONE=tx_init.Xxdlysresetdone, - o_TXPHALIGNDONE=tx_init.Xxphaligndone, - i_TXUSERRDY=tx_init.Xxuserrdy, - i_TXSYNCMODE=mode != "slave", - - i_TXSYNCALLIN=self.txsyncallin, - i_TXSYNCIN=self.txsyncin, - o_TXSYNCOUT=self.txsyncout, - - # TX data - i_TXINHIBIT=~self.txenable, - i_TXCTRL0=Cat(*[txdata[10*i+8] for i in range(nwords)]), - i_TXCTRL1=Cat(*[txdata[10*i+9] for i in range(nwords)]), - i_TXDATA=Cat(*[txdata[10*i:10*i+8] for i in range(nwords)]), - i_TXUSRCLK=ClockSignal("rtio_tx"), - i_TXUSRCLK2=ClockSignal("rtio_tx"), - - # TX electrical - i_TXPD=0b00, - i_TXBUFDIFFCTRL=0b000, - i_TXDIFFCTRL=0b1100, - - # RX Startup/Reset - i_GTRXRESET=rx_init.gtXxreset, - o_RXRESETDONE=rx_init.Xxresetdone, - i_RXDLYSRESET=rx_init.Xxdlysreset, - o_RXPHALIGNDONE=rxphaligndone, - i_RXSYNCALLIN=rxphaligndone, - i_RXUSERRDY=rx_init.Xxuserrdy, - i_RXSYNCIN=0, - i_RXSYNCMODE=1, - o_RXSYNCDONE=rx_init.Xxsyncdone, - - # RX AFE - i_RXDFEAGCCTRL=1, - i_RXDFEXYDEN=1, - i_RXLPMEN=1, - i_RXOSINTCFG=0xd, - i_RXOSINTEN=1, - - # RX clock - i_RXRATE=0, - i_RXDLYBYPASS=0, - i_RXSYSCLKSEL=0b00, - i_RXOUTCLKSEL=0b010, - i_RXPLLCLKSEL=0b00, - o_RXRECCLKOUT=self.rxrecclkout, - o_RXOUTCLK=self.rxoutclk, - i_RXUSRCLK=ClockSignal("rtio_rx"), - i_RXUSRCLK2=ClockSignal("rtio_rx"), - - # RX data - o_RXCTRL0=Cat(*[rxdata[10*i+8] for i in range(nwords)]), - o_RXCTRL1=Cat(*[rxdata[10*i+9] for i in range(nwords)]), - o_RXDATA=Cat(*[rxdata[10*i:10*i+8] for i in range(nwords)]), - - # RX electrical - i_RXPD=Replicate(rx_init.restart, 2), - i_RXELECIDLEMODE=0b11, - - # Pads - i_GTHRXP=pads.rxp, - i_GTHRXN=pads.rxn, - o_GTHTXP=pads.txp, - o_GTHTXN=pads.txn - ) - self.specials += Instance("GTHE3_CHANNEL", **gth_params) - self.comb += self.txphaligndone.eq(tx_init.Xxphaligndone) - - self.submodules += [ - add_probe_async("drtio_gth", "cpll_lock", cpll_lock), - add_probe_async("drtio_gth", "txuserrdy", tx_init.Xxuserrdy), - add_probe_async("drtio_gth", "tx_init_done", tx_init.done), - add_probe_async("drtio_gth", "rxuserrdy", rx_init.Xxuserrdy), - add_probe_async("drtio_gth", "rx_init_done", rx_init.done), - add_probe_buffer("drtio_gth", "txdata", txdata, clock_domain="rtio_tx"), - add_probe_buffer("drtio_gth", "rxdata", rxdata, clock_domain="rtio_rx") - ] - - # tx clocking - tx_reset_deglitched = Signal() - tx_reset_deglitched.attr.add("no_retiming") - self.sync += tx_reset_deglitched.eq(~tx_init.done) - self.clock_domains.cd_rtio_tx = ClockDomain() - self.clock_domains.cd_rtiox_tx = ClockDomain() - if mode == "master" or mode == "single": - self.specials += [ - Instance("BUFG_GT", i_I=self.txoutclk, o_O=self.cd_rtiox_tx.clk, i_DIV=0), - Instance("BUFG_GT", i_I=self.txoutclk, o_O=self.cd_rtio_tx.clk, i_DIV=rtiox_mul-1) - ] - self.specials += AsyncResetSynchronizer(self.cd_rtio_tx, tx_reset_deglitched) - - # rx clocking - rx_reset_deglitched = Signal() - rx_reset_deglitched.attr.add("no_retiming") - self.sync.rtio_tx += rx_reset_deglitched.eq(~rx_init.done) - self.clock_domains.cd_rtio_rx = ClockDomain() - self.specials += [ - Instance("BUFG_GT", i_I=self.rxoutclk, o_O=self.cd_rtio_rx.clk), - AsyncResetSynchronizer(self.cd_rtio_rx, rx_reset_deglitched) - ] - - # tx data - self.comb += txdata.eq(Cat(*[encoder.output[i] for i in range(nwords)])) - - # rx data - for i in range(nwords): - self.comb += decoders[i].input.eq(rxdata[10*i:10*(i+1)]) - - # clock alignment - clock_aligner = BruteforceClockAligner(0b0101111100, rtio_clk_freq) - self.submodules += clock_aligner - self.comb += [ - clock_aligner.rxdata.eq(rxdata), - rx_init.restart.eq(clock_aligner.restart), - self.rx_ready.eq(clock_aligner.ready) - ] - self.submodules += add_probe_async("drtio_gth", "clock_aligner_ready", clock_aligner.ready) - - -class GTHTXPhaseAlignement(Module): - # TX Buffer Bypass in Single-Lane/Multi-Lane Auto Mode (ug576) - def __init__(self, gths): - txsyncallin = Signal() - txsync = Signal() - txphaligndone = Signal(len(gths)) - txdlysreset = Signal() - ready_for_align = Signal(len(gths)) - all_ready_for_align = Signal() - - for i, gth in enumerate(gths): - # Common to all transceivers - self.comb += [ - ready_for_align[i].eq(1), - gth.txsyncin.eq(txsync), - gth.txsyncallin.eq(txsyncallin), - txphaligndone[i].eq(gth.txphaligndone) - ] - # Specific to Master or Single transceivers - if gth.mode == "master" or gth.mode == "single": - self.comb += [ - gth.tx_init.all_ready_for_align.eq(all_ready_for_align), - txsync.eq(gth.txsyncout), - txdlysreset.eq(gth.tx_init.Xxdlysreset) - ] - # Specific to Slave transceivers - else: - self.comb += [ - ready_for_align[i].eq(gth.tx_init.ready_for_align), - gth.txdlysreset.eq(txdlysreset), - ] - - self.comb += [ - txsyncallin.eq(reduce(and_, [txphaligndone[i] for i in range(len(gths))])), - all_ready_for_align.eq(reduce(and_, [ready_for_align[i] for i in range(len(gths))])) - ] - - -class GTH(Module, TransceiverInterface): - def __init__(self, clock_pads, data_pads, sys_clk_freq, rtio_clk_freq, rtiox_mul=2, dw=20, master=0, clock_recout_pads=None): - self.nchannels = nchannels = len(data_pads) - self.gths = [] - - # # # - - create_buf = hasattr(clock_pads, "p") - if create_buf: - refclk = Signal() - ibufds_ceb = Signal() - self.specials += Instance("IBUFDS_GTE3", - i_CEB=ibufds_ceb, - i_I=clock_pads.p, - i_IB=clock_pads.n, - o_O=refclk) - else: - refclk = clock_pads - - rtio_tx_clk = Signal() - channel_interfaces = [] - for i in range(nchannels): - if nchannels == 1: - mode = "single" - else: - mode = "master" if i == master else "slave" - gth = GTHSingle(refclk, data_pads[i], sys_clk_freq, rtio_clk_freq, rtiox_mul, dw, mode) - if mode == "master": - self.comb += rtio_tx_clk.eq(gth.cd_rtio_tx.clk) - elif mode == "slave": - self.comb += gth.cd_rtio_tx.clk.eq(rtio_tx_clk) - self.gths.append(gth) - setattr(self.submodules, "gth"+str(i), gth) - channel_interface = ChannelInterface(gth.encoder, gth.decoders) - self.comb += channel_interface.rx_ready.eq(gth.rx_ready) - channel_interfaces.append(channel_interface) - - self.submodules.tx_phase_alignment = GTHTXPhaseAlignement(self.gths) - - TransceiverInterface.__init__(self, channel_interfaces) - for n, gth in enumerate(self.gths): - self.comb += gth.txenable.eq(self.txenable.storage[n]) - self.clock_domains.cd_rtiox = ClockDomain(reset_less=True) - if create_buf: - # GTH PLLs recover on their own from an interrupted clock input, - # but be paranoid about HMC7043 noise. - self.stable_clkin.storage.attr.add("no_retiming") - self.comb += ibufds_ceb.eq(~self.stable_clkin.storage) - - self.comb += [ - self.cd_rtio.clk.eq(self.gths[master].cd_rtio_tx.clk), - self.cd_rtiox.clk.eq(self.gths[master].cd_rtiox_tx.clk), - self.cd_rtio.rst.eq(reduce(or_, [gth.cd_rtio_tx.rst for gth in self.gths])) - ] - for i in range(nchannels): - self.comb += [ - getattr(self, "cd_rtio_rx" + str(i)).clk.eq(self.gths[i].cd_rtio_rx.clk), - getattr(self, "cd_rtio_rx" + str(i)).rst.eq(self.gths[i].cd_rtio_rx.rst) - ] - - if clock_recout_pads is not None: - self.specials += Instance("OBUFDS_GTE3", - p_REFCLK_EN_TX_PATH=0b1, - p_REFCLK_ICNTL_TX=0b00111, - i_I=self.gths[0].rxrecclkout, - i_CEB=0, - o_O=clock_recout_pads.p, o_OB=clock_recout_pads.n) diff --git a/artiq/gateware/drtio/transceiver/gth_ultrascale_init.py b/artiq/gateware/drtio/transceiver/gth_ultrascale_init.py deleted file mode 100644 index 30645b876..000000000 --- a/artiq/gateware/drtio/transceiver/gth_ultrascale_init.py +++ /dev/null @@ -1,157 +0,0 @@ -from math import ceil - -from migen import * -from migen.genlib.cdc import MultiReg -from migen.genlib.misc import WaitTimer - - -__all__ = ["GTHInit"] - - -class GTHInit(Module): - def __init__(self, sys_clk_freq, rx, mode="master"): - assert not (rx and mode != "master") - self.done = Signal() - self.restart = Signal() - - # GTH signals - self.plllock = Signal() - self.pllreset = Signal() - self.gtXxreset = Signal() - self.Xxresetdone = Signal() - self.Xxdlysreset = Signal() - self.Xxdlysresetdone = Signal() - self.Xxphaligndone = Signal() - self.Xxsyncdone = Signal() - self.Xxuserrdy = Signal() - - self.all_ready_for_align = Signal(reset=1) - self.ready_for_align = Signal() - - # # # - - # Double-latch transceiver asynch outputs - plllock = Signal() - Xxresetdone = Signal() - Xxdlysresetdone = Signal() - Xxphaligndone = Signal() - Xxsyncdone = Signal() - self.specials += [ - MultiReg(self.plllock, plllock), - MultiReg(self.Xxresetdone, Xxresetdone), - MultiReg(self.Xxdlysresetdone, Xxdlysresetdone), - MultiReg(self.Xxphaligndone, Xxphaligndone), - MultiReg(self.Xxsyncdone, Xxsyncdone) - ] - - # Deglitch FSM outputs driving transceiver asynch inputs - gtXxreset = Signal() - Xxdlysreset = Signal() - Xxuserrdy = Signal() - self.sync += [ - self.gtXxreset.eq(gtXxreset), - self.Xxdlysreset.eq(Xxdlysreset), - self.Xxuserrdy.eq(Xxuserrdy) - ] - - # PLL reset must be at least 2us - pll_reset_cycles = ceil(2000*sys_clk_freq/1000000000) - pll_reset_timer = WaitTimer(pll_reset_cycles) - self.submodules += pll_reset_timer - - startup_fsm = ResetInserter()(FSM(reset_state="RESET_ALL")) - self.submodules += startup_fsm - - ready_timer = WaitTimer(int(sys_clk_freq/1000)) - self.submodules += ready_timer - self.comb += [ - ready_timer.wait.eq(~self.done & ~startup_fsm.reset), - startup_fsm.reset.eq(self.restart | ready_timer.done) - ] - - if rx: - cdr_stable_timer = WaitTimer(1024) - self.submodules += cdr_stable_timer - - Xxphaligndone_r = Signal(reset=1) - Xxphaligndone_rising = Signal() - self.sync += Xxphaligndone_r.eq(Xxphaligndone) - self.comb += Xxphaligndone_rising.eq(Xxphaligndone & ~Xxphaligndone_r) - - startup_fsm.act("RESET_ALL", - gtXxreset.eq(1), - self.pllreset.eq(1), - pll_reset_timer.wait.eq(1), - If(pll_reset_timer.done, - NextState("RELEASE_PLL_RESET") - ) - ) - startup_fsm.act("RELEASE_PLL_RESET", - gtXxreset.eq(1), - If(plllock, NextState("RELEASE_GTH_RESET")) - ) - # Release GTH reset and wait for GTH resetdone - # (from UG476, GTH is reset on falling edge - # of gtXxreset) - if rx: - startup_fsm.act("RELEASE_GTH_RESET", - Xxuserrdy.eq(1), - cdr_stable_timer.wait.eq(1), - If(Xxresetdone & cdr_stable_timer.done, NextState("ALIGN")) - ) - else: - startup_fsm.act("RELEASE_GTH_RESET", - Xxuserrdy.eq(1), - If(Xxresetdone, - If(mode == "slave", - NextState("WAIT_ALIGN") - ).Else( - NextState("ALIGN") - ) - ) - ) - # Start delay alignment (pulse) - startup_fsm.act("ALIGN", - Xxuserrdy.eq(1), - If(self.all_ready_for_align, - Xxdlysreset.eq(1), - NextState("WAIT_ALIGN") - ) - ) - if rx: - # Wait for delay alignment - startup_fsm.act("WAIT_ALIGN", - Xxuserrdy.eq(1), - If(Xxsyncdone, - NextState("READY") - ) - ) - else: - # Wait for delay alignment - startup_fsm.act("WAIT_ALIGN", - Xxuserrdy.eq(1), - self.ready_for_align.eq(1), - If(Xxdlysresetdone, - If(mode == "slave", - NextState("WAIT_LAST_ALIGN_DONE") - ).Else( - NextState("WAIT_FIRST_ALIGN_DONE") - ) - ) - ) - - # Wait 2 rising edges of Xxphaligndone - # (from UG576 in TX Buffer Bypass in Single-Lane Auto Mode) - startup_fsm.act("WAIT_FIRST_ALIGN_DONE", - Xxuserrdy.eq(1), - If(Xxphaligndone_rising, NextState("WAIT_LAST_ALIGN_DONE")) - ) - startup_fsm.act("WAIT_LAST_ALIGN_DONE", - Xxuserrdy.eq(1), - If(Xxphaligndone_rising, NextState("READY")) - ) - startup_fsm.act("READY", - Xxuserrdy.eq(1), - self.done.eq(1), - If(self.restart, NextState("RESET_ALL")) - ) diff --git a/artiq/gateware/drtio/transceiver/gtp_7series.py b/artiq/gateware/drtio/transceiver/gtp_7series.py index 5da42b22b..75c31a486 100644 --- a/artiq/gateware/drtio/transceiver/gtp_7series.py +++ b/artiq/gateware/drtio/transceiver/gtp_7series.py @@ -20,8 +20,7 @@ class GTPSingle(Module): self.stable_clkin = Signal() self.txenable = Signal() - self.submodules.encoder = encoder = ClockDomainsRenamer("rtio_tx")( - Encoder(2, True)) + self.submodules.encoder = encoder = Encoder(2, True) self.submodules.decoders = decoders = [ClockDomainsRenamer("rtio_rx")( (Decoder(True))) for _ in range(2)] self.rx_ready = Signal() @@ -33,10 +32,11 @@ class GTPSingle(Module): # # # - # TX generates RTIO clock, init must be in system domain - self.submodules.tx_init = tx_init = GTPTXInit(sys_clk_freq, mode) - # RX receives restart commands from RTIO domain - rx_init = ClockDomainsRenamer("rtio_tx")(GTPRXInit(rtio_clk_freq)) + # TX generates RTIO clock, init must be in bootstrap domain + self.submodules.tx_init = tx_init = ClockDomainsRenamer("bootstrap")( + GTPTXInit(125e6, mode)) + # RX receives restart commands from SYS domain + rx_init = GTPRXInit(rtio_clk_freq) self.submodules += rx_init self.comb += [ @@ -367,7 +367,7 @@ class GTPSingle(Module): # Channel - DRP Ports i_DRPADDR=rx_init.drpaddr, - i_DRPCLK=ClockSignal("rtio_tx"), + i_DRPCLK=ClockSignal("sys"), i_DRPDI=rx_init.drpdi, o_DRPDO=rx_init.drpdo, i_DRPEN=rx_init.drpen, @@ -566,8 +566,8 @@ class GTPSingle(Module): i_PMARSVDIN1 =0b0, # Transmit Ports - FPGA TX Interface Ports i_TXDATA =Cat(txdata[:8], txdata[10:18]), - i_TXUSRCLK =ClockSignal("rtio_tx"), - i_TXUSRCLK2 =ClockSignal("rtio_tx"), + i_TXUSRCLK =ClockSignal("sys"), + i_TXUSRCLK2 =ClockSignal("sys"), # Transmit Ports - PCI Express Ports i_TXELECIDLE =0, @@ -665,19 +665,10 @@ class GTPSingle(Module): raise ValueError self.specials += Instance("GTPE2_CHANNEL", **gtp_params) - # tx clocking - tx_reset_deglitched = Signal() - tx_reset_deglitched.attr.add("no_retiming") - self.sync += tx_reset_deglitched.eq(~tx_init.done) - self.clock_domains.cd_rtio_tx = ClockDomain() - if mode == "master" or mode == "single": - self.specials += Instance("BUFG", i_I=self.txoutclk, o_O=self.cd_rtio_tx.clk) - self.specials += AsyncResetSynchronizer(self.cd_rtio_tx, tx_reset_deglitched) - # rx clocking rx_reset_deglitched = Signal() rx_reset_deglitched.attr.add("no_retiming") - self.sync.rtio_tx += rx_reset_deglitched.eq(~rx_init.done) + self.sync += rx_reset_deglitched.eq(~rx_init.done) self.clock_domains.cd_rtio_rx = ClockDomain() self.specials += [ Instance("BUFG", i_I=self.rxoutclk, o_O=self.cd_rtio_rx.clk), @@ -727,7 +718,6 @@ class GTP(Module, TransceiverInterface): # # # - rtio_tx_clk = Signal() channel_interfaces = [] for i in range(nchannels): if nchannels == 1: @@ -735,10 +725,6 @@ class GTP(Module, TransceiverInterface): else: mode = "master" if i == master else "slave" gtp = GTPSingle(qpll_channel, data_pads[i], sys_clk_freq, rtio_clk_freq, mode) - if mode == "slave": - self.comb += gtp.cd_rtio_tx.clk.eq(rtio_tx_clk) - else: - self.comb += rtio_tx_clk.eq(gtp.cd_rtio_tx.clk) self.gtps.append(gtp) setattr(self.submodules, "gtp"+str(i), gtp) channel_interface = ChannelInterface(gtp.encoder, gtp.decoders) @@ -754,10 +740,6 @@ class GTP(Module, TransceiverInterface): gtp.txenable.eq(self.txenable.storage[n]) ] - self.comb += [ - self.cd_rtio.clk.eq(self.gtps[master].cd_rtio_tx.clk), - self.cd_rtio.rst.eq(reduce(or_, [gtp.cd_rtio_tx.rst for gtp in self.gtps])) - ] for i in range(nchannels): self.comb += [ getattr(self, "cd_rtio_rx" + str(i)).clk.eq(self.gtps[i].cd_rtio_rx.clk), diff --git a/artiq/gateware/drtio/transceiver/gtx_7series.py b/artiq/gateware/drtio/transceiver/gtx_7series.py index 3d96971ca..fe901d90c 100644 --- a/artiq/gateware/drtio/transceiver/gtx_7series.py +++ b/artiq/gateware/drtio/transceiver/gtx_7series.py @@ -1,5 +1,6 @@ from migen import * from migen.genlib.resetsync import AsyncResetSynchronizer +from migen.genlib.cdc import MultiReg from misoc.cores.code_8b10b import Encoder, Decoder from misoc.interconnect.csr import * @@ -16,13 +17,12 @@ class GTX_20X(Module): # * GTX PLL frequency @ 2.5GHz # * GTX line rate (TX & RX) @ 2.5Gb/s # * GTX TX/RX USRCLK @ 125MHz == coarse RTIO frequency - def __init__(self, refclk, pads, sys_clk_freq, rtio_clk_freq=125e6, tx_mode="single", rx_mode="single"): + def __init__(self, refclk, pads, clk_freq=125e6, tx_mode="single", rx_mode="single"): assert tx_mode in ["single", "master", "slave"] assert rx_mode in ["single", "master", "slave"] self.txenable = Signal() - self.submodules.encoder = ClockDomainsRenamer("rtio_tx")( - Encoder(2, True)) + self.submodules.encoder = Encoder(2, True) self.submodules.decoders = [ClockDomainsRenamer("rtio_rx")( (Decoder(True))) for _ in range(2)] self.rx_ready = Signal() @@ -36,11 +36,11 @@ class GTX_20X(Module): cpllreset = Signal() cplllock = Signal() - # TX generates RTIO clock, init must be in system domain - self.submodules.tx_init = tx_init = GTXInit(sys_clk_freq, False, mode=tx_mode) - # RX receives restart commands from RTIO domain - self.submodules.rx_init = rx_init = ClockDomainsRenamer("rtio_tx")( - GTXInit(rtio_clk_freq, True, mode=rx_mode)) + # TX generates SYS clock, init must be in bootstrap domain + self.submodules.tx_init = tx_init = ClockDomainsRenamer("bootstrap")( + GTXInit(clk_freq, False, mode=tx_mode)) + # RX receives restart commands from SYS domain + self.submodules.rx_init = rx_init = GTXInit(clk_freq, True, mode=rx_mode) self.comb += [ cpllreset.eq(tx_init.cpllreset), tx_init.cplllock.eq(cplllock), @@ -113,8 +113,8 @@ class GTX_20X(Module): i_TXCHARDISPMODE=Cat(txdata[9], txdata[19]), i_TXCHARDISPVAL=Cat(txdata[8], txdata[18]), i_TXDATA=Cat(txdata[:8], txdata[10:18]), - i_TXUSRCLK=ClockSignal("rtio_tx"), - i_TXUSRCLK2=ClockSignal("rtio_tx"), + i_TXUSRCLK=ClockSignal("sys"), + i_TXUSRCLK2=ClockSignal("sys"), # TX electrical i_TXBUFDIFFCTRL=0b100, @@ -247,19 +247,10 @@ class GTX_20X(Module): p_ES_EYE_SCAN_EN="TRUE", # Must be TRUE for GTX ) - # TX clocking - tx_reset_deglitched = Signal() - tx_reset_deglitched.attr.add("no_retiming") - self.sync += tx_reset_deglitched.eq(~tx_init.done) - self.clock_domains.cd_rtio_tx = ClockDomain() - if tx_mode == "single" or tx_mode == "master": - self.specials += Instance("BUFG", i_I=self.txoutclk, o_O=self.cd_rtio_tx.clk) - self.specials += AsyncResetSynchronizer(self.cd_rtio_tx, tx_reset_deglitched) - # RX clocking rx_reset_deglitched = Signal() rx_reset_deglitched.attr.add("no_retiming") - self.sync.rtio += rx_reset_deglitched.eq(~rx_init.done) + self.sync += rx_reset_deglitched.eq(~rx_init.done) self.clock_domains.cd_rtio_rx = ClockDomain() if rx_mode == "single" or rx_mode == "master": self.specials += Instance("BUFG", i_I=self.rxoutclk, o_O=self.cd_rtio_rx.clk), @@ -271,7 +262,7 @@ class GTX_20X(Module): self.decoders[1].input.eq(rxdata[10:]) ] - clock_aligner = BruteforceClockAligner(0b0101111100, rtio_clk_freq) + clock_aligner = BruteforceClockAligner(0b0101111100, clk_freq) self.submodules += clock_aligner self.comb += [ clock_aligner.rxdata.eq(rxdata), @@ -282,17 +273,18 @@ class GTX_20X(Module): class GTX(Module, TransceiverInterface): - def __init__(self, clock_pads, pads, sys_clk_freq, rtio_clk_freq=125e6, master=0): + def __init__(self, clock_pads, pads, clk_freq=125e6, master=0): self.nchannels = nchannels = len(pads) self.gtxs = [] - self.rtio_clk_freq = rtio_clk_freq + self.rtio_clk_freq = clk_freq # # # refclk = Signal() - stable_clkin_n = Signal() + + clk_enable = Signal() self.specials += Instance("IBUFDS_GTE2", - i_CEB=stable_clkin_n, + i_CEB=~clk_enable, i_I=clock_pads.p, i_IB=clock_pads.n, o_O=refclk, @@ -301,7 +293,6 @@ class GTX(Module, TransceiverInterface): p_CLKSWING_CFG="0b11" ) - rtio_tx_clk = Signal() channel_interfaces = [] for i in range(nchannels): if nchannels == 1: @@ -309,12 +300,7 @@ class GTX(Module, TransceiverInterface): else: mode = "master" if i == master else "slave" # Note: RX phase alignment is to be done on individual lanes, not multi-lane. - gtx = GTX_20X(refclk, pads[i], sys_clk_freq, rtio_clk_freq=rtio_clk_freq, tx_mode=mode, rx_mode="single") - # Fan-out (to slave) / Fan-in (from master) of the TXUSRCLK - if mode == "slave": - self.comb += gtx.cd_rtio_tx.clk.eq(rtio_tx_clk) - else: - self.comb += rtio_tx_clk.eq(gtx.cd_rtio_tx.clk) + gtx = GTX_20X(refclk, pads[i], clk_freq, tx_mode=mode, rx_mode="single") self.gtxs.append(gtx) setattr(self.submodules, "gtx"+str(i), gtx) channel_interface = ChannelInterface(gtx.encoder, gtx.decoders) @@ -326,15 +312,16 @@ class GTX(Module, TransceiverInterface): TransceiverInterface.__init__(self, channel_interfaces) for n, gtx in enumerate(self.gtxs): self.comb += [ - stable_clkin_n.eq(~self.stable_clkin.storage), - gtx.txenable.eq(self.txenable.storage[n]) + gtx.txenable.eq(self.txenable.storage[n]), + gtx.tx_init.stable_clkin.eq(clk_enable) ] + # rx_init is in SYS domain, rather than bootstrap + self.specials += MultiReg(clk_enable, gtx.rx_init.stable_clkin) + + # stable_clkin resets after reboot since it's in SYS domain + # still need to keep clk_enable high after this + self.sync.bootstrap += clk_enable.eq(self.stable_clkin.storage | self.gtxs[0].tx_init.done) - # Connect master's `rtio_tx` clock to `rtio` clock - self.comb += [ - self.cd_rtio.clk.eq(self.gtxs[master].cd_rtio_tx.clk), - self.cd_rtio.rst.eq(reduce(or_, [gtx.cd_rtio_tx.rst for gtx in self.gtxs])) - ] # Connect slave i's `rtio_rx` clock to `rtio_rxi` clock for i in range(nchannels): self.comb += [ diff --git a/artiq/gateware/drtio/transceiver/gtx_7series_init.py b/artiq/gateware/drtio/transceiver/gtx_7series_init.py index 70c69a19c..5552c308f 100644 --- a/artiq/gateware/drtio/transceiver/gtx_7series_init.py +++ b/artiq/gateware/drtio/transceiver/gtx_7series_init.py @@ -11,11 +11,13 @@ class GTXInit(Module): # Choose between Auto Mode and Manual Mode for TX/RX phase alignment with buffer bypassed: # * Auto Mode: When only single lane is involved, as suggested by Xilinx (AR59612) # * Manual Mode: When only multi-lane is involved, as suggested by Xilinx (AR59612) - def __init__(self, sys_clk_freq, rx, mode="single"): + def __init__(self, clk_freq, rx, mode="single"): assert isinstance(rx, bool) assert mode in ["single", "master", "slave"] self.mode = mode + self.stable_clkin = Signal() + self.done = Signal() self.restart = Signal() @@ -83,13 +85,13 @@ class GTXInit(Module): # After configuration, transceiver resets have to stay low for # at least 500ns (see AR43482) - startup_cycles = ceil(500*sys_clk_freq/1000000000) + startup_cycles = ceil(500*clk_freq/1000000000) startup_timer = WaitTimer(startup_cycles) self.submodules += startup_timer # PLL reset should be 1 period of refclk # (i.e. 1/(125MHz) for the case of RTIO @ 125MHz) - pll_reset_cycles = ceil(sys_clk_freq/125e6) + pll_reset_cycles = ceil(clk_freq/125e6) pll_reset_timer = WaitTimer(pll_reset_cycles) self.submodules += pll_reset_timer @@ -108,7 +110,7 @@ class GTXInit(Module): startup_fsm.act("INITIAL", startup_timer.wait.eq(1), - If(startup_timer.done, NextState("RESET_ALL")) + If(startup_timer.done & self.stable_clkin, NextState("RESET_ALL")) ) startup_fsm.act("RESET_ALL", gtXxreset.eq(1), diff --git a/artiq/gateware/drtio/wrpll/__init__.py b/artiq/gateware/drtio/wrpll/__init__.py deleted file mode 100644 index 25e510f4c..000000000 --- a/artiq/gateware/drtio/wrpll/__init__.py +++ /dev/null @@ -1,2 +0,0 @@ -from artiq.gateware.drtio.wrpll.core import WRPLL -from artiq.gateware.drtio.wrpll.ddmtd import DDMTDSamplerExtFF, DDMTDSamplerGTP diff --git a/artiq/gateware/drtio/wrpll/core.py b/artiq/gateware/drtio/wrpll/core.py deleted file mode 100644 index 52bc91ab7..000000000 --- a/artiq/gateware/drtio/wrpll/core.py +++ /dev/null @@ -1,156 +0,0 @@ -from migen import * -from migen.genlib.resetsync import AsyncResetSynchronizer -from migen.genlib.cdc import MultiReg, PulseSynchronizer -from misoc.interconnect.csr import * - -from artiq.gateware.drtio.wrpll.si549 import Si549 -from artiq.gateware.drtio.wrpll.ddmtd import DDMTD, Collector -from artiq.gateware.drtio.wrpll import thls, filters - - -class FrequencyCounter(Module, AutoCSR): - def __init__(self, timer_width=23, counter_width=23, domains=["helper", "rtio", "rtio_rx0"]): - for domain in domains: - name = "counter_" + domain - counter = CSRStatus(counter_width, name=name) - setattr(self, name, counter) - self.update_en = CSRStorage() - - timer = Signal(timer_width) - timer_tick = Signal() - self.sync += Cat(timer, timer_tick).eq(timer + 1) - - for domain in domains: - sync_domain = getattr(self.sync, domain) - divider = Signal(2) - sync_domain += divider.eq(divider + 1) - - divided = Signal() - divided.attr.add("no_retiming") - sync_domain += divided.eq(divider[-1]) - divided_sys = Signal() - self.specials += MultiReg(divided, divided_sys) - - divided_sys_r = Signal() - divided_tick = Signal() - self.sync += divided_sys_r.eq(divided_sys) - self.comb += divided_tick.eq(divided_sys & ~divided_sys_r) - - counter = Signal(counter_width) - counter_csr = getattr(self, "counter_" + domain) - self.sync += [ - If(timer_tick, - If(self.update_en.storage, counter_csr.status.eq(counter)), - counter.eq(0), - ).Else( - If(divided_tick, counter.eq(counter + 1)) - ) - ] - - -class WRPLL(Module, AutoCSR): - def __init__(self, helper_clk_pads, main_dcxo_i2c, helper_dxco_i2c, ddmtd_inputs, N=15): - self.helper_reset = CSRStorage(reset=1) - self.collector_reset = CSRStorage(reset=1) - self.filter_reset = CSRStorage(reset=1) - self.adpll_offset_helper = CSRStorage(24) - self.adpll_offset_main = CSRStorage(24) - - self.tag_arm = CSR() - self.main_diff_tag = CSRStatus(32) - self.helper_diff_tag = CSRStatus(32) - self.ref_tag = CSRStatus(N) - self.main_tag = CSRStatus(N) - - main_diff_tag_32 = Signal((32, True)) - helper_diff_tag_32 = Signal((32, True)) - self.comb += [ - self.main_diff_tag.status.eq(main_diff_tag_32), - self.helper_diff_tag.status.eq(helper_diff_tag_32) - ] - - self.clock_domains.cd_helper = ClockDomain() - self.clock_domains.cd_collector = ClockDomain() - self.clock_domains.cd_filter = ClockDomain() - self.helper_reset.storage.attr.add("no_retiming") - self.filter_reset.storage.attr.add("no_retiming") - self.specials += Instance("IBUFGDS", - i_I=helper_clk_pads.p, i_IB=helper_clk_pads.n, - o_O=self.cd_helper.clk) - self.comb += [ - self.cd_collector.clk.eq(self.cd_collector.clk), - self.cd_filter.clk.eq(self.cd_helper.clk), - ] - self.specials += [ - AsyncResetSynchronizer(self.cd_helper, self.helper_reset.storage), - AsyncResetSynchronizer(self.cd_collector, self.collector_reset.storage), - AsyncResetSynchronizer(self.cd_filter, self.filter_reset.storage) - ] - - self.submodules.helper_dcxo = Si549(helper_dxco_i2c) - self.submodules.main_dcxo = Si549(main_dcxo_i2c) - - # for diagnostics and PLL initialization - self.submodules.frequency_counter = FrequencyCounter() - - ddmtd_counter = Signal(N) - self.sync.helper += ddmtd_counter.eq(ddmtd_counter + 1) - self.submodules.ddmtd_ref = DDMTD(ddmtd_counter, ddmtd_inputs.rec_clk) - self.submodules.ddmtd_main = DDMTD(ddmtd_counter, ddmtd_inputs.main_xo) - - collector_cd = ClockDomainsRenamer("collector") - filter_cd = ClockDomainsRenamer("filter") - self.submodules.collector = collector_cd(Collector(N)) - self.submodules.filter_helper = filter_cd( - thls.make(filters.helper, data_width=48)) - self.submodules.filter_main = filter_cd( - thls.make(filters.main, data_width=48)) - - self.comb += [ - self.collector.tag_ref.eq(self.ddmtd_ref.h_tag), - self.collector.ref_stb.eq(self.ddmtd_ref.h_tag_update), - self.collector.tag_main.eq(self.ddmtd_main.h_tag), - self.collector.main_stb.eq(self.ddmtd_main.h_tag_update) - ] - - collector_stb_ps = PulseSynchronizer("helper", "sys") - self.submodules += collector_stb_ps - self.sync.helper += collector_stb_ps.i.eq(self.collector.out_stb) - collector_stb_sys = Signal() - self.sync += collector_stb_sys.eq(collector_stb_ps.o) - - main_diff_tag_sys = Signal((N+2, True)) - helper_diff_tag_sys = Signal((N+2, True)) - ref_tag_sys = Signal(N) - main_tag_sys = Signal(N) - self.specials += MultiReg(self.collector.out_main, main_diff_tag_sys) - self.specials += MultiReg(self.collector.out_helper, helper_diff_tag_sys) - self.specials += MultiReg(self.collector.tag_ref, ref_tag_sys) - self.specials += MultiReg(self.collector.tag_main, main_tag_sys) - - self.sync += [ - If(self.tag_arm.re & self.tag_arm.r, self.tag_arm.w.eq(1)), - If(collector_stb_sys, - self.tag_arm.w.eq(0), - If(self.tag_arm.w, - main_diff_tag_32.eq(main_diff_tag_sys), - helper_diff_tag_32.eq(helper_diff_tag_sys), - self.ref_tag.status.eq(ref_tag_sys), - self.main_tag.status.eq(main_tag_sys) - ) - ) - ] - - self.comb += [ - self.filter_helper.input.eq(self.collector.out_helper << 22), - self.filter_helper.input_stb.eq(self.collector.out_stb), - self.filter_main.input.eq(self.collector.out_main), - self.filter_main.input_stb.eq(self.collector.out_stb) - ] - - self.sync.helper += [ - self.helper_dcxo.adpll_stb.eq(self.filter_helper.output_stb), - self.helper_dcxo.adpll.eq(self.filter_helper.output + self.adpll_offset_helper.storage), - self.main_dcxo.adpll_stb.eq(self.filter_main.output_stb), - self.main_dcxo.adpll.eq(self.filter_main.output + self.adpll_offset_main.storage) - ] diff --git a/artiq/gateware/drtio/wrpll/ddmtd.py b/artiq/gateware/drtio/wrpll/ddmtd.py deleted file mode 100644 index ddeeac54e..000000000 --- a/artiq/gateware/drtio/wrpll/ddmtd.py +++ /dev/null @@ -1,221 +0,0 @@ -from migen import * -from migen.genlib.cdc import PulseSynchronizer, MultiReg -from migen.genlib.fsm import FSM -from misoc.interconnect.csr import * - - -class DDMTDSamplerExtFF(Module): - def __init__(self, ddmtd_inputs): - self.rec_clk = Signal() - self.main_xo = Signal() - - # # # - - # TODO: s/h timing at FPGA pads - if hasattr(ddmtd_inputs, "rec_clk"): - rec_clk_1 = ddmtd_inputs.rec_clk - else: - rec_clk_1 = Signal() - self.specials += Instance("IBUFDS", - i_I=ddmtd_inputs.rec_clk_p, i_IB=ddmtd_inputs.rec_clk_n, - o_O=rec_clk_1) - if hasattr(ddmtd_inputs, "main_xo"): - main_xo_1 = ddmtd_inputs.main_xo - else: - main_xo_1 = Signal() - self.specials += Instance("IBUFDS", - i_I=ddmtd_inputs.main_xo_p, i_IB=ddmtd_inputs.main_xo_n, - o_O=main_xo_1) - self.specials += [ - Instance("FD", i_C=ClockSignal("helper"), - i_D=rec_clk_1, o_Q=self.rec_clk, - attr={("IOB", "TRUE")}), - Instance("FD", i_C=ClockSignal("helper"), - i_D=main_xo_1, o_Q=self.main_xo, - attr={("IOB", "TRUE")}), - ] - - -class DDMTDSamplerGTP(Module): - def __init__(self, gtp, main_xo_pads): - self.rec_clk = Signal() - self.main_xo = Signal() - - # # # - - # Getting the main XO signal from IBUFDS_GTE2 is problematic because - # the transceiver PLL craps out if an improper clock signal is applied, - # so we are disabling the buffer until the clock is stable. - main_xo_se = Signal() - rec_clk_1 = Signal() - main_xo_1 = Signal() - self.specials += [ - Instance("IBUFDS", - i_I=main_xo_pads.p, i_IB=main_xo_pads.n, - o_O=main_xo_se), - Instance("FD", i_C=ClockSignal("helper"), - i_D=gtp.cd_rtio_rx0.clk, o_Q=rec_clk_1, - attr={("DONT_TOUCH", "TRUE")}), - Instance("FD", i_C=ClockSignal("helper"), - i_D=rec_clk_1, o_Q=self.rec_clk, - attr={("DONT_TOUCH", "TRUE")}), - Instance("FD", i_C=ClockSignal("helper"), - i_D=main_xo_se, o_Q=main_xo_1, - attr={("IOB", "TRUE")}), - Instance("FD", i_C=ClockSignal("helper"), - i_D=main_xo_1, o_Q=self.main_xo, - attr={("DONT_TOUCH", "TRUE")}), - ] - - -class DDMTDDeglitcherFirstEdge(Module): - def __init__(self, input_signal, blind_period=128): - self.detect = Signal() - self.tag_correction = 0 - - rising = Signal() - input_signal_r = Signal() - self.sync.helper += [ - input_signal_r.eq(input_signal), - rising.eq(input_signal & ~input_signal_r) - ] - - blind_counter = Signal(max=blind_period) - self.sync.helper += [ - If(blind_counter != 0, blind_counter.eq(blind_counter - 1)), - If(input_signal_r, blind_counter.eq(blind_period - 1)), - self.detect.eq(rising & (blind_counter == 0)) - ] - - -class DDMTD(Module): - def __init__(self, counter, input_signal): - - # in helper clock domain - self.h_tag = Signal(len(counter)) - self.h_tag_update = Signal() - - # # # - - deglitcher = DDMTDDeglitcherFirstEdge(input_signal) - self.submodules += deglitcher - - self.sync.helper += [ - self.h_tag_update.eq(0), - If(deglitcher.detect, - self.h_tag_update.eq(1), - self.h_tag.eq(counter + deglitcher.tag_correction) - ) - ] - - -class Collector(Module): - """Generates loop filter inputs from DDMTD outputs. - - The input to the main DCXO lock loop filter is the difference between the - reference and main tags after unwrapping (see below). - - The input to the helper DCXO lock loop filter is the difference between the - current reference tag and the previous reference tag after unwrapping. - - When the WR PLL is locked, the following ideally (no noise/jitter) obtain: - - f_main = f_ref - - f_helper = f_ref * 2^N/(2^N+1) - - f_beat = f_ref - f_helper = f_ref / (2^N + 1) (cycle time is: dt=1/f_beat) - - the reference and main DCXO tags are equal to each other at every cycle - (the main DCXO lock drives this difference to 0) - - the reference and main DCXO tags both have the same value at each cycle - (the tag difference for each DDMTD is given by - f_helper*dt = f_helper/f_beat = 2^N, which causes the N-bit DDMTD counter - to wrap around and come back to its previous value) - - Note that we currently lock the frequency of the helper DCXO to the - reference clock, not it's phase. As a result, while the tag differences are - controlled, their absolute values are arbitrary. We could consider moving - the helper lock to a phase lock at some point in the future... - - Since the DDMTD counter is only N bits, it is possible for tag values to - wrap around. This will happen frequently if the locked tags happens to be - near the edges of the counter, so that jitter can easily cause a phase wrap. - But, it can also easily happen during lock acquisition or other transients. - To avoid glitches in the output, we unwrap the tag differences. Currently - we do this in hardware, but we should consider extending the processor to - allow us to do it inside the filters. Since the processor uses wider - signals, this would significantly extend the overall glitch-free - range of the PLL and may aid lock acquisition. - """ - def __init__(self, N): - self.ref_stb = Signal() - self.main_stb = Signal() - self.tag_ref = Signal(N) - self.tag_main = Signal(N) - - self.out_stb = Signal() - self.out_main = Signal((N+2, True)) - self.out_helper = Signal((N+2, True)) - self.out_tag_ref = Signal(N) - self.out_tag_main = Signal(N) - - tag_ref_r = Signal(N) - tag_main_r = Signal(N) - main_tag_diff = Signal((N+2, True)) - helper_tag_diff = Signal((N+2, True)) - - # # # - - fsm = FSM(reset_state="IDLE") - self.submodules += fsm - - fsm.act("IDLE", - NextValue(self.out_stb, 0), - If(self.ref_stb & self.main_stb, - NextValue(tag_ref_r, self.tag_ref), - NextValue(tag_main_r, self.tag_main), - NextState("DIFF") - ).Elif(self.ref_stb, - NextValue(tag_ref_r, self.tag_ref), - NextState("WAITMAIN") - ).Elif(self.main_stb, - NextValue(tag_main_r, self.tag_main), - NextState("WAITREF") - ) - ) - fsm.act("WAITREF", - If(self.ref_stb, - NextValue(tag_ref_r, self.tag_ref), - NextState("DIFF") - ) - ) - fsm.act("WAITMAIN", - If(self.main_stb, - NextValue(tag_main_r, self.tag_main), - NextState("DIFF") - ) - ) - fsm.act("DIFF", - NextValue(main_tag_diff, tag_main_r - tag_ref_r), - NextValue(helper_tag_diff, tag_ref_r - self.out_tag_ref), - NextState("UNWRAP") - ) - fsm.act("UNWRAP", - If(main_tag_diff - self.out_main > 2**(N-1), - NextValue(main_tag_diff, main_tag_diff - 2**N) - ).Elif(self.out_main - main_tag_diff > 2**(N-1), - NextValue(main_tag_diff, main_tag_diff + 2**N) - ), - - If(helper_tag_diff - self.out_helper > 2**(N-1), - NextValue(helper_tag_diff, helper_tag_diff - 2**N) - ).Elif(self.out_helper - helper_tag_diff > 2**(N-1), - NextValue(helper_tag_diff, helper_tag_diff + 2**N) - ), - NextState("OUTPUT") - ) - fsm.act("OUTPUT", - NextValue(self.out_tag_ref, tag_ref_r), - NextValue(self.out_tag_main, tag_main_r), - NextValue(self.out_main, main_tag_diff), - NextValue(self.out_helper, helper_tag_diff), - NextValue(self.out_stb, 1), - NextState("IDLE") - ) diff --git a/artiq/gateware/drtio/wrpll/filters.py b/artiq/gateware/drtio/wrpll/filters.py deleted file mode 100644 index 470f17bf3..000000000 --- a/artiq/gateware/drtio/wrpll/filters.py +++ /dev/null @@ -1,61 +0,0 @@ -helper_xn1 = 0 -helper_xn2 = 0 -helper_yn0 = 0 -helper_yn1 = 0 -helper_yn2 = 0 -helper_out = 0 - -main_xn1 = 0 -main_xn2 = 0 -main_yn0 = 0 -main_yn1 = 0 -main_yn2 = 0 - - -def helper(tag_diff): - global helper_xn1, helper_xn2, helper_yn0, \ - helper_yn1, helper_yn2, helper_out - - helper_xn0 = 0 - tag_diff # *(2**22) - - helper_yr = 4294967296 - - helper_yn2 = helper_yn1 - helper_yn1 = helper_yn0 - - helper_yn0 = (284885690 * (helper_xn0 - + (217319150 * helper_xn1 >> 44) - - (17591968725108 * helper_xn2 >> 44) - ) >> 44 - ) + (35184372088832*helper_yn1 >> 44) - helper_yn2 - - helper_xn2 = helper_xn1 - helper_xn1 = helper_xn0 - - helper_out = 268435456*helper_yn0 >> 44 - helper_out = min(helper_out, helper_yr) - helper_out = max(helper_out, 0 - helper_yr) - - return helper_out - - -def main(main_xn0): - global main_xn1, main_xn2, main_yn0, main_yn1, main_yn2 - - main_yr = 4294967296 - - main_yn2 = main_yn1 - main_yn1 = main_yn0 - main_yn0 = ( - ((133450380908*(((35184372088832*main_xn0) >> 44) + - ((17592186044417*main_xn1) >> 44))) >> 44) + - ((29455872930889*main_yn1) >> 44) - - ((12673794781453*main_yn2) >> 44)) - - main_xn2 = main_xn1 - main_xn1 = main_xn0 - - main_yn0 = min(main_yn0, main_yr) - main_yn0 = max(main_yn0, 0 - main_yr) - - return main_yn0 diff --git a/artiq/gateware/drtio/wrpll/si549.py b/artiq/gateware/drtio/wrpll/si549.py deleted file mode 100644 index 46ce0c138..000000000 --- a/artiq/gateware/drtio/wrpll/si549.py +++ /dev/null @@ -1,340 +0,0 @@ -from migen import * -from migen.genlib.fsm import * -from migen.genlib.cdc import MultiReg, PulseSynchronizer, BlindTransfer - -from misoc.interconnect.csr import * - - -class I2CClockGen(Module): - def __init__(self, width): - self.load = Signal(width) - self.clk2x = Signal() - - cnt = Signal.like(self.load) - self.comb += [ - self.clk2x.eq(cnt == 0), - ] - self.sync += [ - If(self.clk2x, - cnt.eq(self.load), - ).Else( - cnt.eq(cnt - 1), - ) - ] - - -class I2CMasterMachine(Module): - def __init__(self, clock_width): - self.scl = Signal(reset=1) - self.sda_o = Signal(reset=1) - self.sda_i = Signal() - - self.submodules.cg = CEInserter()(I2CClockGen(clock_width)) - self.start = Signal() - self.stop = Signal() - self.write = Signal() - self.ack = Signal() - self.data = Signal(8) - self.ready = Signal() - - ### - - bits = Signal(4) - data = Signal(8) - - fsm = CEInserter()(FSM("IDLE")) - self.submodules += fsm - - fsm.act("IDLE", - self.ready.eq(1), - If(self.start, - NextState("START0"), - ).Elif(self.stop, - NextState("STOP0"), - ).Elif(self.write, - NextValue(bits, 8), - NextValue(data, self.data), - NextState("WRITE0") - ) - ) - - fsm.act("START0", - NextValue(self.scl, 1), - NextState("START1") - ) - fsm.act("START1", - NextValue(self.sda_o, 0), - NextState("IDLE") - ) - - fsm.act("STOP0", - NextValue(self.scl, 0), - NextState("STOP1") - ) - fsm.act("STOP1", - NextValue(self.sda_o, 0), - NextState("STOP2") - ) - fsm.act("STOP2", - NextValue(self.scl, 1), - NextState("STOP3") - ) - fsm.act("STOP3", - NextValue(self.sda_o, 1), - NextState("IDLE") - ) - - fsm.act("WRITE0", - NextValue(self.scl, 0), - NextState("WRITE1") - ) - fsm.act("WRITE1", - If(bits == 0, - NextValue(self.sda_o, 1), - NextState("READACK0"), - ).Else( - NextValue(self.sda_o, data[7]), - NextState("WRITE2"), - ) - ) - fsm.act("WRITE2", - NextValue(self.scl, 1), - NextValue(data[1:], data[:-1]), - NextValue(bits, bits - 1), - NextState("WRITE0"), - ) - fsm.act("READACK0", - NextValue(self.scl, 1), - NextState("READACK1"), - ) - fsm.act("READACK1", - NextValue(self.ack, ~self.sda_i), - NextState("IDLE") - ) - - run = Signal() - idle = Signal() - self.comb += [ - run.eq((self.start | self.stop | self.write) & self.ready), - idle.eq(~run & fsm.ongoing("IDLE")), - self.cg.ce.eq(~idle), - fsm.ce.eq(run | self.cg.clk2x), - ] - - -class ADPLLProgrammer(Module): - def __init__(self): - self.i2c_divider = Signal(16) - self.i2c_address = Signal(7) - - self.adpll = Signal(24) - self.stb = Signal() - self.busy = Signal() - self.nack = Signal() - - self.scl = Signal() - self.sda_i = Signal() - self.sda_o = Signal() - - self.scl.attr.add("no_retiming") - self.sda_o.attr.add("no_retiming") - - # # # - - master = I2CMasterMachine(16) - self.submodules += master - - self.comb += [ - master.cg.load.eq(self.i2c_divider), - self.scl.eq(master.scl), - master.sda_i.eq(self.sda_i), - self.sda_o.eq(master.sda_o) - ] - - fsm = FSM() - self.submodules += fsm - - adpll = Signal.like(self.adpll) - - fsm.act("IDLE", - If(self.stb, - NextValue(adpll, self.adpll), - NextState("START") - ) - ) - fsm.act("START", - master.start.eq(1), - If(master.ready, NextState("DEVADDRESS")) - ) - fsm.act("DEVADDRESS", - master.data.eq(self.i2c_address << 1), - master.write.eq(1), - If(master.ready, NextState("REGADRESS")) - ) - fsm.act("REGADRESS", - master.data.eq(231), - master.write.eq(1), - If(master.ready, - If(master.ack, - NextState("DATA0") - ).Else( - self.nack.eq(1), - NextState("STOP") - ) - ) - ) - fsm.act("DATA0", - master.data.eq(adpll[0:8]), - master.write.eq(1), - If(master.ready, - If(master.ack, - NextState("DATA1") - ).Else( - self.nack.eq(1), - NextState("STOP") - ) - ) - ) - fsm.act("DATA1", - master.data.eq(adpll[8:16]), - master.write.eq(1), - If(master.ready, - If(master.ack, - NextState("DATA2") - ).Else( - self.nack.eq(1), - NextState("STOP") - ) - ) - ) - fsm.act("DATA2", - master.data.eq(adpll[16:24]), - master.write.eq(1), - If(master.ready, - If(~master.ack, self.nack.eq(1)), - NextState("STOP") - ) - ) - fsm.act("STOP", - master.stop.eq(1), - If(master.ready, - If(~master.ack, self.nack.eq(1)), - NextState("IDLE") - ) - ) - - self.comb += self.busy.eq(~fsm.ongoing("IDLE")) - - -def simulate_programmer(): - from migen.sim.core import run_simulation - - dut = ADPLLProgrammer() - - def generator(): - yield dut.i2c_divider.eq(4) - yield dut.i2c_address.eq(0x55) - yield - yield dut.adpll.eq(0x123456) - yield dut.stb.eq(1) - yield - yield dut.stb.eq(0) - yield - while (yield dut.busy): - yield - for _ in range(20): - yield - - run_simulation(dut, generator(), vcd_name="tb.vcd") - - -class Si549(Module, AutoCSR): - def __init__(self, pads): - self.gpio_enable = CSRStorage(reset=1) - self.gpio_in = CSRStatus(2) - self.gpio_out = CSRStorage(2) - self.gpio_oe = CSRStorage(2) - - self.i2c_divider = CSRStorage(16, reset=75) - self.i2c_address = CSRStorage(7) - self.errors = CSR(2) - - # in helper clock domain - self.adpll = Signal(24) - self.adpll_stb = Signal() - - # # # - - programmer = ClockDomainsRenamer("helper")(ADPLLProgrammer()) - self.submodules += programmer - - self.i2c_divider.storage.attr.add("no_retiming") - self.i2c_address.storage.attr.add("no_retiming") - self.specials += [ - MultiReg(self.i2c_divider.storage, programmer.i2c_divider, "helper"), - MultiReg(self.i2c_address.storage, programmer.i2c_address, "helper") - ] - self.comb += [ - programmer.adpll.eq(self.adpll), - programmer.stb.eq(self.adpll_stb) - ] - - self.gpio_enable.storage.attr.add("no_retiming") - self.gpio_out.storage.attr.add("no_retiming") - self.gpio_oe.storage.attr.add("no_retiming") - - # SCL GPIO and mux - ts_scl = TSTriple(1) - self.specials += ts_scl.get_tristate(pads.scl) - - status = Signal() - self.comb += self.gpio_in.status[0].eq(status) - - self.specials += MultiReg(ts_scl.i, status) - self.comb += [ - If(self.gpio_enable.storage, - ts_scl.o.eq(self.gpio_out.storage[0]), - ts_scl.oe.eq(self.gpio_oe.storage[0]) - ).Else( - ts_scl.o.eq(0), - ts_scl.oe.eq(~programmer.scl) - ) - ] - - # SDA GPIO and mux - ts_sda = TSTriple(1) - self.specials += ts_sda.get_tristate(pads.sda) - - status = Signal() - self.comb += self.gpio_in.status[1].eq(status) - - self.specials += MultiReg(ts_sda.i, status) - self.comb += [ - If(self.gpio_enable.storage, - ts_sda.o.eq(self.gpio_out.storage[1]), - ts_sda.oe.eq(self.gpio_oe.storage[1]) - ).Else( - ts_sda.o.eq(0), - ts_sda.oe.eq(~programmer.sda_o) - ) - ] - self.specials += MultiReg(ts_sda.i, programmer.sda_i, "helper") - - # Error reporting - collision_cdc = BlindTransfer("helper", "sys") - self.submodules += collision_cdc - self.comb += collision_cdc.i.eq(programmer.stb & programmer.busy) - - nack_cdc = PulseSynchronizer("helper", "sys") - self.submodules += nack_cdc - self.comb += nack_cdc.i.eq(programmer.nack) - - for n, trig in enumerate([collision_cdc.o, nack_cdc.o]): - self.sync += [ - If(self.errors.re & self.errors.r[n], self.errors.w[n].eq(0)), - If(trig, self.errors.w[n].eq(1)) - ] - - -if __name__ == "__main__": - simulate_programmer() diff --git a/artiq/gateware/drtio/wrpll/thls.py b/artiq/gateware/drtio/wrpll/thls.py deleted file mode 100644 index b11459692..000000000 --- a/artiq/gateware/drtio/wrpll/thls.py +++ /dev/null @@ -1,618 +0,0 @@ -import inspect -import ast -from copy import copy -import operator -from functools import reduce -from collections import OrderedDict - -from migen import * -from migen.genlib.fsm import * - - -class Isn: - def __init__(self, immediate=None, inputs=None, outputs=None): - if inputs is None: - inputs = [] - if outputs is None: - outputs = [] - self.immediate = immediate - self.inputs = inputs - self.outputs = outputs - - def __repr__(self): - r = "<" - r += self.__class__.__name__ - if self.immediate is not None: - r += " (" + str(self.immediate) + ")" - for inp in self.inputs: - r += " r" + str(inp) - if self.outputs: - r += " ->" - for outp in self.outputs: - r += " r" + str(outp) - r += ">" - return r - - -class NopIsn(Isn): - opcode = 0 - -class AddIsn(Isn): - opcode = 1 - -class SubIsn(Isn): - opcode = 2 - -class MulShiftIsn(Isn): - opcode = 3 - -# opcode = 4: MulShift with alternate shift - -class MinIsn(Isn): - opcode = 5 - -class MaxIsn(Isn): - opcode = 6 - -class CopyIsn(Isn): - opcode = 7 - -class InputIsn(Isn): - opcode = 8 - -class OutputIsn(Isn): - opcode = 9 - -class EndIsn(Isn): - opcode = 10 - - -class ASTCompiler: - def __init__(self): - self.program = [] - self.data = [] - self.next_ssa_reg = -1 - self.constants = dict() - self.names = dict() - self.globals = OrderedDict() - - def get_ssa_reg(self): - r = self.next_ssa_reg - self.next_ssa_reg -= 1 - return r - - def add_global(self, name): - if name not in self.globals: - r = len(self.data) - self.data.append(0) - self.names[name] = r - self.globals[name] = r - - def input(self, name): - target = self.get_ssa_reg() - self.program.append(InputIsn(outputs=[target])) - self.names[name] = target - - def emit(self, node): - if isinstance(node, ast.BinOp): - if isinstance(node.op, ast.RShift): - if not isinstance(node.left, ast.BinOp) or not isinstance(node.left.op, ast.Mult): - raise NotImplementedError - if not isinstance(node.right, ast.Num): - raise NotImplementedError - left = self.emit(node.left.left) - right = self.emit(node.left.right) - cons = lambda **kwargs: MulShiftIsn(immediate=node.right.n, **kwargs) - else: - left = self.emit(node.left) - right = self.emit(node.right) - if isinstance(node.op, ast.Add): - cons = AddIsn - elif isinstance(node.op, ast.Sub): - cons = SubIsn - elif isinstance(node.op, ast.Mult): - cons = lambda **kwargs: MulShiftIsn(immediate=0, **kwargs) - else: - raise NotImplementedError - output = self.get_ssa_reg() - self.program.append(cons(inputs=[left, right], outputs=[output])) - return output - elif isinstance(node, ast.Call): - if not isinstance(node.func, ast.Name): - raise NotImplementedError - funcname = node.func.id - if node.keywords: - raise NotImplementedError - inputs = [self.emit(x) for x in node.args] - if funcname == "min": - cons = MinIsn - elif funcname == "max": - cons = MaxIsn - else: - raise NotImplementedError - output = self.get_ssa_reg() - self.program.append(cons(inputs=inputs, outputs=[output])) - return output - elif isinstance(node, (ast.Num, ast.UnaryOp)): - if isinstance(node, ast.UnaryOp): - if not isinstance(node.operand, ast.Num): - raise NotImplementedError - if isinstance(node.op, ast.UAdd): - transform = lambda x: x - elif isinstance(node.op, ast.USub): - transform = operator.neg - elif isinstance(node.op, ast.Invert): - transform = operator.invert - else: - raise NotImplementedError - node = node.operand - else: - transform = lambda x: x - n = transform(node.n) - if n in self.constants: - return self.constants[n] - else: - r = len(self.data) - self.data.append(n) - self.constants[n] = r - return r - elif isinstance(node, ast.Name): - return self.names[node.id] - elif isinstance(node, ast.Assign): - output = self.emit(node.value) - for target in node.targets: - assert isinstance(target, ast.Name) - self.names[target.id] = output - elif isinstance(node, ast.Return): - value = self.emit(node.value) - self.program.append(OutputIsn(inputs=[value])) - elif isinstance(node, ast.Global): - pass - else: - raise NotImplementedError - - -class Processor: - def __init__(self, data_width=32, multiplier_stages=2): - self.data_width = data_width - self.multiplier_stages = multiplier_stages - self.multiplier_shifts = [] - self.program_rom_size = None - self.data_ram_size = None - self.opcode_bits = 4 - self.reg_bits = None - - def get_instruction_latency(self, isn): - return { - AddIsn: 2, - SubIsn: 2, - MulShiftIsn: 1 + self.multiplier_stages, - MinIsn: 2, - MaxIsn: 2, - CopyIsn: 1, - InputIsn: 1 - }[isn.__class__] - - def encode_instruction(self, isn, exit): - opcode = isn.opcode - if isn.immediate is not None and not isinstance(isn, MulShiftIsn): - r0 = isn.immediate - if len(isn.inputs) >= 1: - r1 = isn.inputs[0] - else: - r1 = 0 - else: - if len(isn.inputs) >= 1: - r0 = isn.inputs[0] - else: - r0 = 0 - if len(isn.inputs) >= 2: - r1 = isn.inputs[1] - else: - r1 = 0 - r = 0 - for value, bits in ((exit, self.reg_bits), (r1, self.reg_bits), (r0, self.reg_bits), (opcode, self.opcode_bits)): - r <<= bits - r |= value - return r - - def instruction_bits(self): - return 3*self.reg_bits + self.opcode_bits - - def implement(self, program, data): - return ProcessorImpl(self, program, data) - - -class Scheduler: - def __init__(self, processor, reserved_data, program): - self.processor = processor - self.reserved_data = reserved_data - self.used_registers = set(range(self.reserved_data)) - self.exits = dict() - self.program = program - self.remaining = copy(program) - self.output = [] - - def allocate_register(self): - r = min(set(range(max(self.used_registers) + 2)) - self.used_registers) - self.used_registers.add(r) - return r - - def free_register(self, r): - assert r >= self.reserved_data - self.used_registers.discard(r) - - def find_inputs(self, cycle, isn): - mapped_inputs = [] - for inp in isn.inputs: - if inp >= 0: - mapped_inputs.append(inp) - else: - found = False - for i in range(cycle): - if i in self.exits: - r, rm = self.exits[i] - if r == inp: - mapped_inputs.append(rm) - found = True - break - if not found: - return None - return mapped_inputs - - def schedule_one(self, isn): - cycle = len(self.output) - mapped_inputs = self.find_inputs(cycle, isn) - if mapped_inputs is None: - return False - - if isn.outputs: - # check that exit slot is free - latency = self.processor.get_instruction_latency(isn) - exit = cycle + latency - if exit in self.exits: - return False - - # avoid RAW hazard with global writeback - for output in isn.outputs: - if output >= 0: - for risn in self.remaining: - for inp in risn.inputs: - if inp == output: - return False - - # Instruction can be scheduled - - self.remaining.remove(isn) - - for inp, minp in zip(isn.inputs, mapped_inputs): - can_free = inp < 0 and all(inp != rinp for risn in self.remaining for rinp in risn.inputs) - if can_free: - self.free_register(minp) - - if isn.outputs: - assert len(isn.outputs) == 1 - if isn.outputs[0] < 0: - output = self.allocate_register() - else: - output = isn.outputs[0] - self.exits[exit] = (isn.outputs[0], output) - self.output.append(isn.__class__(immediate=isn.immediate, inputs=mapped_inputs)) - - return True - - def schedule(self): - while self.remaining: - success = False - for isn in self.remaining: - if self.schedule_one(isn): - success = True - break - if not success: - self.output.append(NopIsn()) - self.output += [NopIsn()]*(max(self.exits.keys()) - len(self.output) + 1) - return self.output - - -class CompiledProgram: - def __init__(self, processor, program, exits, data, glbs): - self.processor = processor - self.program = program - self.exits = exits - self.data = data - self.globals = glbs - - def pretty_print(self): - for cycle, isn in enumerate(self.program): - l = "{:4d} {:15}".format(cycle, str(isn)) - if cycle in self.exits: - l += " -> r{}".format(self.exits[cycle]) - print(l) - - def dimension_processor(self): - self.processor.program_rom_size = len(self.program) - self.processor.data_ram_size = len(self.data) - self.processor.reg_bits = (self.processor.data_ram_size - 1).bit_length() - for isn in self.program: - if isinstance(isn, MulShiftIsn) and isn.immediate not in self.processor.multiplier_shifts: - self.processor.multiplier_shifts.append(isn.immediate) - - def encode(self): - r = [] - for i, isn in enumerate(self.program): - exit = self.exits.get(i, 0) - r.append(self.processor.encode_instruction(isn, exit)) - return r - - -def compile(processor, function): - node = ast.parse(inspect.getsource(function)) - assert isinstance(node, ast.Module) - assert len(node.body) == 1 - node = node.body[0] - assert isinstance(node, ast.FunctionDef) - assert len(node.args.args) == 1 - arg = node.args.args[0].arg - body = node.body - - astcompiler = ASTCompiler() - for node in body: - if isinstance(node, ast.Global): - for name in node.names: - astcompiler.add_global(name) - arg_r = astcompiler.input(arg) - for node in body: - astcompiler.emit(node) - if isinstance(node, ast.Return): - break - for glbl, location in astcompiler.globals.items(): - new_location = astcompiler.names[glbl] - if new_location != location: - astcompiler.program.append(CopyIsn(inputs=[new_location], outputs=[location])) - - scheduler = Scheduler(processor, len(astcompiler.data), astcompiler.program) - scheduler.schedule() - - program = copy(scheduler.output) - program.append(EndIsn()) - - max_reg = max(max(max(isn.inputs + [0]) for isn in program), max(v[1] for k, v in scheduler.exits.items())) - - return CompiledProgram( - processor=processor, - program=program, - exits={k: v[1] for k, v in scheduler.exits.items()}, - data=astcompiler.data + [0]*(max_reg - len(astcompiler.data) + 1), - glbs=astcompiler.globals) - - -class BaseUnit(Module): - def __init__(self, data_width): - self.stb_i = Signal() - self.i0 = Signal((data_width, True)) - self.i1 = Signal((data_width, True)) - self.stb_o = Signal() - self.o = Signal((data_width, True)) - - -class NopUnit(BaseUnit): - pass - - -class OpUnit(BaseUnit): - def __init__(self, op, data_width, stages, op_data_width=None): - BaseUnit.__init__(self, data_width) - # work around Migen's mishandling of Verilog's cretinous operator width rules - if op_data_width is None: - op_data_width = data_width - - if stages > 1: - # Vivado backward retiming for DSP does not work correctly if DSP inputs - # are not registered. - i0 = Signal.like(self.i0) - i1 = Signal.like(self.i1) - stb_i = Signal() - self.sync += [ - i0.eq(self.i0), - i1.eq(self.i1), - stb_i.eq(self.stb_i) - ] - output_stages = stages - 1 - else: - i0, i1, stb_i = self.i0, self.i1, self.stb_i - output_stages = stages - - o = Signal((op_data_width, True)) - self.comb += o.eq(op(i0, i1)) - stb_o = stb_i - for i in range(output_stages): - n_o = Signal((data_width, True)) - if stages > 1: - n_o.attr.add(("retiming_backward", 1)) - n_stb_o = Signal() - self.sync += [ - n_o.eq(o), - n_stb_o.eq(stb_o) - ] - o = n_o - stb_o = n_stb_o - self.comb += [ - self.o.eq(o), - self.stb_o.eq(stb_o) - ] - - -class SelectUnit(BaseUnit): - def __init__(self, op, data_width): - BaseUnit.__init__(self, data_width) - - self.sync += [ - self.stb_o.eq(self.stb_i), - If(op(self.i0, self.i1), - self.o.eq(self.i0) - ).Else( - self.o.eq(self.i1) - ) - ] - - -class CopyUnit(BaseUnit): - def __init__(self, data_width): - BaseUnit.__init__(self, data_width) - - self.comb += [ - self.stb_o.eq(self.stb_i), - self.o.eq(self.i0) - ] - - -class InputUnit(BaseUnit): - def __init__(self, data_width, input_stb, input): - BaseUnit.__init__(self, data_width) - self.buffer = Signal(data_width) - - self.comb += [ - self.stb_o.eq(self.stb_i), - self.o.eq(self.buffer) - ] - - -class OutputUnit(BaseUnit): - def __init__(self, data_width, output_stb, output): - BaseUnit.__init__(self, data_width) - - self.sync += [ - output_stb.eq(self.stb_i), - output.eq(self.i0) - ] - - -class ProcessorImpl(Module): - def __init__(self, pd, program, data): - self.input_stb = Signal() - self.input = Signal((pd.data_width, True)) - - self.output_stb = Signal() - self.output = Signal((pd.data_width, True)) - - self.busy = Signal() - - # # # - - program_mem = Memory(pd.instruction_bits(), pd.program_rom_size, init=program) - data_mem0 = Memory(pd.data_width, pd.data_ram_size, init=data) - data_mem1 = Memory(pd.data_width, pd.data_ram_size, init=data) - self.specials += program_mem, data_mem0, data_mem1 - - pc = Signal(pd.instruction_bits()) - pc_next = Signal.like(pc) - pc_en = Signal() - self.sync += pc.eq(pc_next) - self.comb += [ - If(pc_en, - pc_next.eq(pc + 1) - ).Else( - pc_next.eq(0) - ) - ] - program_mem_port = program_mem.get_port() - self.specials += program_mem_port - self.comb += program_mem_port.adr.eq(pc_next) - - s = 0 - opcode = Signal(pd.opcode_bits) - self.comb += opcode.eq(program_mem_port.dat_r[s:s+pd.opcode_bits]) - s += pd.opcode_bits - r0 = Signal(pd.reg_bits) - self.comb += r0.eq(program_mem_port.dat_r[s:s+pd.reg_bits]) - s += pd.reg_bits - r1 = Signal(pd.reg_bits) - self.comb += r1.eq(program_mem_port.dat_r[s:s+pd.reg_bits]) - s += pd.reg_bits - exit = Signal(pd.reg_bits) - self.comb += exit.eq(program_mem_port.dat_r[s:s+pd.reg_bits]) - - data_read_port0 = data_mem0.get_port() - data_read_port1 = data_mem1.get_port() - self.specials += data_read_port0, data_read_port1 - self.comb += [ - data_read_port0.adr.eq(r0), - data_read_port1.adr.eq(r1) - ] - - data_write_port = data_mem0.get_port(write_capable=True) - data_write_port_dup = data_mem1.get_port(write_capable=True) - self.specials += data_write_port, data_write_port_dup - self.comb += [ - data_write_port_dup.we.eq(data_write_port.we), - data_write_port_dup.adr.eq(data_write_port.adr), - data_write_port_dup.dat_w.eq(data_write_port.dat_w), - data_write_port.adr.eq(exit) - ] - - nop = NopUnit(pd.data_width) - adder = OpUnit(operator.add, pd.data_width, 1) - subtractor = OpUnit(operator.sub, pd.data_width, 1) - if pd.multiplier_shifts: - if len(pd.multiplier_shifts) != 1: - raise NotImplementedError - multiplier = OpUnit(lambda a, b: a * b >> pd.multiplier_shifts[0], - pd.data_width, pd.multiplier_stages, op_data_width=2*pd.data_width) - else: - multiplier = NopUnit(pd.data_width) - minu = SelectUnit(operator.lt, pd.data_width) - maxu = SelectUnit(operator.gt, pd.data_width) - copier = CopyUnit(pd.data_width) - inu = InputUnit(pd.data_width, self.input_stb, self.input) - outu = OutputUnit(pd.data_width, self.output_stb, self.output) - units = [nop, adder, subtractor, multiplier, minu, maxu, copier, inu, outu] - self.submodules += units - - for unit in units: - self.sync += unit.stb_i.eq(0) - self.comb += [ - unit.i0.eq(data_read_port0.dat_r), - unit.i1.eq(data_read_port1.dat_r), - If(unit.stb_o, - data_write_port.we.eq(1), - data_write_port.dat_w.eq(unit.o) - ) - ] - - decode_table = [ - (NopIsn.opcode, nop), - (AddIsn.opcode, adder), - (SubIsn.opcode, subtractor), - (MulShiftIsn.opcode, multiplier), - (MulShiftIsn.opcode + 1, multiplier), - (MinIsn.opcode, minu), - (MaxIsn.opcode, maxu), - (CopyIsn.opcode, copier), - (InputIsn.opcode, inu), - (OutputIsn.opcode, outu) - ] - for allocated_opcode, unit in decode_table: - self.sync += If(pc_en & (opcode == allocated_opcode), unit.stb_i.eq(1)) - - fsm = FSM() - self.submodules += fsm - fsm.act("IDLE", - pc_en.eq(0), - NextValue(inu.buffer, self.input), - If(self.input_stb, NextState("PROCESSING")) - ) - fsm.act("PROCESSING", - self.busy.eq(1), - pc_en.eq(1), - If(opcode == EndIsn.opcode, - pc_en.eq(0), - NextState("IDLE") - ) - ) - - -def make(function, **kwargs): - proc = Processor(**kwargs) - cp = compile(proc, function) - cp.dimension_processor() - return proc.implement(cp.encode(), cp.data) diff --git a/artiq/gateware/rtio/core.py b/artiq/gateware/rtio/core.py index 0b26a1126..c4dae4dc1 100644 --- a/artiq/gateware/rtio/core.py +++ b/artiq/gateware/rtio/core.py @@ -24,7 +24,7 @@ class Core(Module, AutoCSR): self.sequence_error_channel = CSRStatus(16) # Clocking/Reset - # Create rsys, rio and rio_phy domains based on sys and rtio + # Create rio and rio_phy domains based on sys # with reset controlled by CSR. # # The `rio` CD contains logic that is reset with `core.reset()`. @@ -40,20 +40,15 @@ class Core(Module, AutoCSR): cmd_reset.eq(self.reset.re), cmd_reset_phy.eq(self.reset_phy.re) ] - cmd_reset.attr.add("no_retiming") - cmd_reset_phy.attr.add("no_retiming") - self.clock_domains.cd_rsys = ClockDomain() self.clock_domains.cd_rio = ClockDomain() self.clock_domains.cd_rio_phy = ClockDomain() self.comb += [ - self.cd_rsys.clk.eq(ClockSignal()), - self.cd_rsys.rst.eq(cmd_reset), - self.cd_rio.clk.eq(ClockSignal("rtio")), - self.cd_rio_phy.clk.eq(ClockSignal("rtio")) + self.cd_rio.clk.eq(ClockSignal()), + self.cd_rio.rst.eq(cmd_reset), + self.cd_rio_phy.clk.eq(ClockSignal()), + self.cd_rio_phy.rst.eq(cmd_reset_phy) ] - self.specials += AsyncResetSynchronizer(self.cd_rio, cmd_reset) - self.specials += AsyncResetSynchronizer(self.cd_rio_phy, cmd_reset_phy) # TSC chan_fine_ts_width = max(max(rtlink.get_fine_ts_width(channel.interface.o) @@ -65,22 +60,22 @@ class Core(Module, AutoCSR): # Outputs/Inputs quash_channels = [n for n, c in enumerate(channels) if isinstance(c, LogChannel)] - outputs = SED(channels, tsc.glbl_fine_ts_width, "async", + outputs = SED(channels, tsc.glbl_fine_ts_width, quash_channels=quash_channels, lane_count=lane_count, fifo_depth=fifo_depth, interface=self.cri) self.submodules += outputs self.comb += outputs.coarse_timestamp.eq(tsc.coarse_ts) - self.sync += outputs.minimum_coarse_timestamp.eq(tsc.coarse_ts_sys + 16) + self.sync += outputs.minimum_coarse_timestamp.eq(tsc.coarse_ts + 16) - inputs = InputCollector(tsc, channels, "async", + inputs = InputCollector(tsc, channels, quash_channels=quash_channels, interface=self.cri) self.submodules += inputs # Asychronous output errors - o_collision_sync = BlindTransfer("rio", "rsys", data_width=16) - o_busy_sync = BlindTransfer("rio", "rsys", data_width=16) + o_collision_sync = BlindTransfer("rio", "sys", data_width=16) + o_busy_sync = BlindTransfer("rio", "sys", data_width=16) self.submodules += o_collision_sync, o_busy_sync o_collision = Signal() o_busy = Signal() diff --git a/artiq/gateware/rtio/cri.py b/artiq/gateware/rtio/cri.py index c735b9e5f..41bd20eeb 100644 --- a/artiq/gateware/rtio/cri.py +++ b/artiq/gateware/rtio/cri.py @@ -127,7 +127,7 @@ class KernelInitiator(Module, AutoCSR): class CRIDecoder(Module): - def __init__(self, slaves=2, master=None, mode="async", enable_routing=False): + def __init__(self, slaves=2, master=None, enable_routing=False): if isinstance(slaves, int): slaves = [Interface() for _ in range(slaves)] if master is None: @@ -155,12 +155,7 @@ class CRIDecoder(Module): if enable_routing: self.specials.routing_table = Memory(slave_bits, 256) - if mode == "async": - rtp_decoder = self.routing_table.get_port() - elif mode == "sync": - rtp_decoder = self.routing_table.get_port(clock_domain="rtio") - else: - raise ValueError + rtp_decoder = self.routing_table.get_port() self.specials += rtp_decoder self.comb += [ rtp_decoder.adr.eq(self.master.chan_sel[16:]), @@ -187,7 +182,7 @@ class CRIDecoder(Module): class CRISwitch(Module, AutoCSR): - def __init__(self, masters=2, slave=None, mode="async"): + def __init__(self, masters=2, slave=None): if isinstance(masters, int): masters = [Interface() for _ in range(masters)] if slave is None: @@ -199,15 +194,6 @@ class CRISwitch(Module, AutoCSR): # # # - if mode == "async": - selected = self.selected.storage - elif mode == "sync": - self.selected.storage.attr.add("no_retiming") - selected = Signal.like(self.selected.storage) - self.specials += MultiReg(self.selected.storage, selected, "rtio") - else: - raise ValueError - if len(masters) == 1: self.comb += masters[0].connect(slave) else: @@ -215,7 +201,7 @@ class CRISwitch(Module, AutoCSR): for name, size, direction in layout: if direction == DIR_M_TO_S: choices = Array(getattr(m, name) for m in masters) - self.comb += getattr(slave, name).eq(choices[selected]) + self.comb += getattr(slave, name).eq(choices[self.selected.storage]) # connect slave->master signals for name, size, direction in layout: @@ -227,10 +213,10 @@ class CRISwitch(Module, AutoCSR): class CRIInterconnectShared(Module): - def __init__(self, masters=2, slaves=2, mode="async", enable_routing=False): + def __init__(self, masters=2, slaves=2, enable_routing=False): shared = Interface() - self.submodules.switch = CRISwitch(masters, shared, mode) - self.submodules.decoder = CRIDecoder(slaves, shared, mode, enable_routing) + self.submodules.switch = CRISwitch(masters, shared) + self.submodules.decoder = CRIDecoder(slaves, shared, enable_routing) def get_csrs(self): return self.switch.get_csrs() diff --git a/artiq/gateware/rtio/input_collector.py b/artiq/gateware/rtio/input_collector.py index a68f5a7ff..75c018c06 100644 --- a/artiq/gateware/rtio/input_collector.py +++ b/artiq/gateware/rtio/input_collector.py @@ -1,6 +1,6 @@ from migen import * from migen.genlib.record import Record -from migen.genlib.fifo import * +from migen.genlib.fifo import SyncFIFOBuffered from migen.genlib.cdc import BlindTransfer from artiq.gateware.rtio import cri @@ -24,24 +24,13 @@ def get_channel_layout(coarse_ts_width, interface): class InputCollector(Module): - def __init__(self, tsc, channels, mode, quash_channels=[], interface=None): + def __init__(self, tsc, channels, quash_channels=[], interface=None): if interface is None: interface = cri.Interface() self.cri = interface # # # - if mode == "sync": - fifo_factory = SyncFIFOBuffered - sync_io = self.sync - sync_cri = self.sync - elif mode == "async": - fifo_factory = lambda *args: ClockDomainsRenamer({"write": "rio", "read": "rsys"})(AsyncFIFO(*args)) - sync_io = self.sync.rio - sync_cri = self.sync.rsys - else: - raise ValueError - i_statuses, i_datas, i_timestamps = [], [], [] i_ack = Signal() sel = self.cri.chan_sel[:16] @@ -55,7 +44,7 @@ class InputCollector(Module): # FIFO layout = get_channel_layout(len(tsc.coarse_ts), iif) - fifo = fifo_factory(layout_len(layout), channel.ififo_depth) + fifo = SyncFIFOBuffered(layout_len(layout), channel.ififo_depth) self.submodules += fifo fifo_in = Record(layout) fifo_out = Record(layout) @@ -67,7 +56,7 @@ class InputCollector(Module): # FIFO write if iif.delay: counter_rtio = Signal.like(tsc.coarse_ts, reset_less=True) - sync_io += counter_rtio.eq(tsc.coarse_ts - (iif.delay + 1)) + self.sync += counter_rtio.eq(tsc.coarse_ts - (iif.delay + 1)) else: counter_rtio = tsc.coarse_ts if hasattr(fifo_in, "data"): @@ -80,17 +69,8 @@ class InputCollector(Module): self.comb += fifo_in.timestamp.eq(full_ts) self.comb += fifo.we.eq(iif.stb) - overflow_io = Signal() - self.comb += overflow_io.eq(fifo.we & ~fifo.writable) - if mode == "sync": - overflow_trigger = overflow_io - elif mode == "async": - overflow_transfer = BlindTransfer("rio", "rsys") - self.submodules += overflow_transfer - self.comb += overflow_transfer.i.eq(overflow_io) - overflow_trigger = overflow_transfer.o - else: - raise ValueError + overflow_trigger = Signal() + self.comb += overflow_trigger.eq(fifo.we & ~fifo.writable) # FIFO read, CRI connection if hasattr(fifo_out, "data"): @@ -107,7 +87,7 @@ class InputCollector(Module): self.comb += selected.eq(sel == n) overflow = Signal() - sync_cri += [ + self.sync += [ If(selected & i_ack, overflow.eq(0)), If(overflow_trigger, @@ -122,7 +102,7 @@ class InputCollector(Module): input_pending = Signal() self.cri.i_data.reset_less = True self.cri.i_timestamp.reset_less = True - sync_cri += [ + self.sync += [ i_ack.eq(0), If(i_ack, self.cri.i_status.eq(Cat(~i_status_raw[0], i_status_raw[1], 0)), diff --git a/artiq/gateware/rtio/phy/fastino.py b/artiq/gateware/rtio/phy/fastino.py index 474bab0f3..9fcc71390 100644 --- a/artiq/gateware/rtio/phy/fastino.py +++ b/artiq/gateware/rtio/phy/fastino.py @@ -120,7 +120,7 @@ class Fastino(Module): ), ] - self.sync.rtio += [ + self.sync += [ self.rtlink.i.stb.eq(self.rtlink.o.stb & self.rtlink.o.address[-1]), self.rtlink.i.data.eq( diff --git a/artiq/gateware/rtio/phy/grabber.py b/artiq/gateware/rtio/phy/grabber.py index 520450c76..f0783fa42 100644 --- a/artiq/gateware/rtio/phy/grabber.py +++ b/artiq/gateware/rtio/phy/grabber.py @@ -21,14 +21,12 @@ class Synchronizer(Module): # # # - for count in counts_in: - count.attr.add("no_retiming") - self.specials += [MultiReg(i, o, "rtio") for i, o in zip(counts_in, self.counts)] + self.comb += [o.eq(i) for i, o in zip(counts_in, self.counts)] - ps = PulseSynchronizer("cl", "rtio") + ps = PulseSynchronizer("cl", "sys") self.submodules += ps self.comb += ps.i.eq(roi_engines[0].out.update) - self.sync.rtio += self.update.eq(ps.o) + self.sync += self.update.eq(ps.o) class Serializer(Module): @@ -85,7 +83,7 @@ class Grabber(Module): roi_engine.cfg.x1, roi_engine.cfg.y1]): roi_boundary = Signal.like(target) roi_boundary.attr.add("no_retiming") - self.sync.rtio += If(self.config.o.stb & (self.config.o.address == 4*n+offset), + self.sync += If(self.config.o.stb & (self.config.o.address == 4*n+offset), roi_boundary.eq(self.config.o.data)) self.specials += MultiReg(roi_boundary, target, "cl") diff --git a/artiq/gateware/rtio/phy/phaser.py b/artiq/gateware/rtio/phy/phaser.py index 557a65d74..d4771d9ed 100644 --- a/artiq/gateware/rtio/phy/phaser.py +++ b/artiq/gateware/rtio/phy/phaser.py @@ -10,7 +10,7 @@ class Phy(Module): self.rtlink = rtlink.Interface( rtlink.OInterface(data_width=32, address_width=4, enable_replace=True)) - self.sync.rtio += [ + self.sync += [ If(self.rtlink.o.stb, Array(regs)[self.rtlink.o.address].eq(self.rtlink.o.data) ) @@ -70,7 +70,7 @@ class Base(Module): self.comb += self.serializer.payload.eq(Cat(header.raw_bits(), body)) re_dly = Signal(3) # stage, send, respond - self.sync.rtio += [ + self.sync += [ header.type.eq(1), # body type is baseband data If(self.serializer.stb, self.ch0.dds.stb.eq(1), # synchronize @@ -105,7 +105,7 @@ class MiqroChannel(Module): self.pulse.eq(pulse), self.rtlink.o.busy.eq(stb & ~self.ack), ] - self.sync.rtio += [ + self.sync += [ If(~stb, dt.eq(dt + 2), ), @@ -162,7 +162,7 @@ class Miqro(Module): ] re_dly = Signal(3) # stage, send, respond - self.sync.rtio += [ + self.sync += [ header.type.eq(3), # body type is miqro pulse data If(self.serializer.stb, header.we.eq(0), diff --git a/artiq/gateware/rtio/phy/ttl_serdes_7series.py b/artiq/gateware/rtio/phy/ttl_serdes_7series.py index 841032c63..1f467d565 100644 --- a/artiq/gateware/rtio/phy/ttl_serdes_7series.py +++ b/artiq/gateware/rtio/phy/ttl_serdes_7series.py @@ -19,7 +19,7 @@ class _OSERDESE2_8X(Module): p_INIT_OQ=0b11111111 if invert else 0b00000000, o_OQ=self.ser_out, o_TQ=self.t_out, i_RST=ResetSignal("rio_phy"), - i_CLK=ClockSignal("rtiox4"), + i_CLK=ClockSignal("sys4x"), i_CLKDIV=ClockSignal("rio_phy"), i_D1=o[0] ^ invert, i_D2=o[1] ^ invert, i_D3=o[2] ^ invert, i_D4=o[3] ^ invert, i_D5=o[4] ^ invert, i_D6=o[5] ^ invert, i_D7=o[6] ^ invert, i_D8=o[7] ^ invert, @@ -43,8 +43,8 @@ class _ISERDESE2_8X(Module): o_Q1=i[7], o_Q2=i[6], o_Q3=i[5], o_Q4=i[4], o_Q5=i[3], o_Q6=i[2], o_Q7=i[1], o_Q8=i[0], i_D=self.ser_in, - i_CLK=ClockSignal("rtiox4"), - i_CLKB=~ClockSignal("rtiox4"), + i_CLK=ClockSignal("sys4x"), + i_CLKB=~ClockSignal("sys4x"), i_CE1=1, i_RST=ResetSignal("rio_phy"), i_CLKDIV=ClockSignal("rio_phy")) diff --git a/artiq/gateware/rtio/phy/ttl_serdes_ultrascale.py b/artiq/gateware/rtio/phy/ttl_serdes_ultrascale.py index 0dbe613ac..3cc0f02ef 100644 --- a/artiq/gateware/rtio/phy/ttl_serdes_ultrascale.py +++ b/artiq/gateware/rtio/phy/ttl_serdes_ultrascale.py @@ -18,8 +18,8 @@ class _OSERDESE3(Module): p_IS_CLK_INVERTED=0, p_IS_CLKDIV_INVERTED=0, p_IS_RST_INVERTED=0, o_OQ=self.ser_out, o_T_OUT=self.t_out, - i_RST=ResetSignal("rtio"), - i_CLK=ClockSignal("rtiox"), i_CLKDIV=ClockSignal("rtio"), + i_RST=ResetSignal("sys"), + i_CLK=ClockSignal("rtiox"), i_CLKDIV=ClockSignal("sys"), i_D=self.o, i_T=self.t_in) @@ -39,11 +39,11 @@ class _ISERDESE3(Module): p_DATA_WIDTH=dw, i_D=self.ser_in, - i_RST=ResetSignal("rtio"), + i_RST=ResetSignal("sys"), i_FIFO_RD_EN=0, i_CLK=ClockSignal("rtiox"), i_CLK_B=ClockSignal("rtiox"), # locally inverted - i_CLKDIV=ClockSignal("rtio"), + i_CLKDIV=ClockSignal("sys"), o_Q=Cat(*[self.i[i] for i in reversed(range(dw))])) diff --git a/artiq/gateware/rtio/sed/core.py b/artiq/gateware/rtio/sed/core.py index 7d0b0de4e..8953ff9d2 100644 --- a/artiq/gateware/rtio/sed/core.py +++ b/artiq/gateware/rtio/sed/core.py @@ -11,41 +11,25 @@ __all__ = ["SED"] class SED(Module): - def __init__(self, channels, glbl_fine_ts_width, mode, + def __init__(self, channels, glbl_fine_ts_width, lane_count=8, fifo_depth=128, enable_spread=True, quash_channels=[], report_buffer_space=False, interface=None): - if mode == "sync": - lane_dist_cdr = lambda x: x - fifos_cdr = lambda x: x - gates_cdr = lambda x: x - output_driver_cdr = lambda x: x - elif mode == "async": - lane_dist_cdr = ClockDomainsRenamer("rsys") - fifos_cdr = ClockDomainsRenamer({"write": "rsys", "read": "rio"}) - gates_cdr = ClockDomainsRenamer("rio") - output_driver_cdr = ClockDomainsRenamer("rio") - else: - raise ValueError - seqn_width = layouts.seqn_width(lane_count, fifo_depth) - self.submodules.lane_dist = lane_dist_cdr( - LaneDistributor(lane_count, seqn_width, - layouts.fifo_payload(channels), - [channel.interface.o.delay for channel in channels], - glbl_fine_ts_width, - enable_spread=enable_spread, - quash_channels=quash_channels, - interface=interface)) - self.submodules.fifos = fifos_cdr( - FIFOs(lane_count, fifo_depth, - layouts.fifo_payload(channels), mode, report_buffer_space)) - self.submodules.gates = gates_cdr( - Gates(lane_count, seqn_width, - layouts.fifo_payload(channels), - layouts.output_network_payload(channels, glbl_fine_ts_width))) - self.submodules.output_driver = output_driver_cdr( - OutputDriver(channels, glbl_fine_ts_width, lane_count, seqn_width)) + self.submodules.lane_dist = LaneDistributor(lane_count, seqn_width, + layouts.fifo_payload(channels), + [channel.interface.o.delay for channel in channels], + glbl_fine_ts_width, + enable_spread=enable_spread, + quash_channels=quash_channels, + interface=interface) + self.submodules.fifos = FIFOs(lane_count, fifo_depth, + layouts.fifo_payload(channels), report_buffer_space) + self.submodules.gates = Gates(lane_count, seqn_width, + layouts.fifo_payload(channels), + layouts.output_network_payload(channels, glbl_fine_ts_width)) + self.submodules.output_driver = OutputDriver(channels, glbl_fine_ts_width, + lane_count, seqn_width) for o, i in zip(self.lane_dist.output, self.fifos.input): self.comb += o.connect(i) diff --git a/artiq/gateware/rtio/sed/fifos.py b/artiq/gateware/rtio/sed/fifos.py index cbecccf07..3bf1bfc5a 100644 --- a/artiq/gateware/rtio/sed/fifos.py +++ b/artiq/gateware/rtio/sed/fifos.py @@ -2,7 +2,7 @@ from operator import or_ from functools import reduce from migen import * -from migen.genlib.fifo import * +from migen.genlib.fifo import SyncFIFOBuffered from artiq.gateware.rtio.sed import layouts @@ -11,7 +11,7 @@ __all__ = ["FIFOs"] class FIFOs(Module): - def __init__(self, lane_count, fifo_depth, layout_payload, mode, report_buffer_space=False): + def __init__(self, lane_count, fifo_depth, layout_payload, report_buffer_space=False): seqn_width = layouts.seqn_width(lane_count, fifo_depth) self.input = [Record(layouts.fifo_ingress(seqn_width, layout_payload)) for _ in range(lane_count)] @@ -23,16 +23,9 @@ class FIFOs(Module): # # # - if mode == "sync": - fifo_cls = SyncFIFOBuffered - elif mode == "async": - fifo_cls = AsyncFIFOBuffered - else: - raise ValueError - fifos = [] for input, output in zip(self.input, self.output): - fifo = fifo_cls(seqn_width + layout_len(layout_payload), fifo_depth) + fifo = SyncFIFOBuffered(seqn_width + layout_len(layout_payload), fifo_depth) self.submodules += fifo fifos.append(fifo) @@ -47,9 +40,6 @@ class FIFOs(Module): ] if report_buffer_space: - if mode != "sync": - raise NotImplementedError - def compute_max(elts): l = len(elts) if l == 1: diff --git a/artiq/gateware/rtio/tsc.py b/artiq/gateware/rtio/tsc.py index e93744553..063583a3d 100644 --- a/artiq/gateware/rtio/tsc.py +++ b/artiq/gateware/rtio/tsc.py @@ -1,48 +1,26 @@ from migen import * -from artiq.gateware.rtio.cdc import GrayCodeTransfer - - class TSC(Module): - def __init__(self, mode, glbl_fine_ts_width=0): + def __init__(self, glbl_fine_ts_width=0): self.glbl_fine_ts_width = glbl_fine_ts_width # in rtio domain self.coarse_ts = Signal(64 - glbl_fine_ts_width) self.full_ts = Signal(64) - # in sys domain - # monotonic, may lag behind the counter in the IO clock domain, but - # not be ahead of it. - self.coarse_ts_sys = Signal.like(self.coarse_ts) - self.full_ts_sys = Signal(64) - - # in rtio domain self.load = Signal() self.load_value = Signal.like(self.coarse_ts) - if mode == "async": - self.full_ts_cri = self.full_ts_sys - elif mode == "sync": - self.full_ts_cri = self.full_ts - else: - raise ValueError + self.full_ts_cri = self.full_ts # # # - self.sync.rtio += If(self.load, + self.sync += If(self.load, self.coarse_ts.eq(self.load_value) ).Else( self.coarse_ts.eq(self.coarse_ts + 1) ) - coarse_ts_cdc = GrayCodeTransfer(len(self.coarse_ts)) # from rtio to sys - self.submodules += coarse_ts_cdc - self.comb += [ - coarse_ts_cdc.i.eq(self.coarse_ts), - self.coarse_ts_sys.eq(coarse_ts_cdc.o) - ] self.comb += [ self.full_ts.eq(self.coarse_ts << glbl_fine_ts_width), - self.full_ts_sys.eq(self.coarse_ts_sys << glbl_fine_ts_width) ] diff --git a/artiq/gateware/rtio/xilinx_clocking.py b/artiq/gateware/rtio/xilinx_clocking.py index 57e6683c4..aff4786bf 100644 --- a/artiq/gateware/rtio/xilinx_clocking.py +++ b/artiq/gateware/rtio/xilinx_clocking.py @@ -1,39 +1,3 @@ -from migen import * -from migen.genlib.cdc import MultiReg -from misoc.interconnect.csr import * - - -class RTIOClockMultiplier(Module, AutoCSR): - def __init__(self, rtio_clk_freq): - self.pll_reset = CSRStorage(reset=1) - self.pll_locked = CSRStatus() - self.clock_domains.cd_rtiox4 = ClockDomain(reset_less=True) - - # See "Global Clock Network Deskew Using Two BUFGs" in ug472. - clkfbout = Signal() - clkfbin = Signal() - rtiox4_clk = Signal() - pll_locked = Signal() - self.specials += [ - Instance("MMCME2_BASE", - p_CLKIN1_PERIOD=1e9/rtio_clk_freq, - i_CLKIN1=ClockSignal("rtio"), - i_RST=self.pll_reset.storage, - o_LOCKED=pll_locked, - - p_CLKFBOUT_MULT_F=8.0, p_DIVCLK_DIVIDE=1, - - o_CLKFBOUT=clkfbout, i_CLKFBIN=clkfbin, - - p_CLKOUT0_DIVIDE_F=2.0, o_CLKOUT0=rtiox4_clk, - ), - Instance("BUFG", i_I=clkfbout, o_O=clkfbin), - Instance("BUFG", i_I=rtiox4_clk, o_O=self.cd_rtiox4.clk), - - MultiReg(pll_locked, self.pll_locked.status) - ] - - def fix_serdes_timing_path(platform): # ignore timing of path from OSERDESE2 through the pad to ISERDESE2 platform.add_platform_command( diff --git a/artiq/gateware/targets/kasli.py b/artiq/gateware/targets/kasli.py index 7ed52052b..f26e839dd 100755 --- a/artiq/gateware/targets/kasli.py +++ b/artiq/gateware/targets/kasli.py @@ -17,69 +17,15 @@ from misoc.integration.builder import builder_args, builder_argdict from artiq.gateware.amp import AMPSoC 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 RTIOClockMultiplier, fix_serdes_timing_path +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.siphaser import SiPhaser7Series -from artiq.gateware.drtio.wrpll import WRPLL, DDMTDSamplerGTP from artiq.gateware.drtio.rx_synchronizer import XilinxRXSynchronizer from artiq.gateware.drtio import * from artiq.build_soc import * -class _RTIOCRG(Module, AutoCSR): - def __init__(self, platform): - self.pll_reset = CSRStorage(reset=1) - self.pll_locked = CSRStatus() - self.clock_domains.cd_rtio = ClockDomain() - self.clock_domains.cd_rtiox4 = ClockDomain(reset_less=True) - - if platform.hw_rev == "v2.0": - clk_synth = platform.request("cdr_clk_clean_fabric") - else: - clk_synth = platform.request("si5324_clkout_fabric") - clk_synth_se = Signal() - platform.add_period_constraint(clk_synth.p, 8.0) - self.specials += [ - Instance("IBUFGDS", - p_DIFF_TERM="TRUE", p_IBUF_LOW_PWR="FALSE", - i_I=clk_synth.p, i_IB=clk_synth.n, o_O=clk_synth_se), - ] - - pll_locked = Signal() - rtio_clk = Signal() - rtiox4_clk = Signal() - fb_clk = Signal() - self.specials += [ - Instance("PLLE2_ADV", - p_STARTUP_WAIT="FALSE", o_LOCKED=pll_locked, - p_BANDWIDTH="HIGH", - p_REF_JITTER1=0.001, - p_CLKIN1_PERIOD=8.0, p_CLKIN2_PERIOD=8.0, - i_CLKIN2=clk_synth_se, - # Warning: CLKINSEL=0 means CLKIN2 is selected - i_CLKINSEL=0, - - # VCO @ 1.5GHz when using 125MHz input - p_CLKFBOUT_MULT=12, p_DIVCLK_DIVIDE=1, - i_CLKFBIN=fb_clk, - i_RST=self.pll_reset.storage, - - o_CLKFBOUT=fb_clk, - - p_CLKOUT0_DIVIDE=3, p_CLKOUT0_PHASE=0.0, - o_CLKOUT0=rtiox4_clk, - - p_CLKOUT1_DIVIDE=12, p_CLKOUT1_PHASE=0.0, - o_CLKOUT1=rtio_clk), - Instance("BUFG", i_I=rtio_clk, o_O=self.cd_rtio.clk), - Instance("BUFG", i_I=rtiox4_clk, o_O=self.cd_rtiox4.clk), - - AsyncResetSynchronizer(self.cd_rtio, ~pll_locked), - MultiReg(pll_locked, self.pll_locked.status) - ] - - class SMAClkinForward(Module): def __init__(self, platform): sma_clkin = platform.request("sma_clkin") @@ -118,6 +64,8 @@ class StandaloneBase(MiniSoC, AMPSoC): integrated_sram_size=8192, ethmac_nrxslots=4, ethmac_ntxslots=4, + clk_freq=kwargs.get("rtio_frequency", 125.0e6), + rtio_sys_merge=True, **kwargs) AMPSoC.__init__(self) add_identifier(self, gateware_identifier_str=gateware_identifier_str) @@ -127,6 +75,23 @@ class StandaloneBase(MiniSoC, AMPSoC): self.platform.request("error_led"))) self.csr_devices.append("error_led") self.submodules += SMAClkinForward(self.platform) + cdr_clk_out = self.platform.request("cdr_clk_clean") + else: + cdr_clk_out = self.platform.request("si5324_clkout") + + cdr_clk = Signal() + cdr_clk_buf = Signal() + self.platform.add_period_constraint(cdr_clk_out, 8.) + + self.specials += [ + Instance("IBUFDS_GTE2", + i_CEB=0, + i_I=cdr_clk_out.p, i_IB=cdr_clk_out.n, + o_O=cdr_clk), + Instance("BUFG", i_I=cdr_clk, o_O=cdr_clk_buf) + ] + + self.crg.configure(cdr_clk_buf) i2c = self.platform.request("i2c") self.submodules.i2c = gpio.GPIOTristate([i2c.scl, i2c.sda]) @@ -136,10 +101,8 @@ class StandaloneBase(MiniSoC, AMPSoC): self.config["SI5324_SOFT_RESET"] = None def add_rtio(self, rtio_channels, sed_lanes=8): - self.submodules.rtio_crg = _RTIOCRG(self.platform) - self.csr_devices.append("rtio_crg") fix_serdes_timing_path(self.platform) - self.submodules.rtio_tsc = rtio.TSC("async", glbl_fine_ts_width=3) + self.submodules.rtio_tsc = rtio.TSC(glbl_fine_ts_width=3) self.submodules.rtio_core = rtio.Core(self.rtio_tsc, rtio_channels, lane_count=sed_lanes) self.csr_devices.append("rtio_core") self.submodules.rtio = rtio.KernelInitiator(self.rtio_tsc) @@ -157,10 +120,6 @@ class StandaloneBase(MiniSoC, AMPSoC): self.submodules.rtio_moninj = rtio.MonInj(rtio_channels) self.csr_devices.append("rtio_moninj") - self.platform.add_false_path_constraints( - self.crg.cd_sys.clk, - self.rtio_crg.cd_rtio.clk) - self.submodules.rtio_analyzer = rtio.Analyzer(self.rtio_tsc, self.rtio_core.cri, self.get_native_sdram_if(), cpu_dw=self.cpu_dw) self.csr_devices.append("rtio_analyzer") @@ -177,7 +136,6 @@ class Tester(StandaloneBase): dds = "ad9910" StandaloneBase.__init__(self, hw_rev=hw_rev, **kwargs) - self.config["SI5324_AS_SYNTHESIZER"] = None # self.config["SI5324_EXT_REF"] = None self.config["RTIO_FREQUENCY"] = "125.0" if hw_rev == "v1.0": @@ -215,7 +173,6 @@ class SUServo(StandaloneBase): hw_rev = "v2.0" StandaloneBase.__init__(self, hw_rev=hw_rev, **kwargs) - self.config["SI5324_AS_SYNTHESIZER"] = None # self.config["SI5324_EXT_REF"] = None self.config["RTIO_FREQUENCY"] = "125.0" if hw_rev == "v1.0": @@ -247,8 +204,6 @@ class SUServo(StandaloneBase): self.add_rtio(self.rtio_channels) pads = self.platform.lookup_request("sampler3_adc_data_p") - self.platform.add_false_path_constraints( - pads.clkout, self.rtio_crg.cd_rtio.clk) self.platform.add_false_path_constraints( pads.clkout, self.crg.cd_sys.clk) @@ -277,6 +232,8 @@ class MasterBase(MiniSoC, AMPSoC): integrated_sram_size=8192, ethmac_nrxslots=4, ethmac_ntxslots=4, + clk_freq=rtio_clk_freq, + rtio_sys_merge=True, **kwargs) AMPSoC.__init__(self) add_identifier(self, gateware_identifier_str=gateware_identifier_str) @@ -295,7 +252,6 @@ class MasterBase(MiniSoC, AMPSoC): self.config["I2C_BUS_COUNT"] = 1 self.config["HAS_SI5324"] = None self.config["SI5324_SOFT_RESET"] = None - self.config["SI5324_AS_SYNTHESIZER"] = None self.config["RTIO_FREQUENCY"] = str(rtio_clk_freq/1e6) drtio_data_pads = [] @@ -315,8 +271,6 @@ class MasterBase(MiniSoC, AMPSoC): sys_clk_freq=self.clk_freq, rtio_clk_freq=rtio_clk_freq) self.csr_devices.append("drtio_transceiver") - self.sync += self.disable_cdr_clk_ibuf.eq( - ~self.drtio_transceiver.stable_clkin.storage) if enable_sata: sfp_channels = self.drtio_transceiver.channels[1:] @@ -329,7 +283,7 @@ class MasterBase(MiniSoC, AMPSoC): self.comb += [self.virtual_leds.get(i + 1).eq(channel.rx_ready) for i, channel in enumerate(sfp_channels)] - self.submodules.rtio_tsc = rtio.TSC("async", glbl_fine_ts_width=3) + self.submodules.rtio_tsc = rtio.TSC(glbl_fine_ts_width=3) drtio_csr_group = [] drtioaux_csr_group = [] @@ -366,8 +320,14 @@ class MasterBase(MiniSoC, AMPSoC): rtio_clk_period = 1e9/rtio_clk_freq gtp = self.drtio_transceiver.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) + platform.add_period_constraint(gtp.txoutclk, rtio_clk_period) platform.add_period_constraint(gtp.rxoutclk, rtio_clk_period) + platform.add_false_path_constraints( self.crg.cd_sys.clk, gtp.txoutclk, gtp.rxoutclk) @@ -376,8 +336,6 @@ class MasterBase(MiniSoC, AMPSoC): platform.add_false_path_constraints( self.crg.cd_sys.clk, gtp.rxoutclk) - self.submodules.rtio_crg = RTIOClockMultiplier(rtio_clk_freq) - self.csr_devices.append("rtio_crg") fix_serdes_timing_path(platform) def add_rtio(self, rtio_channels, sed_lanes=8): @@ -409,19 +367,18 @@ class MasterBase(MiniSoC, AMPSoC): # Never running out of stupid features, GTs on A7 make you pack # unrelated transceiver PLLs into one GTPE2_COMMON yourself. def create_qpll(self): - # The GTP acts up if you send any glitch to its - # clock input, even while the PLL is held in reset. - self.disable_cdr_clk_ibuf = Signal(reset=1) - self.disable_cdr_clk_ibuf.attr.add("no_retiming") if self.platform.hw_rev == "v2.0": - cdr_clk_clean = self.platform.request("cdr_clk_clean") + cdr_clk_out = self.platform.request("cdr_clk_clean") else: - cdr_clk_clean = self.platform.request("si5324_clkout") - cdr_clk_clean_buf = Signal() + cdr_clk_out = self.platform.request("si5324_clkout") + + cdr_clk = Signal() + self.platform.add_period_constraint(cdr_clk_out, 8.) + self.specials += Instance("IBUFDS_GTE2", - i_CEB=self.disable_cdr_clk_ibuf, - i_I=cdr_clk_clean.p, i_IB=cdr_clk_clean.n, - o_O=cdr_clk_clean_buf) + i_CEB=0, + i_I=cdr_clk_out.p, i_IB=cdr_clk_out.n, + o_O=cdr_clk) # Note precisely the rules Xilinx made up: # refclksel=0b001 GTREFCLK0 selected # refclksel=0b010 GTREFCLK1 selected @@ -436,7 +393,8 @@ class MasterBase(MiniSoC, AMPSoC): fbdiv=4, fbdiv_45=5, refclk_div=1) - qpll = QPLL(cdr_clk_clean_buf, qpll_drtio_settings, + + qpll = QPLL(cdr_clk, qpll_drtio_settings, self.crg.clk125_buf, qpll_eth_settings) self.submodules += qpll self.drtio_qpll_channel, self.ethphy_qpll_channel = qpll.channels @@ -448,7 +406,7 @@ class SatelliteBase(BaseSoC): } mem_map.update(BaseSoC.mem_map) - def __init__(self, rtio_clk_freq=125e6, enable_sata=False, *, with_wrpll=False, gateware_identifier_str=None, hw_rev="v2.0", **kwargs): + def __init__(self, rtio_clk_freq=125e6, enable_sata=False, *, gateware_identifier_str=None, hw_rev="v2.0", **kwargs): if hw_rev in ("v1.0", "v1.1"): cpu_bus_width = 32 else: @@ -459,6 +417,8 @@ class SatelliteBase(BaseSoC): cpu_bus_width=cpu_bus_width, sdram_controller_type="minicon", l2_size=128*1024, + clk_freq=rtio_clk_freq, + rtio_sys_merge=True, **kwargs) add_identifier(self, gateware_identifier_str=gateware_identifier_str) @@ -469,23 +429,24 @@ class SatelliteBase(BaseSoC): self.platform.request("error_led"))) self.csr_devices.append("error_led") - disable_cdr_clk_ibuf = Signal(reset=1) - disable_cdr_clk_ibuf.attr.add("no_retiming") if self.platform.hw_rev == "v2.0": - cdr_clk_clean = self.platform.request("cdr_clk_clean") + cdr_clk_out = self.platform.request("cdr_clk_clean") else: - cdr_clk_clean = self.platform.request("si5324_clkout") - cdr_clk_clean_buf = Signal() + cdr_clk_out = self.platform.request("si5324_clkout") + + cdr_clk = Signal() + self.platform.add_period_constraint(cdr_clk_out, 8.) + self.specials += Instance("IBUFDS_GTE2", - i_CEB=disable_cdr_clk_ibuf, - i_I=cdr_clk_clean.p, i_IB=cdr_clk_clean.n, - o_O=cdr_clk_clean_buf) + i_CEB=0, + i_I=cdr_clk_out.p, i_IB=cdr_clk_out.n, + o_O=cdr_clk) qpll_drtio_settings = QPLLSettings( refclksel=0b001, fbdiv=4, fbdiv_45=5, refclk_div=1) - qpll = QPLL(cdr_clk_clean_buf, qpll_drtio_settings) + qpll = QPLL(cdr_clk, qpll_drtio_settings) self.submodules += qpll drtio_data_pads = [] @@ -504,8 +465,6 @@ class SatelliteBase(BaseSoC): sys_clk_freq=self.clk_freq, rtio_clk_freq=rtio_clk_freq) self.csr_devices.append("drtio_transceiver") - self.sync += disable_cdr_clk_ibuf.eq( - ~self.drtio_transceiver.stable_clkin.storage) if enable_sata: sfp_channels = self.drtio_transceiver.channels[1:] @@ -518,7 +477,7 @@ class SatelliteBase(BaseSoC): self.comb += [self.virtual_leds.get(i).eq(channel.rx_ready) for i, channel in enumerate(sfp_channels)] - self.submodules.rtio_tsc = rtio.TSC("sync", glbl_fine_ts_width=3) + self.submodules.rtio_tsc = rtio.TSC(glbl_fine_ts_width=3) drtioaux_csr_group = [] drtioaux_memory_group = [] @@ -570,52 +529,34 @@ class SatelliteBase(BaseSoC): rtio_clk_period = 1e9/rtio_clk_freq self.config["RTIO_FREQUENCY"] = str(rtio_clk_freq/1e6) - if with_wrpll: - self.submodules.wrpll_sampler = DDMTDSamplerGTP( - self.drtio_transceiver, - platform.request("cdr_clk_clean_fabric")) - helper_clk_pads = platform.request("ddmtd_helper_clk") - self.submodules.wrpll = WRPLL( - helper_clk_pads=helper_clk_pads, - main_dcxo_i2c=platform.request("ddmtd_main_dcxo_i2c"), - helper_dxco_i2c=platform.request("ddmtd_helper_dcxo_i2c"), - ddmtd_inputs=self.wrpll_sampler) - self.csr_devices.append("wrpll") - # note: do not use self.wrpll.cd_helper.clk; otherwise, vivado craps out with: - # critical warning: create_clock attempting to set clock on an unknown port/pin - # command: "create_clock -period 7.920000 -waveform {0.000000 3.960000} -name - # helper_clk [get_xlnx_outside_genome_inst_pin 20 0] - platform.add_period_constraint(helper_clk_pads.p, rtio_clk_period*0.99) - platform.add_false_path_constraints(self.crg.cd_sys.clk, helper_clk_pads.p) - else: - self.submodules.siphaser = SiPhaser7Series( - si5324_clkin=platform.request("cdr_clk") if platform.hw_rev == "v2.0" - else platform.request("si5324_clkin"), - rx_synchronizer=self.rx_synchronizer, - ref_clk=self.crg.clk125_div2, ref_div2=True, - rtio_clk_freq=rtio_clk_freq) - platform.add_false_path_constraints( - self.crg.cd_sys.clk, self.siphaser.mmcm_freerun_output) - self.csr_devices.append("siphaser") - self.config["HAS_SI5324"] = None - self.config["SI5324_SOFT_RESET"] = None + + self.submodules.siphaser = SiPhaser7Series( + si5324_clkin=platform.request("cdr_clk") if platform.hw_rev == "v2.0" + else platform.request("si5324_clkin"), + rx_synchronizer=self.rx_synchronizer, + ref_clk=self.crg.clk125_div2, ref_div2=True, + rtio_clk_freq=rtio_clk_freq) + platform.add_false_path_constraints( + self.crg.cd_sys.clk, self.siphaser.mmcm_freerun_output) + self.csr_devices.append("siphaser") + self.config["HAS_SI5324"] = None + self.config["SI5324_SOFT_RESET"] = None gtp = self.drtio_transceiver.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) + platform.add_period_constraint(gtp.txoutclk, rtio_clk_period) platform.add_period_constraint(gtp.rxoutclk, rtio_clk_period) platform.add_false_path_constraints( self.crg.cd_sys.clk, gtp.txoutclk, gtp.rxoutclk) - if with_wrpll: - platform.add_false_path_constraints( - helper_clk_pads.p, gtp.rxoutclk) for gtp in self.drtio_transceiver.gtps[1:]: platform.add_period_constraint(gtp.rxoutclk, rtio_clk_period) platform.add_false_path_constraints( self.crg.cd_sys.clk, gtp.rxoutclk) - self.submodules.rtio_crg = RTIOClockMultiplier(rtio_clk_freq) - self.csr_devices.append("rtio_crg") fix_serdes_timing_path(platform) def add_rtio(self, rtio_channels, sed_lanes=8): @@ -629,7 +570,7 @@ class SatelliteBase(BaseSoC): self.submodules.cri_con = rtio.CRIInterconnectShared( [self.drtiosat.cri], [self.local_io.cri] + self.drtio_cri, - mode="sync", enable_routing=True) + enable_routing=True) self.csr_devices.append("cri_con") self.submodules.routing_table = rtio.RoutingTableAccess(self.cri_con) self.csr_devices.append("routing_table") @@ -687,7 +628,6 @@ def main(): parser.add_argument("-V", "--variant", default="tester", help="variant: {} (default: %(default)s)".format( "/".join(sorted(VARIANTS.keys())))) - parser.add_argument("--with-wrpll", default=False, action="store_true") parser.add_argument("--tester-dds", default=None, help="Tester variant DDS type: ad9910/ad9912 " "(default: ad9910)") @@ -696,8 +636,6 @@ def main(): args = parser.parse_args() argdict = dict() - if args.with_wrpll: - argdict["with_wrpll"] = True argdict["gateware_identifier_str"] = args.gateware_identifier_str argdict["dds"] = args.tester_dds diff --git a/artiq/gateware/targets/kasli_generic.py b/artiq/gateware/targets/kasli_generic.py index d33e80411..9988cc590 100755 --- a/artiq/gateware/targets/kasli_generic.py +++ b/artiq/gateware/targets/kasli_generic.py @@ -23,7 +23,6 @@ class GenericStandalone(StandaloneBase): self.class_name_override = description["variant"] StandaloneBase.__init__(self, hw_rev=hw_rev, **kwargs) - self.config["SI5324_AS_SYNTHESIZER"] = None self.config["RTIO_FREQUENCY"] = "{:.1f}".format(description["rtio_frequency"]/1e6) if "ext_ref_frequency" in description: self.config["SI5324_EXT_REF"] = None @@ -64,7 +63,7 @@ class GenericStandalone(StandaloneBase): self.add_csr_group("grabber", self.grabber_csr_group) for grabber in self.grabber_csr_group: self.platform.add_false_path_constraints( - self.rtio_crg.cd_rtio.clk, getattr(self, grabber).deserializer.cd_cl.clk) + self.crg.cd_sys.clk, getattr(self, grabber).deserializer.cd_cl.clk) class GenericMaster(MasterBase): diff --git a/artiq/gateware/targets/kc705.py b/artiq/gateware/targets/kc705.py index e04088a92..074abc9f4 100755 --- a/artiq/gateware/targets/kc705.py +++ b/artiq/gateware/targets/kc705.py @@ -17,72 +17,27 @@ from misoc.integration.builder import builder_args, builder_argdict from artiq.gateware.amp import AMPSoC from artiq.gateware import rtio, nist_clock, nist_qc2 from artiq.gateware.rtio.phy import ttl_simple, ttl_serdes_7series, dds, spi2 -from artiq.gateware.rtio.xilinx_clocking import RTIOClockMultiplier, fix_serdes_timing_path +from artiq.gateware.rtio.xilinx_clocking import fix_serdes_timing_path from artiq.gateware.drtio.transceiver import gtx_7series from artiq.gateware.drtio.siphaser import SiPhaser7Series from artiq.gateware.drtio.rx_synchronizer import XilinxRXSynchronizer from artiq.gateware.drtio import * from artiq.build_soc import * - -class _RTIOCRG(Module, AutoCSR): - def __init__(self, platform, rtio_internal_clk, use_sma=True): - self._clock_sel = CSRStorage() - self._pll_reset = CSRStorage(reset=1) - self._pll_locked = CSRStatus() - self.clock_domains.cd_rtio = ClockDomain() - self.clock_domains.cd_rtiox4 = ClockDomain(reset_less=True) - - # 100 MHz when using 125MHz input - self.clock_domains.cd_ext_clkout = ClockDomain(reset_less=True) - platform.add_period_constraint(self.cd_ext_clkout.clk, 5.0) - if use_sma: - ext_clkout = platform.request("user_sma_gpio_p_33") - self.sync.ext_clkout += ext_clkout.eq(~ext_clkout) - - rtio_external_clk = Signal() - if use_sma: - user_sma_clock = platform.request("user_sma_clock") - platform.add_period_constraint(user_sma_clock.p, 8.0) - self.specials += Instance("IBUFDS", - i_I=user_sma_clock.p, i_IB=user_sma_clock.n, - o_O=rtio_external_clk) - - pll_locked = Signal() - rtio_clk = Signal() - rtiox4_clk = Signal() - ext_clkout_clk = Signal() +class SMAClkinForward(Module): + def __init__(self, platform): + sma_clkin = platform.request("user_sma_clock") + sma_clkin_se = Signal() + sma_clkin_buffered = Signal() + cdr_clk_se = Signal() + cdr_clk = platform.request("si5324_clkin_33") self.specials += [ - Instance("PLLE2_ADV", - p_STARTUP_WAIT="FALSE", o_LOCKED=pll_locked, - - p_REF_JITTER1=0.01, - p_CLKIN1_PERIOD=8.0, p_CLKIN2_PERIOD=8.0, - i_CLKIN1=rtio_internal_clk, i_CLKIN2=rtio_external_clk, - # Warning: CLKINSEL=0 means CLKIN2 is selected - i_CLKINSEL=~self._clock_sel.storage, - - # VCO @ 1GHz when using 125MHz input - p_CLKFBOUT_MULT=8, p_DIVCLK_DIVIDE=1, - i_CLKFBIN=self.cd_rtio.clk, - i_RST=self._pll_reset.storage, - - o_CLKFBOUT=rtio_clk, - - p_CLKOUT0_DIVIDE=2, p_CLKOUT0_PHASE=0.0, - o_CLKOUT0=rtiox4_clk, - - p_CLKOUT1_DIVIDE=5, p_CLKOUT1_PHASE=0.0, - o_CLKOUT1=ext_clkout_clk), - Instance("BUFG", i_I=rtio_clk, o_O=self.cd_rtio.clk), - Instance("BUFG", i_I=rtiox4_clk, o_O=self.cd_rtiox4.clk), - Instance("BUFG", i_I=ext_clkout_clk, o_O=self.cd_ext_clkout.clk), - - AsyncResetSynchronizer(self.cd_rtio, ~pll_locked), - MultiReg(pll_locked, self._pll_locked.status) + Instance("IBUFDS", i_I=sma_clkin.p, i_IB=sma_clkin.n, o_O=sma_clkin_se), + Instance("BUFG", i_I=sma_clkin_se, o_O=sma_clkin_buffered), + Instance("ODDR", i_C=sma_clkin_buffered, i_CE=1, i_D1=0, i_D2=1, o_Q=cdr_clk_se), + Instance("OBUFDS", i_I=cdr_clk_se, o_O=cdr_clk.p, o_OB=cdr_clk.n) ] - # The default voltage for these signals on KC705 is 2.5V, and the Migen platform # follows this default. But since the SMAs are on the same bank as the DDS, # which is set to 3.3V by reprogramming the KC705 power ICs, we need to @@ -138,6 +93,7 @@ class _StandaloneBase(MiniSoC, AMPSoC): integrated_sram_size=8192, ethmac_nrxslots=4, ethmac_ntxslots=4, + rtio_sys_merge=True, **kwargs) AMPSoC.__init__(self) add_identifier(self, gateware_identifier_str=gateware_identifier_str) @@ -149,6 +105,30 @@ class _StandaloneBase(MiniSoC, AMPSoC): if isinstance(self.platform.toolchain, XilinxISEToolchain): self.platform.toolchain.bitgen_opt += " -g compress" + self.platform.add_extension(_reprogrammed3v3_io) + + cdr_clk_out = self.platform.request("si5324_clkout") + cdr_clk = Signal() + cdr_clk_buf = Signal() + + self.config["HAS_SI5324"] = None + self.submodules.si5324_rst_n = gpio.GPIOOut(self.platform.request("si5324_33").rst_n, reset_out=1) + self.csr_devices.append("si5324_rst_n") + self.specials += [ + Instance("IBUFDS_GTE2", + i_CEB=0, + i_I=cdr_clk_out.p, i_IB=cdr_clk_out.n, + o_O=cdr_clk, + p_CLKCM_CFG=1, + p_CLKRCV_TRST=1, + p_CLKSWING_CFG="2'b11"), + Instance("BUFG", i_I=cdr_clk, o_O=cdr_clk_buf) + ] + + self.crg.configure(cdr_clk_buf) + + self.submodules += SMAClkinForward(self.platform) + self.submodules.timer1 = timer.Timer() self.csr_devices.append("timer1") self.interrupt_devices.append("timer1") @@ -158,7 +138,6 @@ class _StandaloneBase(MiniSoC, AMPSoC): self.platform.request("user_led", 1))) self.csr_devices.append("leds") - self.platform.add_extension(_reprogrammed3v3_io) self.platform.add_extension(_ams101_dac) i2c = self.platform.request("i2c") @@ -169,10 +148,7 @@ class _StandaloneBase(MiniSoC, AMPSoC): self.config["HAS_DDS"] = None def add_rtio(self, rtio_channels): - self.submodules.rtio_crg = _RTIOCRG(self.platform, self.crg.cd_sys.clk) - self.csr_devices.append("rtio_crg") - self.config["HAS_RTIO_CLOCK_SWITCH"] = None - self.submodules.rtio_tsc = rtio.TSC("async", glbl_fine_ts_width=3) + self.submodules.rtio_tsc = rtio.TSC(glbl_fine_ts_width=3) self.submodules.rtio_core = rtio.Core(self.rtio_tsc, rtio_channels) self.csr_devices.append("rtio_core") self.submodules.rtio = rtio.KernelInitiator(self.rtio_tsc) @@ -187,11 +163,6 @@ class _StandaloneBase(MiniSoC, AMPSoC): self.submodules.rtio_moninj = rtio.MonInj(rtio_channels) self.csr_devices.append("rtio_moninj") - self.platform.add_period_constraint(self.rtio_crg.cd_rtio.clk, 8.) - self.platform.add_false_path_constraints( - self.crg.cd_sys.clk, - self.rtio_crg.cd_rtio.clk) - self.submodules.rtio_analyzer = rtio.Analyzer(self.rtio_tsc, self.rtio_core.cri, self.get_native_sdram_if(), cpu_dw=self.cpu_dw) self.csr_devices.append("rtio_analyzer") @@ -208,6 +179,7 @@ class _MasterBase(MiniSoC, AMPSoC): mem_map.update(MiniSoC.mem_map) def __init__(self, gateware_identifier_str=None, drtio_100mhz=False, **kwargs): + clk_freq = 100e6 if drtio_100mhz else 125e6 MiniSoC.__init__(self, cpu_type="vexriscv", cpu_bus_width=64, @@ -216,6 +188,8 @@ class _MasterBase(MiniSoC, AMPSoC): integrated_sram_size=8192, ethmac_nrxslots=4, ethmac_ntxslots=4, + clk_freq=clk_freq, + rtio_sys_merge=True, **kwargs) AMPSoC.__init__(self) add_identifier(self, gateware_identifier_str=gateware_identifier_str) @@ -236,17 +210,14 @@ class _MasterBase(MiniSoC, AMPSoC): platform.request("sfp"), platform.request("user_sma_mgt") ] - rtio_clk_freq = 100e6 if drtio_100mhz else 125e6 - # 1000BASE_BX10 Ethernet compatible, 100/125MHz RTIO clock self.submodules.drtio_transceiver = gtx_7series.GTX( clock_pads=platform.request("si5324_clkout"), pads=data_pads, - sys_clk_freq=self.clk_freq, - rtio_clk_freq=rtio_clk_freq) + clk_freq=self.clk_freq) self.csr_devices.append("drtio_transceiver") - self.submodules.rtio_tsc = rtio.TSC("async", glbl_fine_ts_width=3) + self.submodules.rtio_tsc = rtio.TSC(glbl_fine_ts_width=3) drtio_csr_group = [] drtioaux_csr_group = [] @@ -283,38 +254,39 @@ class _MasterBase(MiniSoC, AMPSoC): self.add_memory_group("drtioaux_mem", drtioaux_memory_group) self.config["RTIO_FREQUENCY"] = str(self.drtio_transceiver.rtio_clk_freq/1e6) - self.submodules.si5324_rst_n = gpio.GPIOOut(platform.request("si5324_33").rst_n) + 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") self.submodules.i2c = gpio.GPIOTristate([i2c.scl, i2c.sda]) self.csr_devices.append("i2c") self.config["I2C_BUS_COUNT"] = 1 self.config["HAS_SI5324"] = None - self.config["SI5324_AS_SYNTHESIZER"] = None - - self.comb += [ - platform.request("user_sma_clock_p").eq(ClockSignal("rtio_rx0")), - platform.request("user_sma_clock_n").eq(ClockSignal("rtio")) - ] rtio_clk_period = 1e9/self.drtio_transceiver.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] + + txout_buf = Signal() + self.specials += Instance("BUFG", i_I=gtx0.txoutclk, o_O=txout_buf) + self.crg.configure(txout_buf, clk_sw=gtx0.tx_init.done) + + self.comb += [ + platform.request("user_sma_clock_p").eq(ClockSignal("rtio_rx0")), + platform.request("user_sma_clock_n").eq(gtx0.txoutclk) + ] + platform.add_period_constraint(gtx0.txoutclk, rtio_clk_period) platform.add_period_constraint(gtx0.rxoutclk, rtio_clk_period) platform.add_false_path_constraints( - self.crg.cd_sys.clk, - gtx0.txoutclk, gtx0.rxoutclk) + 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:]: platform.add_period_constraint(gtx.rxoutclk, rtio_clk_period) platform.add_false_path_constraints( - self.crg.cd_sys.clk, gtx0.txoutclk, gtx.rxoutclk) + self.crg.cd_sys.clk, gtx.rxoutclk) - self.submodules.rtio_crg = RTIOClockMultiplier(self.drtio_transceiver.rtio_clk_freq) - self.csr_devices.append("rtio_crg") fix_serdes_timing_path(platform) def add_rtio(self, rtio_channels): @@ -345,12 +317,15 @@ class _SatelliteBase(BaseSoC): mem_map.update(BaseSoC.mem_map) def __init__(self, gateware_identifier_str=None, sma_as_sat=False, drtio_100mhz=False, **kwargs): + clk_freq = 100e6 if drtio_100mhz else 125e6 BaseSoC.__init__(self, cpu_type="vexriscv", cpu_bus_width=64, sdram_controller_type="minicon", l2_size=128*1024, integrated_sram_size=8192, + clk_freq=clk_freq, + rtio_sys_merge=True, **kwargs) add_identifier(self, gateware_identifier_str=gateware_identifier_str) @@ -372,17 +347,16 @@ class _SatelliteBase(BaseSoC): if sma_as_sat: data_pads = data_pads[::-1] - rtio_clk_freq = 100e6 if drtio_100mhz else 125e6 + rtio_clk_freq = clk_freq # 1000BASE_BX10 Ethernet compatible, 100/125MHz RTIO clock self.submodules.drtio_transceiver = gtx_7series.GTX( clock_pads=platform.request("si5324_clkout"), pads=data_pads, - sys_clk_freq=self.clk_freq, - rtio_clk_freq=rtio_clk_freq) + clk_freq=self.clk_freq) self.csr_devices.append("drtio_transceiver") - self.submodules.rtio_tsc = rtio.TSC("sync", glbl_fine_ts_width=3) + self.submodules.rtio_tsc = rtio.TSC(glbl_fine_ts_width=3) drtioaux_csr_group = [] drtioaux_memory_group = [] @@ -432,12 +406,13 @@ class _SatelliteBase(BaseSoC): 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) platform.add_false_path_constraints( self.crg.cd_sys.clk, self.siphaser.mmcm_freerun_output) self.csr_devices.append("siphaser") - self.submodules.si5324_rst_n = gpio.GPIOOut(platform.request("si5324_33").rst_n) + 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") self.submodules.i2c = gpio.GPIOTristate([i2c.scl, i2c.sda]) @@ -445,20 +420,22 @@ class _SatelliteBase(BaseSoC): self.config["I2C_BUS_COUNT"] = 1 self.config["HAS_SI5324"] = None - self.comb += [ - platform.request("user_sma_clock_p").eq(ClockSignal("rtio_rx0")), - platform.request("user_sma_clock_n").eq(ClockSignal("rtio")) - ] - rtio_clk_period = 1e9/self.drtio_transceiver.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] + + txout_buf = Signal() + self.specials += Instance("BUFG", i_I=gtx0.txoutclk, o_O=txout_buf) + self.crg.configure(txout_buf, clk_sw=gtx0.tx_init.done) + + self.comb += [ + platform.request("user_sma_clock_p").eq(ClockSignal("rtio_rx0")), + platform.request("user_sma_clock_n").eq(gtx0.txoutclk) + ] + platform.add_period_constraint(gtx0.txoutclk, rtio_clk_period) platform.add_period_constraint(gtx0.rxoutclk, rtio_clk_period) - platform.add_false_path_constraints( - self.crg.cd_sys.clk, - gtx0.txoutclk, 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:]: @@ -466,8 +443,6 @@ class _SatelliteBase(BaseSoC): platform.add_false_path_constraints( self.crg.cd_sys.clk, gtx.rxoutclk) - self.submodules.rtio_crg = RTIOClockMultiplier(self.drtio_transceiver.rtio_clk_freq) - self.csr_devices.append("rtio_crg") fix_serdes_timing_path(platform) def add_rtio(self, rtio_channels): @@ -479,7 +454,7 @@ class _SatelliteBase(BaseSoC): self.submodules.cri_con = rtio.CRIInterconnectShared( [self.drtiosat.cri], [self.local_io.cri] + self.drtio_cri, - mode="sync", enable_routing=True) + enable_routing=True) self.csr_devices.append("cri_con") self.submodules.routing_table = rtio.RoutingTableAccess(self.cri_con) self.csr_devices.append("routing_table") diff --git a/artiq/gateware/test/drtio/test_aux_controller.py b/artiq/gateware/test/drtio/test_aux_controller.py index 68c9f3bbd..6c65307ed 100644 --- a/artiq/gateware/test/drtio/test_aux_controller.py +++ b/artiq/gateware/test/drtio/test_aux_controller.py @@ -36,7 +36,7 @@ class TB(Module): def __init__(self, nwords, dw): self.submodules.link_layer = Loopback(nwords) self.submodules.aux_controller = ClockDomainsRenamer( - {"rtio": "sys", "rtio_rx": "sys"})(DRTIOAuxController(self.link_layer, dw)) + {"rtio_rx": "sys"})(DRTIOAuxController(self.link_layer, dw)) class TestAuxController(unittest.TestCase): diff --git a/artiq/gateware/test/drtio/test_full_stack.py b/artiq/gateware/test/drtio/test_full_stack.py index 02535b7e6..ecae7644b 100644 --- a/artiq/gateware/test/drtio/test_full_stack.py +++ b/artiq/gateware/test/drtio/test_full_stack.py @@ -52,7 +52,7 @@ class DUT(Module): self.ttl1 = Signal() self.transceivers = DummyTransceiverPair(nwords) - self.submodules.tsc_master = rtio.TSC("async") + self.submodules.tsc_master = rtio.TSC() self.submodules.master = DRTIOMaster(self.tsc_master, self.transceivers.alice) self.submodules.master_ki = rtio.KernelInitiator(self.tsc_master, @@ -67,7 +67,7 @@ class DUT(Module): rtio.Channel.from_phy(self.phy1), rtio.Channel.from_phy(self.phy2), ] - self.submodules.tsc_satellite = rtio.TSC("sync") + self.submodules.tsc_satellite = rtio.TSC() self.submodules.satellite = DRTIOSatellite( self.tsc_satellite, self.transceivers.bob, rx_synchronizer) self.satellite.reset.storage.reset = 0 @@ -144,8 +144,8 @@ class OutputsTestbench: class TestFullStack(unittest.TestCase): - clocks = {"sys": 8, "rtio": 5, "rtio_rx": 5, - "rio": 5, "rio_phy": 5} + clocks = {"sys": 8, "rtio_rx": 8, + "rio": 8, "rio_phy": 8} def test_pulses(self): tb = OutputsTestbench() @@ -169,7 +169,7 @@ class TestFullStack(unittest.TestCase): yield from tb.sync() run_simulation(tb.dut, - {"sys": test(), "rtio": tb.check_ttls(ttl_changes)}, self.clocks) + {"sys": [test(), tb.check_ttls(ttl_changes)]}, self.clocks) self.assertEqual(ttl_changes, correct_ttl_changes) def test_underflow(self): @@ -214,7 +214,7 @@ class TestFullStack(unittest.TestCase): yield from tb.sync() run_simulation(tb.dut, - {"sys": test(), "rtio": tb.check_ttls(ttl_changes)}, self.clocks) + {"sys": [test(), tb.check_ttls(ttl_changes)]}, self.clocks) self.assertEqual(ttl_changes, correct_ttl_changes) def test_write_underflow(self): @@ -227,16 +227,16 @@ class TestFullStack(unittest.TestCase): errors = yield from saterr.protocol_error.read() self.assertEqual(errors, 0) yield from csrs.underflow_margin.write(0) - tb.delay(100) + tb.delay(80) yield from tb.write(42, 1) - for i in range(12): + for i in range(21): yield errors = yield from saterr.protocol_error.read() underflow_channel = yield from saterr.underflow_channel.read() underflow_timestamp_event = yield from saterr.underflow_timestamp_event.read() self.assertEqual(errors, 8) # write underflow self.assertEqual(underflow_channel, 42) - self.assertEqual(underflow_timestamp_event, 100) + self.assertEqual(underflow_timestamp_event, 80) yield from saterr.protocol_error.write(errors) yield errors = yield from saterr.protocol_error.read() @@ -284,7 +284,7 @@ class TestFullStack(unittest.TestCase): yield dut.phy2.rtlink.i.stb.eq(0) run_simulation(dut, - {"sys": test(), "rtio": generate_input()}, self.clocks) + {"sys": [test(), generate_input()]}, self.clocks) def test_echo(self): dut = DUT(2) @@ -303,7 +303,7 @@ class TestFullStack(unittest.TestCase): yield yield dut.master.rt_packet.echo_stb.eq(0) - for i in range(15): + for i in range(17): yield self.assertEqual((yield dut.master.rt_packet.packet_cnt_tx), 1) diff --git a/artiq/gateware/test/drtio/test_rt_packet_repeater.py b/artiq/gateware/test/drtio/test_rt_packet_repeater.py index 9c73af625..21a28ae47 100644 --- a/artiq/gateware/test/drtio/test_rt_packet_repeater.py +++ b/artiq/gateware/test/drtio/test_rt_packet_repeater.py @@ -12,7 +12,7 @@ def create_dut(nwords): pt = PacketInterface("s2m", nwords*8) pr = PacketInterface("m2s", nwords*8) ts = Signal(64) - dut = ClockDomainsRenamer({"rtio": "sys", "rtio_rx": "sys"})( + dut = ClockDomainsRenamer({"rtio_rx": "sys"})( RTPacketRepeater( SimpleNamespace(coarse_ts=ts), SimpleNamespace( diff --git a/artiq/gateware/test/drtio/test_switching.py b/artiq/gateware/test/drtio/test_switching.py index dddfb9bd2..942ff0c0e 100644 --- a/artiq/gateware/test/drtio/test_switching.py +++ b/artiq/gateware/test/drtio/test_switching.py @@ -40,12 +40,12 @@ class DUT(Module): def __init__(self, nwords): self.transceivers = DummyTransceiverPair(nwords) - self.submodules.tsc_master = rtio.TSC("async") + self.submodules.tsc_master = rtio.TSC() self.submodules.master = DRTIOMaster(self.tsc_master, self.transceivers.alice) rx_synchronizer = DummyRXSynchronizer() - self.submodules.tsc_satellite = rtio.TSC("sync") + self.submodules.tsc_satellite = rtio.TSC() self.submodules.satellite = DRTIOSatellite( self.tsc_satellite, self.transceivers.bob, rx_synchronizer) self.satellite.reset.storage.reset = 0 @@ -130,7 +130,7 @@ class Testbench: class TestSwitching(unittest.TestCase): - clocks = {"sys": 8, "rtio": 5, "rtio_rx": 5, + clocks = {"sys": 8, "rtio_rx": 5, "rio": 5, "rio_phy": 5} def test_outputs(self): @@ -183,7 +183,7 @@ class TestSwitching(unittest.TestCase): current_request = (packet_type, field_dict, trailer) run_simulation(tb.dut, - {"sys": test(), "rtio": tb.dut.pr.receive(receive), "rtio_rx": send_replies()}, self.clocks) + {"sys": test(), "sys": tb.dut.pr.receive(receive), "rtio_rx": send_replies()}, self.clocks) def test_inputs(self): @@ -244,4 +244,4 @@ class TestSwitching(unittest.TestCase): current_request = (packet_type, field_dict, trailer) run_simulation(tb.dut, - {"sys": test(), "rtio": tb.dut.pr.receive(receive), "rtio_rx": send_replies()}, self.clocks) + {"sys": test(), "sys": tb.dut.pr.receive(receive), "rtio_rx": send_replies()}, self.clocks) diff --git a/artiq/gateware/test/rtio/test_dma.py b/artiq/gateware/test/rtio/test_dma.py index c5d220a9f..54b996b4f 100644 --- a/artiq/gateware/test/rtio/test_dma.py +++ b/artiq/gateware/test/rtio/test_dma.py @@ -128,7 +128,7 @@ class FullStackTB(Module): self.submodules.memory = wishbone.SRAM( 256, init=sequence, bus=bus) self.submodules.dut = dma.DMA(bus, dw) - self.submodules.tsc = rtio.TSC("async") + self.submodules.tsc = rtio.TSC() self.submodules.rtio = rtio.Core(self.tsc, rtio_channels) self.comb += self.dut.cri.connect(self.rtio.cri) @@ -203,11 +203,11 @@ class TestDMA(unittest.TestCase): run_simulation(tb[32], {"sys": [ do_dma(tb[32].dut, 0), monitor(32), (None for _ in range(70)), - ]}, {"sys": 8, "rsys": 8, "rtio": 8, "rio": 8, "rio_phy": 8}) + ]}, {"sys": 8, "rio": 8, "rio_phy": 8}) run_simulation(tb[64], {"sys": [ do_dma(tb[64].dut, 0), monitor(64), (None for _ in range(70)), - ]}, {"sys": 8, "rsys": 8, "rtio": 8, "rio": 8, "rio_phy": 8}) + ]}, {"sys": 8, "rio": 8, "rio_phy": 8}) correct_changes = [(timestamp + 11, channel) for channel, timestamp, _, _ in test_writes_full_stack] diff --git a/artiq/gateware/test/rtio/test_input_collector.py b/artiq/gateware/test/rtio/test_input_collector.py index bf68a17e4..5f8c7b262 100644 --- a/artiq/gateware/test/rtio/test_input_collector.py +++ b/artiq/gateware/test/rtio/test_input_collector.py @@ -38,8 +38,8 @@ class DUT(Module): rtio.Channel.from_phy(self.phy0, ififo_depth=4), rtio.Channel.from_phy(self.phy1, ififo_depth=4) ] - self.submodules.tsc = ClockDomainsRenamer({"rtio": "sys"})(rtio.TSC("sync")) - self.submodules.input_collector = InputCollector(self.tsc, rtio_channels, "sync") + self.submodules.tsc = rtio.TSC() + self.submodules.input_collector = InputCollector(self.tsc, rtio_channels) @property def cri(self): diff --git a/artiq/gateware/test/rtio/test_sed_top.py b/artiq/gateware/test/rtio/test_sed_top.py index 5efc257a0..5c5002fd3 100644 --- a/artiq/gateware/test/rtio/test_sed_top.py +++ b/artiq/gateware/test/rtio/test_sed_top.py @@ -22,7 +22,7 @@ class DUT(Module): rtio.Channel.from_phy(self.phy1) ] - self.submodules.sed = SED(rtio_channels, 0, "sync", **kwargs) + self.submodules.sed = SED(rtio_channels, 0, **kwargs) self.sync += [ self.sed.coarse_timestamp.eq(self.sed.coarse_timestamp + 1), self.sed.minimum_coarse_timestamp.eq(self.sed.coarse_timestamp + 16) diff --git a/artiq/gateware/test/wrpll/__init__.py b/artiq/gateware/test/wrpll/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/artiq/gateware/test/wrpll/test_dsp.py b/artiq/gateware/test/wrpll/test_dsp.py deleted file mode 100644 index 033e69853..000000000 --- a/artiq/gateware/test/wrpll/test_dsp.py +++ /dev/null @@ -1,158 +0,0 @@ -import unittest - -import numpy as np - -from migen import * - -from artiq.gateware.drtio.wrpll.ddmtd import Collector -from artiq.gateware.drtio.wrpll import thls, filters - - -class HelperChainTB(Module): - def __init__(self, N): - self.tag_ref = Signal(N) - self.input_stb = Signal() - self.adpll = Signal((24, True)) - self.out_stb = Signal() - - ### - - self.submodules.collector = Collector(N) - self.submodules.loop_filter = thls.make(filters.helper, data_width=48) - - self.comb += [ - self.collector.tag_ref.eq(self.tag_ref), - self.collector.ref_stb.eq(self.input_stb), - self.collector.main_stb.eq(self.input_stb), - self.loop_filter.input.eq(self.collector.out_helper << 22), - self.loop_filter.input_stb.eq(self.collector.out_stb), - self.adpll.eq(self.loop_filter.output), - self.out_stb.eq(self.loop_filter.output_stb), - ] - - -class TestDSP(unittest.TestCase): - def test_main_collector(self): - N = 2 - collector = Collector(N=N) - # check collector phase unwrapping - tags = [(0, 0, 0), - (0, 1, 1), - (2, 1, -1), - (3, 1, -2), - (0, 1, -3), - (1, 1, -4), - (2, 1, -5), - (3, 1, -6), - (3, 3, -4), - (0, 0, -4), - (0, 1, -3), - (0, 2, -2), - (0, 3, -1), - (0, 0, 0)] - for i in range(10): - tags.append((i % (2**N), (i+1) % (2**N), 1)) - - def generator(): - for tag_ref, tag_main, out in tags: - yield collector.tag_ref.eq(tag_ref) - yield collector.tag_main.eq(tag_main) - yield collector.main_stb.eq(1) - yield collector.ref_stb.eq(1) - - yield - - yield collector.main_stb.eq(0) - yield collector.ref_stb.eq(0) - - while not (yield collector.out_stb): - yield - - out_main = yield collector.out_main - self.assertEqual(out_main, out) - - run_simulation(collector, generator()) - - def test_helper_collector(self): - N = 3 - collector = Collector(N=N) - # check collector phase unwrapping - tags = [((2**N - 1 - tag) % (2**N), -1) for tag in range(20)] - tags += [((tags[-1][0] + 1 + tag) % (2**N), 1) for tag in range(20)] - tags += [((tags[-1][0] - 2 - 2*tag) % (2**N), -2) for tag in range(20)] - - def generator(): - for tag_ref, out in tags: - yield collector.tag_ref.eq(tag_ref) - yield collector.main_stb.eq(1) - yield collector.ref_stb.eq(1) - - yield - - yield collector.main_stb.eq(0) - yield collector.ref_stb.eq(0) - - while not (yield collector.out_stb): - yield - - out_helper = yield collector.out_helper - self.assertEqual(out_helper, out) - - run_simulation(collector, generator()) - - # test helper collector + filter against output from MATLAB model - def test_helper_chain(self): - pll = HelperChainTB(15) - - initial_helper_out = -8000 - ref_tags = np.array([ - 24778, 16789, 8801, 814, 25596, 17612, 9628, 1646, - 26433, 18453, 10474, 2496, 27287, 19311, 11337, 3364, 28160, - 20190, 12221, 4253, 29054, 21088, 13124, 5161, 29966, 22005, - 14045, 6087, 30897, 22940, 14985, 7031, 31847, 23895, 15944, - 7995, 47, 24869, 16923, 8978, 1035, 25861, 17920, 9981, - 2042, 26873, 18937, 11002, 3069, 27904, 19973, 12042, 4113, - 28953, 21026, 13100, 5175, 30020, 22098, 14177, 6257, 31106, - 23189, 15273, 7358, 32212, 24300, 16388, 8478, 569, 25429, - 17522, 9617, 1712, 26577, 18675, 10774, 2875, 27745, 19848, - 11951, 4056, 28930, 21038, 13147, 5256, 30135, 22247, 14361, - 6475, 31359, 23476, 15595, 7714, 32603, 24725, 16847, 8971, - 1096 - ]) - adpll_sim = np.array([ - 8, 24, 41, 57, 74, 91, 107, 124, 140, 157, 173, - 190, 206, 223, 239, 256, 273, 289, 306, 322, 339, 355, - 372, 388, 405, 421, 438, 454, 471, 487, 504, 520, 537, - 553, 570, 586, 603, 619, 636, 652, 668, 685, 701, 718, - 734, 751, 767, 784, 800, 817, 833, 850, 866, 882, 899, - 915, 932, 948, 965, 981, 998, 1014, 1030, 1047, 1063, 1080, - 1096, 1112, 1129, 1145, 1162, 1178, 1194, 1211, 1227, 1244, 1260, - 1276, 1293, 1309, 1326, 1342, 1358, 1375, 1391, 1407, 1424, 1440, - 1457, 1473, 1489, 1506, 1522, 1538, 1555, 1571, 1587, 1604, 1620, - 1636]) - - def sim(): - yield pll.collector.out_helper.eq(initial_helper_out) - for ref_tag, adpll_matlab in zip(ref_tags, adpll_sim): - # feed collector - yield pll.tag_ref.eq(int(ref_tag)) - yield pll.input_stb.eq(1) - - yield - - yield pll.input_stb.eq(0) - - while not (yield pll.collector.out_stb): - yield - - tag_diff = yield pll.collector.out_helper - - while not (yield pll.loop_filter.output_stb): - yield - - adpll_migen = yield pll.adpll - self.assertEqual(adpll_migen, adpll_matlab) - - yield - - run_simulation(pll, [sim()]) diff --git a/artiq/gateware/test/wrpll/test_thls.py b/artiq/gateware/test/wrpll/test_thls.py deleted file mode 100644 index c1013de30..000000000 --- a/artiq/gateware/test/wrpll/test_thls.py +++ /dev/null @@ -1,55 +0,0 @@ -import unittest - -from migen import * - -from artiq.gateware.drtio.wrpll import thls - - -a = 0 - -def simple_test(x): - global a - a = a + (x*4 >> 1) - return a - - -class TestTHLS(unittest.TestCase): - def test_thls(self): - global a - - proc = thls.Processor() - a = 0 - cp = thls.compile(proc, simple_test) - print("Program:") - cp.pretty_print() - cp.dimension_processor() - print("Encoded program:", cp.encode()) - proc_impl = proc.implement(cp.encode(), cp.data) - - def send_values(values): - for value in values: - yield proc_impl.input.eq(value) - yield proc_impl.input_stb.eq(1) - yield - yield proc_impl.input.eq(0) - yield proc_impl.input_stb.eq(0) - yield - while (yield proc_impl.busy): - yield - @passive - def receive_values(callback): - while True: - while not (yield proc_impl.output_stb): - yield - callback((yield proc_impl.output)) - yield - - send_list = [42, 40, 10, 10] - receive_list = [] - - run_simulation(proc_impl, [send_values(send_list), receive_values(receive_list.append)]) - print("Execution:", send_list, "->", receive_list) - - a = 0 - expected_list = [simple_test(x) for x in send_list] - self.assertEqual(receive_list, expected_list) diff --git a/artiq/gui/applets.py b/artiq/gui/applets.py index b119d14bc..275e61ba7 100644 --- a/artiq/gui/applets.py +++ b/artiq/gui/applets.py @@ -92,9 +92,9 @@ class AppletIPCServer(AsyncioParentComm): finally: self.datasets_sub.notify_cbs.remove(self._on_mod) - def start_server(self, embed_cb, fix_initial_size_cb): + def start_server(self, embed_cb, fix_initial_size_cb, *, loop=None): self.server_task = asyncio.ensure_future( - self.serve(embed_cb, fix_initial_size_cb)) + self.serve(embed_cb, fix_initial_size_cb), loop=loop) async def stop_server(self): if hasattr(self, "server_task"): @@ -327,7 +327,7 @@ class _CompleterDelegate(QtWidgets.QStyledItemDelegate): class AppletsDock(QtWidgets.QDockWidget): - def __init__(self, main_window, datasets_sub, extra_substitutes={}): + def __init__(self, main_window, datasets_sub, extra_substitutes={}, *, loop=None): """ :param extra_substitutes: Map of extra ``${strings}`` to substitute in applet commands to their respective values. @@ -342,6 +342,8 @@ class AppletsDock(QtWidgets.QDockWidget): self.extra_substitutes = extra_substitutes self.applet_uids = set() + self._loop = loop + self.table = QtWidgets.QTreeWidget() self.table.setColumnCount(2) self.table.setHeaderLabels(["Name", "Command"]) @@ -441,7 +443,7 @@ class AppletsDock(QtWidgets.QDockWidget): dock = _AppletDock(self.datasets_sub, item.applet_uid, name, spec, self.extra_substitutes) self.main_window.addDockWidget(QtCore.Qt.RightDockWidgetArea, dock) dock.setFloating(True) - asyncio.ensure_future(dock.start()) + asyncio.ensure_future(dock.start(), loop=self._loop) dock.sigClosed.connect(partial(self.on_dock_closed, item, dock)) return dock @@ -480,7 +482,7 @@ class AppletsDock(QtWidgets.QDockWidget): def on_dock_closed(self, item, dock): item.applet_geometry = dock.saveGeometry() - asyncio.ensure_future(dock.terminate()) + asyncio.ensure_future(dock.terminate(), loop=self._loop) item.setCheckState(0, QtCore.Qt.Unchecked) def get_untitled(self): @@ -569,7 +571,7 @@ class AppletsDock(QtWidgets.QDockWidget): if wi.ty == "applet": dock = wi.applet_dock if dock is not None: - asyncio.ensure_future(dock.restart()) + asyncio.ensure_future(dock.restart(), loop=self._loop) elif wi.ty == "group": for i in range(wi.childCount()): walk(wi.child(i)) diff --git a/artiq/gui/log.py b/artiq/gui/log.py index 3fd05d80d..3b5095f98 100644 --- a/artiq/gui/log.py +++ b/artiq/gui/log.py @@ -1,4 +1,3 @@ -import asyncio import logging import time import re diff --git a/artiq/gui/logo_ver.svg b/artiq/gui/logo_ver.svg index fd0b3eb28..4ebd46eed 100644 --- a/artiq/gui/logo_ver.svg +++ b/artiq/gui/logo_ver.svg @@ -1,14 +1,7 @@ image/svg+xml + aria-label="7">8 diff --git a/artiq/language/environment.py b/artiq/language/environment.py index 9ce7034cc..d3ccaf77d 100644 --- a/artiq/language/environment.py +++ b/artiq/language/environment.py @@ -209,8 +209,6 @@ class TraceArgumentManager: self.requested_args[key] = processor, group, tooltip return None - def check_unprocessed_arguments(self): - pass class ProcessArgumentManager: def __init__(self, unprocessed_arguments): @@ -229,7 +227,7 @@ class ProcessArgumentManager: unprocessed = set(self.unprocessed_arguments.keys()) -\ self._processed_arguments if unprocessed: - raise AttributeError("Invalid argument(s): " + + raise AttributeError("Supplied argument(s) not queried in experiment: " + ", ".join(unprocessed)) class HasEnvironment: @@ -252,8 +250,6 @@ class HasEnvironment: self.__in_build = True self.build(*args, **kwargs) self.__in_build = False - if self.__argument_mgr is not None: - self.__argument_mgr.check_unprocessed_arguments() def register_child(self, child): self.children.append(child) diff --git a/artiq/master/experiments.py b/artiq/master/experiments.py index 098ddc334..df29f2a6b 100644 --- a/artiq/master/experiments.py +++ b/artiq/master/experiments.py @@ -124,9 +124,9 @@ class ExperimentDB: self._scanning = False self.status["scanning"] = False - def scan_repository_async(self, new_cur_rev=None): + def scan_repository_async(self, new_cur_rev=None, loop=None): asyncio.ensure_future( - exc_to_warning(self.scan_repository(new_cur_rev))) + exc_to_warning(self.scan_repository(new_cur_rev)), loop=loop) async def examine(self, filename, use_repository=True, revision=None): if use_repository: diff --git a/artiq/master/scheduler.py b/artiq/master/scheduler.py index eea832a17..bc264964e 100644 --- a/artiq/master/scheduler.py +++ b/artiq/master/scheduler.py @@ -1,6 +1,7 @@ import asyncio import logging import csv +import os.path from enum import Enum from time import time @@ -131,7 +132,8 @@ class RunPool: writer.writerow([rid, start_time, expid["file"]]) def submit(self, expid, priority, due_date, flush, pipeline_name): - # mutates expid to insert head repository revision if None. + # mutates expid to insert head repository revision if None and + # replaces relative path with the absolute one. # called through scheduler. rid = self.ridc.get() if "repo_rev" in expid: @@ -140,7 +142,10 @@ class RunPool: wd, repo_msg = self.experiment_db.repo_backend.request_rev( expid["repo_rev"]) else: + if "file" in expid: + expid["file"] = os.path.abspath(expid["file"]) wd, repo_msg = None, None + run = Run(rid, pipeline_name, wd, expid, priority, due_date, flush, self, repo_msg=repo_msg) if self.log_submissions is not None: @@ -327,10 +332,10 @@ class Pipeline: self._run = RunStage(self.pool, deleter.delete) self._analyze = AnalyzeStage(self.pool, deleter.delete) - def start(self): - self._prepare.start() - self._run.start() - self._analyze.start() + def start(self, *, loop=None): + self._prepare.start(loop=loop) + self._run.start(loop=loop) + self._analyze.start(loop=loop) async def stop(self): # NB: restart of a stopped pipeline is not supported @@ -405,8 +410,9 @@ class Scheduler: self._deleter = Deleter(self._pipelines) self._log_submissions = log_submissions - def start(self): - self._deleter.start() + def start(self, *, loop=None): + self._loop = loop + self._deleter.start(loop=self._loop) async def stop(self): # NB: restart of a stopped scheduler is not supported @@ -425,7 +431,8 @@ class Scheduler: When called through an experiment, the default values of ``pipeline_name``, ``expid`` and ``priority`` correspond to those of the current run.""" - # mutates expid to insert head repository revision if None + # mutates expid to insert head repository revision if None, and + # replaces relative file path with absolute one if self._terminated: return try: @@ -436,7 +443,7 @@ class Scheduler: self._worker_handlers, self.notifier, self._experiment_db, self._log_submissions) self._pipelines[pipeline_name] = pipeline - pipeline.start() + pipeline.start(loop=self._loop) return pipeline.pool.submit(expid, priority, due_date, flush, pipeline_name) def delete(self, rid): diff --git a/artiq/master/worker_impl.py b/artiq/master/worker_impl.py index 87d448cd9..33a02237f 100644 --- a/artiq/master/worker_impl.py +++ b/artiq/master/worker_impl.py @@ -306,6 +306,7 @@ def main(): os.chdir(dirname) argument_mgr = ProcessArgumentManager(expid["arguments"]) exp_inst = exp((device_mgr, dataset_mgr, argument_mgr, {})) + argument_mgr.check_unprocessed_arguments() put_completed() elif action == "prepare": exp_inst.prepare() @@ -325,7 +326,10 @@ def main(): exp_inst.analyze() put_completed() finally: - write_results() + # browser's analyze shouldn't write results, + # since it doesn't run the experiment and cannot have rid + if rid is not None: + write_results() elif action == "examine": examine(ExamineDeviceMgr, ExamineDatasetMgr, obj["file"]) put_completed() diff --git a/artiq/test/coredevice/test_embedding.py b/artiq/test/coredevice/test_embedding.py index 1c90b956f..cde3fa3dc 100644 --- a/artiq/test/coredevice/test_embedding.py +++ b/artiq/test/coredevice/test_embedding.py @@ -75,6 +75,9 @@ class RoundtripTest(ExperimentCase): def test_object_list(self): self.assertRoundtrip([object(), object()]) + def test_object_tuple(self): + self.assertRoundtrip((False, object(), True, 0x12345678)) + def test_list_tuple(self): self.assertRoundtrip(([1, 2], [3, 4])) @@ -96,7 +99,7 @@ class RoundtripTest(ExperimentCase): self.assertArrayRoundtrip(numpy.array([["a", "b"], ["c", "d"]])) # FIXME: This should work, but currently passes as the argument is just - # synthesised as a call to array() without forwarding the dype form the host + # synthesised as a call to array() without forwarding the dtype from the host # NumPy object. @unittest.expectedFailure def test_array_jagged(self): diff --git a/artiq/test/test_frontends.py b/artiq/test/test_frontends.py index 622c7a010..caef4839c 100644 --- a/artiq/test/test_frontends.py +++ b/artiq/test/test_frontends.py @@ -13,7 +13,7 @@ class TestFrontends(unittest.TestCase): ], "artiq": [ "client", "compile", "coreanalyzer", "coremgmt", - "flash", "master", "mkfs", "route", + "flash", "master", "mkfs", "route", "rtiomap", "rtiomon", "run", "session", "browser", "dashboard" ] } diff --git a/artiq/test/test_scheduler.py b/artiq/test/test_scheduler.py index 854f17a93..9726190ed 100644 --- a/artiq/test/test_scheduler.py +++ b/artiq/test/test_scheduler.py @@ -104,7 +104,7 @@ class SchedulerCase(unittest.TestCase): done.set() scheduler.notifier.publish = notify - scheduler.start() + scheduler.start(loop=loop) # Verify that a timed experiment far in the future does not # get run, even if it has high priority. @@ -269,7 +269,7 @@ class SchedulerCase(unittest.TestCase): done.set() scheduler.notifier.publish = notify - scheduler.start() + scheduler.start(loop=loop) scheduler.submit("main", expid_bg, low_priority) scheduler.submit("main", expid_empty, high_priority, late) @@ -328,7 +328,7 @@ class SchedulerCase(unittest.TestCase): empty_completed.set() scheduler.notifier.publish = notify - scheduler.start() + scheduler.start(loop=loop) scheduler.submit("main", expid_bg, -99, None, False) loop.run_until_complete(background_running.wait()) self.assertFalse(scheduler.check_pause(0)) @@ -379,7 +379,7 @@ class SchedulerCase(unittest.TestCase): empty_ready.set() scheduler.notifier.publish = notify - scheduler.start() + scheduler.start(loop=loop) scheduler.submit("main", expid_bg, -99, None, False) loop.run_until_complete(background_running.wait()) @@ -417,7 +417,7 @@ class SchedulerCase(unittest.TestCase): done.set() scheduler.notifier.publish = notify - scheduler.start() + scheduler.start(loop=loop) scheduler.submit("main", expid, 0, None, False) loop.run_until_complete(first_preparing.wait()) scheduler.submit("main", expid, 1, None, True) diff --git a/artiq/test/test_worker.py b/artiq/test/test_worker.py index c33daf430..9667e69ea 100644 --- a/artiq/test/test_worker.py +++ b/artiq/test/test_worker.py @@ -62,44 +62,42 @@ async def _call_worker(worker, expid): await worker.close() -def _run_experiment(class_name): - expid = { - "log_level": logging.WARNING, - "file": sys.modules[__name__].__file__, - "class_name": class_name, - "arguments": dict() - } - loop = asyncio.get_event_loop() - worker = Worker({}) - loop.run_until_complete(_call_worker(worker, expid)) - - class WorkerCase(unittest.TestCase): def setUp(self): self.loop = asyncio.new_event_loop() asyncio.set_event_loop(self.loop) + def _run_experiment(self, class_name): + expid = { + "log_level": logging.WARNING, + "file": sys.modules[__name__].__file__, + "class_name": class_name, + "arguments": dict() + } + worker = Worker({}) + self.loop.run_until_complete(_call_worker(worker, expid)) + def test_simple_run(self): - _run_experiment("SimpleExperiment") + self._run_experiment("SimpleExperiment") def test_exception(self): with self.assertLogs() as logs: with self.assertRaises(WorkerInternalException): - _run_experiment("ExceptionTermination") + self._run_experiment("ExceptionTermination") self.assertGreater(len(logs.records), 0) self.assertIn("Terminating with exception (TypeError)", logs.output[-1]) def test_watchdog_no_timeout(self): - _run_experiment("WatchdogNoTimeout") + self._run_experiment("WatchdogNoTimeout") def test_watchdog_timeout(self): with self.assertRaises(WorkerWatchdogTimeout): - _run_experiment("WatchdogTimeout") + self._run_experiment("WatchdogTimeout") def test_watchdog_timeout_in_build(self): with self.assertRaises(WorkerWatchdogTimeout): - _run_experiment("WatchdogTimeoutInBuild") + self._run_experiment("WatchdogTimeoutInBuild") def tearDown(self): self.loop.close() diff --git a/doc/manual/conf.py b/doc/manual/conf.py index 7ca6ada86..7cb08a3a5 100644 --- a/doc/manual/conf.py +++ b/doc/manual/conf.py @@ -89,7 +89,7 @@ master_doc = 'index' # General information about the project. project = 'ARTIQ' -copyright = '2014-2022, M-Labs Limited' +copyright = '2014-2023, M-Labs Limited' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the diff --git a/doc/manual/installing.rst b/doc/manual/installing.rst index 3f8f89d07..1a672d6f1 100644 --- a/doc/manual/installing.rst +++ b/doc/manual/installing.rst @@ -73,6 +73,10 @@ Installing multiple packages and making them visible to the ARTIQ commands requi ]; }; }; + nixConfig = { # work around https://github.com/NixOS/nix/issues/6771 + extra-trusted-public-keys = "nixbld.m-labs.hk-1:5aSRVA5b320xbNvu30tqxVPXpld73bhtOeH6uAjRyHc="; + extra-substituters = "https://nixbld.m-labs.hk"; + }; } @@ -142,11 +146,9 @@ OpenOCD can be used to write the binary images into the core device FPGA board's With Nix, add ``aqmain.openocd-bscanspi`` to the shell packages. Be careful not to add ``pkgs.openocd`` instead - this would install OpenOCD from the NixOS package collection, which does not contain the bscanspi bitstreams necessary to flash ARTIQ boards. -With MSYS2, install ``openocd`` as follows:: +With MSYS2, install ``openocd`` and ``bscan-spi-bitstreams`` as follows:: - $ pacman -S mingw-w64-x86_64-openocd - -and manually copy the bscanspi bitstreams where OpenOCD will find them. + pacman -S mingw-w64-x86_64-openocd mingw-w64-x86_64-bscan-spi-bitstreams .. _configuring-openocd: @@ -289,3 +291,13 @@ Other options include: - ``ext0_bypass_125`` and ``ext0_bypass_100`` - explicit aliases for ``ext0_bypass``. Availability of these options depends on the board and their configuration - specific setting may or may not be supported. + +* Setup resolving RTIO channels to their names + +This feature allows you to print the channels' respective names alongside with their numbers in RTIO error messages. To enable it, run the ``artiq_rtiomap`` tool and write its result into the device config at the ``device_map`` key: :: + + $ artiq_rtiomap dev_map.bin + $ artiq_coremgmt config write -f device_map dev_map.bin + +.. note:: You can find more information about how to use the ``artiq_rtiomap`` utility on the :ref:`Utilities ` page. + diff --git a/doc/manual/introduction.rst b/doc/manual/introduction.rst index d43453088..2f3f23a0e 100644 --- a/doc/manual/introduction.rst +++ b/doc/manual/introduction.rst @@ -27,4 +27,4 @@ Website: https://m-labs.hk/artiq `Cite ARTIQ `_ as ``Bourdeauducq, Sébastien et al. (2016). ARTIQ 1.0. Zenodo. 10.5281/zenodo.51303``. -Copyright (C) 2014-2022 M-Labs Limited. Licensed under GNU LGPL version 3+. +Copyright (C) 2014-2023 M-Labs Limited. Licensed under GNU LGPL version 3+. diff --git a/doc/manual/utilities.rst b/doc/manual/utilities.rst index 11745dc34..d7642113d 100644 --- a/doc/manual/utilities.rst +++ b/doc/manual/utilities.rst @@ -116,6 +116,15 @@ Moninj proxy :ref: artiq.frontend.aqctl_moninj_proxy.get_argparser :prog: aqctl_moninj_proxy +.. _rtiomap-tool: + +RTIO channel name map tool +-------------------------- + +.. argparse:: + :ref: artiq.frontend.artiq_rtiomap.get_argparser + :prog: artiq_rtiomap + .. _core-device-rtio-analyzer-tool: diff --git a/doc/wrpll_diagram.png b/doc/wrpll_diagram.png deleted file mode 100644 index 1085e0b15..000000000 Binary files a/doc/wrpll_diagram.png and /dev/null differ diff --git a/experimental-features/suservo_coherent.diff b/experimental-features/suservo_coherent.diff index 74a528795..12e222729 100644 --- a/experimental-features/suservo_coherent.diff +++ b/experimental-features/suservo_coherent.diff @@ -104,15 +104,15 @@ index 1d0a72dad1..f7b516a4e7 100644 :param core_device: Core device name """ kernel_invariants = {"channel", "core", "pgia", "cplds", "ddses", -- "ref_period_mu"} +- "ref_period_mu", "corrected_fs"} + "ref_period_mu", "num_channels", "coeff_sel", + "state_sel", "io_dly_addr", "config_addr", -+ "write_enable"} ++ "corrected_fs", "write_enable"} def __init__(self, dmgr, channel, pgia_device, cpld_devices, dds_devices, -@@ -83,9 +85,20 @@ def __init__(self, dmgr, channel, pgia_device, - self.core.coarse_ref_period) +@@ -83,13 +85,24 @@ def __init__(self, dmgr, channel, pgia_device, + self.corrected_fs = sampler.Sampler.use_corrected_fs(sampler_hw_rev) assert self.ref_period_mu == self.core.ref_multiplier + # The width of parts of the servo memory address depends on the number @@ -126,6 +126,10 @@ index 1d0a72dad1..f7b516a4e7 100644 + self.coeff_sel = 1 << coeff_depth + self.write_enable = 1 << (coeff_depth + 1) + + @staticmethod + def get_rtio_channels(channel, **kwargs): + return [(channel, None)] + @kernel def init(self): - """Initialize the servo, Sampler and both Urukuls. @@ -191,9 +195,9 @@ index 1d0a72dad1..f7b516a4e7 100644 @kernel def set_pgia_mu(self, channel, gain): -@@ -236,6 +262,18 @@ def get_adc(self, channel): +@@ -242,6 +268,18 @@ def get_adc(self, channel): gain = (self.gains >> (channel*2)) & 0b11 - return adc_mu_to_volts(val, gain) + return adc_mu_to_volts(val, gain, self.corrected_fs) + @kernel + def set_io_update_delays(self, dlys): @@ -211,7 +215,7 @@ index 1d0a72dad1..f7b516a4e7 100644 class Channel: """Sampler-Urukul Servo channel @@ -256,7 +294,7 @@ def __init__(self, dmgr, channel, servo_device): - self.dds = self.servo.ddses[self.servo_channel // 4] + return [(channel, None)] @kernel - def set(self, en_out, en_iir=0, profile=0): diff --git a/experimental-features/suservo_coherent_after_var.diff b/experimental-features/suservo_coherent_after_var.diff new file mode 100644 index 000000000..a1b262ebb --- /dev/null +++ b/experimental-features/suservo_coherent_after_var.diff @@ -0,0 +1,1813 @@ +diff --git a/artiq/coredevice/ad9910.py b/artiq/coredevice/ad9910.py +index 801b689c..bc19afe2 100644 +--- a/artiq/coredevice/ad9910.py ++++ b/artiq/coredevice/ad9910.py +@@ -277,6 +277,10 @@ class AD9910: + + :param addr: Register address + """ ++ return self.read32_impl(addr) ++ ++ @kernel ++ def read32_impl(self, addr): + self.bus.set_config_mu(urukul.SPI_CONFIG, 8, + urukul.SPIT_DDS_WR, self.chip_select) + self.bus.write((addr | 0x80) << 24) +@@ -981,7 +985,8 @@ class AD9910: + + @kernel + def tune_sync_delay(self, +- search_seed: TInt32 = 15) -> TTuple([TInt32, TInt32]): ++ search_seed: TInt32 = 15, ++ cpld_channel_idx: TInt32 = -1) -> TTuple([TInt32, TInt32]): + """Find a stable SYNC_IN delay. + + This method first locates a valid SYNC_IN delay at zero validation +@@ -997,6 +1002,9 @@ class AD9910: + Defaults to 15 (half range). + :return: Tuple of optimal delay and window size. + """ ++ if cpld_channel_idx == -1: ++ cpld_channel_idx = self.chip_select - 4 ++ assert 0 <= cpld_channel_idx < 4, "Invalid channel index" + if not self.cpld.sync_div: + raise ValueError("parent cpld does not drive SYNC") + search_span = 31 +@@ -1019,7 +1027,7 @@ class AD9910: + delay(100 * us) + err = urukul_sta_smp_err(self.cpld.sta_read()) + delay(100 * us) # slack +- if not (err >> (self.chip_select - 4)) & 1: ++ if not (err >> cpld_channel_idx) & 1: + next_seed = in_delay + break + if next_seed >= 0: # valid delay found, scan next window +diff --git a/artiq/coredevice/suservo.py b/artiq/coredevice/suservo.py +index a89cdcca..f7b516a4 100644 +--- a/artiq/coredevice/suservo.py ++++ b/artiq/coredevice/suservo.py +@@ -1,9 +1,11 @@ + from artiq.language.core import kernel, delay, delay_mu, portable + from artiq.language.units import us, ns ++from artiq.language import * + from artiq.coredevice.rtio import rtio_output, rtio_input_data + from artiq.coredevice import spi2 as spi +-from artiq.coredevice import urukul, sampler ++from artiq.coredevice import urukul, sampler, ad9910 + from math import ceil, log2 ++from numpy import int32, int64 + + + COEFF_WIDTH = 18 # Must match gateware IIRWidths.coeff +@@ -11,6 +13,7 @@ Y_FULL_SCALE_MU = (1 << (COEFF_WIDTH - 1)) - 1 + T_CYCLE = (2*(8 + 64) + 2)*8*ns # Must match gateware Servo.t_cycle. + COEFF_SHIFT = 11 # Must match gateware IIRWidths.shift + PROFILE_WIDTH = 5 # Must match gateware IIRWidths.profile ++FINE_TS_WIDTH = 3 # Must match gateware IIRWidths.ioup_dly + + + @portable +@@ -39,7 +42,7 @@ class SUServo: + and a photodetector connected to Sampler. + + Additionally SU Servo supports multiple preconfigured profiles per channel +- and features like automatic integrator hold. ++ and features like automatic integrator hold and coherent phase tracking. + + Notes: + +@@ -63,7 +66,8 @@ class SUServo: + """ + kernel_invariants = {"channel", "core", "pgia", "cplds", "ddses", + "ref_period_mu", "num_channels", "coeff_sel", +- "corrected_fs", "state_sel", "config_addr", "write_enable"} ++ "state_sel", "io_dly_addr", "config_addr", ++ "corrected_fs", "write_enable"} + + def __init__(self, dmgr, channel, pgia_device, + cpld_devices, dds_devices, +@@ -86,6 +90,7 @@ class SUServo: + self.num_channels = 4 * len(dds_devices) + channel_width = ceil(log2(self.num_channels)) + coeff_depth = PROFILE_WIDTH + channel_width + 3 ++ self.io_dly_addr = 1 << (coeff_depth - 2) + self.state_sel = 2 << (coeff_depth - 2) + self.config_addr = 3 << (coeff_depth - 2) + self.coeff_sel = 1 << coeff_depth +@@ -119,8 +124,20 @@ class SUServo: + prev_cpld_cfg = cpld.cfg_reg + cpld.cfg_write(prev_cpld_cfg | (0xf << urukul.CFG_MASK_NU)) + dds.init(blind=True) ++ ++ if dds.sync_data.sync_delay_seed != -1: ++ for channel_idx in range(4): ++ mask_nu_this = 1 << (urukul.CFG_MASK_NU + channel_idx) ++ cpld.cfg_write(prev_cpld_cfg | mask_nu_this) ++ delay(8 * us) ++ dds.tune_sync_delay(dds.sync_data.sync_delay_seed, ++ cpld_channel_idx=channel_idx) ++ delay(50 * us) + cpld.cfg_write(prev_cpld_cfg) + ++ self.set_io_update_delays( ++ [dds.sync_data.io_update_delay for dds in self.ddses]) ++ + @kernel + def write(self, addr, value): + """Write to servo memory. +@@ -245,6 +262,18 @@ class SUServo: + gain = (self.gains >> (channel*2)) & 0b11 + return adc_mu_to_volts(val, gain, self.corrected_fs) + ++ @kernel ++ def set_io_update_delays(self, dlys): ++ """Set IO_UPDATE pulse alignment delays. ++ ++ :param dlys: List of delays for each Urukul ++ """ ++ bits = 0 ++ mask_fine_ts = (1 << FINE_TS_WIDTH) - 1 ++ for i in range(len(dlys)): ++ bits |= (dlys[i] & mask_fine_ts) << (FINE_TS_WIDTH * i) ++ self.write(self.io_dly_addr, bits) ++ + + class Channel: + """Sampler-Urukul Servo channel +@@ -265,7 +294,7 @@ class Channel: + return [(channel, None)] + + @kernel +- def set(self, en_out, en_iir=0, profile=0): ++ def set(self, en_out, en_iir=0, profile=0, en_pt=0): + """Operate channel. + + This method does not advance the timeline. Output RF switch setting +@@ -279,9 +308,26 @@ class Channel: + :param en_out: RF switch enable + :param en_iir: IIR updates enable + :param profile: Active profile (0-31) ++ :param en_pt: Coherent phase tracking enable ++ * en_pt=1: "coherent phase mode" ++ * en_pt=0: "continuous phase mode" ++ (see :func:`artiq.coredevice.ad9910.AD9910.set_phase_mode` for a ++ definition of the phase modes) + """ + rtio_output(self.channel << 8, +- en_out | (en_iir << 1) | (profile << 2)) ++ en_out | (en_iir << 1) | (en_pt << 2) | (profile << 3)) ++ ++ @kernel ++ def set_reference_time(self): ++ """Set reference time for "coherent phase mode" (see :meth:`set`). ++ ++ This method does not advance the timeline. ++ With en_pt=1 (see :meth:`set`), the tracked DDS output phase of ++ this channel will refer to the current timeline position. ++ ++ """ ++ fine_ts = now_mu() & ((1 << FINE_TS_WIDTH) - 1) ++ rtio_output(self.channel << 8 | 1, self.dds.sysclk_per_mu * fine_ts) + + @kernel + def set_dds_mu(self, profile, ftw, offs, pow_=0): +@@ -592,3 +638,217 @@ class Channel: + raise ValueError("Invalid SUServo y-value!") + self.set_y_mu(profile, y_mu) + return y_mu ++ ++ ++class CPLD(urukul.CPLD): ++ """ ++ This module contains a subclass of the Urukul driver class in artiq.coredevice ++ adapted to use CPLD read-back via half-duplex SPI. Only the 8 LSBs can be read ++ back as the read-back buffer on the CPLD is 8 bits wide. ++ """ ++ ++ def __init__(self, dmgr, spi_device, io_update_device=None, ++ **kwargs): ++ # Separate IO_UPDATE TTL output device used by SUServo core, ++ # if active, else by artiq.coredevice.suservo.AD9910 ++ # :meth:`measure_io_update_alignment`. ++ # The urukul.CPLD driver utilises the CPLD CFG register ++ # option instead for pulsing IO_UPDATE of masked DDSs. ++ self.io_update_ttl = dmgr.get(io_update_device) ++ urukul.CPLD.__init__(self, dmgr, spi_device, **kwargs) ++ ++ @kernel ++ def enable_readback(self): ++ """ ++ This method sets the RB_EN flag in the Urukul CPLD configuration ++ register. Once set, the CPLD expects an alternating sequence of ++ two SPI transactions: ++ ++ * 1: Any transaction. If returning data, the 8 LSBs ++ of that will be stored in the CPLD. ++ ++ * 2: One read transaction in half-duplex SPI mode shifting ++ out data from the CPLD over MOSI (use :meth:`readback`). ++ ++ To end this protocol, call :meth:`disable_readback` during step 1. ++ """ ++ self.cfg_write(self.cfg_reg | (1 << urukul.CFG_RB_EN)) ++ ++ @kernel ++ def disable_readback(self): ++ """ ++ This method clears the RB_EN flag in the Urukul CPLD configuration ++ register. This marks the end of the readback protocol (see ++ :meth:`enable_readback`). ++ """ ++ self.cfg_write(self.cfg_reg & ~(1 << urukul.CFG_RB_EN)) ++ ++ @kernel ++ def sta_read(self, full=False): ++ """ ++ Read from status register ++ ++ :param full: retrieve status register by concatenating data from ++ several readback transactions. ++ """ ++ self.enable_readback() ++ self.sta_read_impl() ++ delay(16 * us) # slack ++ r = self.readback() << urukul.STA_RF_SW ++ delay(16 * us) # slack ++ if full: ++ self.enable_readback() # dummy write ++ r |= self.readback(urukul.CS_RB_PLL_LOCK) << urukul.STA_PLL_LOCK ++ delay(16 * us) # slack ++ self.enable_readback() # dummy write ++ r |= self.readback(urukul.CS_RB_PROTO_REV) << urukul.STA_PROTO_REV ++ delay(16 * us) # slack ++ self.disable_readback() ++ return r ++ ++ @kernel ++ def proto_rev_read(self): ++ """Read 8 LSBs of proto_rev""" ++ self.enable_readback() ++ self.enable_readback() # dummy write ++ r = self.readback(urukul.CS_RB_PROTO_REV) ++ self.disable_readback() ++ return r ++ ++ @kernel ++ def pll_lock_read(self): ++ """Read PLL lock status""" ++ self.enable_readback() ++ self.enable_readback() # dummy write ++ r = self.readback(urukul.CS_RB_PLL_LOCK) ++ self.disable_readback() ++ return r & 0xf ++ ++ @kernel ++ def get_att_mu(self): ++ # Different behaviour to urukul.CPLD.get_att_mu: Here, the ++ # latch enable of the attenuators activates 31.5dB ++ # attenuation during the transactions. ++ att_reg = int32(0) ++ self.enable_readback() ++ for i in range(4): ++ self.core.break_realtime() ++ self.bus.set_config_mu(urukul.SPI_CONFIG | spi.SPI_END, 8, ++ urukul.SPIT_ATT_RD, urukul.CS_ATT) ++ self.bus.write(0) # shift in zeros, shift out next 8 bits ++ r = self.readback() & 0xff ++ att_reg |= r << (8 * i) ++ ++ delay(16 * us) # slack ++ self.disable_readback() ++ ++ self.att_reg = int32(att_reg) ++ delay(8 * us) # slack ++ self.set_all_att_mu(self.att_reg) # shift and latch current value again ++ return self.att_reg ++ ++ @kernel ++ def readback(self, cs=urukul.CS_RB_LSBS): ++ """Read from the readback register in half-duplex SPI mode ++ See :meth:`enable_readback` for usage instructions. ++ ++ :param cs: Select data to be returned from the readback register. ++ - urukul.CS_RB_LSBS does not modify the readback register upon readback ++ - urukul.CS_RB_PROTO_REV loads the 8 LSBs of proto_rev ++ - urukul.CS_PLL_LOCK loads the PLL lock status bits concatenated with the ++ IFC mode bits ++ :return: CPLD readback register. ++ """ ++ self.bus.set_config_mu( ++ urukul.SPI_CONFIG | spi.SPI_END | spi.SPI_INPUT | spi.SPI_HALF_DUPLEX, ++ 8, urukul.SPIT_CFG_RD, cs) ++ self.bus.write(0) ++ return int32(self.bus.read()) ++ ++ ++class AD9910(ad9910.AD9910): ++ """ ++ This module contains a subclass of the AD9910 driver class in artiq.coredevice ++ using CPLD read-back via half-duplex SPI. ++ """ ++ ++ # Re-declare set of kernel invariants to avoid warning about non-existent ++ # `sw` attribute, as the AD9910 (instance) constructor writes to the ++ # class attributes. ++ kernel_invariants = { ++ "chip_select", "cpld", "core", "bus", "ftw_per_hz", "sysclk_per_mu" ++ } ++ ++ @kernel ++ def read32(self, addr): ++ """ Read from a 32-bit register ++ ++ This method returns only the 8 LSBs of the return value. ++ """ ++ self.cpld.enable_readback() ++ self.read32_impl(addr) ++ delay(12 * us) # slack ++ r = self.cpld.readback() ++ delay(12 * us) # slack ++ self.cpld.disable_readback() ++ return r ++ ++ @kernel ++ def read64(self, addr): ++ # 3-wire SPI transactions consisting of multiple transfers are not supported. ++ raise NotImplementedError ++ ++ @kernel ++ def read_ram(self, data): ++ # 3-wire SPI transactions consisting of multiple transfers are not supported. ++ raise NotImplementedError ++ ++ @kernel ++ def measure_io_update_alignment(self, delay_start, delay_stop): ++ """Use the digital ramp generator to locate the alignment between ++ IO_UPDATE and SYNC_CLK. ++ ++ Refer to `artiq.coredevice.ad9910` :meth:`measure_io_update_alignment`. ++ In order that this method can operate the io_update_ttl also used by the SUServo ++ core, deactivate the servo before (see :meth:`set_config`). ++ """ ++ # set up DRG ++ self.set_cfr1(drg_load_lrr=1, drg_autoclear=1) ++ # DRG -> FTW, DRG enable ++ self.set_cfr2(drg_enable=1) ++ # no limits ++ self.write64(ad9910._AD9910_REG_RAMP_LIMIT, -1, 0) ++ # DRCTL=0, dt=1 t_SYNC_CLK ++ self.write32(ad9910._AD9910_REG_RAMP_RATE, 0x00010000) ++ # dFTW = 1, (work around negative slope) ++ self.write64(ad9910._AD9910_REG_RAMP_STEP, -1, 0) ++ # un-mask DDS ++ cfg_masked = self.cpld.cfg_reg ++ self.cpld.cfg_write(cfg_masked & ~(0xf << urukul.CFG_MASK_NU)) ++ delay(70 * us) # slack ++ # delay io_update after RTIO edge ++ t = now_mu() + 8 & ~7 ++ at_mu(t + delay_start) ++ # assumes a maximum t_SYNC_CLK period ++ self.cpld.io_update_ttl.pulse(self.core.mu_to_seconds(16 - delay_start)) # realign ++ # re-mask DDS ++ self.cpld.cfg_write(cfg_masked) ++ delay(10 * us) # slack ++ # disable DRG autoclear and LRR on io_update ++ self.set_cfr1() ++ delay(10 * us) # slack ++ # stop DRG ++ self.write64(ad9910._AD9910_REG_RAMP_STEP, 0, 0) ++ delay(10 * us) # slack ++ # un-mask DDS ++ self.cpld.cfg_write(cfg_masked & ~(0xf << urukul.CFG_MASK_NU)) ++ at_mu(t + 0x20000 + delay_stop) ++ self.cpld.io_update_ttl.pulse_mu(16 - delay_stop) # realign ++ # re-mask DDS ++ self.cpld.cfg_write(cfg_masked) ++ ftw = self.read32(ad9910._AD9910_REG_FTW) # read out effective FTW ++ delay(100 * us) # slack ++ # disable DRG ++ self.set_cfr2(drg_enable=0) ++ self.cpld.io_update.pulse_mu(16) ++ return ftw & 1 +diff --git a/artiq/coredevice/urukul.py b/artiq/coredevice/urukul.py +index 2fd66bd6..61fd4762 100644 +--- a/artiq/coredevice/urukul.py ++++ b/artiq/coredevice/urukul.py +@@ -24,6 +24,7 @@ SPIT_DDS_RD = 16 + CFG_RF_SW = 0 + CFG_LED = 4 + CFG_PROFILE = 8 ++CFG_RB_EN = 11 + CFG_IO_UPDATE = 12 + CFG_MASK_NU = 13 + CFG_CLK_SEL0 = 17 +@@ -51,18 +52,23 @@ CS_DDS_CH0 = 4 + CS_DDS_CH1 = 5 + CS_DDS_CH2 = 6 + CS_DDS_CH3 = 7 ++# chip selects for readback ++CS_RB_PROTO_REV = 1 ++CS_RB_PLL_LOCK = 2 ++CS_RB_LSBS = 3 + + # Default profile + DEFAULT_PROFILE = 7 + + + @portable +-def urukul_cfg(rf_sw, led, profile, io_update, mask_nu, ++def urukul_cfg(rf_sw, led, profile, rb_en, io_update, mask_nu, + clk_sel, sync_sel, rst, io_rst, clk_div): + """Build Urukul CPLD configuration register""" + return ((rf_sw << CFG_RF_SW) | + (led << CFG_LED) | + (profile << CFG_PROFILE) | ++ (rb_en << CFG_RB_EN) | + (io_update << CFG_IO_UPDATE) | + (mask_nu << CFG_MASK_NU) | + ((clk_sel & 0x01) << CFG_CLK_SEL0) | +@@ -191,7 +197,7 @@ class CPLD: + assert sync_div is None + sync_div = 0 + +- self.cfg_reg = urukul_cfg(rf_sw=rf_sw, led=0, profile=DEFAULT_PROFILE, ++ self.cfg_reg = urukul_cfg(rf_sw=rf_sw, led=0, profile=DEFAULT_PROFILE, rb_en=0, + io_update=0, mask_nu=0, clk_sel=clk_sel, + sync_sel=sync_sel, + rst=0, io_rst=0, clk_div=clk_div) +@@ -226,6 +232,10 @@ class CPLD: + + :return: The status register value. + """ ++ return self.sta_read_impl() ++ ++ @kernel ++ def sta_read_impl(self): + self.bus.set_config_mu(SPI_CONFIG | spi.SPI_END | spi.SPI_INPUT, 24, + SPIT_CFG_RD, CS_CFG) + self.bus.write(self.cfg_reg << 8) +diff --git a/artiq/examples/kasli_suservo/device_db.py b/artiq/examples/kasli_suservo/device_db.py +index c52b82a9..8e9d8752 100644 +--- a/artiq/examples/kasli_suservo/device_db.py ++++ b/artiq/examples/kasli_suservo/device_db.py +@@ -142,53 +142,66 @@ device_db = { + "arguments": {"channel": 15}, + }, + ++ "ttl_urukul0_io_update": { ++ "type": "local", ++ "module": "artiq.coredevice.ttl", ++ "class": "TTLOut", ++ "arguments": {"channel": 16} ++ }, ++ "ttl_urukul1_io_update": { ++ "type": "local", ++ "module": "artiq.coredevice.ttl", ++ "class": "TTLOut", ++ "arguments": {"channel": 17} ++ }, ++ + "suservo0_ch0": { + "type": "local", + "module": "artiq.coredevice.suservo", + "class": "Channel", +- "arguments": {"channel": 16, "servo_device": "suservo0"} ++ "arguments": {"channel": 18, "servo_device": "suservo0"} + }, + "suservo0_ch1": { + "type": "local", + "module": "artiq.coredevice.suservo", + "class": "Channel", +- "arguments": {"channel": 17, "servo_device": "suservo0"} ++ "arguments": {"channel": 19, "servo_device": "suservo0"} + }, + "suservo0_ch2": { + "type": "local", + "module": "artiq.coredevice.suservo", + "class": "Channel", +- "arguments": {"channel": 18, "servo_device": "suservo0"} ++ "arguments": {"channel": 20, "servo_device": "suservo0"} + }, + "suservo0_ch3": { + "type": "local", + "module": "artiq.coredevice.suservo", + "class": "Channel", +- "arguments": {"channel": 19, "servo_device": "suservo0"} ++ "arguments": {"channel": 21, "servo_device": "suservo0"} + }, + "suservo0_ch4": { + "type": "local", + "module": "artiq.coredevice.suservo", + "class": "Channel", +- "arguments": {"channel": 20, "servo_device": "suservo0"} ++ "arguments": {"channel": 22, "servo_device": "suservo0"} + }, + "suservo0_ch5": { + "type": "local", + "module": "artiq.coredevice.suservo", + "class": "Channel", +- "arguments": {"channel": 21, "servo_device": "suservo0"} ++ "arguments": {"channel": 23, "servo_device": "suservo0"} + }, + "suservo0_ch6": { + "type": "local", + "module": "artiq.coredevice.suservo", + "class": "Channel", +- "arguments": {"channel": 22, "servo_device": "suservo0"} ++ "arguments": {"channel": 24, "servo_device": "suservo0"} + }, + "suservo0_ch7": { + "type": "local", + "module": "artiq.coredevice.suservo", + "class": "Channel", +- "arguments": {"channel": 23, "servo_device": "suservo0"} ++ "arguments": {"channel": 25, "servo_device": "suservo0"} + }, + + "suservo0": { +@@ -196,7 +209,7 @@ device_db = { + "module": "artiq.coredevice.suservo", + "class": "SUServo", + "arguments": { +- "channel": 24, ++ "channel": 26, + "pgia_device": "spi_sampler0_pgia", + "cpld_devices": ["urukul0_cpld", "urukul1_cpld"], + "dds_devices": ["urukul0_dds", "urukul1_dds"], +@@ -207,33 +220,37 @@ device_db = { + "type": "local", + "module": "artiq.coredevice.spi2", + "class": "SPIMaster", +- "arguments": {"channel": 25} ++ "arguments": {"channel": 27} + }, + + "spi_urukul0": { + "type": "local", + "module": "artiq.coredevice.spi2", + "class": "SPIMaster", +- "arguments": {"channel": 26} ++ "arguments": {"channel": 28} + }, + "urukul0_cpld": { + "type": "local", +- "module": "artiq.coredevice.urukul", ++ "module": "artiq.coredevice.suservo", + "class": "CPLD", + "arguments": { + "spi_device": "spi_urukul0", ++ "io_update_device": "ttl_urukul0_io_update", ++ "sync_device": "clkgen_dds_sync_in", + "refclk": 100e6, + "clk_sel": 0 + } + }, + "urukul0_dds": { + "type": "local", +- "module": "artiq.coredevice.ad9910", ++ "module": "artiq.coredevice.suservo", + "class": "AD9910", + "arguments": { + "pll_n": 40, + "chip_select": 3, + "cpld_device": "urukul0_cpld", ++ "io_update_delay": 0, ++ "sync_delay_seed": -1, + } + }, + +@@ -241,26 +258,40 @@ device_db = { + "type": "local", + "module": "artiq.coredevice.spi2", + "class": "SPIMaster", +- "arguments": {"channel": 27} ++ "arguments": {"channel": 29} + }, + "urukul1_cpld": { + "type": "local", +- "module": "artiq.coredevice.urukul", ++ "module": "artiq.coredevice.suservo", + "class": "CPLD", + "arguments": { + "spi_device": "spi_urukul1", ++ "io_update_device": "ttl_urukul1_io_update", ++ "sync_device": "clkgen_dds_sync_in", + "refclk": 100e6, + "clk_sel": 0 + } + }, + "urukul1_dds": { + "type": "local", +- "module": "artiq.coredevice.ad9910", ++ "module": "artiq.coredevice.suservo", + "class": "AD9910", + "arguments": { + "pll_n": 40, + "chip_select": 3, + "cpld_device": "urukul1_cpld", ++ "io_update_delay": 0, ++ "sync_delay_seed": -1, ++ } ++ }, ++ ++ "clkgen_dds_sync_in": { ++ "type": "local", ++ "module": "artiq.coredevice.ttl", ++ "class": "TTLClockGen", ++ "arguments": { ++ "channel": 30, ++ "acc_width": 4 + } + }, + +diff --git a/artiq/frontend/artiq_ddb_template.py b/artiq/frontend/artiq_ddb_template.py +index 5459756f..75eaadcb 100755 +--- a/artiq/frontend/artiq_ddb_template.py ++++ b/artiq/frontend/artiq_ddb_template.py +@@ -424,6 +424,16 @@ class PeripheralManager: + sampler_name = self.get_name("sampler") + urukul_names = [self.get_name("urukul") for _ in range(2)] + channel = count(0) ++ for urukul_name in urukul_names: ++ self.gen(""" ++ device_db["ttl_{urukul_name}_io_update"] = {{ ++ "type": "local", ++ "module": "artiq.coredevice.ttl", ++ "class": "TTLOut", ++ "arguments": {{"channel": 0x{ttl_channel:06x}}} ++ }}""", ++ urukul_name=urukul_name, ++ ttl_channel=rtio_offset+next(channel)) + for i in range(8): + self.gen(""" + device_db["{suservo_name}_ch{suservo_chn}"] = {{ +@@ -472,17 +482,19 @@ class PeripheralManager: + }} + device_db["{urukul_name}_cpld"] = {{ + "type": "local", +- "module": "artiq.coredevice.urukul", ++ "module": "artiq.coredevice.suservo", + "class": "CPLD", + "arguments": {{ + "spi_device": "spi_{urukul_name}", ++ "io_update_device": "ttl_{urukul_name}_io_update", ++ "sync_device": "clkgen_{suservo_name}_dds_sync_in", + "refclk": {refclk}, + "clk_sel": {clk_sel} + }} + }} + device_db["{urukul_name}_dds"] = {{ + "type": "local", +- "module": "artiq.coredevice.ad9910", ++ "module": "artiq.coredevice.suservo", + "class": "AD9910", + "arguments": {{ + "pll_n": {pll_n}, +@@ -490,12 +502,25 @@ class PeripheralManager: + "cpld_device": "{urukul_name}_cpld"{pll_vco} + }} + }}""", ++ suservo_name=suservo_name, + urukul_name=urukul_name, + urukul_channel=rtio_offset+next(channel), + refclk=peripheral.get("refclk", self.master_description["rtio_frequency"]), + clk_sel=peripheral["clk_sel"], + pll_vco=",\n \"pll_vco\": {}".format(pll_vco) if pll_vco is not None else "", + pll_n=peripheral["pll_n"]) ++ self.gen(""" ++ device_db["clkgen_{suservo_name}_dds_sync_in"] = {{ ++ "type": "local", ++ "module": "artiq.coredevice.ttl", ++ "class": "TTLClockGen", ++ "arguments": {{ ++ "channel": 0x{clkgen_channel:06x}, ++ "acc_width": 4 ++ }} ++ }}""", ++ suservo_name=suservo_name, ++ clkgen_channel=rtio_offset+next(channel)) + return next(channel) + + def process_zotino(self, rtio_offset, peripheral): +diff --git a/artiq/gateware/eem.py b/artiq/gateware/eem.py +index ce00f94f..93d01c07 100644 +--- a/artiq/gateware/eem.py ++++ b/artiq/gateware/eem.py +@@ -6,6 +6,7 @@ from artiq.gateware import rtio + from artiq.gateware.rtio.phy import spi2, ad53xx_monitor, dds, grabber + from artiq.gateware.suservo import servo, pads as servo_pads + from artiq.gateware.rtio.phy import servo as rtservo, fastino, phaser ++from artiq.gateware.rtio.phy import ttl_simple + + + def _eem_signal(i): +@@ -545,7 +546,8 @@ class SUServo(_EEM): + @classmethod + def add_std(cls, target, eems_sampler, eems_urukul, + t_rtt=4, clk=1, shift=11, profile=5, +- iostandard=default_iostandard): ++ sync_gen_cls=ttl_simple.ClockGen, ++ iostandard=default_iostandard, sysclk_per_clk=8): + """Add a 8-channel Sampler-Urukul Servo + + :param t_rtt: upper estimate for clock round-trip propagation time from +@@ -561,6 +563,8 @@ class SUServo(_EEM): + (default: 11) + :param profile: log2 of the number of profiles for each DDS channel + (default: 5) ++ :param sysclk_per_clk: DDS "sysclk" (4*refclk = 1GHz typ.) cycles per ++ FPGA "sys" clock (125MHz typ.) cycles (default: 8) + """ + cls.add_extension( + target, *(eems_sampler + sum(eems_urukul, [])), +@@ -572,6 +576,8 @@ class SUServo(_EEM): + urukul_pads = servo_pads.UrukulPads( + target.platform, *eem_urukul) + target.submodules += sampler_pads, urukul_pads ++ target.rtio_channels.extend( ++ rtio.Channel.from_phy(phy) for phy in urukul_pads.io_update_phys) + # timings in units of RTIO coarse period + adc_p = servo.ADCParams(width=16, channels=8, lanes=4, t_cnvh=4, + # account for SCK DDR to CONV latency +@@ -579,19 +585,20 @@ class SUServo(_EEM): + t_conv=57 - 4, t_rtt=t_rtt + 4) + iir_p = servo.IIRWidths(state=25, coeff=18, adc=16, asf=14, word=16, + accu=48, shift=shift, profile=profile, dly=8) +- dds_p = servo.DDSParams(width=8 + 32 + 16 + 16, +- channels=4 * len(eem_urukul), clk=clk) ++ dds_p = servo.DDSParams(width=8 + 32 + 16 + 16, sysclk_per_clk=sysclk_per_clk, ++ channels=4*len(eem_urukul), clk=clk) + su = servo.Servo(sampler_pads, urukul_pads, adc_p, iir_p, dds_p) + su = ClockDomainsRenamer("rio_phy")(su) + # explicitly name the servo submodule to enable the migen namer to derive + # a name for the adc return clock domain + setattr(target.submodules, "suservo_eem{}".format(eems_sampler[0]), su) + +- ctrls = [rtservo.RTServoCtrl(ctrl) for ctrl in su.iir.ctrl] ++ ctrls = [rtservo.RTServoCtrl(ctrl, ctrl_reftime) ++ for ctrl, ctrl_reftime in zip(su.iir.ctrl, su.iir.ctrl_reftime)] + target.submodules += ctrls + target.rtio_channels.extend( + rtio.Channel.from_phy(ctrl) for ctrl in ctrls) +- mem = rtservo.RTServoMem(iir_p, su) ++ mem = rtservo.RTServoMem(iir_p, su, urukul_pads.io_update_phys) + target.submodules += mem + target.rtio_channels.append(rtio.Channel.from_phy(mem, ififo_depth=4)) + +@@ -601,19 +608,20 @@ class SUServo(_EEM): + target.submodules += phy + target.rtio_channels.append(rtio.Channel.from_phy(phy, ififo_depth=4)) + +- dds_sync = Signal(reset=0) +- for j, eem_urukuli in enumerate(eem_urukul): +- # connect quad-SPI ++ for eem_urukuli in eem_urukul: + spi_p, spi_n = ( + target.platform.request("{}_spi_p".format(eem_urukuli)), + target.platform.request("{}_spi_n".format(eem_urukuli))) + phy = spi2.SPIMaster(spi_p, spi_n) + target.submodules += phy + target.rtio_channels.append(rtio.Channel.from_phy(phy, ififo_depth=4)) +- # connect `reset_sync_in` +- pads = target.platform.request("{}_dds_reset_sync_in".format(eem_urukuli)) +- target.specials += DifferentialOutput(dds_sync, pads.p, pads.n) +- # connect RF switches ++ ++ if sync_gen_cls is not None: # AD9910 variant and SYNC_IN from EEM ++ phy = sync_gen_cls(urukul_pads.dds_reset_sync_in, ftw_width=4) ++ target.submodules += phy ++ target.rtio_channels.append(rtio.Channel.from_phy(phy)) ++ ++ for j, eem_urukuli in enumerate(eem_urukul): + for i, signal in enumerate("sw0 sw1 sw2 sw3".split()): + pads = target.platform.request("{}_{}".format(eem_urukuli, signal)) + target.specials += DifferentialOutput( +diff --git a/artiq/gateware/rtio/phy/servo.py b/artiq/gateware/rtio/phy/servo.py +index 379e7ba3..246208c8 100644 +--- a/artiq/gateware/rtio/phy/servo.py ++++ b/artiq/gateware/rtio/phy/servo.py +@@ -1,25 +1,32 @@ + from migen import * +- + from artiq.gateware.rtio import rtlink + + + class RTServoCtrl(Module): + """Per channel RTIO control interface""" +- def __init__(self, ctrl): ++ def __init__(self, ctrl, ctrl_reftime): + self.rtlink = rtlink.Interface( +- rtlink.OInterface(len(ctrl.profile) + 2)) ++ rtlink.OInterface( ++ data_width=max(len(ctrl.profile) + 3, ++ len(ctrl_reftime.sysclks_fine)), ++ address_width=1) ++ ) + + # # # + ++ sel_ref = self.rtlink.o.address[0] + self.comb += [ +- ctrl.stb.eq(self.rtlink.o.stb), +- self.rtlink.o.busy.eq(0) ++ ctrl.stb.eq(self.rtlink.o.stb & ~sel_ref), ++ self.rtlink.o.busy.eq(0), ++ ctrl_reftime.stb.eq(self.rtlink.o.stb & sel_ref), + ] ++ ctrl_cases = { ++ 0: Cat(ctrl.en_out, ctrl.en_iir, ctrl.en_pt, ctrl.profile).eq( ++ self.rtlink.o.data), ++ 1: ctrl_reftime.sysclks_fine.eq(self.rtlink.o.data), ++ } + self.sync.rio_phy += [ +- If(self.rtlink.o.stb, +- Cat(ctrl.en_out, ctrl.en_iir, ctrl.profile).eq( +- self.rtlink.o.data) +- ) ++ If(self.rtlink.o.stb, Case(self.rtlink.o.address, ctrl_cases)) + ] + + +@@ -53,7 +60,7 @@ class RTServoMem(Module): + destination | sel | sel_coeff | + ----------------|-------|--------------| + IIR coeff mem | - | 1 | +- Reserved | 1 | 0 | ++ DDS delay mem | 1 | 0 | + IIR state mem | 2 | 0 | + config (write) | 3 | 0 | + status (read) | 3 | 0 | +@@ -72,7 +79,7 @@ class RTServoMem(Module): + (instead of having to decide whether to sign- or zero-extend per address), as + all unsigned values are less wide than w.coeff. + """ +- def __init__(self, w, servo): ++ def __init__(self, w, servo, io_update_phys): + m_coeff = servo.iir.m_coeff.get_port(write_capable=True, + mode=READ_FIRST, + we_granularity=w.coeff, clock_domain="rio") +@@ -110,7 +117,7 @@ class RTServoMem(Module): + # # # + + config = Signal(w.coeff, reset=0) +- status = Signal(8 + len(servo.iir.ctrl)) ++ status = Signal(len(self.rtlink.i.data)) + pad = Signal(6) + assert len(status) <= len(self.rtlink.i.data) + self.comb += [ +@@ -124,7 +131,7 @@ class RTServoMem(Module): + 1 + # sel_coeff + 1 + # high_coeff + len(m_coeff.adr)) +- # ensure that we can fit config/status into the state address space ++ # ensure that we can fit config/io_dly/status into the state address space + assert len(self.rtlink.o.address) + len(self.rtlink.o.data) - w.coeff >= ( + 1 + # we + 1 + # sel_coeff +@@ -172,6 +179,11 @@ class RTServoMem(Module): + read_high.eq(high_coeff), + ) + ] ++ ++ # I/O update alignment delays ++ ioup_dlys = Cat(*[phy.fine_ts for phy in io_update_phys]) ++ assert w.coeff >= len(ioup_dlys) ++ + self.sync.rio_phy += [ + If(self.rtlink.o.stb & we & (sel == 3), + config.eq(self.rtlink.o.data) +@@ -179,11 +191,15 @@ class RTServoMem(Module): + If(read & (read_sel == 3), + [_.clip.eq(0) for _ in servo.iir.ctrl] + ), ++ If(self.rtlink.o.stb & we & (sel == 1), ++ ioup_dlys.eq(self.rtlink.o.data) ++ ), + ] ++ + # read return value by destination + read_acts = Array([ + Mux(read_high, m_coeff.dat_r[w.coeff:], m_coeff.dat_r[:w.coeff]), +- 0, ++ ioup_dlys, + m_state.dat_r[w.state - w.coeff:], + status + ]) +diff --git a/artiq/gateware/suservo/dds_ser.py b/artiq/gateware/suservo/dds_ser.py +index 38d1f6d9..cdccfcc9 100644 +--- a/artiq/gateware/suservo/dds_ser.py ++++ b/artiq/gateware/suservo/dds_ser.py +@@ -1,4 +1,5 @@ + import logging ++from collections import namedtuple + + from migen import * + +@@ -6,11 +7,11 @@ from artiq.coredevice.urukul import DEFAULT_PROFILE + + from . import spi + +- + logger = logging.getLogger(__name__) + +- +-DDSParams = spi.SPIParams ++DDSParams = namedtuple("DDSParams", spi.SPIParams._fields + ( ++ "sysclk_per_clk", # DDS_CLK per FPGA system clock ++)) + + + class DDS(spi.SPISimple): +diff --git a/artiq/gateware/suservo/iir.py b/artiq/gateware/suservo/iir.py +index 6b975b75..3fad77a6 100644 +--- a/artiq/gateware/suservo/iir.py ++++ b/artiq/gateware/suservo/iir.py +@@ -1,6 +1,7 @@ + from collections import namedtuple + import logging + from migen import * ++from migen.genlib.coding import Encoder + + logger = logging.getLogger(__name__) + +@@ -98,14 +99,14 @@ class IIR(Module): + This module implements a multi-channel IIR (infinite impulse response) + filter processor optimized for synthesis on FPGAs. + +- The module is parametrized by passing a ``IIRWidths()`` object which +- will be abbreviated W here. ++ The module is parametrized by passing a ``IIRWidths()`` object, and ++ two more objects which will be abbreviated W, W_O and W_I here. + +- It reads 1 << W.channels input channels (typically from an ADC) ++ It reads W_I.channels input channels (typically from an ADC) + and on each iteration processes the data using a first-order IIR filter. + At the end of the cycle each the output of the filter together with + additional data (typically frequency tunning word and phase offset word +- for a DDS) are presented at the 1 << W.channels outputs of the module. ++ for a DDS) are presented at the W_O.channels outputs of the module. + + Profile memory + ============== +@@ -144,10 +145,10 @@ class IIR(Module): + ------------- + + The state memory holds all Y1 values (IIR processor outputs) for all +- profiles of all channels in the lower half (1 << W.profile + W.channel +- addresses) and the pairs of old and new ADC input values X1, and X0, +- in the upper half (1 << W.channel addresses). Each memory location is +- W.state bits wide. ++ profiles of all channels in the lower half (1 << W.profile)*W_O.channels ++ addresses, and the pairs of old and new ADC input values X1, and X0, ++ in the upper half (W_I.channels addresses). ++ Each memory location is W.state bits wide. + + Real-time control + ================= +@@ -156,15 +157,16 @@ class IIR(Module): + + * The active profile, PROFILE + * Whether to perform IIR filter iterations, EN_IIR ++ * Whether to track the DDS phase coherently, EN_PT + * The RF switch state enabling output from the channel, EN_OUT + + Delayed IIR processing + ====================== + +- The IIR filter iterations on a given channel are only performed all of the +- following are true: ++ The IIR filter iterations on a given channel are only performed if all of ++ the following are true: + +- * PROFILE, EN_IIR, EN_OUT have not been updated in the within the ++ * PROFILE, EN_IIR, EN_OUT have not been updated within the + last DLY cycles + * EN_IIR is asserted + * EN_OUT is asserted +@@ -175,9 +177,8 @@ class IIR(Module): + Typical design at the DSP level. This does not include the description of + the pipelining or the overall latency involved. + +- IIRWidths(state=25, coeff=18, adc=16, +- asf=14, word=16, accu=48, shift=11, +- channel=3, profile=5, dly=8) ++ IIRWidths(state=25, coeff=18, adc=16, asf=14, ++ word=16, accu=48, shift=11, profile=5, dly=8) + + X0 = ADC * 2^(25 - 1 - 16) + X1 = X0 delayed by one cycle +@@ -212,13 +213,23 @@ class IIR(Module): + --/--: signal with a given bit width always includes a sign bit + -->--: flow is to the right and down unless otherwise indicated + """ +- def __init__(self, w, w_i, w_o): ++ def __init__(self, w, w_i, w_o, t_cycle): + for v in (w, w_i, w_o): + for i, j in enumerate(v): + assert j > 0, (i, j, v) + assert w.word <= w.coeff # same memory + assert w.state + w.coeff + 3 <= w.accu + ++ # Reference counter for coherent phase tracking (we assume this doesn't ++ # roll over – a good assumption, as the period is, for a typical clock ++ # frequency, 2^48 / 125 MHz = ~26 days). ++ self.t_running = Signal(48, reset_less=True) ++ ++ # If true, internal DDS phase tracking state is reset, matching DDS ++ # chips with phase cleared (and zero FTW) before the start of the ++ # iteration. Automatically reset at the end of the iteration. ++ self.reset_dds_phase = Signal() ++ + # m_coeff of active profiles should only be accessed externally during + # ~processing + self.specials.m_coeff = Memory( +@@ -235,9 +246,24 @@ class IIR(Module): + ("profile", w.profile), + ("en_out", 1), + ("en_iir", 1), ++ ("en_pt", 1), + ("clip", 1), + ("stb", 1)]) + for i in range(w_o.channels)] ++ # "Shadow copy" of phase accumulator in DDS accumulator for each output ++ # channel. ++ self.specials.m_accum_ftw = Memory( ++ width=2 * w.word, ++ depth=w_o.channels) ++ # ctrl_reftime should only be updated synchronously ++ self.ctrl_reftime = [Record([ ++ ("sysclks_fine", bits_for(w_o.sysclk_per_clk - 1)), ++ ("stb", 1)]) ++ for i in range(w_o.channels)] ++ # Reference time for each output channel. ++ self.specials.m_t_ref = Memory( ++ width=len(self.t_running), ++ depth=w_o.channels) + # only update during ~loading + self.adc = [Signal((w.adc, True), reset_less=True) + for i in range(w_i.channels)] +@@ -264,8 +290,15 @@ class IIR(Module): + profiles = Array([ch.profile for ch in self.ctrl]) + en_outs = Array([ch.en_out for ch in self.ctrl]) + en_iirs = Array([ch.en_iir for ch in self.ctrl]) ++ en_pts = Array([ch.en_pt for ch in self.ctrl]) + clips = Array([ch.clip for ch in self.ctrl]) + ++ # Sample of the reference counter at the start of the current iteration, ++ # such that a common reference time is used for phase calculations ++ # across all channels, in DDS sysclk units. ++ sysclks_to_iter_start = Signal( ++ len(self.t_running) + bits_for(w_o.sysclk_per_clk - 1)) ++ + # Main state machine sequencing the steps of each servo iteration. The + # module IDLEs until self.start is asserted, and then runs through LOAD, + # PROCESS and SHIFT in order (see description of corresponding flags +@@ -292,6 +325,7 @@ class IIR(Module): + self.done.eq(1), + t_current_step_clr.eq(1), + If(self.start, ++ NextValue(sysclks_to_iter_start, self.t_running * w_o.sysclk_per_clk), + NextState("LOAD") + ) + ) +@@ -310,6 +344,7 @@ class IIR(Module): + If(stages_active == 0, + t_current_step_clr.eq(1), + NextState("SHIFT"), ++ NextValue(self.reset_dds_phase, 0) + ) + ) + fsm.act("SHIFT", +@@ -479,25 +514,81 @@ class IIR(Module): + }), + ] + ++ # Update coarse reference time from t_running upon ctrl_reftime strobe ++ ref_stb_encoder = Encoder(w_o.channels) ++ m_t_ref_stb = self.m_t_ref.get_port(write_capable=True) ++ self.specials += m_t_ref_stb ++ self.submodules += ref_stb_encoder ++ self.comb += [ ++ ref_stb_encoder.i.eq(Cat([ch.stb for ch in self.ctrl_reftime])), ++ m_t_ref_stb.adr.eq(ref_stb_encoder.o), ++ m_t_ref_stb.we.eq(~ref_stb_encoder.n), ++ m_t_ref_stb.dat_w.eq(self.t_running), ++ ] ++ + # +- # Update DDS profile with FTW/POW/ASF +- # Stage 0 loads the POW, stage 1 the FTW, and stage 2 writes +- # the ASF computed by the IIR filter. ++ # Update DDS profile with FTW/POW/ASF (including phase tracking, if ++ # enabled). Stage 0 loads the POW, stage 1 the FTW, and stage 2 writes ++ # the ASF computed by the IIR filter (and adds any phase correction). + # + + # muxing + ddss = Array(self.dds) ++ sysclks_ref_fine = Array([ch.sysclks_fine for ch in self.ctrl_reftime]) ++ ++ # registered copy of FTW on channel[1] ++ current_ftw = Signal(2 * w.word, reset_less=True) ++ # target effective DDS phase (accumulator + POW) at the coming io_update ++ target_dds_phase = Signal.like(current_ftw) ++ # DDS-internal phase accumulated until the coming io_update ++ accum_dds_phase = Signal.like(current_ftw) ++ # correction to add to the bare POW to yield a phase-coherent DDS output ++ correcting_pow = Signal(w.word, reset_less=True) ++ # sum of all FTWs on channel[1], updated with current FTW during the ++ # calculation ++ accum_ftw = Signal.like(current_ftw) ++ # sum of previous FTWs on channel[1] (or 0 on phase coherence reference ++ # reset) ++ prev_accum_ftw = Signal.like(current_ftw) ++ # time since reference time at coming io_update in DDS sysclk units ++ sysclks_to_ref = Signal.like(sysclks_to_iter_start) ++ # t_ref in DDS sysclk units ++ sysclks_ref_to_iter_start = Signal.like(sysclks_to_iter_start) ++ ++ m_t_ref = self.m_t_ref.get_port() ++ m_accum_ftw = self.m_accum_ftw.get_port(write_capable=True, mode=READ_FIRST) ++ self.specials += m_accum_ftw, m_t_ref ++ prev_accum_ftw = Signal.like(accum_ftw) ++ self.comb += [ ++ prev_accum_ftw.eq(Mux(self.reset_dds_phase, 0, m_accum_ftw.dat_r)), ++ m_accum_ftw.adr.eq(channel[1]), ++ m_accum_ftw.we.eq((pipeline_phase == 3) & stages_active[1]), ++ m_accum_ftw.dat_w.eq(accum_ftw), ++ m_t_ref.adr.eq(channel[0]), ++ ] + ++ sysclks_per_iter = t_cycle * w_o.sysclk_per_clk + self.sync += [ + Case(pipeline_phase, { + 0: [ + If(stages_active[1], + ddss[channel[1]][:w.word].eq(m_coeff.dat_r), # ftw0 ++ current_ftw[:w.word].eq(m_coeff.dat_r), ++ sysclks_ref_to_iter_start.eq(m_t_ref.dat_r * w_o.sysclk_per_clk), ++ ), ++ If(stages_active[2] & en_pts[channel[2]], ++ # add pow correction if phase tracking enabled ++ ddss[channel[2]][2*w.word:3*w.word].eq( ++ ddss[channel[2]][2*w.word:3*w.word] + correcting_pow), + ), + ], + 1: [ + If(stages_active[1], + ddss[channel[1]][w.word:2 * w.word].eq(m_coeff.dat_r), # ftw1 ++ current_ftw[w.word:].eq(m_coeff.dat_r), ++ sysclks_to_ref.eq(sysclks_to_iter_start - ( ++ sysclks_ref_to_iter_start + sysclks_ref_fine[channel[1]])), ++ accum_dds_phase.eq(prev_accum_ftw * sysclks_per_iter), + ), + If(stages_active[2], + ddss[channel[2]][3*w.word:].eq( # asf +@@ -506,10 +597,21 @@ class IIR(Module): + ], + 2: [ + If(stages_active[0], +- ddss[channel[0]][2*w.word:3*w.word].eq(m_coeff.dat_r), # pow ++ # Load bare POW from profile memory. ++ ddss[channel[0]][2*w.word:3*w.word].eq(m_coeff.dat_r), ++ ), ++ If(stages_active[1], ++ target_dds_phase.eq(current_ftw * sysclks_to_ref), ++ accum_ftw.eq(prev_accum_ftw + current_ftw), + ), + ], + 3: [ ++ If(stages_active[1], ++ # Prepare most-significant word to add to POW from ++ # profile for phase tracking. ++ correcting_pow.eq( ++ (target_dds_phase - accum_dds_phase)[w.word:]), ++ ), + ], + }), + ] +@@ -518,6 +620,15 @@ class IIR(Module): + self.widths = w + self.widths_adc = w_i + self.widths_dds = w_o ++ self.t_cycle = t_cycle ++ self._state = t_current_step ++ self._stages = stages_active ++ self._dt_start = sysclks_to_iter_start ++ self._sysclks_to_ref = sysclks_to_ref ++ self._sysclks_ref_to_iter_start = sysclks_ref_to_iter_start ++ self._sysclks_ref_fine = sysclks_ref_fine ++ self._ph_acc = accum_dds_phase ++ self._ph_coh = target_dds_phase + self._dlys = dlys + + def _coeff(self, channel, profile, coeff): +@@ -598,6 +709,14 @@ class IIR(Module): + raise ValueError("no such state", coeff) + return signed(val, w.state) + ++ def get_accum_ftw(self, channel): ++ val = yield self.m_accum_ftw[channel] ++ return val ++ ++ def get_t_ref(self, channel): ++ val = yield self.m_t_ref[channel] ++ return val ++ + def fast_iter(self): + """Perform a single processing iteration.""" + assert (yield self.done) +@@ -633,18 +752,26 @@ class IIR(Module): + v_adc = signed((yield self.adc[i]), w.adc) + x0 = yield from self.get_state(i, coeff="x0") + x0s.append(x0) +- assert v_adc << (w.state - w.adc - 1) == x0, (hex(v_adc), hex(x0)) + logger.debug("adc[%d] adc=%x x0=%x", i, v_adc, x0) ++ assert v_adc << (w.state - w.adc - 1) == x0, (hex(v_adc), hex(x0)) + + data = [] + # predict output + for i in range(w_o.channels): ++ t0 = yield self._dt_start ++ dds_ftw_accu = yield from self.get_accum_ftw(i) ++ sysclks_ref = (yield from self.get_t_ref(i)) * self.widths_dds.sysclk_per_clk\ ++ + (yield self.ctrl_reftime[i].sysclks_fine) ++ logger.debug("dt_start=%d dt_ref=%d t_cycle=%d ftw_accu=%#x", ++ t0, sysclks_ref, self.t_cycle, dds_ftw_accu) ++ + j = yield self.ctrl[i].profile + en_iir = yield self.ctrl[i].en_iir + en_out = yield self.ctrl[i].en_out ++ en_pt = yield self.ctrl[i].en_pt + dly_i = yield self._dlys[i] +- logger.debug("ctrl[%d] profile=%d en_iir=%d en_out=%d dly=%d", +- i, j, en_iir, en_out, dly_i) ++ logger.debug("ctrl[%d] profile=%d en_iir=%d en_out=%d en_pt=%d dly=%d", ++ i, j, en_iir, en_out, en_pt, dly_i) + + cfg = yield from self.get_coeff(i, j, "cfg") + k_j = cfg & ((1 << bits_for(w_i.channels - 1)) - 1) +@@ -664,9 +791,13 @@ class IIR(Module): + + ftw0 = yield from self.get_coeff(i, j, "ftw0") + ftw1 = yield from self.get_coeff(i, j, "ftw1") +- pow = yield from self.get_coeff(i, j, "pow") +- logger.debug("dds[%d,%d] ftw0=%#x ftw1=%#x pow=%#x", +- i, j, ftw0, ftw1, pow) ++ _pow = yield from self.get_coeff(i, j, "pow") ++ ph_coh = ((ftw0 | (ftw1 << w.word)) * (t0 - sysclks_ref)) ++ ph_accu = dds_ftw_accu * self.t_cycle * self.widths_dds.sysclk_per_clk ++ ph = ph_coh - ph_accu ++ pow = (_pow + (ph >> w.word)) & 0xffff if en_pt else _pow ++ logger.debug("dds[%d,%d] ftw0=%#x ftw1=%#x ph_coh=%#x _pow=%#x pow=%#x", ++ i, j, ftw0, ftw1, ph_coh, _pow, pow) + + y1 = yield from self.get_state(i, j, "y1") + x1 = yield from self.get_state(k_j, coeff="x1") +@@ -688,6 +819,10 @@ class IIR(Module): + # wait for output + assert (yield self.processing) + while (yield self.processing): ++ logger.debug("sysclks_to_ref=%d sysclks_ref_to_iter_start=%d", ++ (yield self._sysclks_to_ref), ++ (yield self._sysclks_ref_to_iter_start)) ++ # logger.debug("%d %d %d %d", *[x for x in (yield self._sysclks_ref_fine)]) + yield + + assert (yield self.shifting) +diff --git a/artiq/gateware/suservo/pads.py b/artiq/gateware/suservo/pads.py +index 778f05d0..bdae8ee3 100644 +--- a/artiq/gateware/suservo/pads.py ++++ b/artiq/gateware/suservo/pads.py +@@ -1,5 +1,7 @@ + from migen import * + from migen.genlib.io import DifferentialOutput, DifferentialInput, DDROutput ++from artiq.gateware.rtio.phy import ttl_serdes_7series, ttl_serdes_generic ++from artiq.gateware.rtio import rtlink + + + class SamplerPads(Module): +@@ -57,20 +59,79 @@ class SamplerPads(Module): + clk=dp.clkout, port=sdop) + + ++class OutIoUpdate_8X(Module): ++ def __init__(self, pad): ++ serdes = ttl_serdes_7series._OSERDESE2_8X() ++ self.submodules += serdes ++ ++ self.passthrough = Signal() ++ self.data = Signal() ++ self.fine_ts = Signal(3) ++ ++ self.rtlink = rtlink.Interface( ++ rtlink.OInterface(1, fine_ts_width=3)) ++ self.probes = [serdes.o[-1]] ++ override_en = Signal() ++ override_o = Signal() ++ self.overrides = [override_en, override_o] ++ ++ # # # ++ ++ self.specials += Instance("IOBUFDS", ++ i_I=serdes.ser_out, ++ i_T=serdes.t_out, ++ io_IO=pad.p, ++ io_IOB=pad.n) ++ ++ # Just strobe always in non-passthrough mode, as self.data is supposed ++ # to be always valid. ++ self.submodules += ttl_serdes_generic._SerdesDriver( ++ serdes.o, ++ Mux(self.passthrough, self.rtlink.o.stb, 1), ++ Mux(self.passthrough, self.rtlink.o.data, self.data), ++ Mux(self.passthrough, self.rtlink.o.fine_ts, self.fine_ts), ++ override_en, override_o) ++ ++ self.comb += self.rtlink.o.busy.eq(~self.passthrough) ++ ++ + class UrukulPads(Module): + def __init__(self, platform, *eems): + spip, spin = [[ + platform.request("{}_qspi_{}".format(eem, pol), 0) + for eem in eems] for pol in "pn"] +- ioup = [platform.request("{}_io_update".format(eem), 0) +- for eem in eems] ++ + self.cs_n = Signal() + self.clk = Signal() + self.io_update = Signal() ++ self.passthrough = Signal() ++ self.dds_reset_sync_in = Signal(reset=0) # sync_in phy (one for all) ++ ++ # # # ++ ++ self.io_update_phys = [] ++ for eem in eems: ++ phy = OutIoUpdate_8X(platform.request("{}_io_update".format(eem), 0)) ++ self.io_update_phys.append(phy) ++ setattr(self.submodules, "{}_io_update_phy".format(eem), phy) ++ self.comb += [ ++ phy.data.eq(self.io_update), ++ phy.passthrough.eq(self.passthrough), ++ ] ++ ++ sync_in_pads = platform.request("{}_dds_reset_sync_in".format(eem)) ++ sync_in_r = Signal() ++ self.sync.rio_phy += sync_in_r.eq(self.dds_reset_sync_in) ++ sync_in_o = Signal() ++ self.specials += Instance("ODDR", ++ p_DDR_CLK_EDGE="SAME_EDGE", ++ i_C=ClockSignal("rio_phy"), i_CE=1, i_S=0, i_R=0, ++ i_D1=sync_in_r, i_D2=sync_in_r, o_Q=sync_in_o) ++ self.specials += DifferentialOutput(sync_in_o, sync_in_pads.p, sync_in_pads.n) ++ + self.specials += [( + DifferentialOutput(~self.cs_n, spip[i].cs, spin[i].cs), +- DifferentialOutput(self.clk, spip[i].clk, spin[i].clk), +- DifferentialOutput(self.io_update, ioup[i].p, ioup[i].n)) ++ DifferentialOutput(self.clk, spip[i].clk, spin[i].clk)) + for i in range(len(eems))] + for i in range(4 * len(eems)): + mosi = Signal() +diff --git a/artiq/gateware/suservo/servo.py b/artiq/gateware/suservo/servo.py +index 59529320..15d31027 100644 +--- a/artiq/gateware/suservo/servo.py ++++ b/artiq/gateware/suservo/servo.py +@@ -42,7 +42,7 @@ class Servo(Module): + assert t_iir + 2*adc_p.channels < t_cycle, "need shifting time" + + self.submodules.adc = ADC(adc_pads, adc_p) +- self.submodules.iir = IIR(iir_p, adc_p, dds_p) ++ self.submodules.iir = IIR(iir_p, adc_p, dds_p, t_cycle) + self.submodules.dds = DDS(dds_pads, dds_p) + + # adc channels are reversed on Sampler +@@ -63,7 +63,6 @@ class Servo(Module): + assert t_restart > 1 + cnt = Signal(max=t_restart) + cnt_done = Signal() +- active = Signal(3) + + # Indicates whether different steps (0: ADC, 1: IIR, 2: DDS) are + # currently active (exposed for simulation only), with each bit being +@@ -71,6 +70,8 @@ class Servo(Module): + # timing details of the different steps, any number can be concurrently + # active (e.g. ADC read from iteration n, IIR computation from iteration + # n - 1, and DDS write from iteration n - 2). ++ active = Signal(3) ++ self._active = active # Exposed for debugging only. + + # Asserted once per cycle when the DDS write has been completed. + self.done = Signal() +@@ -95,6 +96,17 @@ class Servo(Module): + cnt.eq(t_restart - 1) + ) + ] ++ ++ # Count number of cycles since the servo was last started from idle. ++ self.sync += If(active == 0, ++ self.iir.t_running.eq(0), ++ self.iir.reset_dds_phase.eq(1) ++ ).Else( ++ self.iir.t_running.eq(self.iir.t_running + 1) ++ ) ++ ++ self.sync += dds_pads.passthrough.eq(active == 0) ++ + self.comb += [ + cnt_done.eq(cnt == 0), + self.adc.start.eq(self.start & cnt_done), +diff --git a/artiq/gateware/test/suservo/__init__.py b/artiq/gateware/test/suservo/__init__.py +index e69de29b..7a1df77a 100644 +--- a/artiq/gateware/test/suservo/__init__.py ++++ b/artiq/gateware/test/suservo/__init__.py +@@ -0,0 +1,10 @@ ++"""Gateware implementation of the Sampler-Urukul (AD9910) DDS amplitude servo. ++ ++General conventions: ++ ++ - ``t_...`` signals and constants refer to time spans measured in the gateware ++ module's default clock (typically a 125 MHz RTIO clock). ++ - ``start`` signals cause modules to proceed with the next servo iteration iff ++ they are currently idle (i.e. their value is irrelevant while the module is ++ busy, so they are not necessarily one-clock-period strobes). ++""" +diff --git a/artiq/gateware/test/suservo/test_dds.py b/artiq/gateware/test/suservo/test_dds.py +index a666f14c..d9a81675 100644 +--- a/artiq/gateware/test/suservo/test_dds.py ++++ b/artiq/gateware/test/suservo/test_dds.py +@@ -5,6 +5,9 @@ from migen import * + + from artiq.gateware.suservo.dds_ser import DDSParams, DDS + ++class OutIoUpdateTB(Module): ++ def __init__(self): ++ self.fine_ts = Signal(3) + + class TB(Module): + def __init__(self, p): +@@ -15,6 +18,12 @@ class TB(Module): + setattr(self, "mosi{}".format(i), m) + self.miso = Signal() + self.io_update = Signal() ++ self.passthrough = Signal() ++ ++ self.io_update_phys = [] ++ for i in range(p.channels//4): ++ phy = OutIoUpdateTB() ++ self.io_update_phys.append(phy) + + clk0 = Signal() + self.sync += clk0.eq(self.clk) +@@ -23,16 +32,19 @@ class TB(Module): + + self.ddss = [] + for i in range(p.channels): +- dds = Record([("ftw", 32), ("pow", 16), ("asf", 16), ("cmd", 8)]) +- sr = Signal(len(dds)) ++ dds = Record([("ftw", 32), ("pow", 16), ("asf", 16), ++ ("cmd", 8), ("accu", 32), ("phase", 19)]) ++ sr = Signal(32 + 16 + 16 + 8) + self.sync += [ ++ dds.accu.eq(dds.accu + p.sysclk_per_clk * dds.ftw), + If(~self.cs_n & sample, + sr.eq(Cat(self.mosi[i], sr)) + ), + If(self.io_update, +- dds.raw_bits().eq(sr) ++ dds.raw_bits()[:len(sr)].eq(sr) + ) + ] ++ self.comb += dds.phase.eq((dds.pow << 3) + (dds.accu >> 13)) + self.ddss.append(dds) + + @passive +@@ -55,7 +67,7 @@ class TB(Module): + + + def main(): +- p = DDSParams(channels=4, width=8 + 32 + 16 + 16, clk=1) ++ p = DDSParams(channels=4, width=8 + 32 + 16 + 16, clk=1, sysclk_per_clk=8) + tb = TB(p) + dds = DDS(tb, p) + tb.submodules += dds +diff --git a/artiq/gateware/test/suservo/test_iir.py b/artiq/gateware/test/suservo/test_iir.py +index 919e7a6b..ab8a9a4a 100644 +--- a/artiq/gateware/test/suservo/test_iir.py ++++ b/artiq/gateware/test/suservo/test_iir.py +@@ -2,48 +2,67 @@ import logging + import unittest + + from migen import * +-from artiq.gateware.suservo import iir ++from artiq.gateware.suservo import servo ++from collections import namedtuple + ++logger = logging.getLogger(__name__) ++ ++ADCParamsSim = namedtuple("ADCParams", ["channels"]) ++DDSParamsSim = namedtuple("ADCParams", ["channels", "sysclk_per_clk"]) + + def main(): +- w_kasli = iir.IIRWidths(state=25, coeff=18, adc=16, +- asf=14, word=16, accu=48, shift=11, +- channel=3, profile=5, dly=8) +- w = iir.IIRWidths(state=17, coeff=16, adc=16, +- asf=14, word=16, accu=48, shift=11, +- channel=2, profile=1, dly=8) ++ w_kasli = servo.IIRWidths(state=25, coeff=18, adc=16, asf=14, ++ word=16, accu=48, shift=11, profile=5, dly=8) ++ p_adc = ADCParamsSim(channels=8) ++ p_dds = DDSParamsSim(channels=4, sysclk_per_clk=8) ++ w = servo.IIRWidths(state=17, coeff=16, adc=16, asf=14, ++ word=16, accu=48, shift=11, profile=2, dly=8) + ++ t_iir = p_adc.channels + 4*p_dds.channels + 8 + 1 + def run(dut): ++ yield dut.t_running.eq(0) + for i, ch in enumerate(dut.adc): + yield ch.eq(i) + for i, ch in enumerate(dut.ctrl): + yield ch.en_iir.eq(1) + yield ch.en_out.eq(1) + yield ch.profile.eq(i) +- for i in range(1 << w.channel): ++ yield ch.en_pt.eq(i) ++ for i, ch in enumerate(dut.ctrl_reftime): ++ yield ch.sysclks_fine.eq(i) ++ yield ch.stb.eq(1) ++ yield ++ yield dut.t_running.eq(dut.t_running + 1) ++ yield ch.stb.eq(0) ++ yield ++ yield dut.t_running.eq(dut.t_running + 1) ++ for i in range(p_adc.channels): + yield from dut.set_state(i, i << 8, coeff="x1") + yield from dut.set_state(i, i << 8, coeff="x0") ++ for i in range(p_dds.channels): + for j in range(1 << w.profile): + yield from dut.set_state(i, + (j << 1) | (i << 8), profile=j, coeff="y1") + for k, l in enumerate("pow offset ftw0 ftw1".split()): + yield from dut.set_coeff(i, profile=j, coeff=l, +- value=(i << 12) | (j << 8) | (k << 4)) ++ value=(i << 10) | (j << 8) | (k << 4)) + yield +- for i in range(1 << w.channel): ++ for i in range(p_dds.channels): + for j in range(1 << w.profile): +- for k, l in enumerate("cfg a1 b0 b1".split()): ++ for k, l in enumerate("a1 b0 b1".split()): + yield from dut.set_coeff(i, profile=j, coeff=l, +- value=(i << 12) | (j << 8) | (k << 4)) ++ value=(i << 10) | (j << 8) | (k << 4)) + yield from dut.set_coeff(i, profile=j, coeff="cfg", +- value=(i << 0) | (j << 8)) # sel, dly ++ value=(i % p_adc.channels) | (j << 8)) # sel, dly + yield +- for i in range(10): ++ for i in range(4): ++ logger.debug("check_iter {}".format(i)) + yield from dut.check_iter() ++ yield dut.t_running.eq((yield dut.t_running) + t_iir) + yield + +- dut = iir.IIR(w) +- run_simulation(dut, [run(dut)], vcd_name="iir.vcd") ++ dut = servo.IIR(w, p_adc, p_dds, t_iir) ++ run_simulation(dut, [run(dut)], vcd_name="servo.vcd") + + + class IIRTest(unittest.TestCase): +diff --git a/artiq/gateware/test/suservo/test_servo.py b/artiq/gateware/test/suservo/test_servo.py +index cc1a73a2..fe1708d0 100644 +--- a/artiq/gateware/test/suservo/test_servo.py ++++ b/artiq/gateware/test/suservo/test_servo.py +@@ -1,5 +1,6 @@ + import logging + import unittest ++import numpy as np + + from migen import * + from migen.genlib import io +@@ -7,15 +8,17 @@ from migen.genlib import io + from artiq.gateware.test.suservo import test_adc, test_dds + from artiq.gateware.suservo import servo + ++logger = logging.getLogger(__name__) ++ + + class ServoSim(servo.Servo): + def __init__(self): + adc_p = servo.ADCParams(width=16, channels=8, lanes=4, + t_cnvh=4, t_conv=57 - 4, t_rtt=4 + 4) + iir_p = servo.IIRWidths(state=25, coeff=18, adc=16, asf=14, word=16, +- accu=48, shift=11, channel=3, profile=5, dly=8) ++ accu=48, shift=11, profile=5, dly=8) + dds_p = servo.DDSParams(width=8 + 32 + 16 + 16, +- channels=adc_p.channels, clk=1) ++ channels=4, clk=1, sysclk_per_clk=8) + + self.submodules.adc_tb = test_adc.TB(adc_p) + self.submodules.dds_tb = test_dds.TB(dds_p) +@@ -23,37 +26,156 @@ class ServoSim(servo.Servo): + servo.Servo.__init__(self, self.adc_tb, self.dds_tb, + adc_p, iir_p, dds_p) + ++ self.dds_output = [] ++ ++ def log_flow(self, cycle): ++ su_start = yield self.start ++ adc_start = yield self.adc.start ++ iir_start = yield self.iir.start ++ dds_start = yield self.dds.start ++ su_done = yield self.done ++ adc_done = yield self.adc.done ++ iir_done = yield self.iir.done ++ dds_done = yield self.dds.done ++ active = yield self._active ++ io_update = yield self.dds_tb.io_update ++ passthrough = yield self.dds_tb.passthrough ++ iir_loading = yield self.iir.loading ++ iir_processing = yield self.iir.processing ++ iir_shifting = yield self.iir.shifting ++ dt = yield self.iir.t_running ++ dt_iir = yield self.iir._dt_start ++ state = yield self.iir._state ++ stage0 = yield self.iir._stages[0] ++ stage1 = yield self.iir._stages[1] ++ stage2 = yield self.iir._stages[2] ++ logger.debug( ++ "cycle=%d " ++ #"start=[su=%d adc=%d iir=%d dds=%d] " ++ #"done=[su=%d adc=%d iir=%d dds=%d] " ++ "active=%s load_proc_shft=%d%d%d stages_active=%d%d%d " ++ "io_update=%d passthrough=%d " ++ "dt=%d dt_iir=%d state=%d", ++ cycle, ++ #su_start, adc_start, iir_start, dds_start, ++ #su_done, adc_done, iir_done, dds_done, ++ '{:03b}'.format(active), iir_loading, iir_processing, iir_shifting, stage0, stage1, stage2, ++ io_update, passthrough, ++ dt, dt_iir//8, state ++ ) ++ ++ def log_state(self, channel, profile, calls=[0]): ++ calls[0] += 1 ++ # if not (yield self._active[1]): ++ # return ++ yield from self.log_flow(calls[0] - 2) ++ return ++ cfg = yield from self.iir.get_coeff(channel, profile, "cfg") ++ sel = cfg & 0x7 ++ x0 = yield from self.iir.get_state(sel, coeff="x0") ++ x1 = yield from self.iir.get_state(sel, coeff="x1") ++ y1 = yield from self.iir.get_state(channel, profile, coeff="y1") ++ _pow = yield from self.iir.get_coeff(channel, profile, "pow") ++ pow_iir = yield self.iir.dds[channel][2*self.iir.widths.word:3*self.iir.widths.word] ++ pow_dds = yield self.dds_tb.ddss[channel].pow ++ asf_dds = yield self.dds_tb.ddss[channel].asf ++ ftw_dds = yield self.dds_tb.ddss[channel].ftw ++ accu_dds = yield self.dds_tb.ddss[channel].accu ++ phase_dds = (yield self.dds_tb.ddss[channel].phase) ++ dds_output = np.cos(2*np.pi*phase_dds/2**19) ++ ph_coh = yield self.iir._ph_coh ++ ph_acc = yield self.iir._ph_acc ++ offset = yield from self.iir.get_coeff(channel, profile, "offset") ++ ftw0 = yield from self.iir.get_coeff(channel, profile, "ftw0") ++ ftw1 = yield from self.iir.get_coeff(channel, profile, "ftw1") ++ m_phase = yield from self.iir.get_accum_ftw(channel) ++ iir_adc = yield self.iir.adc[sel] ++ logger.debug("\t" ++ "ch=%d pr=%d " ++ # "x0=%d x1=%d adc=%d y1=%d sel=%d " ++ "ftw=%#x pow_coeff=%#x ftw_accu=%#x " ++ "ph_coh=%#x ph_acc=%#x " ++ "pow_iir=%#x pow_dds=%#x ftw_dds=%#x asf_dds=%#x accu_dds=%#x phase_dds=%#x dds_output=%04.3f", ++ channel, profile, ++ # x0, x1, iir_adc, y1, sel, ++ ftw0 | (ftw1 << 16), _pow, m_phase, ++ ph_coh, ph_acc, ++ pow_iir, pow_dds, ftw_dds, asf_dds, accu_dds, phase_dds >> 3, dds_output ++ ) ++ self.dds_output.append(dds_output) ++ # yield from self.log_registers(profile) ++ ++ def log_registers(self, profile): ++ adc_channels = self.iir.widths_adc.channels ++ dds_channels = self.iir.widths_dds.channels ++ x0s = [0]*adc_channels ++ x1s = [0]*adc_channels ++ y1s = [0]*dds_channels ++ for ch in range(adc_channels): ++ x0s[ch] = yield from self.iir.get_state(ch, coeff="x0") ++ x1s[ch] = yield from self.iir.get_state(ch, coeff="x1") ++ for ch in range(dds_channels): ++ y1s[ch] = yield from self.iir.get_state(ch, profile, coeff="y1") ++ ++ logger.debug(("x0s = " + '{:05X} ' * adc_channels).format(*x0s)) ++ logger.debug(("x1s = " + '{:05X} ' * adc_channels).format(*x1s)) ++ logger.debug(("y1s = " + '{:05X} ' * dds_channels).format(*y1s)) ++ + def test(self): + assert (yield self.done) + +- adc = 1 ++ adc = 7 + x0 = 0x0141 + yield self.adc_tb.data[-adc-1].eq(x0) +- channel = 3 +- yield self.iir.adc[channel].eq(adc) ++ channel = 0 + yield self.iir.ctrl[channel].en_iir.eq(1) + yield self.iir.ctrl[channel].en_out.eq(1) +- profile = 5 ++ yield self.iir.ctrl[channel].en_pt.eq(1) ++ profile = 31 + yield self.iir.ctrl[channel].profile.eq(profile) + x1 = 0x0743 + yield from self.iir.set_state(adc, x1, coeff="x1") + y1 = 0x1145 + yield from self.iir.set_state(channel, y1, + profile=profile, coeff="y1") +- coeff = dict(pow=0x1333, offset=0x1531, ftw0=0x1727, ftw1=0x1929, +- a1=0x0135, b0=0x0337, b1=0x0539, cfg=adc | (0 << 3)) ++ coeff = dict(pow=0, offset=0x1531, ftw0=0xeb85, ftw1=0x51, ++ a1=0x0135, b0=0x0337, b1=0x0539, cfg=adc) + for ks in "pow offset ftw0 ftw1", "a1 b0 b1 cfg": + for k in ks.split(): + yield from self.iir.set_coeff(channel, value=coeff[k], + profile=profile, coeff=k) + yield + ++ num_it = 1 ++ num_proc_its = [0]*num_it # number of iterations while iir.processing ++ yield from self.log_state(channel, profile) + yield self.start.eq(1) + yield +- yield self.start.eq(0) +- while not (yield self.dds_tb.io_update): +- yield +- yield # io_update ++ for i in range(num_it): ++ if i == 1: # change ftw ++ yield from self.iir.set_coeff(channel, ++ profile=profile, coeff='ftw0', value=coeff['ftw1']) ++ yield from self.iir.set_coeff(channel, ++ profile=profile, coeff='ftw1', value=coeff['ftw0']) ++ if i == 2: # change ftw back ++ yield from self.iir.set_coeff(channel, ++ profile=profile, coeff='ftw0', value=coeff['ftw0']) ++ yield from self.iir.set_coeff(channel, ++ profile=profile, coeff='ftw1', value=coeff['ftw1']) ++ logger.debug("iteration {}".format(i)) ++ yield from self.log_state(channel, profile) ++ if i == num_it-1: ++ yield self.start.eq(0) ++ while not (yield self.dds_tb.io_update): ++ yield ++ if (yield self.iir.processing): ++ num_proc_its[i] += 1 ++ if (yield self.iir._stages) != 0: ++ yield from self.log_state(channel, profile) ++ yield # io_update ++ yield from self.log_state(channel, profile) ++ yield ++ yield from self.log_state(channel, profile) + + w = self.iir.widths + +@@ -63,6 +185,8 @@ class ServoSim(servo.Servo): + + offset = coeff["offset"] << (w.state - w.coeff - 1) + a1, b0, b1 = coeff["a1"], coeff["b0"], coeff["b1"] ++ ++ # works only for 1 iteration + out = ( + 0*(1 << w.shift - 1) + # rounding + a1*(y1 + 0) + b0*(x0 + offset) + b1*(x1 + offset) +@@ -76,8 +200,15 @@ class ServoSim(servo.Servo): + ftw = (coeff["ftw1"] << 16) | coeff["ftw0"] + assert _ == ftw, (hex(_), hex(ftw)) + ++ t0 = yield self.iir._dt_start ++ # todo: include phase accumulator ++ ph = (ftw * t0) >> 16 ++ if (yield self.iir.ctrl[channel].en_pt): ++ pow = (coeff["pow"] + ph) & 0xffff ++ else: ++ pow = coeff["pow"] + _ = yield self.dds_tb.ddss[channel].pow +- assert _ == coeff["pow"], (hex(_), hex(coeff["pow"])) ++ assert _ == pow, (hex(_), hex(pow)) + + _ = yield self.dds_tb.ddss[channel].asf + asf = y1 >> (w.state - w.asf - 1) +@@ -101,4 +232,5 @@ class ServoTest(unittest.TestCase): + + + if __name__ == "__main__": ++ logging.basicConfig(level=logging.DEBUG) + main() diff --git a/experimental-features/suservo_var_urukul.diff b/experimental-features/suservo_var_urukul.diff index e4f5a749a..ce981ba66 100644 --- a/experimental-features/suservo_var_urukul.diff +++ b/experimental-features/suservo_var_urukul.diff @@ -48,14 +48,14 @@ index 1d0a72dad..a89cdcca4 100644 :param core_device: Core device name """ kernel_invariants = {"channel", "core", "pgia", "cplds", "ddses", -- "ref_period_mu"} +- "ref_period_mu", "corrected_fs"} + "ref_period_mu", "num_channels", "coeff_sel", -+ "state_sel", "config_addr", "write_enable"} ++ "corrected_fs", "state_sel", "config_addr", "write_enable"} def __init__(self, dmgr, channel, pgia_device, cpld_devices, dds_devices, -@@ -83,9 +81,19 @@ def __init__(self, dmgr, channel, pgia_device, - self.core.coarse_ref_period) +@@ -83,13 +81,23 @@ def __init__(self, dmgr, channel, pgia_device, + self.corrected_fs = sampler.Sampler.use_corrected_fs(sampler_hw_rev) assert self.ref_period_mu == self.core.ref_multiplier + # The width of parts of the servo memory address depends on the number @@ -68,6 +68,10 @@ index 1d0a72dad..a89cdcca4 100644 + self.coeff_sel = 1 << coeff_depth + self.write_enable = 1 << (coeff_depth + 1) + + @staticmethod + def get_rtio_channels(channel, **kwargs): + return [(channel, None)] + @kernel def init(self): - """Initialize the servo, Sampler and both Urukuls. diff --git a/flake.lock b/flake.lock index 1288d944f..0e9cd2eaf 100644 --- a/flake.lock +++ b/flake.lock @@ -43,11 +43,11 @@ "mozilla-overlay": { "flake": false, "locked": { - "lastModified": 1664789696, - "narHash": "sha256-UGWJHQShiwLCr4/DysMVFrYdYYHcOqAOVsWNUu+l6YU=", + "lastModified": 1672878308, + "narHash": "sha256-0+fl6PHokhtSV+w58z2QD2rTf8QhcOGsT9o4LwHHZHE=", "owner": "mozilla", "repo": "nixpkgs-mozilla", - "rev": "80627b282705101e7b38e19ca6e8df105031b072", + "rev": "d38863db88e100866b3e494a651ee4962b762fcc", "type": "github" }, "original": { @@ -61,11 +61,11 @@ "nixpkgs": "nixpkgs" }, "locked": { - "lastModified": 1668759346, - "narHash": "sha256-Bp12lH4VUZfdeHXAtBEQf0mJVCPbpFUsBv7mi1bl8Lc=", - "ref": "master", - "rev": "085c6ee738c5971c57375e9055321823e2592eec", - "revCount": 800, + "lastModified": 1673683761, + "narHash": "sha256-mvYqz1ZNGwVwB9cFe1yoMNSYm8qr5FN2baqCFYXfxh4=", + "ref": "refs/heads/master", + "rev": "c269444c0bbfadb0fae662cc4a3b4582a9362dbc", + "revCount": 805, "type": "git", "url": "https://git.m-labs.hk/m-labs/nac3.git" }, @@ -76,16 +76,16 @@ }, "nixpkgs": { "locked": { - "lastModified": 1668759079, - "narHash": "sha256-WWi1+WXgppqLp9V/P8n2tBgrVL1a8d4FnbWbL+nF/Jw=", + "lastModified": 1673345971, + "narHash": "sha256-4DfFcKLRfVUTyuGrGNNmw37IeIZSoku9tgTVmu/iD98=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "bcb2de4443268e009b82f22570567b2b013d5523", + "rev": "54644f409ab471e87014bb305eac8c50190bcf48", "type": "github" }, "original": { "owner": "NixOS", - "ref": "master", + "ref": "nixos-22.11", "repo": "nixpkgs", "type": "github" } @@ -108,11 +108,11 @@ ] }, "locked": { - "lastModified": 1664319253, - "narHash": "sha256-hycJAgy+NFF9f5I6++7yo8KdhMSyKCPKJazRPxeedI4=", + "lastModified": 1673433867, + "narHash": "sha256-a7Oq35YoDzPtISbqAsaT+2/v15HZ7G1q0ukXmKWdb7Q=", "owner": "m-labs", "repo": "sipyco", - "rev": "d58ded7280e0f020be2446d4fee70f4393e6045f", + "rev": "38f8f4185d7db6b68bd7f71546da9077b1e2561c", "type": "github" }, "original": { @@ -124,11 +124,11 @@ "src-migen": { "flake": false, "locked": { - "lastModified": 1662111470, - "narHash": "sha256-IPyhoFZLhY8d3jHB8jyvGdbey7V+X5eCzBZYSrJ18ec=", + "lastModified": 1673433200, + "narHash": "sha256-ribBG06gsucz5oBS+O6aL8s2oJjx+qfl+vXmspts8gg=", "owner": "m-labs", "repo": "migen", - "rev": "639e66f4f453438e83d86dc13491b9403bbd8ec6", + "rev": "f3e9145c9825514a1b4225378936569da4df8e12", "type": "github" }, "original": { @@ -140,11 +140,11 @@ "src-misoc": { "flake": false, "locked": { - "lastModified": 1665395741, - "narHash": "sha256-7ULMGBPPn5NxZX6rdxU5GheoSNBiJklHQEVf04jU9tI=", - "ref": "master", - "rev": "4fb0730db4c5de7e86f82fa3bd204e6c4608af85", - "revCount": 2427, + "lastModified": 1671158014, + "narHash": "sha256-50w0K2E2ympYrG1Tte/HVbsp4FS2U+yohqZByXTOo4I=", + "ref": "refs/heads/master", + "rev": "26f039f9f6931a20a04ccd0f0a5402f67f553916", + "revCount": 2436, "submodules": true, "type": "git", "url": "https://github.com/m-labs/misoc.git" diff --git a/flake.nix b/flake.nix index 3009b3374..ef5d538a3 100644 --- a/flake.nix +++ b/flake.nix @@ -153,20 +153,6 @@ propagatedBuildInputs = with pkgs.python3Packages; [ pyserial prettytable msgpack migen ]; }; - cargo-xbuild = rustPlatform.buildRustPackage rec { - pname = "cargo-xbuild"; - version = "0.6.5"; - - src = pkgs.fetchFromGitHub { - owner = "rust-osdev"; - repo = pname; - rev = "v${version}"; - sha256 = "18djvygq9v8rmfchvi2hfj0i6fhn36m716vqndqnj56fiqviwxvf"; - }; - - cargoSha256 = "13sj9j9kl6js75h9xq0yidxy63vixxm9q3f8jil6ymarml5wkhx8"; - }; - vivadoEnv = pkgs.buildFHSUserEnv { name = "vivado-env"; targetPkgs = vivadoDeps; @@ -175,7 +161,7 @@ vivado = pkgs.buildFHSUserEnv { name = "vivado"; targetPkgs = vivadoDeps; - profile = "set -e; source /opt/Xilinx/Vivado/2021.2/settings64.sh"; + profile = "set -e; source /opt/Xilinx/Vivado/2022.2/settings64.sh"; runScript = "vivado"; }; @@ -193,12 +179,12 @@ (pkgs.python3.withPackages(ps: [ ps.jsonschema migen misoc (artiq.withExperimentalFeatures experimentalFeatures) ])) rustPlatform.rust.rustc rustPlatform.rust.cargo + pkgs.cargo-xbuild pkgs.llvmPackages_14.clang-unwrapped pkgs.llvm_14 pkgs.lld_14 vivado rustPlatform.cargoSetupHook - cargo-xbuild ]; buildPhase = '' @@ -372,7 +358,7 @@ (packages.x86_64-linux.python3-mimalloc.withPackages(ps: with packages.x86_64-linux; [ migen misoc artiq ps.paramiko ps.jsonschema microscope ])) rustPlatform.rust.rustc rustPlatform.rust.cargo - cargo-xbuild + pkgs.cargo-xbuild pkgs.llvmPackages_14.clang-unwrapped pkgs.llvm_14 pkgs.lld_14 @@ -400,6 +386,18 @@ artiq-msys2-pkg = packages.x86_64-w64-mingw32.artiq-pkg; msys2-repos = packages.x86_64-w64-mingw32.msys2-repos; inherit (packages.x86_64-linux) artiq-manual-html artiq-manual-pdf; + gateware-sim = pkgs.stdenvNoCC.mkDerivation { + name = "gateware-sim"; + buildInputs = [ + (pkgs.python3.withPackages(ps: with packages.x86_64-linux; [ migen misoc artiq ])) + ]; + phases = [ "buildPhase" ]; + buildPhase = + '' + python -m unittest discover -v artiq.gateware.test + touch $out + ''; + }; }; }; diff --git a/setup.py b/setup.py index b43d3a6f1..92bc68b76 100755 --- a/setup.py +++ b/setup.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 -from setuptools import setup, find_packages +from setuptools import setup, find_namespace_packages import sys import versioneer @@ -15,6 +15,7 @@ console_scripts = [ "artiq_compile = artiq.frontend.artiq_compile:main", "artiq_coreanalyzer = artiq.frontend.artiq_coreanalyzer:main", "artiq_coremgmt = artiq.frontend.artiq_coremgmt:main", + "artiq_rtiomap = artiq.frontend.artiq_rtiomap:main", "artiq_ddb_template = artiq.frontend.artiq_ddb_template:main", "artiq_master = artiq.frontend.artiq_master:main", "artiq_mkfs = artiq.frontend.artiq_mkfs:main", @@ -56,7 +57,7 @@ Programming Language :: Python :: 3.9 Topic :: Scientific/Engineering :: Physics Topic :: System :: Hardware """.splitlines(), - packages=find_packages(), + packages=find_namespace_packages(exclude=["doc.manual"]), namespace_packages=[], include_package_data=True, ext_modules=[],