mirror of https://github.com/m-labs/artiq.git
Merge branch 'master' into nac3
This commit is contained in:
commit
7635b9ed92
|
@ -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
|
||||||
|
|
|
@ -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.
|
||||||
|
|
||||||
|
|
|
@ -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,
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -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": {
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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."""
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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",
|
||||||
|
|
|
@ -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"] }
|
||||||
|
|
|
@ -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)]
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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))?;
|
||||||
|
|
|
@ -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)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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]
|
||||||
|
|
|
@ -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];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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));
|
||||||
|
|
|
@ -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>(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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");
|
||||||
|
|
||||||
|
|
|
@ -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");
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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() }
|
||||||
|
|
|
@ -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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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")
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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}"] = {{
|
||||||
|
|
|
@ -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())
|
||||||
|
|
|
@ -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()
|
|
@ -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):
|
||||||
|
|
|
@ -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()))
|
||||||
|
|
|
@ -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")
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
|
@ -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(
|
||||||
|
|
|
@ -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",
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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)
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
|
@ -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 += [
|
||||||
|
|
|
@ -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",
|
||||||
|
|
|
@ -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))})
|
||||||
]
|
]
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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",
|
||||||
|
|
|
@ -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)
|
|
|
@ -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"))
|
|
||||||
)
|
|
|
@ -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),
|
||||||
|
|
|
@ -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 += [
|
||||||
|
|
|
@ -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),
|
||||||
|
|
|
@ -1,2 +0,0 @@
|
||||||
from artiq.gateware.drtio.wrpll.core import WRPLL
|
|
||||||
from artiq.gateware.drtio.wrpll.ddmtd import DDMTDSamplerExtFF, DDMTDSamplerGTP
|
|
|
@ -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)
|
|
||||||
]
|
|
|
@ -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")
|
|
||||||
)
|
|
|
@ -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
|
|
|
@ -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()
|
|
|
@ -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)
|
|
|
@ -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()
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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)),
|
||||||
|
|
|
@ -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(
|
||||||
|
|
|
@ -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")
|
||||||
|
|
||||||
|
|
|
@ -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),
|
||||||
|
|
|
@ -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"))
|
||||||
|
|
|
@ -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))]))
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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)
|
|
||||||
]
|
]
|
||||||
|
|
|
@ -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(
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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):
|
||||||
|
|
|
@ -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")
|
||||||
|
|
|
@ -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):
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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(
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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]
|
||||||
|
|
|
@ -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):
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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()])
|
|
|
@ -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)
|
|
|
@ -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))
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
import asyncio
|
|
||||||
import logging
|
import logging
|
||||||
import time
|
import time
|
||||||
import re
|
import re
|
||||||
|
|
|
@ -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 |
|
@ -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)
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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):
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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):
|
||||||
|
|
|
@ -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"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
Loading…
Reference in New Issue