Merge branch 'master' into nac3

This commit is contained in:
Sebastien Bourdeauducq 2023-01-15 12:23:25 +08:00
commit 7635b9ed92
112 changed files with 2991 additions and 4243 deletions

View File

@ -29,7 +29,7 @@ Website: https://m-labs.hk/artiq
License 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 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 it under the terms of the GNU Lesser General Public License as published by

View File

@ -8,10 +8,21 @@ ARTIQ-8 (Unreleased)
Highlights: Highlights:
* Implemented Phaser-servo. This requires recent gateware on Phaser. * Hardware support:
* Implemented Phaser-MIQRO support. This requires the Phaser MIQRO gateware - Implemented Phaser-servo. This requires recent gateware on Phaser.
variant. - Implemented Phaser-MIQRO support. This requires the Phaser MIQRO gateware
* MSYS2 packaging for Windows. 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 ARTIQ-7
------- -------
@ -27,7 +38,7 @@ Highlights:
- Almazny mezzanine board for Mirny - Almazny mezzanine board for Mirny
- Phaser: improved documentation, exposed the DAC coarse mixer and ``sif_sync``, exposed upconverter calibration - 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 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. - 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). * Softcore targets now use the RISC-V architecture (VexRiscv) instead of OR1K (mor1kx).
* Gateware FPU is supported on KC705 and Kasli 2.0. * Gateware FPU is supported on KC705 and Kasli 2.0.
@ -77,9 +88,9 @@ Breaking changes:
generated for some configurations. generated for some configurations.
* Phaser: fixed coarse mixer frequency configuration * Phaser: fixed coarse mixer frequency configuration
* Mirny: Added extra delays in ``ADF5356.sync()``. This avoids the need of an extra delay before * Mirny: Added extra delays in ``ADF5356.sync()``. This avoids the need of an extra delay before
calling `ADF5356.init()`. calling ``ADF5356.init()``.
* The deprecated ``set_dataset(..., save=...)`` is no longer supported. * 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 and possibly other switches in future. Readback has been removed, and now only one channel per
switch is supported. switch is supported.

View File

@ -406,7 +406,7 @@ class ExperimentsArea(QtWidgets.QMdiArea):
self.worker_handlers = { self.worker_handlers = {
"get_device_db": lambda: {}, "get_device_db": lambda: {},
"get_device": lambda k: {"type": "dummy"}, "get_device": lambda key, resolve_alias=False: {"type": "dummy"},
"get_dataset": self._ddb.get, "get_dataset": self._ddb.get,
"update_dataset": self._ddb.update, "update_dataset": self._ddb.update,
} }

View File

@ -102,13 +102,14 @@ class Hdf5FileSystemModel(QtWidgets.QFileSystemModel):
h5 = open_h5(info) h5 = open_h5(info)
if h5 is not None: if h5 is not None:
try: try:
expid = pyon.decode(h5["expid"][()]) expid = pyon.decode(h5["expid"][()]) if "expid" in h5 else dict()
start_time = datetime.fromtimestamp(h5["start_time"][()]) start_time = datetime.fromtimestamp(h5["start_time"][()]) if "start_time" in h5 else "<none>"
v = ("artiq_version: {}\nrepo_rev: {}\nfile: {}\n" v = ("artiq_version: {}\nrepo_rev: {}\nfile: {}\n"
"class_name: {}\nrid: {}\nstart_time: {}").format( "class_name: {}\nrid: {}\nstart_time: {}").format(
h5["artiq_version"][()], expid["repo_rev"], h5["artiq_version"].asstr()[()] if "artiq_version" in h5 else "<none>",
expid.get("file", "<none>"), expid["class_name"], expid.get("repo_rev", "<none>"),
h5["rid"][()], start_time) expid.get("file", "<none>"), expid.get("class_name", "<none>"),
h5["rid"][()] if "rid" in h5 else "<none>", start_time)
return v return v
except: except:
logger.warning("unable to read metadata from %s", logger.warning("unable to read metadata from %s",
@ -174,14 +175,14 @@ class FilesDock(QtWidgets.QDockWidget):
logger.debug("loading datasets from %s", info.filePath()) logger.debug("loading datasets from %s", info.filePath())
with f: with f:
try: try:
expid = pyon.decode(f["expid"][()]) expid = pyon.decode(f["expid"][()]) if "expid" in f else dict()
start_time = datetime.fromtimestamp(f["start_time"][()]) start_time = datetime.fromtimestamp(f["start_time"][()]) if "start_time" in f else "<none>"
v = { v = {
"artiq_version": f["artiq_version"][()], "artiq_version": f["artiq_version"].asstr()[()] if "artiq_version" in f else "<none>",
"repo_rev": expid["repo_rev"], "repo_rev": expid.get("repo_rev", "<none>"),
"file": expid.get("file", "<none>"), "file": expid.get("file", "<none>"),
"class_name": expid["class_name"], "class_name": expid.get("class_name", "<none>"),
"rid": f["rid"][()], "rid": f["rid"][()] if "rid" in f else "<none>",
"start_time": start_time, "start_time": start_time,
} }
self.metadata_changed.emit(v) self.metadata_changed.emit(v)

View File

@ -27,6 +27,8 @@ class AD9912:
f_ref/clk_div*pll_n where f_ref is the reference frequency and clk_div 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 is the reference clock divider (both set in the parent Urukul CPLD
instance). 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] core: KernelInvariant[Core]
@ -34,11 +36,12 @@ class AD9912:
bus: KernelInvariant[SPIMaster] bus: KernelInvariant[SPIMaster]
chip_select: KernelInvariant[int32] chip_select: KernelInvariant[int32]
pll_n: KernelInvariant[int32] pll_n: KernelInvariant[int32]
pll_en: KernelInvariant[bool]
ftw_per_hz: KernelInvariant[float] ftw_per_hz: KernelInvariant[float]
sw: KernelInvariant[Option[TTLOut]] sw: KernelInvariant[Option[TTLOut]]
def __init__(self, dmgr, chip_select, cpld_device, sw_device=None, 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.cpld = dmgr.get(cpld_device)
self.core = self.cpld.core self.core = self.cpld.core
self.bus = self.cpld.bus self.bus = self.cpld.bus
@ -48,8 +51,12 @@ class AD9912:
self.sw = Some(dmgr.get(sw_device)) self.sw = Some(dmgr.get(sw_device))
else: else:
self.sw = none self.sw = none
self.pll_en = pll_en
self.pll_n = pll_n 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 assert sysclk <= 1e9
self.ftw_per_hz = 1 / sysclk * (1 << 48) self.ftw_per_hz = 1 / sysclk * (1 << 48)
@ -111,13 +118,15 @@ class AD9912:
raise ValueError("Urukul AD9912 product id mismatch") raise ValueError("Urukul AD9912 product id mismatch")
self.core.delay(50. * us) self.core.delay(50. * us)
# HSTL power down, CMOS power down # HSTL power down, CMOS power down
self.write(AD9912_PWRCNTRL1, 0x80, 1) pwrcntrl1 = 0x80 | (int32(not self.pll_en) << 4)
self.cpld.io_update.pulse(2. * us) self.write(AD9912_PWRCNTRL1, pwrcntrl1, 1)
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.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) self.core.delay(1. * ms)
@kernel @kernel

View File

@ -91,6 +91,10 @@ class AD9914:
self.set_x_duration_mu = 7 * self.write_duration_mu self.set_x_duration_mu = 7 * self.write_duration_mu
self.exit_x_duration_mu = 3 * 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 @kernel
def write(self, addr: int32, data: int32): def write(self, addr: int32, data: int32):
rtio_output((self.bus_channel << 8) | addr, data) rtio_output((self.bus_channel << 8) | addr, data)

View File

@ -83,6 +83,10 @@ class ADF5356:
self._init_registers() self._init_registers()
@staticmethod
def get_rtio_channels(channel, **kwargs):
return [(channel, None)]
@kernel @kernel
def init(self, blind: bool = False): def init(self, blind: bool = False):
""" """

View File

@ -332,6 +332,9 @@
"minItems": 2, "minItems": 2,
"maxItems": 2 "maxItems": 2
}, },
"sampler_hw_rev": {
"type": "string"
},
"urukul0_ports": { "urukul0_ports": {
"type": "array", "type": "array",
"items": { "items": {

View File

@ -97,6 +97,10 @@ class EdgeCounter:
self.channel = channel self.channel = channel
self.counter_max = (1 << (gateware_width - 1)) - 1 self.counter_max = (1 << (gateware_width - 1)) - 1
@staticmethod
def get_rtio_channels(channel, **kwargs):
return [(channel, None)]
@kernel @kernel
def gate_rising(self, duration: float) -> int64: def gate_rising(self, duration: float) -> int64:
"""Count rising edges for the given duration and request the total at """Count rising edges for the given duration and request the total at

View File

@ -22,7 +22,7 @@ class Fastino:
DAC updates synchronized to a frame edge. DAC updates synchronized to a frame edge.
The `log2_width=0` RTIO layout uses one DAC channel per RTIO address and a 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 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. tracking in kernels, at the cost of more DMA and RTIO data.
The setting here and in the RTIO PHY (gateware) must match. The setting here and in the RTIO PHY (gateware) must match.
@ -57,6 +57,10 @@ class Fastino:
assert self.core.ref_period == 1*ns assert self.core.ref_period == 1*ns
self.t_frame = int64(14*7*4) self.t_frame = int64(14*7*4)
@staticmethod
def get_rtio_channels(channel, **kwargs):
return [(channel, None)]
@kernel @kernel
def init(self): def init(self):
"""Initialize the device. """Initialize the device.

View File

@ -29,6 +29,10 @@ class Grabber:
# ROI engine outputs for one video frame. # ROI engine outputs for one video frame.
self.sentinel = int32(int64(2**count_width)) 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 @kernel
def setup_roi(self, n: int32, x0: int32, y0: int32, x1: int32, y1: int32): def setup_roi(self, n: int32, x0: int32, y0: int32, x1: int32, y1: int32):
""" """

View File

@ -241,7 +241,7 @@ class Phaser:
channel: Kernel[list[PhaserChannel]] channel: Kernel[list[PhaserChannel]]
def __init__(self, dmgr, channel_base, miso_delay=1, tune_fifo_offset=True, 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"): core_device="core"):
self.channel_base = channel_base self.channel_base = channel_base
self.core = dmgr.get(core_device) self.core = dmgr.get(core_device)
@ -255,7 +255,7 @@ class Phaser:
self.clk_sel = clk_sel self.clk_sel = clk_sel
self.tune_fifo_offset = tune_fifo_offset self.tune_fifo_offset = tune_fifo_offset
self.sync_dly = sync_dly 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 = 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 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) self.channel = [PhaserChannel(self, ch, trf)
for ch, trf in enumerate([trf0, trf1])] 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 @kernel
def init(self, debug: bool = False): def init(self, debug: bool = False):
"""Initialize the board. """Initialize the board.
@ -280,10 +292,11 @@ class Phaser:
self.core.delay(.1*ms) # slack self.core.delay(.1*ms) # slack
is_baseband = hw_rev & PHASER_HW_REV_VARIANT != 0 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: if debug:
print_rpc(("gw_rev:", self.gw_rev)) print_rpc(("gw_rev:", self.gw_rev))
self.core.break_realtime() self.core.break_realtime()
assert gw_rev == self.gw_rev
self.core.delay(.1*ms) # slack self.core.delay(.1*ms) # slack
# allow a few errors during startup and alignment since boot # allow a few errors during startup and alignment since boot

View File

@ -19,25 +19,27 @@ SPI_CS_PGIA = 1 # separate SPI bus, CS used as RCLK
@portable @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. """Convert ADC data in machine units to Volts.
:param data: 16 bit signed ADC word :param data: 16 bit signed ADC word
:param gain: PGIA gain setting (0: 1, ..., 3: 1000) :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 :return: Voltage in Volts
""" """
volt_per_lsb = 0. volt_per_lsb = 0.
if gain == 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: 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: 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: 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: else:
raise ValueError("invalid gain") raise ValueError("invalid gain")
return float(data)*volt_per_lsb return float(data)* volt_per_lsb
@nac3 @nac3
@ -54,6 +56,7 @@ class Sampler:
:param gains: Initial value for PGIA gains shift register :param gains: Initial value for PGIA gains shift register
(default: 0x0000). Knowledge of this state is not transferred (default: 0x0000). Knowledge of this state is not transferred
between experiments. between experiments.
:param hw_rev: Sampler's hardware revision string (default 'v2.2')
:param core_device: Core device name :param core_device: Core device name
""" """
core: KernelInvariant[Core] core: KernelInvariant[Core]
@ -62,9 +65,10 @@ class Sampler:
cnv: KernelInvariant[TTLOut] cnv: KernelInvariant[TTLOut]
div: KernelInvariant[int32] div: KernelInvariant[int32]
gains: Kernel[int32] gains: Kernel[int32]
corrected_fs: KernelInvariant[bool]
def __init__(self, dmgr, spi_adc_device, spi_pgia_device, cnv_device, 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 = dmgr.get(spi_adc_device)
self.bus_adc.update_xfer_duration_mu(div, 32) self.bus_adc.update_xfer_duration_mu(div, 32)
self.bus_pgia = dmgr.get(spi_pgia_device) self.bus_pgia = dmgr.get(spi_pgia_device)
@ -73,6 +77,11 @@ class Sampler:
self.cnv = dmgr.get(cnv_device) self.cnv = dmgr.get(cnv_device)
self.div = div self.div = div
self.gains = gains 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 @kernel
def init(self): def init(self):
@ -155,4 +164,4 @@ class Sampler:
for i in range(n): for i in range(n):
channel = i + 8 - len(data) channel = i + 8 - len(data)
gain = (self.gains >> (channel*2)) & 0b11 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)

View File

@ -77,6 +77,10 @@ class SPIMaster:
self.channel = channel self.channel = channel
self.update_xfer_duration_mu(div, length) self.update_xfer_duration_mu(div, length)
@staticmethod
def get_rtio_channels(channel, **kwargs):
return [(channel, None)]
@portable @portable
def frequency_to_div(self, f: float) -> int32: def frequency_to_div(self, f: float) -> int32:
"""Convert a SPI clock frequency to the closest SPI clock divider.""" """Convert a SPI clock frequency to the closest SPI clock divider."""

View File

@ -28,12 +28,12 @@ def y_mu_to_full_scale(y: int32) -> float:
@portable @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.""" """Convert servo ADC data from machine units to Volt."""
val = (x >> 1) & 0xffff val = (x >> 1) & 0xffff
mask = 1 << 15 mask = 1 << 15
val = -(val & mask) + (val & ~mask) val = -(val & mask) + (val & ~mask)
return sampler_adc_mu_to_volt(val, gain) return sampler_adc_mu_to_volt(val, gain, corrected_fs)
@nac3 @nac3
@ -68,6 +68,7 @@ class SUServo:
:param gains: Initial value for PGIA gains shift register :param gains: Initial value for PGIA gains shift register
(default: 0x0000). Knowledge of this state is not transferred (default: 0x0000). Knowledge of this state is not transferred
between experiments. between experiments.
:param sampler_hw_rev: Sampler's revision string
:param core_device: Core device name :param core_device: Core device name
""" """
@ -78,11 +79,11 @@ class SUServo:
channel: KernelInvariant[int32] channel: KernelInvariant[int32]
gains: Kernel[int32] gains: Kernel[int32]
ref_period_mu: KernelInvariant[int64] ref_period_mu: KernelInvariant[int64]
corrected_fs: KernelInvariant[bool]
def __init__(self, dmgr, channel, pgia_device, def __init__(self, dmgr, channel, pgia_device,
cpld_devices, dds_devices, 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.core = dmgr.get(core_device)
self.pgia = dmgr.get(pgia_device) self.pgia = dmgr.get(pgia_device)
@ -94,8 +95,13 @@ class SUServo:
self.gains = gains self.gains = gains
self.ref_period_mu = self.core.seconds_to_mu( self.ref_period_mu = self.core.seconds_to_mu(
self.core.coarse_ref_period) 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 assert self.ref_period_mu == self.core.ref_multiplier
@staticmethod
def get_rtio_channels(channel, **kwargs):
return [(channel, None)]
@kernel @kernel
def init(self): def init(self):
"""Initialize the servo, Sampler and both Urukuls. """Initialize the servo, Sampler and both Urukuls.
@ -247,7 +253,7 @@ class SUServo:
""" """
val = self.get_adc_mu(channel) val = self.get_adc_mu(channel)
gain = (self.gains >> (channel*2)) & 0b11 gain = (self.gains >> (channel*2)) & 0b11
return adc_mu_to_volts(val, gain) return adc_mu_to_volts(val, gain, self.corrected_fs)
@nac3 @nac3
@ -274,6 +280,10 @@ class Channel:
self.servo.channel) self.servo.channel)
self.dds = self.servo.ddses[self.servo_channel // 4] self.dds = self.servo.ddses[self.servo_channel // 4]
@staticmethod
def get_rtio_channels(channel, **kwargs):
return [(channel, None)]
@kernel @kernel
def set(self, en_out: bool, en_iir: bool = False, profile: int32 = 0): def set(self, en_out: bool, en_iir: bool = False, profile: int32 = 0):
"""Operate channel. """Operate channel.

View File

@ -39,6 +39,10 @@ class TTLOut:
self.channel = channel self.channel = channel
self.target_o = channel << 8 self.target_o = channel << 8
@staticmethod
def get_rtio_channels(channel, **kwargs):
return [(channel, None)]
@kernel @kernel
def output(self): def output(self):
pass pass
@ -137,6 +141,10 @@ class TTLInOut:
self.target_sens = (channel << 8) + 2 self.target_sens = (channel << 8) + 2
self.target_sample = (channel << 8) + 3 self.target_sample = (channel << 8) + 3
@staticmethod
def get_rtio_channels(channel, **kwargs):
return [(channel, None)]
@kernel @kernel
def set_oe(self, oe: bool): def set_oe(self, oe: bool):
rtio_output(self.target_oe, 1 if oe else 0) rtio_output(self.target_oe, 1 if oe else 0)
@ -477,6 +485,10 @@ class TTLClockGen:
self.target = channel << 8 self.target = channel << 8
self.acc_width = acc_width self.acc_width = acc_width
@staticmethod
def get_rtio_channels(channel, **kwargs):
return [(channel, None)]
@portable @portable
def frequency_to_ftw(self, frequency: float) -> int32: def frequency_to_ftw(self, frequency: float) -> int32:
"""Returns the frequency tuning word corresponding to the given """Returns the frequency tuning word corresponding to the given

View File

@ -14,92 +14,18 @@ from artiq.gui.scientific_spinbox import ScientificSpinBox
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
async def rename(key, newkey, value, dataset_ctl): async def rename(key, new_key, value, persist, dataset_ctl):
if key != newkey: if key != new_key:
await dataset_ctl.delete(key) await dataset_ctl.delete(key)
await dataset_ctl.set(newkey, value) await dataset_ctl.set(new_key, value, persist)
class Editor(QtWidgets.QDialog): class CreateEditDialog(QtWidgets.QDialog):
def __init__(self, parent, dataset_ctl, key, value): def __init__(self, parent, dataset_ctl, key=None, value=None, persist=False):
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):
QtWidgets.QDialog.__init__(self, parent=parent) QtWidgets.QDialog.__init__(self, parent=parent)
self.dataset_ctl = dataset_ctl self.dataset_ctl = dataset_ctl
self.setWindowTitle("Create dataset") self.setWindowTitle("Create dataset" if key is None else "Edit dataset")
grid = QtWidgets.QGridLayout() grid = QtWidgets.QGridLayout()
grid.setRowMinimumHeight(1, 40) grid.setRowMinimumHeight(1, 40)
grid.setColumnMinimumWidth(2, 60) grid.setColumnMinimumWidth(2, 60)
@ -130,16 +56,24 @@ class Creator(QtWidgets.QDialog):
self.buttons.addButton( self.buttons.addButton(
self.cancel, QtWidgets.QDialogButtonBox.RejectRole) self.cancel, QtWidgets.QDialogButtonBox.RejectRole)
grid.setRowStretch(3, 1) 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.accepted.connect(self.accept)
self.buttons.rejected.connect(self.reject) 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): def accept(self):
key = self.name_widget.text() key = self.name_widget.text()
value = self.value_widget.text() value = self.value_widget.text()
persist = self.box_widget.isChecked() persist = self.box_widget.isChecked()
asyncio.ensure_future(exc_to_warning(self.dataset_ctl.set( if self.key and self.key != key:
key, pyon.decode(value), persist))) 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) QtWidgets.QDialog.accept(self)
def dtype(self): def dtype(self):
@ -226,7 +160,7 @@ class DatasetsDock(QtWidgets.QDockWidget):
self.table.setModel(self.table_model_filter) self.table.setModel(self.table_model_filter)
def create_clicked(self): def create_clicked(self):
Creator(self, self.dataset_ctl).open() CreateEditDialog(self, self.dataset_ctl).open()
def edit_clicked(self): def edit_clicked(self):
idx = self.table.selectedIndexes() idx = self.table.selectedIndexes()
@ -236,17 +170,13 @@ class DatasetsDock(QtWidgets.QDockWidget):
if key is not None: if key is not None:
persist, value = self.table_model.backing_store[key] persist, value = self.table_model.backing_store[key]
t = type(value) t = type(value)
if np.issubdtype(t, np.number): if np.issubdtype(t, np.number) or np.issubdtype(t, np.bool_):
dialog_cls = NumberEditor value = str(value)
elif np.issubdtype(t, np.bool_):
dialog_cls = BoolEditor
elif np.issubdtype(t, np.unicode_): elif np.issubdtype(t, np.unicode_):
dialog_cls = StringEditor value = '"{}"'.format(str(value))
else: else:
logger.error("Cannot edit dataset %s: " value = pyon.encode(value)
"type %s is not supported", key, t) CreateEditDialog(self, self.dataset_ctl, key, value, persist).open()
return
dialog_cls(self, self.dataset_ctl, key, value).open()
def delete_clicked(self): def delete_clicked(self):
idx = self.table.selectedIndexes() idx = self.table.selectedIndexes()

View File

@ -371,9 +371,9 @@ checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
[[package]] [[package]]
name = "smoltcp" name = "smoltcp"
version = "0.8.0" version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2308a1657c8db1f5b4993bab4e620bdbe5623bd81f254cf60326767bb243237" checksum = "ee34c1e1bfc7e9206cc0fb8030a90129b4e319ab53856249bb27642cab914fb3"
dependencies = [ dependencies = [
"bitflags", "bitflags",
"byteorder", "byteorder",

View File

@ -16,5 +16,5 @@ build_misoc = { path = "../libbuild_misoc" }
byteorder = { version = "1.0", default-features = false } byteorder = { version = "1.0", default-features = false }
crc = { version = "1.7", default-features = false } crc = { version = "1.7", default-features = false }
board_misoc = { path = "../libboard_misoc", features = ["uart_console", "smoltcp"] } 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"] } riscv = { version = "0.6.0", features = ["inline-asm"] }

View File

@ -497,7 +497,7 @@ pub extern fn main() -> i32 {
println!(r"|_| |_|_|____/ \___/ \____|"); println!(r"|_| |_|_|____/ \___/ \____|");
println!(""); println!("");
println!("MiSoC Bootloader"); println!("MiSoC Bootloader");
println!("Copyright (c) 2017-2022 M-Labs Limited"); println!("Copyright (c) 2017-2023 M-Labs Limited");
println!(""); println!("");
#[cfg(has_ethmac)] #[cfg(has_ethmac)]

View File

@ -414,13 +414,13 @@ extern fn dma_playback(timestamp: i64, ptr: i32) {
csr::rtio_dma::error_write(1); csr::rtio_dma::error_write(1);
if error & 1 != 0 { if error & 1 != 0 {
raise!("RTIOUnderflow", raise!("RTIOUnderflow",
"RTIO underflow at {0} mu, channel {1}", "RTIO underflow at channel {rtio_channel_info:0}, {1} mu",
timestamp as i64, channel as i64, 0); channel as i64, timestamp as i64, 0);
} }
if error & 2 != 0 { if error & 2 != 0 {
raise!("RTIODestinationUnreachable", raise!("RTIODestinationUnreachable",
"RTIO destination unreachable, output, at {0} mu, channel {1}", "RTIO destination unreachable, output, at channel {rtio_channel_info:0}, {1} mu",
timestamp as i64, channel as i64, 0); 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] #[no_mangle]
pub unsafe fn main() { pub unsafe fn main() {
eh_artiq::reset_exception_buffer(KERNELCPU_PAYLOAD_ADDRESS); 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); ptr::write_bytes(__bss_start as *mut u8, 0, (_end - __bss_start) as usize);
board_misoc::pmp::init_stack_guard(_sstack_guard 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_dcache();
board_misoc::cache::flush_cpu_icache(); board_misoc::cache::flush_cpu_icache();
@ -531,10 +534,20 @@ pub unsafe fn main() {
#[no_mangle] #[no_mangle]
#[unwind(allowed)] #[unwind(allowed)]
pub extern fn exception(_regs: *const u32) { pub unsafe extern fn exception(_regs: *const u32) {
let pc = mepc::read(); let pc = mepc::read();
let cause = mcause::read().cause(); let cause = mcause::read().cause();
let mtval = mtval::read(); 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); panic!("{:?} at PC {:#08x}, trap value {:#08x}", cause, u32::try_from(pc).unwrap(), mtval);
} }

View File

@ -67,13 +67,13 @@ mod imp {
} }
if status & RTIO_O_STATUS_UNDERFLOW != 0 { if status & RTIO_O_STATUS_UNDERFLOW != 0 {
raise!("RTIOUnderflow", raise!("RTIOUnderflow",
"RTIO underflow at {0} mu, channel {1}, slack {2} mu", "RTIO underflow at channel {rtio_channel_info:0}, {1} mu, slack {2} mu",
timestamp, channel as i64, timestamp - get_counter()); channel as i64, timestamp, timestamp - get_counter());
} }
if status & RTIO_O_STATUS_DESTINATION_UNREACHABLE != 0 { if status & RTIO_O_STATUS_DESTINATION_UNREACHABLE != 0 {
raise!("RTIODestinationUnreachable", raise!("RTIODestinationUnreachable",
"RTIO destination unreachable, output, at {0} mu, channel {1}", "RTIO destination unreachable, output, at channel {rtio_channel_info:0}, {1} mu",
timestamp, channel as i64, 0); channel as i64, timestamp, 0);
} }
} }
@ -115,7 +115,7 @@ mod imp {
if status & RTIO_I_STATUS_OVERFLOW != 0 { if status & RTIO_I_STATUS_OVERFLOW != 0 {
raise!("RTIOOverflow", raise!("RTIOOverflow",
"RTIO input overflow on channel {0}", "RTIO input overflow on channel {rtio_channel_info:0}",
channel as i64, 0, 0); channel as i64, 0, 0);
} }
if status & RTIO_I_STATUS_WAIT_EVENT != 0 { if status & RTIO_I_STATUS_WAIT_EVENT != 0 {
@ -123,7 +123,7 @@ mod imp {
} }
if status & RTIO_I_STATUS_DESTINATION_UNREACHABLE != 0 { if status & RTIO_I_STATUS_DESTINATION_UNREACHABLE != 0 {
raise!("RTIODestinationUnreachable", raise!("RTIODestinationUnreachable",
"RTIO destination unreachable, input, on channel {0}", "RTIO destination unreachable, input, on channel {rtio_channel_info:0}",
channel as i64, 0, 0); channel as i64, 0, 0);
} }
@ -143,12 +143,12 @@ mod imp {
if status & RTIO_I_STATUS_OVERFLOW != 0 { if status & RTIO_I_STATUS_OVERFLOW != 0 {
raise!("RTIOOverflow", raise!("RTIOOverflow",
"RTIO input overflow on channel {0}", "RTIO input overflow on channel {rtio_channel_info:0}",
channel as i64, 0, 0); channel as i64, 0, 0);
} }
if status & RTIO_I_STATUS_DESTINATION_UNREACHABLE != 0 { if status & RTIO_I_STATUS_DESTINATION_UNREACHABLE != 0 {
raise!("RTIODestinationUnreachable", raise!("RTIODestinationUnreachable",
"RTIO destination unreachable, input, on channel {0}", "RTIO destination unreachable, input, on channel {rtio_channel_info:0}",
channel as i64, 0, 0); channel as i64, 0, 0);
} }
@ -168,7 +168,7 @@ mod imp {
if status & RTIO_I_STATUS_OVERFLOW != 0 { if status & RTIO_I_STATUS_OVERFLOW != 0 {
raise!("RTIOOverflow", raise!("RTIOOverflow",
"RTIO input overflow on channel {0}", "RTIO input overflow on channel {rtio_channel_info:0}",
channel as i64, 0, 0); channel as i64, 0, 0);
} }
if status & RTIO_I_STATUS_WAIT_EVENT != 0 { if status & RTIO_I_STATUS_WAIT_EVENT != 0 {
@ -176,7 +176,7 @@ mod imp {
} }
if status & RTIO_I_STATUS_DESTINATION_UNREACHABLE != 0 { if status & RTIO_I_STATUS_DESTINATION_UNREACHABLE != 0 {
raise!("RTIODestinationUnreachable", raise!("RTIODestinationUnreachable",
"RTIO destination unreachable, input, on channel {0}", "RTIO destination unreachable, input, on channel {rtio_channel_info:0}",
channel as i64, 0, 0); channel as i64, 0, 0);
} }

View File

@ -22,8 +22,6 @@ pub mod rpc_queue;
#[cfg(has_si5324)] #[cfg(has_si5324)]
pub mod si5324; pub mod si5324;
#[cfg(has_wrpll)]
pub mod wrpll;
#[cfg(has_grabber)] #[cfg(has_grabber)]
pub mod grabber; pub mod grabber;

View File

@ -28,7 +28,7 @@ pub struct FrequencySettings {
pub n31: u32, pub n31: u32,
pub n32: u32, pub n32: u32,
pub bwsel: u8, pub bwsel: u8,
pub crystal_ref: bool pub crystal_as_ckin2: bool
} }
pub enum Input { pub enum Input {
@ -83,7 +83,7 @@ fn map_frequency_settings(settings: &FrequencySettings) -> Result<FrequencySetti
n31: settings.n31 - 1, n31: settings.n31 - 1,
n32: settings.n32 - 1, n32: settings.n32 - 1,
bwsel: settings.bwsel, bwsel: settings.bwsel,
crystal_ref: settings.crystal_ref crystal_as_ckin2: settings.crystal_as_ckin2
}; };
Ok(r) Ok(r)
} }
@ -210,13 +210,14 @@ pub fn bypass(input: Input) -> Result<()> {
pub fn setup(settings: &FrequencySettings, input: Input) -> Result<()> { pub fn setup(settings: &FrequencySettings, input: Input) -> Result<()> {
let s = map_frequency_settings(settings)?; let s = map_frequency_settings(settings)?;
let cksel_reg = match input { let cksel_reg = match input {
Input::Ckin1 => 0b00, Input::Ckin1 => 0b00,
Input::Ckin2 => 0b01, Input::Ckin2 => 0b01,
}; };
init()?; init()?;
if settings.crystal_ref { if settings.crystal_as_ckin2 {
write(0, read(0)? | 0x40)?; // FREE_RUN=1 write(0, read(0)? | 0x40)?; // FREE_RUN=1
} }
write(2, (read(2)? & 0x0f) | (s.bwsel << 4))?; write(2, (read(2)? & 0x0f) | (s.bwsel << 4))?;

View File

@ -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<u8, &'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::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<i32, &'static str> {
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)
}
}

View File

@ -15,7 +15,7 @@ build_misoc = { path = "../libbuild_misoc" }
[dependencies] [dependencies]
byteorder = { version = "1.0", default-features = false } byteorder = { version = "1.0", default-features = false }
log = { version = "0.4", default-features = false, optional = true } 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"] } riscv = { version = "0.6.0", features = ["inline-asm"] }
[features] [features]

View File

@ -1,5 +1,14 @@
use i2c;
use csr; 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 { pub struct IoExpander {
busno: u8, busno: u8,
@ -9,15 +18,17 @@ pub struct IoExpander {
iodir: [u8; 2], iodir: [u8; 2],
out_current: [u8; 2], out_current: [u8; 2],
out_target: [u8; 2], out_target: [u8; 2],
registers: Registers,
} }
impl IoExpander { impl IoExpander {
#[cfg(all(soc_platform = "kasli", hw_rev = "v2.0"))] #[cfg(all(soc_platform = "kasli", hw_rev = "v2.0"))]
pub fn new(index: u8) -> Self { pub fn new(index: u8) -> Result<Self, &'static str> {
const VIRTUAL_LED_MAPPING0: [(u8, u8, u8); 2] = [(0, 0, 6), (1, 1, 6)]; 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)]; const VIRTUAL_LED_MAPPING1: [(u8, u8, u8); 2] = [(2, 0, 6), (3, 1, 6)];
// Both expanders on SHARED I2C bus // Both expanders on SHARED I2C bus
match index { let mut io_expander = match index {
0 => IoExpander { 0 => IoExpander {
busno: 0, busno: 0,
port: 11, port: 11,
@ -26,6 +37,12 @@ impl IoExpander {
iodir: [0xff; 2], iodir: [0xff; 2],
out_current: [0; 2], out_current: [0; 2],
out_target: [0; 2], out_target: [0; 2],
registers: Registers {
iodira: 0x00,
iodirb: 0x01,
gpioa: 0x12,
gpiob: 0x13,
},
}, },
1 => IoExpander { 1 => IoExpander {
busno: 0, busno: 0,
@ -35,9 +52,33 @@ impl IoExpander {
iodir: [0xff; 2], iodir: [0xff; 2],
out_current: [0; 2], out_current: [0; 2],
out_target: [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")] #[cfg(soc_platform = "kasli")]
@ -57,9 +98,18 @@ impl IoExpander {
Ok(()) Ok(())
} }
fn check_ack(&self) -> Result<bool, &'static str> {
// 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> { fn update_iodir(&self) -> Result<(), &'static str> {
self.write(0x00, self.iodir[0])?; self.write(self.registers.iodira, self.iodir[0])?;
self.write(0x01, self.iodir[1])?; self.write(self.registers.iodirb, self.iodir[1])?;
Ok(()) Ok(())
} }
@ -72,9 +122,9 @@ impl IoExpander {
self.update_iodir()?; self.update_iodir()?;
self.out_current[0] = 0x00; self.out_current[0] = 0x00;
self.write(0x12, 0x00)?; self.write(self.registers.gpioa, 0x00)?;
self.out_current[1] = 0x00; self.out_current[1] = 0x00;
self.write(0x13, 0x00)?; self.write(self.registers.gpiob, 0x00)?;
Ok(()) Ok(())
} }
@ -94,20 +144,18 @@ impl IoExpander {
pub fn service(&mut self) -> Result<(), &'static str> { pub fn service(&mut self) -> Result<(), &'static str> {
for (led, port, bit) in self.virtual_led_mapping.iter() { for (led, port, bit) in self.virtual_led_mapping.iter() {
let level = unsafe { let level = unsafe { (csr::virtual_leds::status_read() >> led) & 1 };
(csr::virtual_leds::status_read() >> led) & 1
};
self.set(*port, *bit, level != 0); self.set(*port, *bit, level != 0);
} }
if self.out_target != self.out_current { if self.out_target != self.out_current {
self.select()?; self.select()?;
if self.out_target[0] != self.out_current[0] { 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]; self.out_current[0] = self.out_target[0];
} }
if self.out_target[1] != self.out_current[1] { 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]; self.out_current[1] = self.out_target[1];
} }
} }

View File

@ -9,6 +9,8 @@ const PMP_W : usize = 0b00000010;
const PMP_R : usize = 0b00000001; const PMP_R : usize = 0b00000001;
const PMP_OFF : usize = 0b00000000; const PMP_OFF : usize = 0b00000000;
pub const STACK_GUARD_SIZE: usize = 0x1000;
#[inline(always)] #[inline(always)]
pub unsafe fn init_stack_guard(guard_base: usize) { pub unsafe fn init_stack_guard(guard_base: usize) {
pmpaddr2::write((guard_base >> 2) | ((0x1000 - 1) >> 3)); pmpaddr2::write((guard_base >> 2) | ((0x1000 - 1) >> 3));

View File

@ -429,8 +429,8 @@ mod tag {
// the ptr/length(s) pair is basically CSlice // the ptr/length(s) pair is basically CSlice
Tag::Bytes | Tag::String | Tag::ByteArray | Tag::List(_) | Tag::Array(_, _) => Tag::Bytes | Tag::String | Tag::ByteArray | Tag::List(_) | Tag::Array(_, _) =>
core::mem::align_of::<CSlice<()>>(), core::mem::align_of::<CSlice<()>>(),
// will not be sent from the host Tag::Keyword(_) => unreachable!("Tag::Keyword should not appear in composite types"),
_ => unreachable!("unexpected tag from host") Tag::Object => core::mem::align_of::<u32>(),
} }
} }

View File

@ -1,4 +1,4 @@
#![feature(lang_items, panic_info_message)] #![feature(lang_items, panic_info_message, const_btree_new, iter_advance_by)]
#![no_std] #![no_std]
extern crate eh; extern crate eh;
@ -104,8 +104,8 @@ fn startup() {
let (mut io_expander0, mut io_expander1); let (mut io_expander0, mut io_expander1);
#[cfg(all(soc_platform = "kasli", hw_rev = "v2.0"))] #[cfg(all(soc_platform = "kasli", hw_rev = "v2.0"))]
{ {
io_expander0 = board_misoc::io_expander::IoExpander::new(0); io_expander0 = board_misoc::io_expander::IoExpander::new(0).unwrap();
io_expander1 = board_misoc::io_expander::IoExpander::new(1); io_expander1 = board_misoc::io_expander::IoExpander::new(1).unwrap();
io_expander0.init().expect("I2C I/O expander #0 initialization failed"); io_expander0.init().expect("I2C I/O expander #0 initialization failed");
io_expander1.init().expect("I2C I/O expander #1 initialization failed"); io_expander1.init().expect("I2C I/O expander #1 initialization failed");

View File

@ -1,7 +1,5 @@
use board_misoc::config; use board_misoc::config;
#[cfg(si5324_as_synthesizer)]
use board_artiq::si5324; use board_artiq::si5324;
#[cfg(has_drtio)]
use board_misoc::{csr, clock}; use board_misoc::{csr, clock};
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
@ -10,7 +8,6 @@ pub enum RtioClock {
Default, Default,
Int_125, Int_125,
Int_100, Int_100,
Int_150,
Ext0_Bypass, Ext0_Bypass,
Ext0_Synth0_10to125, Ext0_Synth0_10to125,
Ext0_Synth0_100to125, Ext0_Synth0_100to125,
@ -23,7 +20,6 @@ fn get_rtio_clock_cfg() -> RtioClock {
let res = match result { let res = match result {
Ok("int_125") => RtioClock::Int_125, Ok("int_125") => RtioClock::Int_125,
Ok("int_100") => RtioClock::Int_100, Ok("int_100") => RtioClock::Int_100,
Ok("int_150") => RtioClock::Int_150,
Ok("ext0_bypass") => RtioClock::Ext0_Bypass, Ok("ext0_bypass") => RtioClock::Ext0_Bypass,
Ok("ext0_bypass_125") => RtioClock::Ext0_Bypass, Ok("ext0_bypass_125") => RtioClock::Ext0_Bypass,
Ok("ext0_bypass_100") => 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; return RtioClock::Ext0_Synth0_125to125;
#[cfg(all(rtio_frequency = "125.0", not(si5324_ext_ref)))] #[cfg(all(rtio_frequency = "125.0", not(si5324_ext_ref)))]
return RtioClock::Int_125; return RtioClock::Int_125;
#[cfg(all(rtio_frequency = "150.0", not(si5324_ext_ref)))] #[cfg(all(rtio_frequency = "100.0", not(si5324_ext_ref), not(soc_platform = "kasli")))]
return RtioClock::Int_150;
#[cfg(all(rtio_frequency = "100.0", not(si5324_ext_ref)))]
return RtioClock::Int_100; return RtioClock::Int_100;
//in case nothing is set //in case nothing is set
return RtioClock::Int_125; return RtioClock::Int_125;
@ -68,40 +62,12 @@ fn get_rtio_clock_cfg() -> RtioClock {
#[cfg(has_rtio_crg)] #[cfg(has_rtio_crg)]
pub mod crg { pub mod crg {
#[cfg(has_rtio_clock_switch)]
use super::RtioClock;
use board_misoc::{clock, csr}; use board_misoc::{clock, csr};
pub fn check() -> bool { pub fn check() -> bool {
unsafe { csr::rtio_crg::pll_locked_read() != 0 } 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 { pub fn init() -> bool {
info!("Using internal RTIO clock"); info!("Using internal RTIO clock");
unsafe { unsafe {
@ -117,147 +83,177 @@ pub mod crg {
pub fn check() -> bool { true } pub fn check() -> bool { true }
} }
#[cfg(si5324_as_synthesizer)] // Si5324 input to select for locking to an external clock (as opposed to
fn setup_si5324_as_synthesizer(cfg: RtioClock) { // a recovered link clock in DRTIO satellites, which is handled elsewhere).
let si5324_settings = match cfg { #[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 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"); info!("using 10MHz reference to make 125MHz RTIO clock with PLL");
si5324::FrequencySettings { (
n1_hs : 10, si5324::FrequencySettings {
nc1_ls : 4, n1_hs : 10,
n2_hs : 10, nc1_ls : 4,
n2_ls : 300, n2_hs : 10,
n31 : 6, n2_ls : 300,
n32 : 6, n31 : 6,
bwsel : 4, n32 : 6,
crystal_ref: false bwsel : 4,
} crystal_as_ckin2: false
},
SI5324_EXT_INPUT
)
}, },
RtioClock::Ext0_Synth0_100to125 => { // 125MHz output, from 100MHz CLKINx reference, 586 Hz loop bandwidth 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"); info!("using 100MHz reference to make 125MHz RTIO clock with PLL");
si5324::FrequencySettings { (
n1_hs : 10, si5324::FrequencySettings {
nc1_ls : 4, n1_hs : 10,
n2_hs : 10, nc1_ls : 4,
n2_ls : 260, n2_hs : 10,
n31 : 52, n2_ls : 260,
n32 : 52, n31 : 52,
bwsel : 4, n32 : 52,
crystal_ref: false bwsel : 4,
} crystal_as_ckin2: false
},
SI5324_EXT_INPUT
)
}, },
RtioClock::Ext0_Synth0_125to125 => { // 125MHz output, from 125MHz CLKINx reference, 606 Hz loop bandwidth 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"); info!("using 125MHz reference to make 125MHz RTIO clock with PLL");
si5324::FrequencySettings { (
n1_hs : 5, si5324::FrequencySettings {
nc1_ls : 8, n1_hs : 5,
n2_hs : 7, nc1_ls : 8,
n2_ls : 360, n2_hs : 7,
n31 : 63, n2_ls : 360,
n32 : 63, n31 : 63,
bwsel : 4, n32 : 63,
crystal_ref: false bwsel : 4,
} crystal_as_ckin2: false
}, },
RtioClock::Int_150 => { // 150MHz output, from crystal SI5324_EXT_INPUT
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
}
}, },
RtioClock::Int_100 => { // 100MHz output, from crystal RtioClock::Int_100 => { // 100MHz output, from crystal
info!("using internal 100MHz RTIO clock"); info!("using internal 100MHz RTIO clock");
si5324::FrequencySettings { (
n1_hs : 9, si5324::FrequencySettings {
nc1_ls : 6, n1_hs : 9,
n2_hs : 10, nc1_ls : 6,
n2_ls : 33732, n2_hs : 10,
n31 : 7139, n2_ls : 33732,
n32 : 7139, n31 : 7139,
bwsel : 3, n32 : 7139,
crystal_ref: true bwsel : 3,
} crystal_as_ckin2: true
},
si5324::Input::Ckin2
)
}, },
RtioClock::Int_125 => { // 125MHz output, from crystal, 7 Hz RtioClock::Int_125 => { // 125MHz output, from crystal, 7 Hz
info!("using internal 125MHz RTIO clock"); info!("using internal 125MHz RTIO clock");
si5324::FrequencySettings { (
n1_hs : 10, si5324::FrequencySettings {
nc1_ls : 4, n1_hs : 10,
n2_hs : 10, nc1_ls : 4,
n2_ls : 19972, n2_hs : 10,
n31 : 4565, n2_ls : 19972,
n32 : 4565, n31 : 4565,
bwsel : 4, n32 : 4565,
crystal_ref: true bwsel : 4,
} crystal_as_ckin2: true
} },
si5324::Input::Ckin2
)
},
_ => { // 125MHz output like above, default (if chosen option is not supported) _ => { // 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); warn!("rtio_clock setting '{:?}' is not supported. Falling back to default internal 125MHz RTIO clock.", cfg);
si5324::FrequencySettings { (
n1_hs : 10, si5324::FrequencySettings {
nc1_ls : 4, n1_hs : 10,
n2_hs : 10, nc1_ls : 4,
n2_ls : 19972, n2_hs : 10,
n31 : 4565, n2_ls : 19972,
n32 : 4565, n31 : 4565,
bwsel : 4, n32 : 4565,
crystal_ref: true 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"); si5324::setup(&si5324_settings, si5324_ref_input).expect("cannot initialize Si5324");
} }
pub fn init() { fn setup_si5324(clock_cfg: RtioClock) {
let clock_cfg = get_rtio_clock_cfg(); let switched = unsafe {
#[cfg(si5324_as_synthesizer)] 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"))] info!("Switching sys clock, rebooting...");
let si5324_ext_input = si5324::Input::Ckin1; // delay for clean UART log, wait until UART FIFO is empty
#[cfg(all(soc_platform = "kasli", not(hw_rev = "v2.0")))] clock::spin_us(1300);
let si5324_ext_input = si5324::Input::Ckin2; unsafe {
#[cfg(soc_platform = "kc705")] csr::crg::clock_sel_write(1);
let si5324_ext_input = si5324::Input::Ckin2; loop {}
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),
} }
} }
}
pub fn init() {
let clock_cfg = get_rtio_clock_cfg();
setup_si5324(clock_cfg);
#[cfg(has_drtio)] #[cfg(has_drtio)]
{ {
unsafe { let switched = unsafe {
csr::drtio_transceiver::stable_clkin_write(1); 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 else {
unsafe { // enable TX after the reboot, with stable clock
csr::drtio_transceiver::txenable_write(0xffffffffu32 as _); unsafe {
csr::drtio_transceiver::txenable_write(0xffffffffu32 as _);
}
} }
} }
#[cfg(has_rtio_crg)] #[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(); let result = crg::init();
if !result { if !result {
error!("RTIO clock failed"); error!("RTIO clock failed");

View File

@ -1,15 +1,20 @@
use alloc::collections::BTreeMap;
use alloc::string::String;
use core::cell::RefCell; use core::cell::RefCell;
use urc::Urc; use urc::Urc;
use board_misoc::csr; use board_misoc::{csr, config};
#[cfg(has_drtio)] #[cfg(has_drtio)]
use board_misoc::clock; use board_misoc::clock;
use board_artiq::drtio_routing; use board_artiq::drtio_routing;
use sched::Io; use sched::Io;
use sched::Mutex; use sched::Mutex;
use io::{Cursor, ProtoRead};
const ASYNC_ERROR_COLLISION: u8 = 1 << 0; const ASYNC_ERROR_COLLISION: u8 = 1 << 0;
const ASYNC_ERROR_BUSY: u8 = 1 << 1; const ASYNC_ERROR_BUSY: u8 = 1 << 1;
const ASYNC_ERROR_SEQUENCE_ERROR: u8 = 1 << 2; const ASYNC_ERROR_SEQUENCE_ERROR: u8 = 1 << 2;
static mut RTIO_DEVICE_MAP: BTreeMap<u32, String> = BTreeMap::new();
#[cfg(has_drtio)] #[cfg(has_drtio)]
pub mod drtio { pub mod drtio {
use super::*; use super::*;
@ -215,15 +220,15 @@ pub mod drtio {
destination_set_up(routing_table, up_destinations, destination, false), destination_set_up(routing_table, up_destinations, destination, false),
Ok(drtioaux::Packet::DestinationOkReply) => (), Ok(drtioaux::Packet::DestinationOkReply) => (),
Ok(drtioaux::Packet::DestinationSequenceErrorReply { channel }) => { 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 }; unsafe { SEEN_ASYNC_ERRORS |= ASYNC_ERROR_SEQUENCE_ERROR };
} }
Ok(drtioaux::Packet::DestinationCollisionReply { channel }) => { 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 }; unsafe { SEEN_ASYNC_ERRORS |= ASYNC_ERROR_COLLISION };
} }
Ok(drtioaux::Packet::DestinationBusyReply { channel }) => { 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 }; unsafe { SEEN_ASYNC_ERRORS |= ASYNC_ERROR_BUSY };
} }
Ok(packet) => error!("[DEST#{}] received unexpected aux packet: {:?}", destination, packet), 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(); io.until(|| csr::rtio_core::async_error_read() != 0).unwrap();
let errors = csr::rtio_core::async_error_read(); let errors = csr::rtio_core::async_error_read();
if errors & ASYNC_ERROR_COLLISION != 0 { if errors & ASYNC_ERROR_COLLISION != 0 {
error!("RTIO collision involving channel {}", let channel = csr::rtio_core::collision_channel_read();
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 { if errors & ASYNC_ERROR_BUSY != 0 {
error!("RTIO busy error involving channel {}", let channel = csr::rtio_core::busy_channel_read();
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 { if errors & ASYNC_ERROR_SEQUENCE_ERROR != 0 {
error!("RTIO sequence error involving channel {}", let channel = csr::rtio_core::sequence_error_channel_read();
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; SEEN_ASYNC_ERRORS = errors;
csr::rtio_core::async_error_write(errors); csr::rtio_core::async_error_write(errors);
@ -366,9 +371,47 @@ fn async_error_thread(io: Io) {
} }
} }
fn read_device_map() -> BTreeMap<u32, String> {
let mut device_map: BTreeMap<u32, String> = 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<u32, String>) -> 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, pub fn startup(io: &Io, aux_mutex: &Mutex,
routing_table: &Urc<RefCell<drtio_routing::RoutingTable>>, routing_table: &Urc<RefCell<drtio_routing::RoutingTable>>,
up_destinations: &Urc<RefCell<[bool; drtio_routing::DEST_COUNT]>>) { up_destinations: &Urc<RefCell<[bool; drtio_routing::DEST_COUNT]>>) {
unsafe { RTIO_DEVICE_MAP = read_device_map(); }
drtio::startup(io, aux_mutex, routing_table, up_destinations); drtio::startup(io, aux_mutex, routing_table, up_destinations);
unsafe { unsafe {
csr::rtio_core::reset_phy_write(1); csr::rtio_core::reset_phy_write(1);

View File

@ -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 alloc::{vec::Vec, string::String};
use byteorder::{ByteOrder, NativeEndian}; use byteorder::{ByteOrder, NativeEndian};
use cslice::CSlice; use cslice::CSlice;
@ -10,7 +10,7 @@ use urc::Urc;
use sched::{ThreadHandle, Io, Mutex, TcpListener, TcpStream, Error as SchedError}; use sched::{ThreadHandle, Io, Mutex, TcpListener, TcpStream, Error as SchedError};
use rtio_clocking; use rtio_clocking;
use rtio_dma::Manager as DmaManager; 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 cache::Cache;
use kern_hwreq; use kern_hwreq;
use board_artiq::drtio_routing; use board_artiq::drtio_routing;
@ -449,6 +449,29 @@ fn process_kern_message(io: &Io, aux_mutex: &Mutex,
session.kernel_state = KernelState::Absent; session.kernel_state = KernelState::Absent;
unsafe { session.congress.cache.unborrow() } unsafe { session.congress.cache.unborrow() }
let exceptions_with_channel: Vec<Option<eh::eh_artiq::Exception>> = 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 { match stream {
None => { None => {
error!("exception in flash kernel"); error!("exception in flash kernel");
@ -459,7 +482,7 @@ fn process_kern_message(io: &Io, aux_mutex: &Mutex,
}, },
Some(ref mut stream) => { Some(ref mut stream) => {
host_write(stream, host::Reply::KernelException { host_write(stream, host::Reply::KernelException {
exceptions: exceptions, exceptions: &exceptions_with_channel,
stack_pointers: stack_pointers, stack_pointers: stack_pointers,
backtrace: backtrace, backtrace: backtrace,
async_errors: unsafe { get_async_errors() } async_errors: unsafe { get_async_errors() }

View File

@ -12,8 +12,6 @@ use core::convert::TryFrom;
use board_misoc::{csr, ident, clock, uart_logger, i2c, pmp}; use board_misoc::{csr, ident, clock, uart_logger, i2c, pmp};
#[cfg(has_si5324)] #[cfg(has_si5324)]
use board_artiq::si5324; use board_artiq::si5324;
#[cfg(has_wrpll)]
use board_artiq::wrpll;
use board_artiq::{spi, drtioaux}; use board_artiq::{spi, drtioaux};
use board_artiq::drtio_routing; use board_artiq::drtio_routing;
use riscv::register::{mcause, mepc, mtval}; 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"))] #[cfg(all(has_si5324, rtio_frequency = "125.0"))]
const SI5324_SETTINGS: si5324::FrequencySettings const SI5324_SETTINGS: si5324::FrequencySettings
= si5324::FrequencySettings { = si5324::FrequencySettings {
@ -406,7 +391,7 @@ const SI5324_SETTINGS: si5324::FrequencySettings
n31 : 63, n31 : 63,
n32 : 63, n32 : 63,
bwsel : 4, bwsel : 4,
crystal_ref: true crystal_as_ckin2: true
}; };
#[cfg(all(has_si5324, rtio_frequency = "100.0"))] #[cfg(all(has_si5324, rtio_frequency = "100.0"))]
@ -419,9 +404,31 @@ const SI5324_SETTINGS: si5324::FrequencySettings
n31 : 50, n31 : 50,
n32 : 50, n32 : 50,
bwsel : 4, 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] #[no_mangle]
pub extern fn main() -> i32 { pub extern fn main() -> i32 {
extern { extern {
@ -445,21 +452,10 @@ pub extern fn main() -> i32 {
let (mut io_expander0, mut io_expander1); let (mut io_expander0, mut io_expander1);
#[cfg(all(soc_platform = "kasli", hw_rev = "v2.0"))] #[cfg(all(soc_platform = "kasli", hw_rev = "v2.0"))]
{ {
io_expander0 = board_misoc::io_expander::IoExpander::new(0); io_expander0 = board_misoc::io_expander::IoExpander::new(0).unwrap();
io_expander1 = board_misoc::io_expander::IoExpander::new(1); io_expander1 = board_misoc::io_expander::IoExpander::new(1).unwrap();
io_expander0.init().expect("I2C I/O expander #0 initialization failed"); io_expander0.init().expect("I2C I/O expander #0 initialization failed");
io_expander1.init().expect("I2C I/O expander #1 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 // Actively drive TX_DISABLE to false on SFP0..3
io_expander0.set_oe(0, 1 << 1).unwrap(); io_expander0.set_oe(0, 1 << 1).unwrap();
@ -474,21 +470,12 @@ pub extern fn main() -> i32 {
io_expander1.service().unwrap(); io_expander1.service().unwrap();
} }
#[cfg(has_si5324)] sysclk_setup();
si5324::setup(&SI5324_SETTINGS, si5324::Input::Ckin1).expect("cannot initialize Si5324");
#[cfg(has_wrpll)]
wrpll::init();
unsafe {
csr::drtio_transceiver::stable_clkin_write(1);
}
clock::spin_us(1500); // wait for CPLL/QPLL lock
#[cfg(not(has_jdcg))]
unsafe { unsafe {
csr::drtio_transceiver::txenable_write(0xffffffffu32 as _); csr::drtio_transceiver::txenable_write(0xffffffffu32 as _);
} }
#[cfg(has_wrpll)]
wrpll::diagnostics();
init_rtio_crg(); init_rtio_crg();
#[cfg(has_drtio_routing)] #[cfg(has_drtio_routing)]
@ -504,11 +491,6 @@ pub extern fn main() -> i32 {
let mut hardware_tick_ts = 0; let mut hardware_tick_ts = 0;
loop { loop {
#[cfg(has_jdcg)]
unsafe {
// Hide from uplink until RTM is ready
csr::drtio_transceiver::txenable_write(0xfffffffeu32 as _);
}
while !drtiosat_link_rx_up() { while !drtiosat_link_rx_up() {
drtiosat_process_errors(); drtiosat_process_errors();
for rep in repeaters.iter_mut() { 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::select_recovered_clock(true).expect("failed to switch clocks");
si5324::siphaser::calibrate_skew().expect("failed to calibrate skew"); si5324::siphaser::calibrate_skew().expect("failed to calibrate skew");
} }
#[cfg(has_wrpll)]
wrpll::select_recovered_clock(true);
drtioaux::reset(0); drtioaux::reset(0);
drtiosat_reset(false); drtiosat_reset(false);
drtiosat_reset_phy(false); drtiosat_reset_phy(false);
#[cfg(has_jdcg)]
let mut was_up = false;
while drtiosat_link_rx_up() { while drtiosat_link_rx_up() {
drtiosat_process_errors(); drtiosat_process_errors();
process_aux_packets(&mut repeaters, &mut routing_table, &mut rank); 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); hardware_tick(&mut hardware_tick_ts);
if drtiosat_tsc_loaded() { if drtiosat_tsc_loaded() {
info!("TSC loaded from uplink"); 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() { for rep in repeaters.iter() {
if let Err(e) = rep.sync_tsc() { if let Err(e) = rep.sync_tsc() {
error!("failed to sync TSC ({})", e); error!("failed to sync TSC ({})", e);
@ -572,46 +538,14 @@ pub extern fn main() -> i32 {
error!("aux packet error: {}", e); 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_phy(true);
drtiosat_reset(true); drtiosat_reset(true);
drtiosat_tsc_loaded(); drtiosat_tsc_loaded();
info!("uplink is down, switching to local oscillator clock"); info!("uplink is down, switching to local oscillator clock");
#[cfg(has_si5324)] #[cfg(has_si5324)]
si5324::siphaser::select_recovered_clock(false).expect("failed to switch clocks"); si5324::siphaser::select_recovered_clock(false).expect("failed to switch clocks");
#[cfg(has_wrpll)]
wrpll::select_recovered_clock(false);
} }
} }

View File

@ -96,14 +96,15 @@ def main():
signal_handler.setup() signal_handler.setup()
try: try:
get_logs_task = asyncio.ensure_future( 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: try:
server = Server({"corelog": PingTarget()}, None, True) server = Server({"corelog": PingTarget()}, None, True)
loop.run_until_complete(server.start(common_args.bind_address_from_args(args), args.port)) loop.run_until_complete(server.start(common_args.bind_address_from_args(args), args.port))
try: try:
_, pending = loop.run_until_complete(asyncio.wait( _, pending = loop.run_until_complete(asyncio.wait(
[signal_handler.wait_terminate(), [loop.create_task(signal_handler.wait_terminate()),
server.wait_terminate(), loop.create_task(server.wait_terminate()),
get_logs_task], get_logs_task],
return_when=asyncio.FIRST_COMPLETED)) return_when=asyncio.FIRST_COMPLETED))
for task in pending: for task in pending:

View File

@ -219,8 +219,8 @@ def main():
loop.run_until_complete(server.start(bind_address, args.port_control)) loop.run_until_complete(server.start(bind_address, args.port_control))
try: try:
_, pending = loop.run_until_complete(asyncio.wait( _, pending = loop.run_until_complete(asyncio.wait(
[signal_handler.wait_terminate(), [loop.create_task(signal_handler.wait_terminate()),
server.wait_terminate(), loop.create_task(server.wait_terminate()),
comm_moninj.wait_terminate()], comm_moninj.wait_terminate()],
return_when=asyncio.FIRST_COMPLETED)) return_when=asyncio.FIRST_COMPLETED))
for task in pending: for task in pending:

View File

@ -49,7 +49,7 @@ def get_argparser():
class Browser(QtWidgets.QMainWindow): class Browser(QtWidgets.QMainWindow):
def __init__(self, smgr, datasets_sub, browse_root, def __init__(self, smgr, datasets_sub, browse_root,
master_host, master_port): master_host, master_port, *, loop=None):
QtWidgets.QMainWindow.__init__(self) QtWidgets.QMainWindow.__init__(self)
smgr.register(self) smgr.register(self)
@ -81,9 +81,9 @@ class Browser(QtWidgets.QMainWindow):
self.files.dataset_changed.connect( self.files.dataset_changed.connect(
self.experiments.dataset_changed) self.experiments.dataset_changed)
self.applets = applets.AppletsDock(self, datasets_sub) self.applets = applets.AppletsDock(self, datasets_sub, loop=loop)
smgr.register(self.applets) smgr.register(self.applets)
atexit_register_coroutine(self.applets.stop) atexit_register_coroutine(self.applets.stop, loop=loop)
self.datasets = datasets.DatasetsDock( self.datasets = datasets.DatasetsDock(
datasets_sub, master_host, master_port) datasets_sub, master_host, master_port)
@ -152,7 +152,7 @@ def main():
smgr = state.StateManager(args.db_file) smgr = state.StateManager(args.db_file)
browser = Browser(smgr, datasets_sub, args.browse_root, 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 widget_log_handler.callback = browser.log.model.append
if os.name == "nt": if os.name == "nt":
@ -161,8 +161,8 @@ def main():
# QDockWidgets fail to be embedded. # QDockWidgets fail to be embedded.
browser.show() browser.show()
smgr.load() smgr.load()
smgr.start() smgr.start(loop=loop)
atexit_register_coroutine(smgr.stop) atexit_register_coroutine(smgr.stop, loop=loop)
if args.select is not None: if args.select is not None:
browser.files.select(args.select) browser.files.select(args.select)

View File

@ -60,6 +60,8 @@ def main():
arguments = parse_arguments(args.arguments) arguments = parse_arguments(args.arguments)
argument_mgr = ProcessArgumentManager(arguments) argument_mgr = ProcessArgumentManager(arguments)
exp_inst = exp((device_mgr, dataset_mgr, argument_mgr, {})) exp_inst = exp((device_mgr, dataset_mgr, argument_mgr, {}))
argument_mgr.check_unprocessed_arguments()
if not getattr(exp.run, "__artiq_kernel__", False): if not getattr(exp.run, "__artiq_kernel__", False):
raise ValueError("Experiment entry point must be a kernel") raise ValueError("Experiment entry point must be a kernel")

View File

@ -148,7 +148,7 @@ def main():
report_disconnect) report_disconnect)
loop.run_until_complete(subscriber.connect( loop.run_until_complete(subscriber.connect(
args.server, args.port_notify)) args.server, args.port_notify))
atexit_register_coroutine(subscriber.close) atexit_register_coroutine(subscriber.close, loop=loop)
sub_clients[notifier_name] = subscriber sub_clients[notifier_name] = subscriber
broadcast_clients = dict() broadcast_clients = dict()
@ -156,7 +156,7 @@ def main():
client = Receiver(target, [], report_disconnect) client = Receiver(target, [], report_disconnect)
loop.run_until_complete(client.connect( loop.run_until_complete(client.connect(
args.server, args.port_broadcast)) args.server, args.port_broadcast))
atexit_register_coroutine(client.close) atexit_register_coroutine(client.close, loop=loop)
broadcast_clients[target] = client broadcast_clients[target] = client
# initialize main window # initialize main window
@ -195,14 +195,15 @@ def main():
"server": args.server, "server": args.server,
"port_notify": args.port_notify, "port_notify": args.port_notify,
"port_control": args.port_control, "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) smgr.register(d_applets)
broadcast_clients["ccb"].notify_cbs.append(d_applets.ccb_notify) broadcast_clients["ccb"].notify_cbs.append(d_applets.ccb_notify)
d_ttl_dds = moninj.MonInj(rpc_clients["schedule"]) d_ttl_dds = moninj.MonInj(rpc_clients["schedule"])
loop.run_until_complete(d_ttl_dds.start(args.server, args.port_notify)) 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( d_schedule = schedule.ScheduleDock(
rpc_clients["schedule"], sub_clients["schedule"]) rpc_clients["schedule"], sub_clients["schedule"])
@ -231,8 +232,8 @@ def main():
# QDockWidgets fail to be embedded. # QDockWidgets fail to be embedded.
main_window.show() main_window.show()
smgr.load() smgr.load()
smgr.start() smgr.start(loop=loop)
atexit_register_coroutine(smgr.stop) atexit_register_coroutine(smgr.stop, loop=loop)
# work around for https://github.com/m-labs/artiq/issues/1307 # work around for https://github.com/m-labs/artiq/issues/1307
d_ttl_dds.ttl_dock.show() d_ttl_dds.ttl_dock.show()

View File

@ -8,6 +8,7 @@ from itertools import count
from artiq import __version__ as artiq_version from artiq import __version__ as artiq_version
from artiq.coredevice import jsondesc from artiq.coredevice import jsondesc
from artiq.coredevice.phaser import PHASER_GW_MIQRO, PHASER_GW_BASE
def process_header(output, description): def process_header(output, description):
@ -382,10 +383,12 @@ class PeripheralManager:
"arguments": {{ "arguments": {{
"spi_adc_device": "spi_{name}_adc", "spi_adc_device": "spi_{name}_adc",
"spi_pgia_device": "spi_{name}_pgia", "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"), name=self.get_name("sampler"),
hw_rev=peripheral.get("hw_rev", "v2.2"),
adc_channel=rtio_offset, adc_channel=rtio_offset,
pgia_channel=rtio_offset + 1, pgia_channel=rtio_offset + 1,
cnv_channel=rtio_offset + 2) cnv_channel=rtio_offset + 2)
@ -416,11 +419,13 @@ class PeripheralManager:
"channel": 0x{suservo_channel:06x}, "channel": 0x{suservo_channel:06x},
"pgia_device": "spi_{sampler_name}_pgia", "pgia_device": "spi_{sampler_name}_pgia",
"cpld_devices": {cpld_names_list}, "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, suservo_name=suservo_name,
sampler_name=sampler_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], cpld_names_list=[urukul_name + "_cpld" for urukul_name in urukul_names],
dds_names_list=[urukul_name + "_dds" for urukul_name in urukul_names], dds_names_list=[urukul_name + "_dds" for urukul_name in urukul_names],
suservo_channel=rtio_offset+next(channel)) suservo_channel=rtio_offset+next(channel))
@ -534,10 +539,10 @@ class PeripheralManager:
def process_phaser(self, rtio_offset, peripheral): def process_phaser(self, rtio_offset, peripheral):
mode = peripheral.get("mode", "base") mode = peripheral.get("mode", "base")
if mode == "miqro": 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 n_channels = 3
else: else:
dac = "" dac = f', "gw_rev"={PHASER_GW_BASE}'
n_channels = 5 n_channels = 5
self.gen(""" self.gen("""
device_db["{name}"] = {{ device_db["{name}"] = {{

View File

@ -86,7 +86,7 @@ def main():
server_broadcast = Broadcaster() server_broadcast = Broadcaster()
loop.run_until_complete(server_broadcast.start( loop.run_until_complete(server_broadcast.start(
bind, args.port_broadcast)) bind, args.port_broadcast))
atexit_register_coroutine(server_broadcast.stop) atexit_register_coroutine(server_broadcast.stop, loop=loop)
log_forwarder.callback = (lambda msg: log_forwarder.callback = (lambda msg:
server_broadcast.broadcast("log", msg)) server_broadcast.broadcast("log", msg))
@ -100,8 +100,8 @@ def main():
device_db = DeviceDB(args.device_db) device_db = DeviceDB(args.device_db)
dataset_db = DatasetDB(args.dataset_db) dataset_db = DatasetDB(args.dataset_db)
dataset_db.start() dataset_db.start(loop=loop)
atexit_register_coroutine(dataset_db.stop) atexit_register_coroutine(dataset_db.stop, loop=loop)
worker_handlers = dict() worker_handlers = dict()
if args.git: if args.git:
@ -113,8 +113,8 @@ def main():
atexit.register(experiment_db.close) atexit.register(experiment_db.close)
scheduler = Scheduler(RIDCounter(), worker_handlers, experiment_db, args.log_submissions) scheduler = Scheduler(RIDCounter(), worker_handlers, experiment_db, args.log_submissions)
scheduler.start() scheduler.start(loop=loop)
atexit_register_coroutine(scheduler.stop) atexit_register_coroutine(scheduler.stop, loop=loop)
config = MasterConfig(args.name) config = MasterConfig(args.name)
@ -131,7 +131,7 @@ def main():
"scheduler_check_termination": scheduler.check_termination, "scheduler_check_termination": scheduler.check_termination,
"ccb_issue": ccb_issue, "ccb_issue": ccb_issue,
}) })
experiment_db.scan_repository_async() experiment_db.scan_repository_async(loop=loop)
server_control = RPCServer({ server_control = RPCServer({
"master_config": config, "master_config": config,
@ -142,7 +142,7 @@ def main():
}, allow_parallel=True) }, allow_parallel=True)
loop.run_until_complete(server_control.start( loop.run_until_complete(server_control.start(
bind, args.port_control)) bind, args.port_control))
atexit_register_coroutine(server_control.stop) atexit_register_coroutine(server_control.stop, loop=loop)
server_notify = Publisher({ server_notify = Publisher({
"schedule": scheduler.notifier, "schedule": scheduler.notifier,
@ -153,12 +153,12 @@ def main():
}) })
loop.run_until_complete(server_notify.start( loop.run_until_complete(server_notify.start(
bind, args.port_notify)) bind, args.port_notify))
atexit_register_coroutine(server_notify.stop) atexit_register_coroutine(server_notify.stop, loop=loop)
server_logging = LoggingServer() server_logging = LoggingServer()
loop.run_until_complete(server_logging.start( loop.run_until_complete(server_logging.start(
bind, args.port_logging)) bind, args.port_logging))
atexit_register_coroutine(server_logging.stop) atexit_register_coroutine(server_logging.stop, loop=loop)
print("ARTIQ master is now ready.") print("ARTIQ master is now ready.")
loop.run_until_complete(signal_handler.wait_terminate()) loop.run_until_complete(signal_handler.wait_terminate())

89
artiq/frontend/artiq_rtiomap.py Executable file
View File

@ -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("<I", len(channel_map)))
for dev_num, dev_name in channel_map.items():
dev_name_bytes = dev_name.encode("utf-8")
outfile.write(struct.pack("<II{}s".format(len(dev_name_bytes)), dev_num, len(dev_name_bytes), dev_name_bytes))
def deserialize_device_map(infile):
result_map = dict()
ch_count, = struct.unpack_from("<I", infile.read(4))
for _ in range(ch_count):
dev_num, dev_name_len = struct.unpack_from("<II", infile.read(8))
dev_name = struct.unpack_from("<{}s".format(dev_name_len), infile.read(dev_name_len))[0].decode("utf-8")
assert dev_num not in result_map
result_map[dev_num] = dev_name
return result_map
def main():
args = get_argparser().parse_args()
common_args.init_logger_from_args(args)
if args.show:
with open(args.file, "rb") as infile:
chan_map = deserialize_device_map(infile)
for chan, device in sorted(chan_map.items(), key=lambda x: x[0]):
print(f"{chan} -> {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()

View File

@ -133,7 +133,9 @@ def _build_experiment(device_mgr, dataset_mgr, args):
"arguments": arguments "arguments": arguments
} }
device_mgr.virtual_devices["scheduler"].expid = expid 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): def run(with_file=False):

View File

@ -451,7 +451,7 @@ class SinaraTester(EnvExperiment):
print("Frequencies:") print("Frequencies:")
for card_n, channels in enumerate(chunker(self.mirnies, 4)): for card_n, channels in enumerate(chunker(self.mirnies, 4)):
for channel_n, (channel_name, channel_dev) in enumerate(channels): 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)) print("{}\t{}MHz".format(channel_name, frequency))
self.setup_mirny(channel_dev, frequency) self.setup_mirny(channel_dev, frequency)
print("{} info: {}".format(channel_name, channel_dev.info())) print("{} info: {}".format(channel_name, channel_dev.info()))

View File

@ -22,8 +22,7 @@ class Transmitter(Module, AutoCSR):
self.aux_tx = CSR() self.aux_tx = CSR()
self.specials.mem = Memory(mem_dw, max_packet//(mem_dw//8)) self.specials.mem = Memory(mem_dw, max_packet//(mem_dw//8))
converter = ClockDomainsRenamer("rtio")( converter = stream.Converter(mem_dw, ll_dw)
stream.Converter(mem_dw, ll_dw))
self.submodules += converter self.submodules += converter
# when continuously fed, the Converter outputs data continuously # when continuously fed, the Converter outputs data continuously
@ -36,7 +35,7 @@ class Transmitter(Module, AutoCSR):
seen_eop_rst = Signal() seen_eop_rst = Signal()
frame_r = Signal() frame_r = Signal()
seen_eop = Signal() seen_eop = Signal()
self.sync.rtio += [ self.sync += [
If(link_layer.tx_aux_ack, If(link_layer.tx_aux_ack,
frame_r.eq(link_layer.tx_aux_frame), frame_r.eq(link_layer.tx_aux_frame),
If(frame_r & ~link_layer.tx_aux_frame, seen_eop.eq(1)) 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)) 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.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_nbits = bits_for(max_packet) - log2_int(mem_dw//8)
frame_counter = Signal(frame_counter_nbits) frame_counter = Signal(frame_counter_nbits)
frame_counter_next = 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), mem_port.adr.eq(frame_counter_next),
converter.sink.data.eq(mem_port.dat_r) converter.sink.data.eq(mem_port.dat_r)
] ]
self.sync.rtio += frame_counter.eq(frame_counter_next)
start_tx = PulseSynchronizer("sys", "rtio") tx_done = Signal()
tx_done = PulseSynchronizer("rtio", "sys")
self.submodules += start_tx, tx_done
self.comb += start_tx.i.eq(self.aux_tx.re)
self.sync += [ self.sync += [
If(tx_done.o, self.aux_tx.w.eq(0)), frame_counter.eq(frame_counter_next),
If(self.aux_tx.re, self.aux_tx.w.eq(1)) 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 self.submodules += fsm
fsm.act("IDLE", fsm.act("IDLE",
frame_counter_rst.eq(1), frame_counter_rst.eq(1),
seen_eop_rst.eq(1), seen_eop_rst.eq(1),
If(start_tx.o, NextState("TRANSMIT")) If(self.aux_tx.re, NextState("TRANSMIT"))
) )
fsm.act("TRANSMIT", fsm.act("TRANSMIT",
converter.sink.stb.eq(1), converter.sink.stb.eq(1),
If(converter.sink.ack, If(converter.sink.ack,
frame_counter_ce.eq(1) 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", fsm.act("WAIT_INTERFRAME",
If(seen_eop, If(seen_eop,
tx_done.i.eq(1), tx_done.eq(1),
NextState("IDLE") NextState("IDLE")
) )
) )

View File

@ -2,7 +2,6 @@ from types import SimpleNamespace
from migen import * from migen import *
from migen.genlib.resetsync import AsyncResetSynchronizer from migen.genlib.resetsync import AsyncResetSynchronizer
from migen.genlib.cdc import PulseSynchronizer
from misoc.interconnect.csr import * from misoc.interconnect.csr import *
from artiq.gateware.rtio import cri, rtlink from artiq.gateware.rtio import cri, rtlink
@ -31,7 +30,6 @@ class TransceiverInterface(AutoCSR):
def __init__(self, channel_interfaces): def __init__(self, channel_interfaces):
self.stable_clkin = CSRStorage() self.stable_clkin = CSRStorage()
self.txenable = CSRStorage(len(channel_interfaces)) self.txenable = CSRStorage(len(channel_interfaces))
self.clock_domains.cd_rtio = ClockDomain()
for i in range(len(channel_interfaces)): for i in range(len(channel_interfaces)):
name = "rtio_rx" + str(i) name = "rtio_rx" + str(i)
setattr(self.clock_domains, "cd_"+name, ClockDomain(name=name)) 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 assert tsc.glbl_fine_ts_width >= chan_fine_ts_width
self.submodules.outputs = ClockDomainsRenamer("rio")( 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, lane_count=lane_count, fifo_depth=fifo_depth,
enable_spread=False, report_buffer_space=True, enable_spread=False, report_buffer_space=True,
interface=self.cri)) interface=self.cri))
self.comb += self.outputs.coarse_timestamp.eq(tsc.coarse_ts) 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")( self.submodules.inputs = ClockDomainsRenamer("rio")(
InputCollector(tsc, channels, "sync", interface=self.cri)) InputCollector(tsc, channels, interface=self.cri))
for attr, _ in async_errors_layout: for attr, _ in async_errors_layout:
self.comb += getattr(self.async_errors, attr).eq(getattr(self.outputs, attr)) 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 = CSRStorage(reset=1)
self.reset_phy = CSRStorage(reset=1) self.reset_phy = CSRStorage(reset=1)
self.tsc_loaded = CSR() self.tsc_loaded = CSR()
# master interface in the rtio domain # master interface in the sys domain
self.cri = cri.Interface() self.cri = cri.Interface()
self.async_errors = Record(async_errors_layout) self.async_errors = Record(async_errors_layout)
self.clock_domains.cd_rio = ClockDomain() self.clock_domains.cd_rio = ClockDomain()
self.clock_domains.cd_rio_phy = ClockDomain() self.clock_domains.cd_rio_phy = ClockDomain()
self.comb += [ self.comb += [
self.cd_rio.clk.eq(ClockSignal("rtio")), self.cd_rio.clk.eq(ClockSignal("sys")),
self.cd_rio_phy.clk.eq(ClockSignal("rtio")) self.cd_rio_phy.clk.eq(ClockSignal("sys"))
] ]
reset = Signal() reset = Signal()
reset_phy = 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_frame_perm=rx_synchronizer.resync(self.link_layer.rx_rt_frame_perm),
rx_rt_data=rx_synchronizer.resync(self.link_layer.rx_rt_data) 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.link_stats = link_layer.LinkLayerStats(link_layer_sync, "sys")
self.submodules.rt_packet = ClockDomainsRenamer("rtio")( self.submodules.rt_packet = rt_packet_satellite.RTPacketSatellite(
rt_packet_satellite.RTPacketSatellite(link_layer_sync, interface=self.cri)) link_layer_sync, interface=self.cri)
self.comb += self.rt_packet.reset.eq(self.cd_rio.rst) self.comb += self.rt_packet.reset.eq(self.cd_rio.rst)
self.comb += [ self.comb += [
@ -135,12 +133,9 @@ class DRTIOSatellite(Module):
tsc.load_value.eq(self.rt_packet.tsc_load_value) 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 += [ self.sync += [
If(self.tsc_loaded.re, self.tsc_loaded.w.eq(0)), 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( self.submodules.rt_errors = rt_errors_satellite.RTErrorsSatellite(

View File

@ -232,7 +232,7 @@ class LinkLayer(Module, AutoCSR):
# receiver locked, comma aligned, receiving valid 8b10b symbols # receiver locked, comma aligned, receiving valid 8b10b symbols
self.rx_ready = Signal() self.rx_ready = Signal()
tx = ClockDomainsRenamer("rtio")(LinkLayerTX(encoder)) tx = LinkLayerTX(encoder)
rx = ClockDomainsRenamer("rtio_rx")(LinkLayerRX(decoders)) rx = ClockDomainsRenamer("rtio_rx")(LinkLayerRX(decoders))
self.submodules += tx, rx self.submodules += tx, rx
@ -256,31 +256,23 @@ class LinkLayer(Module, AutoCSR):
rx_up = Signal() rx_up = Signal()
rx_up_r = 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_rx = Signal()
rx_up_r.attr.add("no_retiming") rx_up_r.attr.add("no_retiming")
self.specials += [ self.specials += [
MultiReg(rx_up_r, rx_up_rx, "rtio_rx"), MultiReg(rx_up_r, rx_up_rx, "rtio_rx"),
MultiReg(rx_up_r, self.rx_up.status)] 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() rx_disable_rx = Signal()
self.rx_disable.storage.attr.add("no_retiming") self.rx_disable.storage.attr.add("no_retiming")
self.specials += MultiReg(self.rx_disable.storage, rx_disable_rx, "rtio_rx") self.specials += MultiReg(self.rx_disable.storage, rx_disable_rx, "rtio_rx")
self.comb += [ self.comb += [
tx.aux_frame.eq(self.tx_aux_frame | tx_force_aux_zero_rtio), tx.aux_frame.eq(self.tx_aux_frame | self.tx_force_aux_zero.storage),
tx.aux_data.eq(Mux(tx_force_aux_zero_rtio, 0, self.tx_aux_data)), tx.aux_data.eq(Mux(self.tx_force_aux_zero.storage, 0, self.tx_aux_data)),
self.tx_aux_ack.eq(tx.aux_ack), self.tx_aux_ack.eq(tx.aux_ack),
tx.rt_frame.eq(self.tx_rt_frame | tx_force_rt_zero_rtio), tx.rt_frame.eq(self.tx_rt_frame | self.tx_force_rt_zero.storage),
tx.rt_data.eq(Mux(tx_force_rt_zero_rtio, 0, self.tx_rt_data)) 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 # we register those to improve timing margins, as the data may need
# to be recaptured by RXSynchronizer. # to be recaptured by RXSynchronizer.
@ -294,10 +286,10 @@ class LinkLayer(Module, AutoCSR):
self.rx_rt_data.eq(rx.rt_data) self.rx_rt_data.eq(rx.rt_data)
] ]
wait_scrambler = ClockDomainsRenamer("rtio")(WaitTimer(15)) wait_scrambler = WaitTimer(15)
self.submodules += wait_scrambler self.submodules += wait_scrambler
fsm = ClockDomainsRenamer("rtio")(FSM(reset_state="WAIT_RX_READY")) fsm = FSM(reset_state="WAIT_RX_READY")
self.submodules += fsm self.submodules += fsm
fsm.act("WAIT_RX_READY", fsm.act("WAIT_RX_READY",

View File

@ -108,7 +108,7 @@ class RTController(Module):
cond_underflow = Signal() cond_underflow = Signal()
self.comb += cond_underflow.eq((self.cri.o_timestamp[tsc.glbl_fine_ts_width:] 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
buffer_space = Memory(16, 256) buffer_space = Memory(16, 256)

View File

@ -15,15 +15,11 @@ class RTController(Module, AutoCSR):
self.command_missed_chan_sel = CSRStatus(24) self.command_missed_chan_sel = CSRStatus(24)
self.buffer_space_timeout_dest = CSRStatus(8) 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_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 += [ 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)) If(self.set_time.re, set_time_stb.eq(1))
] ]
self.comb += self.set_time.w.eq(set_time_stb) self.comb += self.set_time.w.eq(set_time_stb)
@ -31,10 +27,10 @@ class RTController(Module, AutoCSR):
errors = [ errors = [
(rt_packet.err_unknown_packet_type, "rtio_rx", None, None), (rt_packet.err_unknown_packet_type, "rtio_rx", None, None),
(rt_packet.err_packet_truncated, "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(rt_packet.command_missed_cmd, rt_packet.command_missed_chan_sel),
Cat(self.command_missed_cmd.status, self.command_missed_chan_sel.status)), 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) rt_packet.buffer_space_destination, self.buffer_space_timeout_dest.status)
] ]

View File

@ -2,7 +2,7 @@
from migen import * from migen import *
from migen.genlib.fsm 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 migen.genlib.cdc import BlindTransfer
from artiq.gateware.rtio.cdc import GrayCodeTransfer from artiq.gateware.rtio.cdc import GrayCodeTransfer
@ -76,8 +76,8 @@ class RTPacketMaster(Module):
assert len(link_layer.tx_rt_data) % 8 == 0 assert len(link_layer.tx_rt_data) % 8 == 0
ws = len(link_layer.tx_rt_data) ws = len(link_layer.tx_rt_data)
tx_plm = get_m2s_layouts(ws) tx_plm = get_m2s_layouts(ws)
tx_dp = ClockDomainsRenamer("rtio")(TransmitDatapath( tx_dp = TransmitDatapath(
link_layer.tx_rt_frame, link_layer.tx_rt_data, tx_plm)) link_layer.tx_rt_frame, link_layer.tx_rt_data, tx_plm)
self.submodules += tx_dp self.submodules += tx_dp
rx_plm = get_s2m_layouts(ws) rx_plm = get_s2m_layouts(ws)
rx_dp = ClockDomainsRenamer("rtio_rx")(ReceiveDatapath( rx_dp = ClockDomainsRenamer("rtio_rx")(ReceiveDatapath(
@ -85,8 +85,7 @@ class RTPacketMaster(Module):
self.submodules += rx_dp self.submodules += rx_dp
# Write FIFO and extra data count # Write FIFO and extra data count
sr_fifo = ClockDomainsRenamer({"write": "sys", "read": "rtio"})( sr_fifo = SyncFIFO(1+64+24+8+512, sr_fifo_depth)
AsyncFIFO(1+64+24+8+512, sr_fifo_depth))
self.submodules += sr_fifo self.submodules += sr_fifo
sr_notwrite_d = Signal() sr_notwrite_d = Signal()
sr_timestamp_d = Signal(64) sr_timestamp_d = Signal(64)
@ -106,7 +105,7 @@ class RTPacketMaster(Module):
sr_buf_re = Signal() sr_buf_re = Signal()
self.comb += sr_fifo.re.eq(sr_fifo.readable & (~sr_buf_readable | sr_buf_re)) self.comb += sr_fifo.re.eq(sr_fifo.readable & (~sr_buf_readable | sr_buf_re))
self.sync.rtio += \ self.sync += \
If(sr_fifo.re, If(sr_fifo.re,
sr_buf_readable.eq(1), sr_buf_readable.eq(1),
).Elif(sr_buf_re, ).Elif(sr_buf_re,
@ -120,7 +119,7 @@ class RTPacketMaster(Module):
sr_extra_data_cnt = Signal(8) sr_extra_data_cnt = Signal(8)
sr_data = Signal(512) sr_data = Signal(512)
self.sync.rtio += If(sr_fifo.re, self.sync += If(sr_fifo.re,
sr_notwrite.eq(sr_notwrite_d), sr_notwrite.eq(sr_notwrite_d),
sr_timestamp.eq(sr_timestamp_d), sr_timestamp.eq(sr_timestamp_d),
sr_chan_sel.eq(sr_chan_sel_d), sr_chan_sel.eq(sr_chan_sel_d),
@ -131,11 +130,11 @@ class RTPacketMaster(Module):
sr_extra_data_d = Signal(512) sr_extra_data_d = Signal(512)
self.comb += sr_extra_data_d.eq(sr_data_d[short_data_len:]) self.comb += sr_extra_data_d.eq(sr_data_d[short_data_len:])
for i in range(512//ws): 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))) If(sr_extra_data_d[ws*i:ws*(i+1)] != 0, sr_extra_data_cnt.eq(i+1)))
sr_extra_data = Signal(512) 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_ce = Signal()
extra_data_last = Signal() extra_data_last = Signal()
@ -146,7 +145,7 @@ class RTPacketMaster(Module):
for i in range(512//ws)}), for i in range(512//ws)}),
extra_data_last.eq(extra_data_counter == sr_extra_data_cnt) extra_data_last.eq(extra_data_counter == sr_extra_data_cnt)
] ]
self.sync.rtio += \ self.sync += \
If(extra_data_ce, If(extra_data_ce,
extra_data_counter.eq(extra_data_counter + 1), extra_data_counter.eq(extra_data_counter + 1),
).Else( ).Else(
@ -160,18 +159,6 @@ class RTPacketMaster(Module):
buffer_space_not, buffer_space, buffer_space_not, buffer_space,
self.buffer_space_not, self.buffer_space_not_ack, self.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_not = Signal()
read_no_event = Signal() read_no_event = Signal()
read_is_overflow = Signal() read_is_overflow = Signal()
@ -199,14 +186,14 @@ class RTPacketMaster(Module):
] ]
# TX FSM # TX FSM
tx_fsm = ClockDomainsRenamer("rtio")(FSM(reset_state="IDLE")) tx_fsm = FSM(reset_state="IDLE")
self.submodules += tx_fsm self.submodules += tx_fsm
echo_sent_now = Signal() 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 = Signal(64)
tsc_value_load = Signal() 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", tx_fsm.act("IDLE",
# Ensure 2 cycles between frames on the link. # Ensure 2 cycles between frames on the link.
@ -223,10 +210,10 @@ class RTPacketMaster(Module):
NextState("WRITE") NextState("WRITE")
) )
).Else( ).Else(
If(echo_stb, If(self.echo_stb,
echo_sent_now.eq(1), echo_sent_now.eq(1),
NextState("ECHO") NextState("ECHO")
).Elif(set_time_stb, ).Elif(self.set_time_stb,
tsc_value_load.eq(1), tsc_value_load.eq(1),
NextState("SET_TIME") NextState("SET_TIME")
) )
@ -273,14 +260,14 @@ class RTPacketMaster(Module):
tx_fsm.act("ECHO", tx_fsm.act("ECHO",
tx_dp.send("echo_request"), tx_dp.send("echo_request"),
If(tx_dp.packet_last, If(tx_dp.packet_last,
echo_ack.eq(1), self.echo_ack.eq(1),
NextState("IDLE") NextState("IDLE")
) )
) )
tx_fsm.act("SET_TIME", tx_fsm.act("SET_TIME",
tx_dp.send("set_time", timestamp=tsc_value), tx_dp.send("set_time", timestamp=tsc_value),
If(tx_dp.packet_last, If(tx_dp.packet_last,
set_time_ack.eq(1), self.set_time_ack.eq(1),
NextState("IDLE") NextState("IDLE")
) )
) )
@ -334,16 +321,14 @@ class RTPacketMaster(Module):
# packet counters # packet counters
tx_frame_r = Signal() tx_frame_r = Signal()
packet_cnt_tx = Signal(32) packet_cnt_tx = Signal(32)
self.sync.rtio += [ self.sync += [
tx_frame_r.eq(link_layer.tx_rt_frame), tx_frame_r.eq(link_layer.tx_rt_frame),
If(link_layer.tx_rt_frame & ~tx_frame_r, If(link_layer.tx_rt_frame & ~tx_frame_r,
packet_cnt_tx.eq(packet_cnt_tx + 1)) packet_cnt_tx.eq(packet_cnt_tx + 1))
] ]
cdc_packet_cnt_tx = GrayCodeTransfer(32)
self.submodules += cdc_packet_cnt_tx
self.comb += [ self.comb += [
cdc_packet_cnt_tx.i.eq(packet_cnt_tx), self.packet_cnt_tx.eq(packet_cnt_tx)
self.packet_cnt_tx.eq(cdc_packet_cnt_tx.o)
] ]
rx_frame_r = Signal() rx_frame_r = Signal()
@ -353,7 +338,7 @@ class RTPacketMaster(Module):
If(link_layer.rx_rt_frame & ~rx_frame_r, If(link_layer.rx_rt_frame & ~rx_frame_r,
packet_cnt_rx.eq(packet_cnt_rx + 1)) 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)) GrayCodeTransfer(32))
self.submodules += cdc_packet_cnt_rx self.submodules += cdc_packet_cnt_rx
self.comb += [ self.comb += [

View File

@ -38,8 +38,8 @@ class RTPacketRepeater(Module):
assert len(link_layer.tx_rt_data) % 8 == 0 assert len(link_layer.tx_rt_data) % 8 == 0
ws = len(link_layer.tx_rt_data) ws = len(link_layer.tx_rt_data)
tx_plm = get_m2s_layouts(ws) tx_plm = get_m2s_layouts(ws)
tx_dp = ClockDomainsRenamer("rtio")(TransmitDatapath( tx_dp = TransmitDatapath(
link_layer.tx_rt_frame, link_layer.tx_rt_data, tx_plm)) link_layer.tx_rt_frame, link_layer.tx_rt_data, tx_plm)
self.submodules += tx_dp self.submodules += tx_dp
rx_plm = get_s2m_layouts(ws) rx_plm = get_s2m_layouts(ws)
rx_dp = ClockDomainsRenamer("rtio_rx")(ReceiveDatapath( rx_dp = ClockDomainsRenamer("rtio_rx")(ReceiveDatapath(
@ -49,7 +49,7 @@ class RTPacketRepeater(Module):
# TSC sync # TSC sync
tsc_value = Signal(64) tsc_value = Signal(64)
tsc_value_load = Signal() 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 # CRI buffer stage 1
cb0_loaded = Signal() cb0_loaded = Signal()
@ -60,7 +60,7 @@ class RTPacketRepeater(Module):
cb0_chan_sel = Signal(24) cb0_chan_sel = Signal(24)
cb0_o_address = Signal(8) cb0_o_address = Signal(8)
cb0_o_data = Signal(512) cb0_o_data = Signal(512)
self.sync.rtio += [ self.sync += [
If(self.reset | cb0_ack, If(self.reset | cb0_ack,
cb0_loaded.eq(0), cb0_loaded.eq(0),
cb0_cmd.eq(cri.commands["nop"]) cb0_cmd.eq(cri.commands["nop"])
@ -91,7 +91,7 @@ class RTPacketRepeater(Module):
cb_chan_sel = Signal(24) cb_chan_sel = Signal(24)
cb_o_address = Signal(8) cb_o_address = Signal(8)
cb_o_data = Signal(512) cb_o_data = Signal(512)
self.sync.rtio += [ self.sync += [
If(self.reset | cb_ack, If(self.reset | cb_ack,
cb_loaded.eq(0), cb_loaded.eq(0),
cb_cmd.eq(cri.commands["nop"]) cb_cmd.eq(cri.commands["nop"])
@ -112,11 +112,11 @@ class RTPacketRepeater(Module):
wb_extra_data_a = Signal(512) wb_extra_data_a = Signal(512)
self.comb += wb_extra_data_a.eq(self.cri.o_data[short_data_len:]) self.comb += wb_extra_data_a.eq(self.cri.o_data[short_data_len:])
for i in range(512//ws): 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))) If(wb_extra_data_a[ws*i:ws*(i+1)] != 0, wb_extra_data_cnt.eq(i+1)))
wb_extra_data = Signal(512) 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)) wb_extra_data.eq(wb_extra_data_a))
extra_data_ce = Signal() extra_data_ce = Signal()
@ -128,7 +128,7 @@ class RTPacketRepeater(Module):
for i in range(512//ws)}), for i in range(512//ws)}),
extra_data_last.eq(extra_data_counter == wb_extra_data_cnt) extra_data_last.eq(extra_data_counter == wb_extra_data_cnt)
] ]
self.sync.rtio += \ self.sync += \
If(extra_data_ce, If(extra_data_ce,
extra_data_counter.eq(extra_data_counter + 1), extra_data_counter.eq(extra_data_counter + 1),
).Else( ).Else(
@ -136,19 +136,19 @@ class RTPacketRepeater(Module):
) )
# Buffer space # 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:])) self.buffer_space_destination.eq(self.cri.chan_sel[16:]))
rx_buffer_space_not = Signal() rx_buffer_space_not = Signal()
rx_buffer_space = Signal(16) rx_buffer_space = Signal(16)
buffer_space_not = Signal() buffer_space_not = Signal()
buffer_space_not_ack = 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, rx_buffer_space_not, rx_buffer_space,
buffer_space_not, buffer_space_not_ack, buffer_space_not, buffer_space_not_ack,
self.cri.o_buffer_space) self.cri.o_buffer_space)
timeout_counter = ClockDomainsRenamer("rtio")(WaitTimer(8191)) timeout_counter = WaitTimer(8191)
self.submodules += timeout_counter self.submodules += timeout_counter
# Read # Read
@ -163,7 +163,7 @@ class RTPacketRepeater(Module):
rtio_read_is_overflow = Signal() rtio_read_is_overflow = Signal()
rtio_read_data = Signal(32) rtio_read_data = Signal(32)
rtio_read_timestamp = Signal(64) rtio_read_timestamp = Signal(64)
self.submodules += CrossDomainNotification("rtio_rx", "rtio", self.submodules += CrossDomainNotification("rtio_rx", "sys",
read_not, read_not,
Cat(read_no_event, read_is_overflow, read_data, read_timestamp), 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)) i_status_wait_event, i_status_overflow, cb0_loaded | cb_loaded))
load_read_reply = Signal() load_read_reply = Signal()
self.sync.rtio += [ self.sync += [
If(load_read_reply, If(load_read_reply,
i_status_wait_event.eq(0), i_status_wait_event.eq(0),
i_status_overflow.eq(0), i_status_overflow.eq(0),
@ -200,7 +200,7 @@ class RTPacketRepeater(Module):
] ]
# TX and CRI FSM # TX and CRI FSM
tx_fsm = ClockDomainsRenamer("rtio")(FSM(reset_state="IDLE")) tx_fsm = FSM(reset_state="IDLE")
self.submodules += tx_fsm self.submodules += tx_fsm
tx_fsm.act("IDLE", tx_fsm.act("IDLE",

View File

@ -17,7 +17,7 @@ class GenericRXSynchronizer(Module):
return synchronized return synchronized
def do_finalize(self): 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.submodules += eb
self.comb += [ self.comb += [
eb.din.eq(Cat(*[s[0] for s in self.signals])), eb.din.eq(Cat(*[s[0] for s in self.signals])),
@ -57,6 +57,6 @@ class XilinxRXSynchronizer(Module):
self.specials += [ self.specials += [
Instance("FD", i_C=ClockSignal("rtio_rx"), i_D=din[i], o_Q=inter[i], Instance("FD", i_C=ClockSignal("rtio_rx"), i_D=din[i], o_Q=inter[i],
attr={hu_set, ("RLOC", "X0Y{}".format(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))}) attr={hu_set, ("RLOC", "X1Y{}".format(i))})
] ]

View File

@ -1,23 +1,23 @@
from migen import * from migen import *
from migen.genlib.cdc import MultiReg, PulseSynchronizer from migen.genlib.cdc import MultiReg
from misoc.interconnect.csr import * 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. # frequency.
class SiPhaser7Series(Module, AutoCSR): class SiPhaser7Series(Module, AutoCSR):
def __init__(self, si5324_clkin, rx_synchronizer, 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.switch_clocks = CSRStorage()
self.phase_shift = CSR() self.phase_shift = CSR()
self.phase_shift_done = CSRStatus(reset=1) self.phase_shift_done = CSRStatus(reset=1)
self.error = CSR() 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, # 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 # we do not use the crystal reference so that the PFD (f3) frequency
# can be high. # can be high.
@ -97,17 +97,14 @@ class SiPhaser7Series(Module, AutoCSR):
toggle_out = rx_synchronizer.resync(toggle_in) toggle_out = rx_synchronizer.resync(toggle_in)
toggle_out_expected = Signal() toggle_out_expected = Signal()
self.sync.rtio += toggle_out_expected.eq(~toggle_out) self.sync += toggle_out_expected.eq(~toggle_out)
error = Signal() error = Signal()
error_clear = PulseSynchronizer("sys", "rtio") self.sync += [
self.submodules += error_clear
self.sync.rtio += [
If(toggle_out != toggle_out_expected, error.eq(1)), 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.specials += MultiReg(error, self.error.w)
self.comb += error_clear.i.eq(self.error.re)
# expose MMCM outputs - used for clock constraints # expose MMCM outputs - used for clock constraints
self.mmcm_freerun_output = mmcm_freerun_output self.mmcm_freerun_output = mmcm_freerun_output

View File

@ -33,7 +33,7 @@ class BruteforceClockAligner(Module):
check_counter = Signal(max=check_max_val+1) check_counter = Signal(max=check_max_val+1)
check = Signal() check = Signal()
reset_check_counter = Signal() reset_check_counter = Signal()
self.sync.rtio_tx += [ self.sync += [
check.eq(0), check.eq(0),
If(reset_check_counter, If(reset_check_counter,
check_counter.eq(check_max_val) 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 self.submodules += checks_reset
comma_n = ~comma & 0b1111111111 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 self.submodules += fsm
fsm.act("WAIT_COMMA", fsm.act("WAIT_COMMA",

View File

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

View File

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

View File

@ -20,8 +20,7 @@ class GTPSingle(Module):
self.stable_clkin = Signal() self.stable_clkin = Signal()
self.txenable = Signal() self.txenable = Signal()
self.submodules.encoder = encoder = ClockDomainsRenamer("rtio_tx")( self.submodules.encoder = encoder = Encoder(2, True)
Encoder(2, True))
self.submodules.decoders = decoders = [ClockDomainsRenamer("rtio_rx")( self.submodules.decoders = decoders = [ClockDomainsRenamer("rtio_rx")(
(Decoder(True))) for _ in range(2)] (Decoder(True))) for _ in range(2)]
self.rx_ready = Signal() self.rx_ready = Signal()
@ -33,10 +32,11 @@ class GTPSingle(Module):
# # # # # #
# TX generates RTIO clock, init must be in system domain # TX generates RTIO clock, init must be in bootstrap domain
self.submodules.tx_init = tx_init = GTPTXInit(sys_clk_freq, mode) self.submodules.tx_init = tx_init = ClockDomainsRenamer("bootstrap")(
# RX receives restart commands from RTIO domain GTPTXInit(125e6, mode))
rx_init = ClockDomainsRenamer("rtio_tx")(GTPRXInit(rtio_clk_freq)) # RX receives restart commands from SYS domain
rx_init = GTPRXInit(rtio_clk_freq)
self.submodules += rx_init self.submodules += rx_init
self.comb += [ self.comb += [
@ -367,7 +367,7 @@ class GTPSingle(Module):
# Channel - DRP Ports # Channel - DRP Ports
i_DRPADDR=rx_init.drpaddr, i_DRPADDR=rx_init.drpaddr,
i_DRPCLK=ClockSignal("rtio_tx"), i_DRPCLK=ClockSignal("sys"),
i_DRPDI=rx_init.drpdi, i_DRPDI=rx_init.drpdi,
o_DRPDO=rx_init.drpdo, o_DRPDO=rx_init.drpdo,
i_DRPEN=rx_init.drpen, i_DRPEN=rx_init.drpen,
@ -566,8 +566,8 @@ class GTPSingle(Module):
i_PMARSVDIN1 =0b0, i_PMARSVDIN1 =0b0,
# Transmit Ports - FPGA TX Interface Ports # Transmit Ports - FPGA TX Interface Ports
i_TXDATA =Cat(txdata[:8], txdata[10:18]), i_TXDATA =Cat(txdata[:8], txdata[10:18]),
i_TXUSRCLK =ClockSignal("rtio_tx"), i_TXUSRCLK =ClockSignal("sys"),
i_TXUSRCLK2 =ClockSignal("rtio_tx"), i_TXUSRCLK2 =ClockSignal("sys"),
# Transmit Ports - PCI Express Ports # Transmit Ports - PCI Express Ports
i_TXELECIDLE =0, i_TXELECIDLE =0,
@ -665,19 +665,10 @@ class GTPSingle(Module):
raise ValueError raise ValueError
self.specials += Instance("GTPE2_CHANNEL", **gtp_params) 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 clocking
rx_reset_deglitched = Signal() rx_reset_deglitched = Signal()
rx_reset_deglitched.attr.add("no_retiming") 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.clock_domains.cd_rtio_rx = ClockDomain()
self.specials += [ self.specials += [
Instance("BUFG", i_I=self.rxoutclk, o_O=self.cd_rtio_rx.clk), 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 = [] channel_interfaces = []
for i in range(nchannels): for i in range(nchannels):
if nchannels == 1: if nchannels == 1:
@ -735,10 +725,6 @@ class GTP(Module, TransceiverInterface):
else: else:
mode = "master" if i == master else "slave" mode = "master" if i == master else "slave"
gtp = GTPSingle(qpll_channel, data_pads[i], sys_clk_freq, rtio_clk_freq, mode) 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) self.gtps.append(gtp)
setattr(self.submodules, "gtp"+str(i), gtp) setattr(self.submodules, "gtp"+str(i), gtp)
channel_interface = ChannelInterface(gtp.encoder, gtp.decoders) channel_interface = ChannelInterface(gtp.encoder, gtp.decoders)
@ -754,10 +740,6 @@ class GTP(Module, TransceiverInterface):
gtp.txenable.eq(self.txenable.storage[n]) 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): for i in range(nchannels):
self.comb += [ self.comb += [
getattr(self, "cd_rtio_rx" + str(i)).clk.eq(self.gtps[i].cd_rtio_rx.clk), getattr(self, "cd_rtio_rx" + str(i)).clk.eq(self.gtps[i].cd_rtio_rx.clk),

View File

@ -1,5 +1,6 @@
from migen import * from migen import *
from migen.genlib.resetsync import AsyncResetSynchronizer from migen.genlib.resetsync import AsyncResetSynchronizer
from migen.genlib.cdc import MultiReg
from misoc.cores.code_8b10b import Encoder, Decoder from misoc.cores.code_8b10b import Encoder, Decoder
from misoc.interconnect.csr import * from misoc.interconnect.csr import *
@ -16,13 +17,12 @@ class GTX_20X(Module):
# * GTX PLL frequency @ 2.5GHz # * GTX PLL frequency @ 2.5GHz
# * GTX line rate (TX & RX) @ 2.5Gb/s # * GTX line rate (TX & RX) @ 2.5Gb/s
# * GTX TX/RX USRCLK @ 125MHz == coarse RTIO frequency # * 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 tx_mode in ["single", "master", "slave"]
assert rx_mode in ["single", "master", "slave"] assert rx_mode in ["single", "master", "slave"]
self.txenable = Signal() self.txenable = Signal()
self.submodules.encoder = ClockDomainsRenamer("rtio_tx")( self.submodules.encoder = Encoder(2, True)
Encoder(2, True))
self.submodules.decoders = [ClockDomainsRenamer("rtio_rx")( self.submodules.decoders = [ClockDomainsRenamer("rtio_rx")(
(Decoder(True))) for _ in range(2)] (Decoder(True))) for _ in range(2)]
self.rx_ready = Signal() self.rx_ready = Signal()
@ -36,11 +36,11 @@ class GTX_20X(Module):
cpllreset = Signal() cpllreset = Signal()
cplllock = Signal() cplllock = Signal()
# TX generates RTIO clock, init must be in system domain # TX generates SYS clock, init must be in bootstrap domain
self.submodules.tx_init = tx_init = GTXInit(sys_clk_freq, False, mode=tx_mode) self.submodules.tx_init = tx_init = ClockDomainsRenamer("bootstrap")(
# RX receives restart commands from RTIO domain GTXInit(clk_freq, False, mode=tx_mode))
self.submodules.rx_init = rx_init = ClockDomainsRenamer("rtio_tx")( # RX receives restart commands from SYS domain
GTXInit(rtio_clk_freq, True, mode=rx_mode)) self.submodules.rx_init = rx_init = GTXInit(clk_freq, True, mode=rx_mode)
self.comb += [ self.comb += [
cpllreset.eq(tx_init.cpllreset), cpllreset.eq(tx_init.cpllreset),
tx_init.cplllock.eq(cplllock), tx_init.cplllock.eq(cplllock),
@ -113,8 +113,8 @@ class GTX_20X(Module):
i_TXCHARDISPMODE=Cat(txdata[9], txdata[19]), i_TXCHARDISPMODE=Cat(txdata[9], txdata[19]),
i_TXCHARDISPVAL=Cat(txdata[8], txdata[18]), i_TXCHARDISPVAL=Cat(txdata[8], txdata[18]),
i_TXDATA=Cat(txdata[:8], txdata[10:18]), i_TXDATA=Cat(txdata[:8], txdata[10:18]),
i_TXUSRCLK=ClockSignal("rtio_tx"), i_TXUSRCLK=ClockSignal("sys"),
i_TXUSRCLK2=ClockSignal("rtio_tx"), i_TXUSRCLK2=ClockSignal("sys"),
# TX electrical # TX electrical
i_TXBUFDIFFCTRL=0b100, i_TXBUFDIFFCTRL=0b100,
@ -247,19 +247,10 @@ class GTX_20X(Module):
p_ES_EYE_SCAN_EN="TRUE", # Must be TRUE for GTX 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 clocking
rx_reset_deglitched = Signal() rx_reset_deglitched = Signal()
rx_reset_deglitched.attr.add("no_retiming") 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() self.clock_domains.cd_rtio_rx = ClockDomain()
if rx_mode == "single" or rx_mode == "master": if rx_mode == "single" or rx_mode == "master":
self.specials += Instance("BUFG", i_I=self.rxoutclk, o_O=self.cd_rtio_rx.clk), 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:]) 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.submodules += clock_aligner
self.comb += [ self.comb += [
clock_aligner.rxdata.eq(rxdata), clock_aligner.rxdata.eq(rxdata),
@ -282,17 +273,18 @@ class GTX_20X(Module):
class GTX(Module, TransceiverInterface): 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.nchannels = nchannels = len(pads)
self.gtxs = [] self.gtxs = []
self.rtio_clk_freq = rtio_clk_freq self.rtio_clk_freq = clk_freq
# # # # # #
refclk = Signal() refclk = Signal()
stable_clkin_n = Signal()
clk_enable = Signal()
self.specials += Instance("IBUFDS_GTE2", self.specials += Instance("IBUFDS_GTE2",
i_CEB=stable_clkin_n, i_CEB=~clk_enable,
i_I=clock_pads.p, i_I=clock_pads.p,
i_IB=clock_pads.n, i_IB=clock_pads.n,
o_O=refclk, o_O=refclk,
@ -301,7 +293,6 @@ class GTX(Module, TransceiverInterface):
p_CLKSWING_CFG="0b11" p_CLKSWING_CFG="0b11"
) )
rtio_tx_clk = Signal()
channel_interfaces = [] channel_interfaces = []
for i in range(nchannels): for i in range(nchannels):
if nchannels == 1: if nchannels == 1:
@ -309,12 +300,7 @@ class GTX(Module, TransceiverInterface):
else: else:
mode = "master" if i == master else "slave" mode = "master" if i == master else "slave"
# Note: RX phase alignment is to be done on individual lanes, not multi-lane. # 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") gtx = GTX_20X(refclk, pads[i], 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)
self.gtxs.append(gtx) self.gtxs.append(gtx)
setattr(self.submodules, "gtx"+str(i), gtx) setattr(self.submodules, "gtx"+str(i), gtx)
channel_interface = ChannelInterface(gtx.encoder, gtx.decoders) channel_interface = ChannelInterface(gtx.encoder, gtx.decoders)
@ -326,15 +312,16 @@ class GTX(Module, TransceiverInterface):
TransceiverInterface.__init__(self, channel_interfaces) TransceiverInterface.__init__(self, channel_interfaces)
for n, gtx in enumerate(self.gtxs): for n, gtx in enumerate(self.gtxs):
self.comb += [ 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 # Connect slave i's `rtio_rx` clock to `rtio_rxi` clock
for i in range(nchannels): for i in range(nchannels):
self.comb += [ self.comb += [

View File

@ -11,11 +11,13 @@ class GTXInit(Module):
# Choose between Auto Mode and Manual Mode for TX/RX phase alignment with buffer bypassed: # 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) # * 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) # * 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 isinstance(rx, bool)
assert mode in ["single", "master", "slave"] assert mode in ["single", "master", "slave"]
self.mode = mode self.mode = mode
self.stable_clkin = Signal()
self.done = Signal() self.done = Signal()
self.restart = Signal() self.restart = Signal()
@ -83,13 +85,13 @@ class GTXInit(Module):
# After configuration, transceiver resets have to stay low for # After configuration, transceiver resets have to stay low for
# at least 500ns (see AR43482) # 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) startup_timer = WaitTimer(startup_cycles)
self.submodules += startup_timer self.submodules += startup_timer
# PLL reset should be 1 period of refclk # PLL reset should be 1 period of refclk
# (i.e. 1/(125MHz) for the case of RTIO @ 125MHz) # (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) pll_reset_timer = WaitTimer(pll_reset_cycles)
self.submodules += pll_reset_timer self.submodules += pll_reset_timer
@ -108,7 +110,7 @@ class GTXInit(Module):
startup_fsm.act("INITIAL", startup_fsm.act("INITIAL",
startup_timer.wait.eq(1), 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", startup_fsm.act("RESET_ALL",
gtXxreset.eq(1), gtXxreset.eq(1),

View File

@ -1,2 +0,0 @@
from artiq.gateware.drtio.wrpll.core import WRPLL
from artiq.gateware.drtio.wrpll.ddmtd import DDMTDSamplerExtFF, DDMTDSamplerGTP

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -24,7 +24,7 @@ class Core(Module, AutoCSR):
self.sequence_error_channel = CSRStatus(16) self.sequence_error_channel = CSRStatus(16)
# Clocking/Reset # 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. # with reset controlled by CSR.
# #
# The `rio` CD contains logic that is reset with `core.reset()`. # 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.eq(self.reset.re),
cmd_reset_phy.eq(self.reset_phy.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 = ClockDomain()
self.clock_domains.cd_rio_phy = ClockDomain() self.clock_domains.cd_rio_phy = ClockDomain()
self.comb += [ self.comb += [
self.cd_rsys.clk.eq(ClockSignal()), self.cd_rio.clk.eq(ClockSignal()),
self.cd_rsys.rst.eq(cmd_reset), self.cd_rio.rst.eq(cmd_reset),
self.cd_rio.clk.eq(ClockSignal("rtio")), self.cd_rio_phy.clk.eq(ClockSignal()),
self.cd_rio_phy.clk.eq(ClockSignal("rtio")) 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 # TSC
chan_fine_ts_width = max(max(rtlink.get_fine_ts_width(channel.interface.o) chan_fine_ts_width = max(max(rtlink.get_fine_ts_width(channel.interface.o)
@ -65,22 +60,22 @@ class Core(Module, AutoCSR):
# Outputs/Inputs # Outputs/Inputs
quash_channels = [n for n, c in enumerate(channels) if isinstance(c, LogChannel)] 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, quash_channels=quash_channels,
lane_count=lane_count, fifo_depth=fifo_depth, lane_count=lane_count, fifo_depth=fifo_depth,
interface=self.cri) interface=self.cri)
self.submodules += outputs self.submodules += outputs
self.comb += outputs.coarse_timestamp.eq(tsc.coarse_ts) 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, quash_channels=quash_channels,
interface=self.cri) interface=self.cri)
self.submodules += inputs self.submodules += inputs
# Asychronous output errors # Asychronous output errors
o_collision_sync = BlindTransfer("rio", "rsys", data_width=16) o_collision_sync = BlindTransfer("rio", "sys", data_width=16)
o_busy_sync = BlindTransfer("rio", "rsys", data_width=16) o_busy_sync = BlindTransfer("rio", "sys", data_width=16)
self.submodules += o_collision_sync, o_busy_sync self.submodules += o_collision_sync, o_busy_sync
o_collision = Signal() o_collision = Signal()
o_busy = Signal() o_busy = Signal()

View File

@ -127,7 +127,7 @@ class KernelInitiator(Module, AutoCSR):
class CRIDecoder(Module): 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): if isinstance(slaves, int):
slaves = [Interface() for _ in range(slaves)] slaves = [Interface() for _ in range(slaves)]
if master is None: if master is None:
@ -155,12 +155,7 @@ class CRIDecoder(Module):
if enable_routing: if enable_routing:
self.specials.routing_table = Memory(slave_bits, 256) self.specials.routing_table = Memory(slave_bits, 256)
if mode == "async": rtp_decoder = self.routing_table.get_port()
rtp_decoder = self.routing_table.get_port()
elif mode == "sync":
rtp_decoder = self.routing_table.get_port(clock_domain="rtio")
else:
raise ValueError
self.specials += rtp_decoder self.specials += rtp_decoder
self.comb += [ self.comb += [
rtp_decoder.adr.eq(self.master.chan_sel[16:]), rtp_decoder.adr.eq(self.master.chan_sel[16:]),
@ -187,7 +182,7 @@ class CRIDecoder(Module):
class CRISwitch(Module, AutoCSR): class CRISwitch(Module, AutoCSR):
def __init__(self, masters=2, slave=None, mode="async"): def __init__(self, masters=2, slave=None):
if isinstance(masters, int): if isinstance(masters, int):
masters = [Interface() for _ in range(masters)] masters = [Interface() for _ in range(masters)]
if slave is None: 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: if len(masters) == 1:
self.comb += masters[0].connect(slave) self.comb += masters[0].connect(slave)
else: else:
@ -215,7 +201,7 @@ class CRISwitch(Module, AutoCSR):
for name, size, direction in layout: for name, size, direction in layout:
if direction == DIR_M_TO_S: if direction == DIR_M_TO_S:
choices = Array(getattr(m, name) for m in masters) 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 # connect slave->master signals
for name, size, direction in layout: for name, size, direction in layout:
@ -227,10 +213,10 @@ class CRISwitch(Module, AutoCSR):
class CRIInterconnectShared(Module): 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() shared = Interface()
self.submodules.switch = CRISwitch(masters, shared, mode) self.submodules.switch = CRISwitch(masters, shared)
self.submodules.decoder = CRIDecoder(slaves, shared, mode, enable_routing) self.submodules.decoder = CRIDecoder(slaves, shared, enable_routing)
def get_csrs(self): def get_csrs(self):
return self.switch.get_csrs() return self.switch.get_csrs()

View File

@ -1,6 +1,6 @@
from migen import * from migen import *
from migen.genlib.record import Record from migen.genlib.record import Record
from migen.genlib.fifo import * from migen.genlib.fifo import SyncFIFOBuffered
from migen.genlib.cdc import BlindTransfer from migen.genlib.cdc import BlindTransfer
from artiq.gateware.rtio import cri from artiq.gateware.rtio import cri
@ -24,24 +24,13 @@ def get_channel_layout(coarse_ts_width, interface):
class InputCollector(Module): 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: if interface is None:
interface = cri.Interface() interface = cri.Interface()
self.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_statuses, i_datas, i_timestamps = [], [], []
i_ack = Signal() i_ack = Signal()
sel = self.cri.chan_sel[:16] sel = self.cri.chan_sel[:16]
@ -55,7 +44,7 @@ class InputCollector(Module):
# FIFO # FIFO
layout = get_channel_layout(len(tsc.coarse_ts), iif) 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 self.submodules += fifo
fifo_in = Record(layout) fifo_in = Record(layout)
fifo_out = Record(layout) fifo_out = Record(layout)
@ -67,7 +56,7 @@ class InputCollector(Module):
# FIFO write # FIFO write
if iif.delay: if iif.delay:
counter_rtio = Signal.like(tsc.coarse_ts, reset_less=True) 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: else:
counter_rtio = tsc.coarse_ts counter_rtio = tsc.coarse_ts
if hasattr(fifo_in, "data"): if hasattr(fifo_in, "data"):
@ -80,17 +69,8 @@ class InputCollector(Module):
self.comb += fifo_in.timestamp.eq(full_ts) self.comb += fifo_in.timestamp.eq(full_ts)
self.comb += fifo.we.eq(iif.stb) self.comb += fifo.we.eq(iif.stb)
overflow_io = Signal() overflow_trigger = Signal()
self.comb += overflow_io.eq(fifo.we & ~fifo.writable) self.comb += overflow_trigger.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
# FIFO read, CRI connection # FIFO read, CRI connection
if hasattr(fifo_out, "data"): if hasattr(fifo_out, "data"):
@ -107,7 +87,7 @@ class InputCollector(Module):
self.comb += selected.eq(sel == n) self.comb += selected.eq(sel == n)
overflow = Signal() overflow = Signal()
sync_cri += [ self.sync += [
If(selected & i_ack, If(selected & i_ack,
overflow.eq(0)), overflow.eq(0)),
If(overflow_trigger, If(overflow_trigger,
@ -122,7 +102,7 @@ class InputCollector(Module):
input_pending = Signal() input_pending = Signal()
self.cri.i_data.reset_less = True self.cri.i_data.reset_less = True
self.cri.i_timestamp.reset_less = True self.cri.i_timestamp.reset_less = True
sync_cri += [ self.sync += [
i_ack.eq(0), i_ack.eq(0),
If(i_ack, If(i_ack,
self.cri.i_status.eq(Cat(~i_status_raw[0], i_status_raw[1], 0)), self.cri.i_status.eq(Cat(~i_status_raw[0], i_status_raw[1], 0)),

View File

@ -120,7 +120,7 @@ class Fastino(Module):
), ),
] ]
self.sync.rtio += [ self.sync += [
self.rtlink.i.stb.eq(self.rtlink.o.stb & self.rtlink.i.stb.eq(self.rtlink.o.stb &
self.rtlink.o.address[-1]), self.rtlink.o.address[-1]),
self.rtlink.i.data.eq( self.rtlink.i.data.eq(

View File

@ -21,14 +21,12 @@ class Synchronizer(Module):
# # # # # #
for count in counts_in: self.comb += [o.eq(i) for i, o in zip(counts_in, self.counts)]
count.attr.add("no_retiming")
self.specials += [MultiReg(i, o, "rtio") for i, o in zip(counts_in, self.counts)]
ps = PulseSynchronizer("cl", "rtio") ps = PulseSynchronizer("cl", "sys")
self.submodules += ps self.submodules += ps
self.comb += ps.i.eq(roi_engines[0].out.update) 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): class Serializer(Module):
@ -85,7 +83,7 @@ class Grabber(Module):
roi_engine.cfg.x1, roi_engine.cfg.y1]): roi_engine.cfg.x1, roi_engine.cfg.y1]):
roi_boundary = Signal.like(target) roi_boundary = Signal.like(target)
roi_boundary.attr.add("no_retiming") 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)) roi_boundary.eq(self.config.o.data))
self.specials += MultiReg(roi_boundary, target, "cl") self.specials += MultiReg(roi_boundary, target, "cl")

View File

@ -10,7 +10,7 @@ class Phy(Module):
self.rtlink = rtlink.Interface( self.rtlink = rtlink.Interface(
rtlink.OInterface(data_width=32, address_width=4, rtlink.OInterface(data_width=32, address_width=4,
enable_replace=True)) enable_replace=True))
self.sync.rtio += [ self.sync += [
If(self.rtlink.o.stb, If(self.rtlink.o.stb,
Array(regs)[self.rtlink.o.address].eq(self.rtlink.o.data) 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)) self.comb += self.serializer.payload.eq(Cat(header.raw_bits(), body))
re_dly = Signal(3) # stage, send, respond re_dly = Signal(3) # stage, send, respond
self.sync.rtio += [ self.sync += [
header.type.eq(1), # body type is baseband data header.type.eq(1), # body type is baseband data
If(self.serializer.stb, If(self.serializer.stb,
self.ch0.dds.stb.eq(1), # synchronize self.ch0.dds.stb.eq(1), # synchronize
@ -105,7 +105,7 @@ class MiqroChannel(Module):
self.pulse.eq(pulse), self.pulse.eq(pulse),
self.rtlink.o.busy.eq(stb & ~self.ack), self.rtlink.o.busy.eq(stb & ~self.ack),
] ]
self.sync.rtio += [ self.sync += [
If(~stb, If(~stb,
dt.eq(dt + 2), dt.eq(dt + 2),
), ),
@ -162,7 +162,7 @@ class Miqro(Module):
] ]
re_dly = Signal(3) # stage, send, respond re_dly = Signal(3) # stage, send, respond
self.sync.rtio += [ self.sync += [
header.type.eq(3), # body type is miqro pulse data header.type.eq(3), # body type is miqro pulse data
If(self.serializer.stb, If(self.serializer.stb,
header.we.eq(0), header.we.eq(0),

View File

@ -19,7 +19,7 @@ class _OSERDESE2_8X(Module):
p_INIT_OQ=0b11111111 if invert else 0b00000000, p_INIT_OQ=0b11111111 if invert else 0b00000000,
o_OQ=self.ser_out, o_TQ=self.t_out, o_OQ=self.ser_out, o_TQ=self.t_out,
i_RST=ResetSignal("rio_phy"), i_RST=ResetSignal("rio_phy"),
i_CLK=ClockSignal("rtiox4"), i_CLK=ClockSignal("sys4x"),
i_CLKDIV=ClockSignal("rio_phy"), 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_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, 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_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], o_Q5=i[3], o_Q6=i[2], o_Q7=i[1], o_Q8=i[0],
i_D=self.ser_in, i_D=self.ser_in,
i_CLK=ClockSignal("rtiox4"), i_CLK=ClockSignal("sys4x"),
i_CLKB=~ClockSignal("rtiox4"), i_CLKB=~ClockSignal("sys4x"),
i_CE1=1, i_CE1=1,
i_RST=ResetSignal("rio_phy"), i_RST=ResetSignal("rio_phy"),
i_CLKDIV=ClockSignal("rio_phy")) i_CLKDIV=ClockSignal("rio_phy"))

View File

@ -18,8 +18,8 @@ class _OSERDESE3(Module):
p_IS_CLK_INVERTED=0, p_IS_CLKDIV_INVERTED=0, p_IS_RST_INVERTED=0, 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, o_OQ=self.ser_out, o_T_OUT=self.t_out,
i_RST=ResetSignal("rtio"), i_RST=ResetSignal("sys"),
i_CLK=ClockSignal("rtiox"), i_CLKDIV=ClockSignal("rtio"), i_CLK=ClockSignal("rtiox"), i_CLKDIV=ClockSignal("sys"),
i_D=self.o, i_T=self.t_in) i_D=self.o, i_T=self.t_in)
@ -39,11 +39,11 @@ class _ISERDESE3(Module):
p_DATA_WIDTH=dw, p_DATA_WIDTH=dw,
i_D=self.ser_in, i_D=self.ser_in,
i_RST=ResetSignal("rtio"), i_RST=ResetSignal("sys"),
i_FIFO_RD_EN=0, i_FIFO_RD_EN=0,
i_CLK=ClockSignal("rtiox"), i_CLK=ClockSignal("rtiox"),
i_CLK_B=ClockSignal("rtiox"), # locally inverted 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))])) o_Q=Cat(*[self.i[i] for i in reversed(range(dw))]))

View File

@ -11,41 +11,25 @@ __all__ = ["SED"]
class SED(Module): 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, lane_count=8, fifo_depth=128, enable_spread=True,
quash_channels=[], report_buffer_space=False, interface=None): 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) seqn_width = layouts.seqn_width(lane_count, fifo_depth)
self.submodules.lane_dist = lane_dist_cdr( self.submodules.lane_dist = LaneDistributor(lane_count, seqn_width,
LaneDistributor(lane_count, seqn_width, layouts.fifo_payload(channels),
layouts.fifo_payload(channels), [channel.interface.o.delay for channel in channels],
[channel.interface.o.delay for channel in channels], glbl_fine_ts_width,
glbl_fine_ts_width, enable_spread=enable_spread,
enable_spread=enable_spread, quash_channels=quash_channels,
quash_channels=quash_channels, interface=interface)
interface=interface)) self.submodules.fifos = FIFOs(lane_count, fifo_depth,
self.submodules.fifos = fifos_cdr( layouts.fifo_payload(channels), report_buffer_space)
FIFOs(lane_count, fifo_depth, self.submodules.gates = Gates(lane_count, seqn_width,
layouts.fifo_payload(channels), mode, report_buffer_space)) layouts.fifo_payload(channels),
self.submodules.gates = gates_cdr( layouts.output_network_payload(channels, glbl_fine_ts_width))
Gates(lane_count, seqn_width, self.submodules.output_driver = OutputDriver(channels, glbl_fine_ts_width,
layouts.fifo_payload(channels), lane_count, seqn_width)
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))
for o, i in zip(self.lane_dist.output, self.fifos.input): for o, i in zip(self.lane_dist.output, self.fifos.input):
self.comb += o.connect(i) self.comb += o.connect(i)

View File

@ -2,7 +2,7 @@ from operator import or_
from functools import reduce from functools import reduce
from migen import * from migen import *
from migen.genlib.fifo import * from migen.genlib.fifo import SyncFIFOBuffered
from artiq.gateware.rtio.sed import layouts from artiq.gateware.rtio.sed import layouts
@ -11,7 +11,7 @@ __all__ = ["FIFOs"]
class FIFOs(Module): 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) seqn_width = layouts.seqn_width(lane_count, fifo_depth)
self.input = [Record(layouts.fifo_ingress(seqn_width, layout_payload)) self.input = [Record(layouts.fifo_ingress(seqn_width, layout_payload))
for _ in range(lane_count)] 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 = [] fifos = []
for input, output in zip(self.input, self.output): 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 self.submodules += fifo
fifos.append(fifo) fifos.append(fifo)
@ -47,9 +40,6 @@ class FIFOs(Module):
] ]
if report_buffer_space: if report_buffer_space:
if mode != "sync":
raise NotImplementedError
def compute_max(elts): def compute_max(elts):
l = len(elts) l = len(elts)
if l == 1: if l == 1:

View File

@ -1,48 +1,26 @@
from migen import * from migen import *
from artiq.gateware.rtio.cdc import GrayCodeTransfer
class TSC(Module): 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 self.glbl_fine_ts_width = glbl_fine_ts_width
# in rtio domain # in rtio domain
self.coarse_ts = Signal(64 - glbl_fine_ts_width) self.coarse_ts = Signal(64 - glbl_fine_ts_width)
self.full_ts = Signal(64) 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 = Signal()
self.load_value = Signal.like(self.coarse_ts) self.load_value = Signal.like(self.coarse_ts)
if mode == "async": self.full_ts_cri = self.full_ts
self.full_ts_cri = self.full_ts_sys
elif mode == "sync":
self.full_ts_cri = self.full_ts
else:
raise ValueError
# # # # # #
self.sync.rtio += If(self.load, self.sync += If(self.load,
self.coarse_ts.eq(self.load_value) self.coarse_ts.eq(self.load_value)
).Else( ).Else(
self.coarse_ts.eq(self.coarse_ts + 1) 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.comb += [
self.full_ts.eq(self.coarse_ts << glbl_fine_ts_width), self.full_ts.eq(self.coarse_ts << glbl_fine_ts_width),
self.full_ts_sys.eq(self.coarse_ts_sys << glbl_fine_ts_width)
] ]

View File

@ -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): def fix_serdes_timing_path(platform):
# ignore timing of path from OSERDESE2 through the pad to ISERDESE2 # ignore timing of path from OSERDESE2 through the pad to ISERDESE2
platform.add_platform_command( platform.add_platform_command(

View File

@ -17,69 +17,15 @@ from misoc.integration.builder import builder_args, builder_argdict
from artiq.gateware.amp import AMPSoC from artiq.gateware.amp import AMPSoC
from artiq.gateware import rtio from artiq.gateware import rtio
from artiq.gateware.rtio.phy import ttl_simple, ttl_serdes_7series, edge_counter 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 import eem
from artiq.gateware.drtio.transceiver import gtp_7series from artiq.gateware.drtio.transceiver import gtp_7series
from artiq.gateware.drtio.siphaser import SiPhaser7Series 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.rx_synchronizer import XilinxRXSynchronizer
from artiq.gateware.drtio import * from artiq.gateware.drtio import *
from artiq.build_soc 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): class SMAClkinForward(Module):
def __init__(self, platform): def __init__(self, platform):
sma_clkin = platform.request("sma_clkin") sma_clkin = platform.request("sma_clkin")
@ -118,6 +64,8 @@ class StandaloneBase(MiniSoC, AMPSoC):
integrated_sram_size=8192, integrated_sram_size=8192,
ethmac_nrxslots=4, ethmac_nrxslots=4,
ethmac_ntxslots=4, ethmac_ntxslots=4,
clk_freq=kwargs.get("rtio_frequency", 125.0e6),
rtio_sys_merge=True,
**kwargs) **kwargs)
AMPSoC.__init__(self) AMPSoC.__init__(self)
add_identifier(self, gateware_identifier_str=gateware_identifier_str) add_identifier(self, gateware_identifier_str=gateware_identifier_str)
@ -127,6 +75,23 @@ class StandaloneBase(MiniSoC, AMPSoC):
self.platform.request("error_led"))) self.platform.request("error_led")))
self.csr_devices.append("error_led") self.csr_devices.append("error_led")
self.submodules += SMAClkinForward(self.platform) 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") i2c = self.platform.request("i2c")
self.submodules.i2c = gpio.GPIOTristate([i2c.scl, i2c.sda]) self.submodules.i2c = gpio.GPIOTristate([i2c.scl, i2c.sda])
@ -136,10 +101,8 @@ class StandaloneBase(MiniSoC, AMPSoC):
self.config["SI5324_SOFT_RESET"] = None self.config["SI5324_SOFT_RESET"] = None
def add_rtio(self, rtio_channels, sed_lanes=8): 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) 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.submodules.rtio_core = rtio.Core(self.rtio_tsc, rtio_channels, lane_count=sed_lanes)
self.csr_devices.append("rtio_core") self.csr_devices.append("rtio_core")
self.submodules.rtio = rtio.KernelInitiator(self.rtio_tsc) 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.submodules.rtio_moninj = rtio.MonInj(rtio_channels)
self.csr_devices.append("rtio_moninj") 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.submodules.rtio_analyzer = rtio.Analyzer(self.rtio_tsc, self.rtio_core.cri,
self.get_native_sdram_if(), cpu_dw=self.cpu_dw) self.get_native_sdram_if(), cpu_dw=self.cpu_dw)
self.csr_devices.append("rtio_analyzer") self.csr_devices.append("rtio_analyzer")
@ -177,7 +136,6 @@ class Tester(StandaloneBase):
dds = "ad9910" dds = "ad9910"
StandaloneBase.__init__(self, hw_rev=hw_rev, **kwargs) StandaloneBase.__init__(self, hw_rev=hw_rev, **kwargs)
self.config["SI5324_AS_SYNTHESIZER"] = None
# self.config["SI5324_EXT_REF"] = None # self.config["SI5324_EXT_REF"] = None
self.config["RTIO_FREQUENCY"] = "125.0" self.config["RTIO_FREQUENCY"] = "125.0"
if hw_rev == "v1.0": if hw_rev == "v1.0":
@ -215,7 +173,6 @@ class SUServo(StandaloneBase):
hw_rev = "v2.0" hw_rev = "v2.0"
StandaloneBase.__init__(self, hw_rev=hw_rev, **kwargs) StandaloneBase.__init__(self, hw_rev=hw_rev, **kwargs)
self.config["SI5324_AS_SYNTHESIZER"] = None
# self.config["SI5324_EXT_REF"] = None # self.config["SI5324_EXT_REF"] = None
self.config["RTIO_FREQUENCY"] = "125.0" self.config["RTIO_FREQUENCY"] = "125.0"
if hw_rev == "v1.0": if hw_rev == "v1.0":
@ -247,8 +204,6 @@ class SUServo(StandaloneBase):
self.add_rtio(self.rtio_channels) self.add_rtio(self.rtio_channels)
pads = self.platform.lookup_request("sampler3_adc_data_p") 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( self.platform.add_false_path_constraints(
pads.clkout, self.crg.cd_sys.clk) pads.clkout, self.crg.cd_sys.clk)
@ -277,6 +232,8 @@ class MasterBase(MiniSoC, AMPSoC):
integrated_sram_size=8192, integrated_sram_size=8192,
ethmac_nrxslots=4, ethmac_nrxslots=4,
ethmac_ntxslots=4, ethmac_ntxslots=4,
clk_freq=rtio_clk_freq,
rtio_sys_merge=True,
**kwargs) **kwargs)
AMPSoC.__init__(self) AMPSoC.__init__(self)
add_identifier(self, gateware_identifier_str=gateware_identifier_str) 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["I2C_BUS_COUNT"] = 1
self.config["HAS_SI5324"] = None self.config["HAS_SI5324"] = None
self.config["SI5324_SOFT_RESET"] = None self.config["SI5324_SOFT_RESET"] = None
self.config["SI5324_AS_SYNTHESIZER"] = None
self.config["RTIO_FREQUENCY"] = str(rtio_clk_freq/1e6) self.config["RTIO_FREQUENCY"] = str(rtio_clk_freq/1e6)
drtio_data_pads = [] drtio_data_pads = []
@ -315,8 +271,6 @@ class MasterBase(MiniSoC, AMPSoC):
sys_clk_freq=self.clk_freq, sys_clk_freq=self.clk_freq,
rtio_clk_freq=rtio_clk_freq) rtio_clk_freq=rtio_clk_freq)
self.csr_devices.append("drtio_transceiver") self.csr_devices.append("drtio_transceiver")
self.sync += self.disable_cdr_clk_ibuf.eq(
~self.drtio_transceiver.stable_clkin.storage)
if enable_sata: if enable_sata:
sfp_channels = self.drtio_transceiver.channels[1:] 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) self.comb += [self.virtual_leds.get(i + 1).eq(channel.rx_ready)
for i, channel in enumerate(sfp_channels)] 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 = [] drtio_csr_group = []
drtioaux_csr_group = [] drtioaux_csr_group = []
@ -366,8 +320,14 @@ class MasterBase(MiniSoC, AMPSoC):
rtio_clk_period = 1e9/rtio_clk_freq rtio_clk_period = 1e9/rtio_clk_freq
gtp = self.drtio_transceiver.gtps[0] 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.txoutclk, rtio_clk_period)
platform.add_period_constraint(gtp.rxoutclk, rtio_clk_period) platform.add_period_constraint(gtp.rxoutclk, rtio_clk_period)
platform.add_false_path_constraints( platform.add_false_path_constraints(
self.crg.cd_sys.clk, self.crg.cd_sys.clk,
gtp.txoutclk, gtp.rxoutclk) gtp.txoutclk, gtp.rxoutclk)
@ -376,8 +336,6 @@ class MasterBase(MiniSoC, AMPSoC):
platform.add_false_path_constraints( platform.add_false_path_constraints(
self.crg.cd_sys.clk, gtp.rxoutclk) 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) fix_serdes_timing_path(platform)
def add_rtio(self, rtio_channels, sed_lanes=8): 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 # Never running out of stupid features, GTs on A7 make you pack
# unrelated transceiver PLLs into one GTPE2_COMMON yourself. # unrelated transceiver PLLs into one GTPE2_COMMON yourself.
def create_qpll(self): 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": 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: else:
cdr_clk_clean = self.platform.request("si5324_clkout") cdr_clk_out = self.platform.request("si5324_clkout")
cdr_clk_clean_buf = Signal()
cdr_clk = Signal()
self.platform.add_period_constraint(cdr_clk_out, 8.)
self.specials += Instance("IBUFDS_GTE2", self.specials += Instance("IBUFDS_GTE2",
i_CEB=self.disable_cdr_clk_ibuf, i_CEB=0,
i_I=cdr_clk_clean.p, i_IB=cdr_clk_clean.n, i_I=cdr_clk_out.p, i_IB=cdr_clk_out.n,
o_O=cdr_clk_clean_buf) o_O=cdr_clk)
# Note precisely the rules Xilinx made up: # Note precisely the rules Xilinx made up:
# refclksel=0b001 GTREFCLK0 selected # refclksel=0b001 GTREFCLK0 selected
# refclksel=0b010 GTREFCLK1 selected # refclksel=0b010 GTREFCLK1 selected
@ -436,7 +393,8 @@ class MasterBase(MiniSoC, AMPSoC):
fbdiv=4, fbdiv=4,
fbdiv_45=5, fbdiv_45=5,
refclk_div=1) 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.crg.clk125_buf, qpll_eth_settings)
self.submodules += qpll self.submodules += qpll
self.drtio_qpll_channel, self.ethphy_qpll_channel = qpll.channels self.drtio_qpll_channel, self.ethphy_qpll_channel = qpll.channels
@ -448,7 +406,7 @@ class SatelliteBase(BaseSoC):
} }
mem_map.update(BaseSoC.mem_map) 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"): if hw_rev in ("v1.0", "v1.1"):
cpu_bus_width = 32 cpu_bus_width = 32
else: else:
@ -459,6 +417,8 @@ class SatelliteBase(BaseSoC):
cpu_bus_width=cpu_bus_width, cpu_bus_width=cpu_bus_width,
sdram_controller_type="minicon", sdram_controller_type="minicon",
l2_size=128*1024, l2_size=128*1024,
clk_freq=rtio_clk_freq,
rtio_sys_merge=True,
**kwargs) **kwargs)
add_identifier(self, gateware_identifier_str=gateware_identifier_str) add_identifier(self, gateware_identifier_str=gateware_identifier_str)
@ -469,23 +429,24 @@ class SatelliteBase(BaseSoC):
self.platform.request("error_led"))) self.platform.request("error_led")))
self.csr_devices.append("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": 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: else:
cdr_clk_clean = self.platform.request("si5324_clkout") cdr_clk_out = self.platform.request("si5324_clkout")
cdr_clk_clean_buf = Signal()
cdr_clk = Signal()
self.platform.add_period_constraint(cdr_clk_out, 8.)
self.specials += Instance("IBUFDS_GTE2", self.specials += Instance("IBUFDS_GTE2",
i_CEB=disable_cdr_clk_ibuf, i_CEB=0,
i_I=cdr_clk_clean.p, i_IB=cdr_clk_clean.n, i_I=cdr_clk_out.p, i_IB=cdr_clk_out.n,
o_O=cdr_clk_clean_buf) o_O=cdr_clk)
qpll_drtio_settings = QPLLSettings( qpll_drtio_settings = QPLLSettings(
refclksel=0b001, refclksel=0b001,
fbdiv=4, fbdiv=4,
fbdiv_45=5, fbdiv_45=5,
refclk_div=1) refclk_div=1)
qpll = QPLL(cdr_clk_clean_buf, qpll_drtio_settings) qpll = QPLL(cdr_clk, qpll_drtio_settings)
self.submodules += qpll self.submodules += qpll
drtio_data_pads = [] drtio_data_pads = []
@ -504,8 +465,6 @@ class SatelliteBase(BaseSoC):
sys_clk_freq=self.clk_freq, sys_clk_freq=self.clk_freq,
rtio_clk_freq=rtio_clk_freq) rtio_clk_freq=rtio_clk_freq)
self.csr_devices.append("drtio_transceiver") self.csr_devices.append("drtio_transceiver")
self.sync += disable_cdr_clk_ibuf.eq(
~self.drtio_transceiver.stable_clkin.storage)
if enable_sata: if enable_sata:
sfp_channels = self.drtio_transceiver.channels[1:] 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) self.comb += [self.virtual_leds.get(i).eq(channel.rx_ready)
for i, channel in enumerate(sfp_channels)] 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_csr_group = []
drtioaux_memory_group = [] drtioaux_memory_group = []
@ -570,52 +529,34 @@ class SatelliteBase(BaseSoC):
rtio_clk_period = 1e9/rtio_clk_freq rtio_clk_period = 1e9/rtio_clk_freq
self.config["RTIO_FREQUENCY"] = str(rtio_clk_freq/1e6) self.config["RTIO_FREQUENCY"] = str(rtio_clk_freq/1e6)
if with_wrpll:
self.submodules.wrpll_sampler = DDMTDSamplerGTP( self.submodules.siphaser = SiPhaser7Series(
self.drtio_transceiver, si5324_clkin=platform.request("cdr_clk") if platform.hw_rev == "v2.0"
platform.request("cdr_clk_clean_fabric")) else platform.request("si5324_clkin"),
helper_clk_pads = platform.request("ddmtd_helper_clk") rx_synchronizer=self.rx_synchronizer,
self.submodules.wrpll = WRPLL( ref_clk=self.crg.clk125_div2, ref_div2=True,
helper_clk_pads=helper_clk_pads, rtio_clk_freq=rtio_clk_freq)
main_dcxo_i2c=platform.request("ddmtd_main_dcxo_i2c"), platform.add_false_path_constraints(
helper_dxco_i2c=platform.request("ddmtd_helper_dcxo_i2c"), self.crg.cd_sys.clk, self.siphaser.mmcm_freerun_output)
ddmtd_inputs=self.wrpll_sampler) self.csr_devices.append("siphaser")
self.csr_devices.append("wrpll") self.config["HAS_SI5324"] = None
# note: do not use self.wrpll.cd_helper.clk; otherwise, vivado craps out with: self.config["SI5324_SOFT_RESET"] = None
# 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
gtp = self.drtio_transceiver.gtps[0] 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.txoutclk, rtio_clk_period)
platform.add_period_constraint(gtp.rxoutclk, rtio_clk_period) platform.add_period_constraint(gtp.rxoutclk, rtio_clk_period)
platform.add_false_path_constraints( platform.add_false_path_constraints(
self.crg.cd_sys.clk, self.crg.cd_sys.clk,
gtp.txoutclk, gtp.rxoutclk) 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:]: for gtp in self.drtio_transceiver.gtps[1:]:
platform.add_period_constraint(gtp.rxoutclk, rtio_clk_period) platform.add_period_constraint(gtp.rxoutclk, rtio_clk_period)
platform.add_false_path_constraints( platform.add_false_path_constraints(
self.crg.cd_sys.clk, gtp.rxoutclk) 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) fix_serdes_timing_path(platform)
def add_rtio(self, rtio_channels, sed_lanes=8): def add_rtio(self, rtio_channels, sed_lanes=8):
@ -629,7 +570,7 @@ class SatelliteBase(BaseSoC):
self.submodules.cri_con = rtio.CRIInterconnectShared( self.submodules.cri_con = rtio.CRIInterconnectShared(
[self.drtiosat.cri], [self.drtiosat.cri],
[self.local_io.cri] + self.drtio_cri, [self.local_io.cri] + self.drtio_cri,
mode="sync", enable_routing=True) enable_routing=True)
self.csr_devices.append("cri_con") self.csr_devices.append("cri_con")
self.submodules.routing_table = rtio.RoutingTableAccess(self.cri_con) self.submodules.routing_table = rtio.RoutingTableAccess(self.cri_con)
self.csr_devices.append("routing_table") self.csr_devices.append("routing_table")
@ -687,7 +628,6 @@ def main():
parser.add_argument("-V", "--variant", default="tester", parser.add_argument("-V", "--variant", default="tester",
help="variant: {} (default: %(default)s)".format( help="variant: {} (default: %(default)s)".format(
"/".join(sorted(VARIANTS.keys())))) "/".join(sorted(VARIANTS.keys()))))
parser.add_argument("--with-wrpll", default=False, action="store_true")
parser.add_argument("--tester-dds", default=None, parser.add_argument("--tester-dds", default=None,
help="Tester variant DDS type: ad9910/ad9912 " help="Tester variant DDS type: ad9910/ad9912 "
"(default: ad9910)") "(default: ad9910)")
@ -696,8 +636,6 @@ def main():
args = parser.parse_args() args = parser.parse_args()
argdict = dict() argdict = dict()
if args.with_wrpll:
argdict["with_wrpll"] = True
argdict["gateware_identifier_str"] = args.gateware_identifier_str argdict["gateware_identifier_str"] = args.gateware_identifier_str
argdict["dds"] = args.tester_dds argdict["dds"] = args.tester_dds

View File

@ -23,7 +23,6 @@ class GenericStandalone(StandaloneBase):
self.class_name_override = description["variant"] self.class_name_override = description["variant"]
StandaloneBase.__init__(self, hw_rev=hw_rev, **kwargs) StandaloneBase.__init__(self, hw_rev=hw_rev, **kwargs)
self.config["SI5324_AS_SYNTHESIZER"] = None
self.config["RTIO_FREQUENCY"] = "{:.1f}".format(description["rtio_frequency"]/1e6) self.config["RTIO_FREQUENCY"] = "{:.1f}".format(description["rtio_frequency"]/1e6)
if "ext_ref_frequency" in description: if "ext_ref_frequency" in description:
self.config["SI5324_EXT_REF"] = None self.config["SI5324_EXT_REF"] = None
@ -64,7 +63,7 @@ class GenericStandalone(StandaloneBase):
self.add_csr_group("grabber", self.grabber_csr_group) self.add_csr_group("grabber", self.grabber_csr_group)
for grabber in self.grabber_csr_group: for grabber in self.grabber_csr_group:
self.platform.add_false_path_constraints( 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): class GenericMaster(MasterBase):

View File

@ -17,72 +17,27 @@ from misoc.integration.builder import builder_args, builder_argdict
from artiq.gateware.amp import AMPSoC from artiq.gateware.amp import AMPSoC
from artiq.gateware import rtio, nist_clock, nist_qc2 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.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.transceiver import gtx_7series
from artiq.gateware.drtio.siphaser import SiPhaser7Series from artiq.gateware.drtio.siphaser import SiPhaser7Series
from artiq.gateware.drtio.rx_synchronizer import XilinxRXSynchronizer from artiq.gateware.drtio.rx_synchronizer import XilinxRXSynchronizer
from artiq.gateware.drtio import * from artiq.gateware.drtio import *
from artiq.build_soc import * from artiq.build_soc import *
class SMAClkinForward(Module):
class _RTIOCRG(Module, AutoCSR): def __init__(self, platform):
def __init__(self, platform, rtio_internal_clk, use_sma=True): sma_clkin = platform.request("user_sma_clock")
self._clock_sel = CSRStorage() sma_clkin_se = Signal()
self._pll_reset = CSRStorage(reset=1) sma_clkin_buffered = Signal()
self._pll_locked = CSRStatus() cdr_clk_se = Signal()
self.clock_domains.cd_rtio = ClockDomain() cdr_clk = platform.request("si5324_clkin_33")
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()
self.specials += [ self.specials += [
Instance("PLLE2_ADV", Instance("IBUFDS", i_I=sma_clkin.p, i_IB=sma_clkin.n, o_O=sma_clkin_se),
p_STARTUP_WAIT="FALSE", o_LOCKED=pll_locked, 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),
p_REF_JITTER1=0.01, Instance("OBUFDS", i_I=cdr_clk_se, o_O=cdr_clk.p, o_OB=cdr_clk.n)
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)
] ]
# The default voltage for these signals on KC705 is 2.5V, and the Migen platform # 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, # 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 # 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, integrated_sram_size=8192,
ethmac_nrxslots=4, ethmac_nrxslots=4,
ethmac_ntxslots=4, ethmac_ntxslots=4,
rtio_sys_merge=True,
**kwargs) **kwargs)
AMPSoC.__init__(self) AMPSoC.__init__(self)
add_identifier(self, gateware_identifier_str=gateware_identifier_str) add_identifier(self, gateware_identifier_str=gateware_identifier_str)
@ -149,6 +105,30 @@ class _StandaloneBase(MiniSoC, AMPSoC):
if isinstance(self.platform.toolchain, XilinxISEToolchain): if isinstance(self.platform.toolchain, XilinxISEToolchain):
self.platform.toolchain.bitgen_opt += " -g compress" 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.submodules.timer1 = timer.Timer()
self.csr_devices.append("timer1") self.csr_devices.append("timer1")
self.interrupt_devices.append("timer1") self.interrupt_devices.append("timer1")
@ -158,7 +138,6 @@ class _StandaloneBase(MiniSoC, AMPSoC):
self.platform.request("user_led", 1))) self.platform.request("user_led", 1)))
self.csr_devices.append("leds") self.csr_devices.append("leds")
self.platform.add_extension(_reprogrammed3v3_io)
self.platform.add_extension(_ams101_dac) self.platform.add_extension(_ams101_dac)
i2c = self.platform.request("i2c") i2c = self.platform.request("i2c")
@ -169,10 +148,7 @@ class _StandaloneBase(MiniSoC, AMPSoC):
self.config["HAS_DDS"] = None self.config["HAS_DDS"] = None
def add_rtio(self, rtio_channels): def add_rtio(self, rtio_channels):
self.submodules.rtio_crg = _RTIOCRG(self.platform, self.crg.cd_sys.clk) self.submodules.rtio_tsc = rtio.TSC(glbl_fine_ts_width=3)
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_core = rtio.Core(self.rtio_tsc, rtio_channels) self.submodules.rtio_core = rtio.Core(self.rtio_tsc, rtio_channels)
self.csr_devices.append("rtio_core") self.csr_devices.append("rtio_core")
self.submodules.rtio = rtio.KernelInitiator(self.rtio_tsc) 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.submodules.rtio_moninj = rtio.MonInj(rtio_channels)
self.csr_devices.append("rtio_moninj") 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.submodules.rtio_analyzer = rtio.Analyzer(self.rtio_tsc, self.rtio_core.cri,
self.get_native_sdram_if(), cpu_dw=self.cpu_dw) self.get_native_sdram_if(), cpu_dw=self.cpu_dw)
self.csr_devices.append("rtio_analyzer") self.csr_devices.append("rtio_analyzer")
@ -208,6 +179,7 @@ class _MasterBase(MiniSoC, AMPSoC):
mem_map.update(MiniSoC.mem_map) mem_map.update(MiniSoC.mem_map)
def __init__(self, gateware_identifier_str=None, drtio_100mhz=False, **kwargs): def __init__(self, gateware_identifier_str=None, drtio_100mhz=False, **kwargs):
clk_freq = 100e6 if drtio_100mhz else 125e6
MiniSoC.__init__(self, MiniSoC.__init__(self,
cpu_type="vexriscv", cpu_type="vexriscv",
cpu_bus_width=64, cpu_bus_width=64,
@ -216,6 +188,8 @@ class _MasterBase(MiniSoC, AMPSoC):
integrated_sram_size=8192, integrated_sram_size=8192,
ethmac_nrxslots=4, ethmac_nrxslots=4,
ethmac_ntxslots=4, ethmac_ntxslots=4,
clk_freq=clk_freq,
rtio_sys_merge=True,
**kwargs) **kwargs)
AMPSoC.__init__(self) AMPSoC.__init__(self)
add_identifier(self, gateware_identifier_str=gateware_identifier_str) 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") 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 # 1000BASE_BX10 Ethernet compatible, 100/125MHz RTIO clock
self.submodules.drtio_transceiver = gtx_7series.GTX( self.submodules.drtio_transceiver = gtx_7series.GTX(
clock_pads=platform.request("si5324_clkout"), clock_pads=platform.request("si5324_clkout"),
pads=data_pads, pads=data_pads,
sys_clk_freq=self.clk_freq, clk_freq=self.clk_freq)
rtio_clk_freq=rtio_clk_freq)
self.csr_devices.append("drtio_transceiver") 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 = [] drtio_csr_group = []
drtioaux_csr_group = [] drtioaux_csr_group = []
@ -283,38 +254,39 @@ class _MasterBase(MiniSoC, AMPSoC):
self.add_memory_group("drtioaux_mem", drtioaux_memory_group) self.add_memory_group("drtioaux_mem", drtioaux_memory_group)
self.config["RTIO_FREQUENCY"] = str(self.drtio_transceiver.rtio_clk_freq/1e6) self.config["RTIO_FREQUENCY"] = str(self.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") self.csr_devices.append("si5324_rst_n")
i2c = self.platform.request("i2c") i2c = self.platform.request("i2c")
self.submodules.i2c = gpio.GPIOTristate([i2c.scl, i2c.sda]) self.submodules.i2c = gpio.GPIOTristate([i2c.scl, i2c.sda])
self.csr_devices.append("i2c") self.csr_devices.append("i2c")
self.config["I2C_BUS_COUNT"] = 1 self.config["I2C_BUS_COUNT"] = 1
self.config["HAS_SI5324"] = None 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 rtio_clk_period = 1e9/self.drtio_transceiver.rtio_clk_freq
# Constrain TX & RX timing for the first transceiver channel # Constrain TX & RX timing for the first transceiver channel
# (First channel acts as master for phase alignment for all channels' TX) # (First channel acts as master for phase alignment for all channels' TX)
gtx0 = self.drtio_transceiver.gtxs[0] 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.txoutclk, rtio_clk_period)
platform.add_period_constraint(gtx0.rxoutclk, rtio_clk_period) platform.add_period_constraint(gtx0.rxoutclk, rtio_clk_period)
platform.add_false_path_constraints( platform.add_false_path_constraints(
self.crg.cd_sys.clk, self.crg.cd_sys.clk, gtx0.rxoutclk)
gtx0.txoutclk, gtx0.rxoutclk)
# Constrain RX timing for the each transceiver channel # Constrain RX timing for the each transceiver channel
# (Each channel performs single-lane phase alignment for RX) # (Each channel performs single-lane phase alignment for RX)
for gtx in self.drtio_transceiver.gtxs[1:]: for gtx in self.drtio_transceiver.gtxs[1:]:
platform.add_period_constraint(gtx.rxoutclk, rtio_clk_period) platform.add_period_constraint(gtx.rxoutclk, rtio_clk_period)
platform.add_false_path_constraints( 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) fix_serdes_timing_path(platform)
def add_rtio(self, rtio_channels): def add_rtio(self, rtio_channels):
@ -345,12 +317,15 @@ class _SatelliteBase(BaseSoC):
mem_map.update(BaseSoC.mem_map) mem_map.update(BaseSoC.mem_map)
def __init__(self, gateware_identifier_str=None, sma_as_sat=False, drtio_100mhz=False, **kwargs): 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, BaseSoC.__init__(self,
cpu_type="vexriscv", cpu_type="vexriscv",
cpu_bus_width=64, cpu_bus_width=64,
sdram_controller_type="minicon", sdram_controller_type="minicon",
l2_size=128*1024, l2_size=128*1024,
integrated_sram_size=8192, integrated_sram_size=8192,
clk_freq=clk_freq,
rtio_sys_merge=True,
**kwargs) **kwargs)
add_identifier(self, gateware_identifier_str=gateware_identifier_str) add_identifier(self, gateware_identifier_str=gateware_identifier_str)
@ -372,17 +347,16 @@ class _SatelliteBase(BaseSoC):
if sma_as_sat: if sma_as_sat:
data_pads = data_pads[::-1] 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 # 1000BASE_BX10 Ethernet compatible, 100/125MHz RTIO clock
self.submodules.drtio_transceiver = gtx_7series.GTX( self.submodules.drtio_transceiver = gtx_7series.GTX(
clock_pads=platform.request("si5324_clkout"), clock_pads=platform.request("si5324_clkout"),
pads=data_pads, pads=data_pads,
sys_clk_freq=self.clk_freq, clk_freq=self.clk_freq)
rtio_clk_freq=rtio_clk_freq)
self.csr_devices.append("drtio_transceiver") 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_csr_group = []
drtioaux_memory_group = [] drtioaux_memory_group = []
@ -432,12 +406,13 @@ class _SatelliteBase(BaseSoC):
self.submodules.siphaser = SiPhaser7Series( self.submodules.siphaser = SiPhaser7Series(
si5324_clkin=platform.request("si5324_clkin_33"), si5324_clkin=platform.request("si5324_clkin_33"),
rx_synchronizer=self.rx_synchronizer, rx_synchronizer=self.rx_synchronizer,
ref_clk=ClockSignal("bootstrap"),
ultrascale=False, ultrascale=False,
rtio_clk_freq=self.drtio_transceiver.rtio_clk_freq) rtio_clk_freq=self.drtio_transceiver.rtio_clk_freq)
platform.add_false_path_constraints( platform.add_false_path_constraints(
self.crg.cd_sys.clk, self.siphaser.mmcm_freerun_output) self.crg.cd_sys.clk, self.siphaser.mmcm_freerun_output)
self.csr_devices.append("siphaser") 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") self.csr_devices.append("si5324_rst_n")
i2c = self.platform.request("i2c") i2c = self.platform.request("i2c")
self.submodules.i2c = gpio.GPIOTristate([i2c.scl, i2c.sda]) self.submodules.i2c = gpio.GPIOTristate([i2c.scl, i2c.sda])
@ -445,20 +420,22 @@ class _SatelliteBase(BaseSoC):
self.config["I2C_BUS_COUNT"] = 1 self.config["I2C_BUS_COUNT"] = 1
self.config["HAS_SI5324"] = None 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 rtio_clk_period = 1e9/self.drtio_transceiver.rtio_clk_freq
# Constrain TX & RX timing for the first transceiver channel # Constrain TX & RX timing for the first transceiver channel
# (First channel acts as master for phase alignment for all channels' TX) # (First channel acts as master for phase alignment for all channels' TX)
gtx0 = self.drtio_transceiver.gtxs[0] 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.txoutclk, rtio_clk_period)
platform.add_period_constraint(gtx0.rxoutclk, 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 # Constrain RX timing for the each transceiver channel
# (Each channel performs single-lane phase alignment for RX) # (Each channel performs single-lane phase alignment for RX)
for gtx in self.drtio_transceiver.gtxs[1:]: for gtx in self.drtio_transceiver.gtxs[1:]:
@ -466,8 +443,6 @@ class _SatelliteBase(BaseSoC):
platform.add_false_path_constraints( platform.add_false_path_constraints(
self.crg.cd_sys.clk, 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) fix_serdes_timing_path(platform)
def add_rtio(self, rtio_channels): def add_rtio(self, rtio_channels):
@ -479,7 +454,7 @@ class _SatelliteBase(BaseSoC):
self.submodules.cri_con = rtio.CRIInterconnectShared( self.submodules.cri_con = rtio.CRIInterconnectShared(
[self.drtiosat.cri], [self.drtiosat.cri],
[self.local_io.cri] + self.drtio_cri, [self.local_io.cri] + self.drtio_cri,
mode="sync", enable_routing=True) enable_routing=True)
self.csr_devices.append("cri_con") self.csr_devices.append("cri_con")
self.submodules.routing_table = rtio.RoutingTableAccess(self.cri_con) self.submodules.routing_table = rtio.RoutingTableAccess(self.cri_con)
self.csr_devices.append("routing_table") self.csr_devices.append("routing_table")

View File

@ -36,7 +36,7 @@ class TB(Module):
def __init__(self, nwords, dw): def __init__(self, nwords, dw):
self.submodules.link_layer = Loopback(nwords) self.submodules.link_layer = Loopback(nwords)
self.submodules.aux_controller = ClockDomainsRenamer( 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): class TestAuxController(unittest.TestCase):

View File

@ -52,7 +52,7 @@ class DUT(Module):
self.ttl1 = Signal() self.ttl1 = Signal()
self.transceivers = DummyTransceiverPair(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.submodules.master = DRTIOMaster(self.tsc_master,
self.transceivers.alice) self.transceivers.alice)
self.submodules.master_ki = rtio.KernelInitiator(self.tsc_master, 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.phy1),
rtio.Channel.from_phy(self.phy2), rtio.Channel.from_phy(self.phy2),
] ]
self.submodules.tsc_satellite = rtio.TSC("sync") self.submodules.tsc_satellite = rtio.TSC()
self.submodules.satellite = DRTIOSatellite( self.submodules.satellite = DRTIOSatellite(
self.tsc_satellite, self.transceivers.bob, rx_synchronizer) self.tsc_satellite, self.transceivers.bob, rx_synchronizer)
self.satellite.reset.storage.reset = 0 self.satellite.reset.storage.reset = 0
@ -144,8 +144,8 @@ class OutputsTestbench:
class TestFullStack(unittest.TestCase): class TestFullStack(unittest.TestCase):
clocks = {"sys": 8, "rtio": 5, "rtio_rx": 5, clocks = {"sys": 8, "rtio_rx": 8,
"rio": 5, "rio_phy": 5} "rio": 8, "rio_phy": 8}
def test_pulses(self): def test_pulses(self):
tb = OutputsTestbench() tb = OutputsTestbench()
@ -169,7 +169,7 @@ class TestFullStack(unittest.TestCase):
yield from tb.sync() yield from tb.sync()
run_simulation(tb.dut, 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) self.assertEqual(ttl_changes, correct_ttl_changes)
def test_underflow(self): def test_underflow(self):
@ -214,7 +214,7 @@ class TestFullStack(unittest.TestCase):
yield from tb.sync() yield from tb.sync()
run_simulation(tb.dut, 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) self.assertEqual(ttl_changes, correct_ttl_changes)
def test_write_underflow(self): def test_write_underflow(self):
@ -227,16 +227,16 @@ class TestFullStack(unittest.TestCase):
errors = yield from saterr.protocol_error.read() errors = yield from saterr.protocol_error.read()
self.assertEqual(errors, 0) self.assertEqual(errors, 0)
yield from csrs.underflow_margin.write(0) yield from csrs.underflow_margin.write(0)
tb.delay(100) tb.delay(80)
yield from tb.write(42, 1) yield from tb.write(42, 1)
for i in range(12): for i in range(21):
yield yield
errors = yield from saterr.protocol_error.read() errors = yield from saterr.protocol_error.read()
underflow_channel = yield from saterr.underflow_channel.read() underflow_channel = yield from saterr.underflow_channel.read()
underflow_timestamp_event = yield from saterr.underflow_timestamp_event.read() underflow_timestamp_event = yield from saterr.underflow_timestamp_event.read()
self.assertEqual(errors, 8) # write underflow self.assertEqual(errors, 8) # write underflow
self.assertEqual(underflow_channel, 42) 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 from saterr.protocol_error.write(errors)
yield yield
errors = yield from saterr.protocol_error.read() errors = yield from saterr.protocol_error.read()
@ -284,7 +284,7 @@ class TestFullStack(unittest.TestCase):
yield dut.phy2.rtlink.i.stb.eq(0) yield dut.phy2.rtlink.i.stb.eq(0)
run_simulation(dut, run_simulation(dut,
{"sys": test(), "rtio": generate_input()}, self.clocks) {"sys": [test(), generate_input()]}, self.clocks)
def test_echo(self): def test_echo(self):
dut = DUT(2) dut = DUT(2)
@ -303,7 +303,7 @@ class TestFullStack(unittest.TestCase):
yield yield
yield dut.master.rt_packet.echo_stb.eq(0) yield dut.master.rt_packet.echo_stb.eq(0)
for i in range(15): for i in range(17):
yield yield
self.assertEqual((yield dut.master.rt_packet.packet_cnt_tx), 1) self.assertEqual((yield dut.master.rt_packet.packet_cnt_tx), 1)

View File

@ -12,7 +12,7 @@ def create_dut(nwords):
pt = PacketInterface("s2m", nwords*8) pt = PacketInterface("s2m", nwords*8)
pr = PacketInterface("m2s", nwords*8) pr = PacketInterface("m2s", nwords*8)
ts = Signal(64) ts = Signal(64)
dut = ClockDomainsRenamer({"rtio": "sys", "rtio_rx": "sys"})( dut = ClockDomainsRenamer({"rtio_rx": "sys"})(
RTPacketRepeater( RTPacketRepeater(
SimpleNamespace(coarse_ts=ts), SimpleNamespace(coarse_ts=ts),
SimpleNamespace( SimpleNamespace(

View File

@ -40,12 +40,12 @@ class DUT(Module):
def __init__(self, nwords): def __init__(self, nwords):
self.transceivers = DummyTransceiverPair(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.submodules.master = DRTIOMaster(self.tsc_master,
self.transceivers.alice) self.transceivers.alice)
rx_synchronizer = DummyRXSynchronizer() rx_synchronizer = DummyRXSynchronizer()
self.submodules.tsc_satellite = rtio.TSC("sync") self.submodules.tsc_satellite = rtio.TSC()
self.submodules.satellite = DRTIOSatellite( self.submodules.satellite = DRTIOSatellite(
self.tsc_satellite, self.transceivers.bob, rx_synchronizer) self.tsc_satellite, self.transceivers.bob, rx_synchronizer)
self.satellite.reset.storage.reset = 0 self.satellite.reset.storage.reset = 0
@ -130,7 +130,7 @@ class Testbench:
class TestSwitching(unittest.TestCase): class TestSwitching(unittest.TestCase):
clocks = {"sys": 8, "rtio": 5, "rtio_rx": 5, clocks = {"sys": 8, "rtio_rx": 5,
"rio": 5, "rio_phy": 5} "rio": 5, "rio_phy": 5}
def test_outputs(self): def test_outputs(self):
@ -183,7 +183,7 @@ class TestSwitching(unittest.TestCase):
current_request = (packet_type, field_dict, trailer) current_request = (packet_type, field_dict, trailer)
run_simulation(tb.dut, 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): def test_inputs(self):
@ -244,4 +244,4 @@ class TestSwitching(unittest.TestCase):
current_request = (packet_type, field_dict, trailer) current_request = (packet_type, field_dict, trailer)
run_simulation(tb.dut, 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)

View File

@ -128,7 +128,7 @@ class FullStackTB(Module):
self.submodules.memory = wishbone.SRAM( self.submodules.memory = wishbone.SRAM(
256, init=sequence, bus=bus) 256, init=sequence, bus=bus)
self.submodules.dut = dma.DMA(bus, dw) 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.submodules.rtio = rtio.Core(self.tsc, rtio_channels)
self.comb += self.dut.cri.connect(self.rtio.cri) self.comb += self.dut.cri.connect(self.rtio.cri)
@ -203,11 +203,11 @@ class TestDMA(unittest.TestCase):
run_simulation(tb[32], {"sys": [ run_simulation(tb[32], {"sys": [
do_dma(tb[32].dut, 0), monitor(32), do_dma(tb[32].dut, 0), monitor(32),
(None for _ in range(70)), (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": [ run_simulation(tb[64], {"sys": [
do_dma(tb[64].dut, 0), monitor(64), do_dma(tb[64].dut, 0), monitor(64),
(None for _ in range(70)), (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) correct_changes = [(timestamp + 11, channel)
for channel, timestamp, _, _ in test_writes_full_stack] for channel, timestamp, _, _ in test_writes_full_stack]

View File

@ -38,8 +38,8 @@ class DUT(Module):
rtio.Channel.from_phy(self.phy0, ififo_depth=4), rtio.Channel.from_phy(self.phy0, ififo_depth=4),
rtio.Channel.from_phy(self.phy1, ififo_depth=4) rtio.Channel.from_phy(self.phy1, ififo_depth=4)
] ]
self.submodules.tsc = ClockDomainsRenamer({"rtio": "sys"})(rtio.TSC("sync")) self.submodules.tsc = rtio.TSC()
self.submodules.input_collector = InputCollector(self.tsc, rtio_channels, "sync") self.submodules.input_collector = InputCollector(self.tsc, rtio_channels)
@property @property
def cri(self): def cri(self):

View File

@ -22,7 +22,7 @@ class DUT(Module):
rtio.Channel.from_phy(self.phy1) 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.sync += [
self.sed.coarse_timestamp.eq(self.sed.coarse_timestamp + 1), self.sed.coarse_timestamp.eq(self.sed.coarse_timestamp + 1),
self.sed.minimum_coarse_timestamp.eq(self.sed.coarse_timestamp + 16) self.sed.minimum_coarse_timestamp.eq(self.sed.coarse_timestamp + 16)

View File

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

View File

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

View File

@ -92,9 +92,9 @@ class AppletIPCServer(AsyncioParentComm):
finally: finally:
self.datasets_sub.notify_cbs.remove(self._on_mod) 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.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): async def stop_server(self):
if hasattr(self, "server_task"): if hasattr(self, "server_task"):
@ -327,7 +327,7 @@ class _CompleterDelegate(QtWidgets.QStyledItemDelegate):
class AppletsDock(QtWidgets.QDockWidget): 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 :param extra_substitutes: Map of extra ``${strings}`` to substitute in applet
commands to their respective values. commands to their respective values.
@ -342,6 +342,8 @@ class AppletsDock(QtWidgets.QDockWidget):
self.extra_substitutes = extra_substitutes self.extra_substitutes = extra_substitutes
self.applet_uids = set() self.applet_uids = set()
self._loop = loop
self.table = QtWidgets.QTreeWidget() self.table = QtWidgets.QTreeWidget()
self.table.setColumnCount(2) self.table.setColumnCount(2)
self.table.setHeaderLabels(["Name", "Command"]) 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) dock = _AppletDock(self.datasets_sub, item.applet_uid, name, spec, self.extra_substitutes)
self.main_window.addDockWidget(QtCore.Qt.RightDockWidgetArea, dock) self.main_window.addDockWidget(QtCore.Qt.RightDockWidgetArea, dock)
dock.setFloating(True) dock.setFloating(True)
asyncio.ensure_future(dock.start()) asyncio.ensure_future(dock.start(), loop=self._loop)
dock.sigClosed.connect(partial(self.on_dock_closed, item, dock)) dock.sigClosed.connect(partial(self.on_dock_closed, item, dock))
return dock return dock
@ -480,7 +482,7 @@ class AppletsDock(QtWidgets.QDockWidget):
def on_dock_closed(self, item, dock): def on_dock_closed(self, item, dock):
item.applet_geometry = dock.saveGeometry() item.applet_geometry = dock.saveGeometry()
asyncio.ensure_future(dock.terminate()) asyncio.ensure_future(dock.terminate(), loop=self._loop)
item.setCheckState(0, QtCore.Qt.Unchecked) item.setCheckState(0, QtCore.Qt.Unchecked)
def get_untitled(self): def get_untitled(self):
@ -569,7 +571,7 @@ class AppletsDock(QtWidgets.QDockWidget):
if wi.ty == "applet": if wi.ty == "applet":
dock = wi.applet_dock dock = wi.applet_dock
if dock is not None: if dock is not None:
asyncio.ensure_future(dock.restart()) asyncio.ensure_future(dock.restart(), loop=self._loop)
elif wi.ty == "group": elif wi.ty == "group":
for i in range(wi.childCount()): for i in range(wi.childCount()):
walk(wi.child(i)) walk(wi.child(i))

View File

@ -1,4 +1,3 @@
import asyncio
import logging import logging
import time import time
import re import re

View File

@ -1,14 +1,7 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?> <?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg <svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
sodipodi:docname="logo_ver.svg" sodipodi:docname="logo_ver.svg"
inkscape:version="1.0 (4035a4fb49, 2020-05-01)" inkscape:version="1.2.1 (9c6d41e410, 2022-07-14)"
id="svg2" id="svg2"
xml:space="preserve" xml:space="preserve"
enable-background="new 0 0 800 800" enable-background="new 0 0 800 800"
@ -17,18 +10,25 @@
width="332.55151" width="332.55151"
y="0px" y="0px"
x="0px" x="0px"
version="1.1"><metadata version="1.1"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:dc="http://purl.org/dc/elements/1.1/"><metadata
id="metadata548"><rdf:RDF><cc:Work id="metadata548"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title /></cc:Work></rdf:RDF></metadata><defs rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title /></cc:Work></rdf:RDF></metadata><defs
id="defs546" /><sodipodi:namedview id="defs546" /><sodipodi:namedview
inkscape:document-rotation="0" inkscape:document-rotation="0"
inkscape:current-layer="text3371" inkscape:current-layer="text861"
inkscape:window-maximized="1" inkscape:window-maximized="1"
inkscape:window-y="0" inkscape:window-y="32"
inkscape:window-x="0" inkscape:window-x="0"
inkscape:cy="136.24589" inkscape:cy="136.44068"
inkscape:cx="-304.09576" inkscape:cx="-147.0339"
inkscape:zoom="1.18" inkscape:zoom="1.18"
fit-margin-bottom="0" fit-margin-bottom="0"
fit-margin-right="0" fit-margin-right="0"
@ -36,7 +36,7 @@
fit-margin-top="0" fit-margin-top="0"
showgrid="false" showgrid="false"
id="namedview544" id="namedview544"
inkscape:window-height="1376" inkscape:window-height="1371"
inkscape:window-width="2560" inkscape:window-width="2560"
inkscape:pageshadow="2" inkscape:pageshadow="2"
inkscape:pageopacity="0" inkscape:pageopacity="0"
@ -45,7 +45,10 @@
objecttolerance="10" objecttolerance="10"
borderopacity="1" borderopacity="1"
bordercolor="#666666" bordercolor="#666666"
pagecolor="#ffffff" /><path pagecolor="#ffffff"
inkscape:showpageshadow="2"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1" /><path
d="m 271.242,315.584 c 5.09,-0.035 9.227,-4.208 9.217,-9.303 -0.01,-5.062 -4.225,-9.248 -9.291,-9.229 -5.066,0.021 -9.246,4.237 -9.217,9.302 0.027,5.085 4.236,9.264 9.291,9.23 z" d="m 271.242,315.584 c 5.09,-0.035 9.227,-4.208 9.217,-9.303 -0.01,-5.062 -4.225,-9.248 -9.291,-9.229 -5.066,0.021 -9.246,4.237 -9.217,9.302 0.027,5.085 4.236,9.264 9.291,9.23 z"
id="path381" id="path381"
style="fill:#ffffff;fill-opacity:1" style="fill:#ffffff;fill-opacity:1"
@ -153,10 +156,17 @@
id="text3371" id="text3371"
style="font-style:normal;font-variant:normal;font-weight:600;font-stretch:expanded;font-size:45px;line-height:125%;font-family:'Novecento sans wide';-inkscape-font-specification:'Novecento sans wide, Semi-Bold Expanded';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"><g style="font-style:normal;font-variant:normal;font-weight:600;font-stretch:expanded;font-size:45px;line-height:125%;font-family:'Novecento sans wide';-inkscape-font-specification:'Novecento sans wide, Semi-Bold Expanded';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"><g
transform="translate(-1.7346398,0.84745763)" transform="translate(-1.7346398,0.84745763)"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:96px;line-height:25px;font-family:'Droid Sans Thai';-inkscape-font-specification:'Droid Sans Thai, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#ffffff;fill-opacity:1;stroke:none" style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:96px;line-height:25px;font-family:'Droid Sans Thai';-inkscape-font-specification:'Droid Sans Thai, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none"
id="text861" id="text861"
aria-label="7"><path aria-label="7"><text
id="path863" xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:53.3333px;font-family:Intro;-inkscape-font-specification:'Intro, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#ffffff" style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:53.3333px;line-height:13.8889px;font-family:'Droid Sans Thai';-inkscape-font-specification:'Droid Sans Thai, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#ffffff;fill-opacity:1;stroke:none"
d="m 332.63049,339.3616 h -27.25332 c 0,3.25334 0,5.28 0,8.53333 h 14.66666 l -12.31999,28.79999 c 3.25333,0 6.55999,0 9.81332,0 l 15.09333,-35.89332 z" /></g> x="299.60599"
y="380.02783"
id="text248"><tspan
sodipodi:role="line"
id="tspan246"
x="299.60599"
y="380.02783"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:53.3333px;font-family:Intro;-inkscape-font-specification:Intro;fill:#ffffff">8</tspan></text></g>
</g></svg> </g></svg>

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 16 KiB

View File

@ -209,8 +209,6 @@ class TraceArgumentManager:
self.requested_args[key] = processor, group, tooltip self.requested_args[key] = processor, group, tooltip
return None return None
def check_unprocessed_arguments(self):
pass
class ProcessArgumentManager: class ProcessArgumentManager:
def __init__(self, unprocessed_arguments): def __init__(self, unprocessed_arguments):
@ -229,7 +227,7 @@ class ProcessArgumentManager:
unprocessed = set(self.unprocessed_arguments.keys()) -\ unprocessed = set(self.unprocessed_arguments.keys()) -\
self._processed_arguments self._processed_arguments
if unprocessed: if unprocessed:
raise AttributeError("Invalid argument(s): " + raise AttributeError("Supplied argument(s) not queried in experiment: " +
", ".join(unprocessed)) ", ".join(unprocessed))
class HasEnvironment: class HasEnvironment:
@ -252,8 +250,6 @@ class HasEnvironment:
self.__in_build = True self.__in_build = True
self.build(*args, **kwargs) self.build(*args, **kwargs)
self.__in_build = False self.__in_build = False
if self.__argument_mgr is not None:
self.__argument_mgr.check_unprocessed_arguments()
def register_child(self, child): def register_child(self, child):
self.children.append(child) self.children.append(child)

View File

@ -124,9 +124,9 @@ class ExperimentDB:
self._scanning = False self._scanning = False
self.status["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( 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): async def examine(self, filename, use_repository=True, revision=None):
if use_repository: if use_repository:

View File

@ -1,6 +1,7 @@
import asyncio import asyncio
import logging import logging
import csv import csv
import os.path
from enum import Enum from enum import Enum
from time import time from time import time
@ -131,7 +132,8 @@ class RunPool:
writer.writerow([rid, start_time, expid["file"]]) writer.writerow([rid, start_time, expid["file"]])
def submit(self, expid, priority, due_date, flush, pipeline_name): 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. # called through scheduler.
rid = self.ridc.get() rid = self.ridc.get()
if "repo_rev" in expid: if "repo_rev" in expid:
@ -140,7 +142,10 @@ class RunPool:
wd, repo_msg = self.experiment_db.repo_backend.request_rev( wd, repo_msg = self.experiment_db.repo_backend.request_rev(
expid["repo_rev"]) expid["repo_rev"])
else: else:
if "file" in expid:
expid["file"] = os.path.abspath(expid["file"])
wd, repo_msg = None, None wd, repo_msg = None, None
run = Run(rid, pipeline_name, wd, expid, priority, due_date, flush, run = Run(rid, pipeline_name, wd, expid, priority, due_date, flush,
self, repo_msg=repo_msg) self, repo_msg=repo_msg)
if self.log_submissions is not None: if self.log_submissions is not None:
@ -327,10 +332,10 @@ class Pipeline:
self._run = RunStage(self.pool, deleter.delete) self._run = RunStage(self.pool, deleter.delete)
self._analyze = AnalyzeStage(self.pool, deleter.delete) self._analyze = AnalyzeStage(self.pool, deleter.delete)
def start(self): def start(self, *, loop=None):
self._prepare.start() self._prepare.start(loop=loop)
self._run.start() self._run.start(loop=loop)
self._analyze.start() self._analyze.start(loop=loop)
async def stop(self): async def stop(self):
# NB: restart of a stopped pipeline is not supported # NB: restart of a stopped pipeline is not supported
@ -405,8 +410,9 @@ class Scheduler:
self._deleter = Deleter(self._pipelines) self._deleter = Deleter(self._pipelines)
self._log_submissions = log_submissions self._log_submissions = log_submissions
def start(self): def start(self, *, loop=None):
self._deleter.start() self._loop = loop
self._deleter.start(loop=self._loop)
async def stop(self): async def stop(self):
# NB: restart of a stopped scheduler is not supported # NB: restart of a stopped scheduler is not supported
@ -425,7 +431,8 @@ class Scheduler:
When called through an experiment, the default values of When called through an experiment, the default values of
``pipeline_name``, ``expid`` and ``priority`` correspond to those of ``pipeline_name``, ``expid`` and ``priority`` correspond to those of
the current run.""" 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: if self._terminated:
return return
try: try:
@ -436,7 +443,7 @@ class Scheduler:
self._worker_handlers, self.notifier, self._worker_handlers, self.notifier,
self._experiment_db, self._log_submissions) self._experiment_db, self._log_submissions)
self._pipelines[pipeline_name] = pipeline self._pipelines[pipeline_name] = pipeline
pipeline.start() pipeline.start(loop=self._loop)
return pipeline.pool.submit(expid, priority, due_date, flush, pipeline_name) return pipeline.pool.submit(expid, priority, due_date, flush, pipeline_name)
def delete(self, rid): def delete(self, rid):

View File

@ -306,6 +306,7 @@ def main():
os.chdir(dirname) os.chdir(dirname)
argument_mgr = ProcessArgumentManager(expid["arguments"]) argument_mgr = ProcessArgumentManager(expid["arguments"])
exp_inst = exp((device_mgr, dataset_mgr, argument_mgr, {})) exp_inst = exp((device_mgr, dataset_mgr, argument_mgr, {}))
argument_mgr.check_unprocessed_arguments()
put_completed() put_completed()
elif action == "prepare": elif action == "prepare":
exp_inst.prepare() exp_inst.prepare()
@ -325,7 +326,10 @@ def main():
exp_inst.analyze() exp_inst.analyze()
put_completed() put_completed()
finally: 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": elif action == "examine":
examine(ExamineDeviceMgr, ExamineDatasetMgr, obj["file"]) examine(ExamineDeviceMgr, ExamineDatasetMgr, obj["file"])
put_completed() put_completed()

View File

@ -75,6 +75,9 @@ class RoundtripTest(ExperimentCase):
def test_object_list(self): def test_object_list(self):
self.assertRoundtrip([object(), object()]) self.assertRoundtrip([object(), object()])
def test_object_tuple(self):
self.assertRoundtrip((False, object(), True, 0x12345678))
def test_list_tuple(self): def test_list_tuple(self):
self.assertRoundtrip(([1, 2], [3, 4])) self.assertRoundtrip(([1, 2], [3, 4]))
@ -96,7 +99,7 @@ class RoundtripTest(ExperimentCase):
self.assertArrayRoundtrip(numpy.array([["a", "b"], ["c", "d"]])) self.assertArrayRoundtrip(numpy.array([["a", "b"], ["c", "d"]]))
# FIXME: This should work, but currently passes as the argument is just # 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. # NumPy object.
@unittest.expectedFailure @unittest.expectedFailure
def test_array_jagged(self): def test_array_jagged(self):

View File

@ -13,7 +13,7 @@ class TestFrontends(unittest.TestCase):
], ],
"artiq": [ "artiq": [
"client", "compile", "coreanalyzer", "coremgmt", "client", "compile", "coreanalyzer", "coremgmt",
"flash", "master", "mkfs", "route", "flash", "master", "mkfs", "route", "rtiomap",
"rtiomon", "run", "session", "browser", "dashboard" "rtiomon", "run", "session", "browser", "dashboard"
] ]
} }

View File

@ -104,7 +104,7 @@ class SchedulerCase(unittest.TestCase):
done.set() done.set()
scheduler.notifier.publish = notify scheduler.notifier.publish = notify
scheduler.start() scheduler.start(loop=loop)
# Verify that a timed experiment far in the future does not # Verify that a timed experiment far in the future does not
# get run, even if it has high priority. # get run, even if it has high priority.
@ -269,7 +269,7 @@ class SchedulerCase(unittest.TestCase):
done.set() done.set()
scheduler.notifier.publish = notify scheduler.notifier.publish = notify
scheduler.start() scheduler.start(loop=loop)
scheduler.submit("main", expid_bg, low_priority) scheduler.submit("main", expid_bg, low_priority)
scheduler.submit("main", expid_empty, high_priority, late) scheduler.submit("main", expid_empty, high_priority, late)
@ -328,7 +328,7 @@ class SchedulerCase(unittest.TestCase):
empty_completed.set() empty_completed.set()
scheduler.notifier.publish = notify scheduler.notifier.publish = notify
scheduler.start() scheduler.start(loop=loop)
scheduler.submit("main", expid_bg, -99, None, False) scheduler.submit("main", expid_bg, -99, None, False)
loop.run_until_complete(background_running.wait()) loop.run_until_complete(background_running.wait())
self.assertFalse(scheduler.check_pause(0)) self.assertFalse(scheduler.check_pause(0))
@ -379,7 +379,7 @@ class SchedulerCase(unittest.TestCase):
empty_ready.set() empty_ready.set()
scheduler.notifier.publish = notify scheduler.notifier.publish = notify
scheduler.start() scheduler.start(loop=loop)
scheduler.submit("main", expid_bg, -99, None, False) scheduler.submit("main", expid_bg, -99, None, False)
loop.run_until_complete(background_running.wait()) loop.run_until_complete(background_running.wait())
@ -417,7 +417,7 @@ class SchedulerCase(unittest.TestCase):
done.set() done.set()
scheduler.notifier.publish = notify scheduler.notifier.publish = notify
scheduler.start() scheduler.start(loop=loop)
scheduler.submit("main", expid, 0, None, False) scheduler.submit("main", expid, 0, None, False)
loop.run_until_complete(first_preparing.wait()) loop.run_until_complete(first_preparing.wait())
scheduler.submit("main", expid, 1, None, True) scheduler.submit("main", expid, 1, None, True)

Some files were not shown because too many files have changed in this diff Show More