Merge branch 'syncrtio'

pull/2051/head
Sebastien Bourdeauducq 2023-01-12 12:58:19 +08:00
commit 0a37a1a4c1
91 changed files with 437 additions and 13004 deletions

File diff suppressed because it is too large Load Diff

View File

@ -1,23 +0,0 @@
from artiq.language.core import kernel
class AD9154:
"""Kernel interface to AD9154 registers, using non-realtime SPI."""
def __init__(self, dmgr, spi_device, chip_select):
self.core = dmgr.get("core")
self.bus = dmgr.get(spi_device)
self.chip_select = chip_select
@kernel
def setup_bus(self, div=16):
self.bus.set_config_mu(0, 24, div, self.chip_select)
@kernel
def write(self, addr, data):
self.bus.write((addr << 16) | (data<< 8))
@kernel
def read(self, addr):
self.write((1 << 15) | addr, 0)
return self.bus.read()

View File

@ -1,79 +0,0 @@
from artiq.language.core import kernel, portable, delay
from artiq.language.units import us, ms
from artiq.coredevice.shiftreg import ShiftReg
@portable
def to_mu(att):
return round(att*2.0) ^ 0x3f
@portable
def from_mu(att_mu):
return 0.5*(att_mu ^ 0x3f)
class BaseModAtt:
def __init__(self, dmgr, rst_n, clk, le, mosi, miso):
self.rst_n = dmgr.get(rst_n)
self.shift_reg = ShiftReg(dmgr,
clk=clk, ser=mosi, latch=le, ser_in=miso, n=8*4)
@kernel
def reset(self):
# HMC's incompetence in digital design and interfaces means that
# the HMC542 needs a level low on RST_N and then a rising edge
# on Latch Enable. Their "latch" isn't a latch but a DFF.
# Of course, it also powers up with a random attenuation, and
# that cannot be fixed with simple pull-ups/pull-downs.
self.rst_n.off()
self.shift_reg.latch.off()
delay(1*us)
self.shift_reg.latch.on()
delay(1*us)
self.shift_reg.latch.off()
self.rst_n.on()
delay(1*us)
@kernel
def set_mu(self, att0, att1, att2, att3):
"""
Sets the four attenuators on BaseMod.
The values are in half decibels, between 0 (no attenuation)
and 63 (31.5dB attenuation).
"""
word = (
(att0 << 2) |
(att1 << 10) |
(att2 << 18) |
(att3 << 26)
)
self.shift_reg.set(word)
@kernel
def get_mu(self):
"""
Retrieves the current settings of the four attenuators on BaseMod.
"""
word = self.shift_reg.get()
att0 = (word >> 2) & 0x3f
att1 = (word >> 10) & 0x3f
att2 = (word >> 18) & 0x3f
att3 = (word >> 26) & 0x3f
return att0, att1, att2, att3
@kernel
def set(self, att0, att1, att2, att3):
"""
Sets the four attenuators on BaseMod.
The values are in decibels.
"""
self.set_mu(to_mu(att0), to_mu(att1), to_mu(att2), to_mu(att3))
@kernel
def get(self):
"""
Retrieves the current settings of the four attenuators on BaseMod.
The values are in decibels.
"""
att0, att1, att2, att3 = self.get_mu()
return from_mu(att0), from_mu(att1), from_mu(att2), from_mu(att3)

View File

@ -1,385 +0,0 @@
"""
Driver for the Smart Arbitrary Waveform Generator (SAWG) on RTIO.
The SAWG is an "improved DDS" built in gateware and interfacing to
high-speed DACs.
Output event replacement is supported except on the configuration channel.
"""
from artiq.language.types import TInt32, TFloat
from numpy import int32, int64
from artiq.language.core import kernel
from artiq.coredevice.spline import Spline
from artiq.coredevice.rtio import rtio_output
# sawg.Config addresses
_SAWG_DIV = 0
_SAWG_CLR = 1
_SAWG_IQ_EN = 2
# _SAWF_PAD = 3 # reserved
_SAWG_OUT_MIN = 4
_SAWG_OUT_MAX = 5
_SAWG_DUC_MIN = 6
_SAWG_DUC_MAX = 7
class Config:
"""SAWG configuration.
Exposes the configurable quantities of a single SAWG channel.
Access to the configuration registers for a SAWG channel can not
be concurrent. There must be at least :attr:`_rtio_interval` machine
units of delay between accesses. Replacement is not supported and will be
lead to an ``RTIOCollision`` as this is likely a programming error.
All methods therefore advance the timeline by the duration of one
configuration register transfer.
:param channel: RTIO channel number of the channel.
:param core: Core device.
"""
kernel_invariants = {"channel", "core", "_out_scale", "_duc_scale",
"_rtio_interval"}
def __init__(self, channel, core, cordic_gain=1.):
self.channel = channel
self.core = core
# normalized DAC output
self._out_scale = (1 << 15) - 1.
# normalized DAC output including DUC cordic gain
self._duc_scale = self._out_scale/cordic_gain
# configuration channel access interval
self._rtio_interval = int64(3*self.core.ref_multiplier)
@kernel
def set_div(self, div: TInt32, n: TInt32=0):
"""Set the spline evolution divider and current counter value.
The divider and the spline evolution are synchronized across all
spline channels within a SAWG channel. The DDS/DUC phase accumulators
always evolves at full speed.
.. note:: The spline evolution divider has not been tested extensively
and is currently considered a technological preview only.
:param div: Spline evolution divider, such that
``t_sawg_spline/t_rtio_coarse = div + 1``. Default: ``0``.
:param n: Current value of the counter. Default: ``0``.
"""
rtio_output((self.channel << 8) | _SAWG_DIV, div | (n << 16))
delay_mu(self._rtio_interval)
@kernel
def set_clr(self, clr0: TInt32, clr1: TInt32, clr2: TInt32):
"""Set the accumulator clear mode for the three phase accumulators.
When the ``clr`` bit for a given DDS/DUC phase accumulator is
set, that phase accumulator will be cleared with every phase offset
RTIO command and the output phase of the DDS/DUC will be
exactly the phase RTIO value ("absolute phase update mode").
.. math::
q^\prime(t) = p^\prime + (t - t^\prime) f^\prime
In turn, when the bit is cleared, the phase RTIO channels
determine a phase offset to the current (carrier-) value of the
DDS/DUC phase accumulator. This "relative phase update mode" is
sometimes also called continuous phase mode.
.. math::
q^\prime(t) = q(t^\prime) + (p^\prime - p) +
(t - t^\prime) f^\prime
Where:
* :math:`q`, :math:`q^\prime`: old/new phase accumulator
* :math:`p`, :math:`p^\prime`: old/new phase offset
* :math:`f^\prime`: new frequency
* :math:`t^\prime`: timestamp of setting new :math:`p`, :math:`f`
* :math:`t`: running time
:param clr0: Auto-clear phase accumulator of the ``phase0``/
``frequency0`` DUC. Default: ``True``
:param clr1: Auto-clear phase accumulator of the ``phase1``/
``frequency1`` DDS. Default: ``True``
:param clr2: Auto-clear phase accumulator of the ``phase2``/
``frequency2`` DDS. Default: ``True``
"""
rtio_output((self.channel << 8) | _SAWG_CLR, clr0 |
(clr1 << 1) | (clr2 << 2))
delay_mu(self._rtio_interval)
@kernel
def set_iq_en(self, i_enable: TInt32, q_enable: TInt32):
"""Enable I/Q data on this DAC channel.
Every pair of SAWG channels forms a buddy pair.
The ``iq_en`` configuration controls which DDS data is emitted to the
DACs.
Refer to the documentation of :class:`SAWG` for a mathematical
description of ``i_enable`` and ``q_enable``.
.. note:: Quadrature data from the buddy channel is currently
a technological preview only. The data is ignored in the SAWG
gateware and not added to the DAC output.
This is equivalent to the ``q_enable`` switch always being ``0``.
:param i_enable: Controls adding the in-phase
DUC-DDS data of *this* SAWG channel to *this* DAC channel.
Default: ``1``.
:param q_enable: controls adding the quadrature
DUC-DDS data of this SAWG's *buddy* channel to *this* DAC
channel. Default: ``0``.
"""
rtio_output((self.channel << 8) | _SAWG_IQ_EN, i_enable |
(q_enable << 1))
delay_mu(self._rtio_interval)
@kernel
def set_duc_max_mu(self, limit: TInt32):
"""Set the digital up-converter (DUC) I and Q data summing junctions
upper limit. In machine units.
The default limits are chosen to reach maximum and minimum DAC output
amplitude.
For a description of the limiter functions in normalized units see:
.. seealso:: :meth:`set_duc_max`
"""
rtio_output((self.channel << 8) | _SAWG_DUC_MAX, limit)
delay_mu(self._rtio_interval)
@kernel
def set_duc_min_mu(self, limit: TInt32):
""".. seealso:: :meth:`set_duc_max_mu`"""
rtio_output((self.channel << 8) | _SAWG_DUC_MIN, limit)
delay_mu(self._rtio_interval)
@kernel
def set_out_max_mu(self, limit: TInt32):
""".. seealso:: :meth:`set_duc_max_mu`"""
rtio_output((self.channel << 8) | _SAWG_OUT_MAX, limit)
delay_mu(self._rtio_interval)
@kernel
def set_out_min_mu(self, limit: TInt32):
""".. seealso:: :meth:`set_duc_max_mu`"""
rtio_output((self.channel << 8) | _SAWG_OUT_MIN, limit)
delay_mu(self._rtio_interval)
@kernel
def set_duc_max(self, limit: TFloat):
"""Set the digital up-converter (DUC) I and Q data summing junctions
upper limit.
Each of the three summing junctions has a saturating adder with
configurable upper and lower limits. The three summing junctions are:
* At the in-phase input to the ``phase0``/``frequency0`` fast DUC,
after the anti-aliasing FIR filter.
* At the quadrature input to the ``phase0``/``frequency0``
fast DUC, after the anti-aliasing FIR filter. The in-phase and
quadrature data paths both use the same limits.
* Before the DAC, where the following three data streams
are added together:
* the output of the ``offset`` spline,
* (optionally, depending on ``i_enable``) the in-phase output
of the ``phase0``/``frequency0`` fast DUC, and
* (optionally, depending on ``q_enable``) the quadrature
output of the ``phase0``/``frequency0`` fast DUC of the
buddy channel.
Refer to the documentation of :class:`SAWG` for a mathematical
description of the summing junctions.
:param limit: Limit value ``[-1, 1]``. The output of the limiter will
never exceed this limit. The default limits are the full range
``[-1, 1]``.
.. seealso::
* :meth:`set_duc_max`: Upper limit of the in-phase and quadrature
inputs to the DUC.
* :meth:`set_duc_min`: Lower limit of the in-phase and quadrature
inputs to the DUC.
* :meth:`set_out_max`: Upper limit of the DAC output.
* :meth:`set_out_min`: Lower limit of the DAC output.
"""
self.set_duc_max_mu(int32(round(limit*self._duc_scale)))
@kernel
def set_duc_min(self, limit: TFloat):
""".. seealso:: :meth:`set_duc_max`"""
self.set_duc_min_mu(int32(round(limit*self._duc_scale)))
@kernel
def set_out_max(self, limit: TFloat):
""".. seealso:: :meth:`set_duc_max`"""
self.set_out_max_mu(int32(round(limit*self._out_scale)))
@kernel
def set_out_min(self, limit: TFloat):
""".. seealso:: :meth:`set_duc_max`"""
self.set_out_min_mu(int32(round(limit*self._out_scale)))
class SAWG:
"""Smart arbitrary waveform generator channel.
The channel is parametrized as: ::
oscillators = exp(2j*pi*(frequency0*t + phase0))*(
amplitude1*exp(2j*pi*(frequency1*t + phase1)) +
amplitude2*exp(2j*pi*(frequency2*t + phase2)))
output = (offset +
i_enable*Re(oscillators) +
q_enable*Im(buddy_oscillators))
This parametrization can be viewed as two complex (quadrature) oscillators
(``frequency1``/``phase1`` and ``frequency2``/``phase2``) that are
executing and sampling at the coarse RTIO frequency. They can represent
frequencies within the first Nyquist zone from ``-f_rtio_coarse/2`` to
``f_rtio_coarse/2``.
.. note:: The coarse RTIO frequency ``f_rtio_coarse`` is the inverse of
``ref_period*multiplier``. Both are arguments of the ``Core`` device,
specified in the device database ``device_db.py``.
The sum of their outputs is then interpolated by a factor of
:attr:`parallelism` (2, 4, 8 depending on the bitstream) using a
finite-impulse-response (FIR) anti-aliasing filter (more accurately
a half-band filter).
The filter is followed by a configurable saturating limiter.
After the limiter, the data is shifted in frequency using a complex
digital up-converter (DUC, ``frequency0``/``phase0``) running at
:attr:`parallelism` times the coarse RTIO frequency. The first Nyquist
zone of the DUC extends from ``-f_rtio_coarse*parallelism/2`` to
``f_rtio_coarse*parallelism/2``. Other Nyquist zones are usable depending
on the interpolation/modulation options configured in the DAC.
The real/in-phase data after digital up-conversion can be offset using
another spline interpolator ``offset``.
The ``i_enable``/``q_enable`` switches enable emission of quadrature
signals for later analog quadrature mixing distinguishing upper and lower
sidebands and thus doubling the bandwidth. They can also be used to emit
four-tone signals.
.. note:: Quadrature data from the buddy channel is currently
ignored in the SAWG gateware and not added to the DAC output.
This is equivalent to the ``q_enable`` switch always being ``0``.
The configuration channel and the nine
:class:`artiq.coredevice.spline.Spline` interpolators are accessible as
attributes:
* :attr:`config`: :class:`Config`
* :attr:`offset`, :attr:`amplitude1`, :attr:`amplitude2`: in units
of full scale
* :attr:`phase0`, :attr:`phase1`, :attr:`phase2`: in units of turns
* :attr:`frequency0`, :attr:`frequency1`, :attr:`frequency2`: in units
of Hz
.. note:: The latencies (pipeline depths) of the nine data channels (i.e.
all except :attr:`config`) are matched. Equivalent channels (e.g.
:attr:`phase1` and :attr:`phase2`) are exactly matched. Channels of
different type or functionality (e.g. :attr:`offset` vs
:attr:`amplitude1`, DDS vs DUC, :attr:`phase0` vs :attr:`phase1`) are
only matched to within one coarse RTIO cycle.
:param channel_base: RTIO channel number of the first channel (amplitude).
The configuration channel and frequency/phase/amplitude channels are
then assumed to be successive channels.
:param parallelism: Number of output samples per coarse RTIO clock cycle.
:param core_device: Name of the core device that this SAWG is on.
"""
kernel_invariants = {"channel_base", "core", "parallelism",
"amplitude1", "frequency1", "phase1",
"amplitude2", "frequency2", "phase2",
"frequency0", "phase0", "offset"}
def __init__(self, dmgr, channel_base, parallelism, core_device="core"):
self.core = dmgr.get(core_device)
self.channel_base = channel_base
self.parallelism = parallelism
width = 16
time_width = 16
cordic_gain = 1.646760258057163 # Cordic(width=16, guard=None).gain
head_room = 1.001
self.config = Config(channel_base, self.core, cordic_gain)
self.offset = Spline(width, time_width, channel_base + 1,
self.core, 2.*head_room)
self.amplitude1 = Spline(width, time_width, channel_base + 2,
self.core, 2*head_room*cordic_gain**2)
self.frequency1 = Spline(3*width, time_width, channel_base + 3,
self.core, 1/self.core.coarse_ref_period)
self.phase1 = Spline(width, time_width, channel_base + 4,
self.core, 1.)
self.amplitude2 = Spline(width, time_width, channel_base + 5,
self.core, 2*head_room*cordic_gain**2)
self.frequency2 = Spline(3*width, time_width, channel_base + 6,
self.core, 1/self.core.coarse_ref_period)
self.phase2 = Spline(width, time_width, channel_base + 7,
self.core, 1.)
self.frequency0 = Spline(2*width, time_width, channel_base + 8,
self.core,
parallelism/self.core.coarse_ref_period)
self.phase0 = Spline(width, time_width, channel_base + 9,
self.core, 1.)
@staticmethod
def get_rtio_channels(channel_base, **kwargs):
return [(channel_base, "base"),
(channel_base+1, "offset"),
(channel_base+2, "amplitude 1"),
(channel_base+3, "frequency 1"),
(channel_base+4, "phase 1"),
(channel_base+5, "amplitude 2"),
(channel_base+6, "frequency 2"),
(channel_base+7, "phase 2"),
(channel_base+8, "frequency 0"),
(channel_base+9, "phase0")]
@kernel
def reset(self):
"""Re-establish initial conditions.
This clears all spline interpolators, accumulators and configuration
settings.
This method advances the timeline by the time required to perform all
7 writes to the configuration channel, plus 9 coarse RTIO cycles.
"""
self.config.set_div(0, 0)
self.config.set_clr(1, 1, 1)
self.config.set_iq_en(1, 0)
self.config.set_duc_min(-1.)
self.config.set_duc_max(1.)
self.config.set_out_min(-1.)
self.config.set_out_max(1.)
self.frequency0.set_mu(0)
coarse_cycle = int64(self.core.ref_multiplier)
delay_mu(coarse_cycle)
self.frequency1.set_mu(0)
delay_mu(coarse_cycle)
self.frequency2.set_mu(0)
delay_mu(coarse_cycle)
self.phase0.set_mu(0)
delay_mu(coarse_cycle)
self.phase1.set_mu(0)
delay_mu(coarse_cycle)
self.phase2.set_mu(0)
delay_mu(coarse_cycle)
self.amplitude1.set_mu(0)
delay_mu(coarse_cycle)
self.amplitude2.set_mu(0)
delay_mu(coarse_cycle)
self.offset.set_mu(0)
delay_mu(coarse_cycle)

View File

@ -1,54 +0,0 @@
from artiq.language.core import kernel, delay
from artiq.language.units import us
class ShiftReg:
"""Driver for shift registers/latch combos connected to TTLs"""
kernel_invariants = {"dt", "n"}
def __init__(self, dmgr, clk, ser, latch, n=32, dt=10*us, ser_in=None):
self.core = dmgr.get("core")
self.clk = dmgr.get(clk)
self.ser = dmgr.get(ser)
self.latch = dmgr.get(latch)
self.n = n
self.dt = dt
if ser_in is not None:
self.ser_in = dmgr.get(ser_in)
@kernel
def set(self, data):
"""Sets the values of the latch outputs. This does not
advance the timeline and the waveform is generated before
`now`."""
delay(-2*(self.n + 1)*self.dt)
for i in range(self.n):
if (data >> (self.n-i-1)) & 1 == 0:
self.ser.off()
else:
self.ser.on()
self.clk.off()
delay(self.dt)
self.clk.on()
delay(self.dt)
self.clk.off()
self.latch.on()
delay(self.dt)
self.latch.off()
delay(self.dt)
@kernel
def get(self):
delay(-2*(self.n + 1)*self.dt)
data = 0
for i in range(self.n):
data <<= 1
self.ser_in.sample_input()
if self.ser_in.sample_get():
data |= 1
delay(self.dt)
self.clk.on()
delay(self.dt)
self.clk.off()
delay(self.dt)
return data

View File

@ -1,232 +0,0 @@
from numpy import int32, int64
from artiq.language.core import kernel, portable, delay
from artiq.coredevice.rtio import rtio_output, rtio_output_wide
from artiq.language.types import TInt32, TInt64, TFloat
class Spline:
r"""Spline interpolating RTIO channel.
One knot of a polynomial basis spline (B-spline) :math:`u(t)`
is defined by the coefficients :math:`u_n` up to order :math:`n = k`.
If the coefficients are evaluated starting at time :math:`t_0`,
the output :math:`u(t)` for :math:`t > t_0, t_0` is:
.. math::
u(t) &= \sum_{n=0}^k \frac{u_n}{n!} (t - t_0)^n \\
&= u_0 + u_1 (t - t_0) + \frac{u_2}{2} (t - t_0)^2 + \dots
This class contains multiple methods to convert spline knot data from SI
to machine units and multiple methods that set the current spline
coefficient data. None of these advance the timeline. The :meth:`smooth`
method is the only method that advances the timeline.
:param width: Width in bits of the quantity that this spline controls
:param time_width: Width in bits of the time counter of this spline
:param channel: RTIO channel number
:param core_device: Core device that this spline is attached to
:param scale: Scale for conversion between machine units and physical
units; to be given as the "full scale physical value".
"""
kernel_invariants = {"channel", "core", "scale", "width",
"time_width", "time_scale"}
def __init__(self, width, time_width, channel, core_device, scale=1.):
self.core = core_device
self.channel = channel
self.width = width
self.scale = float((int64(1) << width) / scale)
self.time_width = time_width
self.time_scale = float((1 << time_width) *
core_device.coarse_ref_period)
@staticmethod
def get_rtio_channels(channel, **kwargs):
return [(channel, None)]
@portable(flags={"fast-math"})
def to_mu(self, value: TFloat) -> TInt32:
"""Convert floating point ``value`` from physical units to 32 bit
integer machine units."""
return int32(round(value*self.scale))
@portable(flags={"fast-math"})
def from_mu(self, value: TInt32) -> TFloat:
"""Convert 32 bit integer ``value`` from machine units to floating
point physical units."""
return value/self.scale
@portable(flags={"fast-math"})
def to_mu64(self, value: TFloat) -> TInt64:
"""Convert floating point ``value`` from physical units to 64 bit
integer machine units."""
return int64(round(value*self.scale))
@kernel
def set_mu(self, value: TInt32):
"""Set spline value (machine units).
:param value: Spline value in integer machine units.
"""
rtio_output(self.channel << 8, value)
@kernel(flags={"fast-math"})
def set(self, value: TFloat):
"""Set spline value.
:param value: Spline value relative to full-scale.
"""
if self.width > 32:
l = [int32(0)] * 2
self.pack_coeff_mu([self.to_mu64(value)], l)
rtio_output_wide(self.channel << 8, l)
else:
rtio_output(self.channel << 8, self.to_mu(value))
@kernel
def set_coeff_mu(self, value): # TList(TInt32)
"""Set spline raw values.
:param value: Spline packed raw values.
"""
rtio_output_wide(self.channel << 8, value)
@portable(flags={"fast-math"})
def pack_coeff_mu(self, coeff, packed): # TList(TInt64), TList(TInt32)
"""Pack coefficients into RTIO data
:param coeff: TList(TInt64) list of machine units spline coefficients.
Lowest (zeroth) order first. The coefficient list is zero-extended
by the RTIO gateware.
:param packed: TList(TInt32) list for packed RTIO data. Must be
pre-allocated. Length in bits is
``n*width + (n - 1)*n//2*time_width``
"""
pos = 0
for i in range(len(coeff)):
wi = self.width + i*self.time_width
ci = coeff[i]
while wi != 0:
j = pos//32
used = pos - 32*j
avail = 32 - used
if avail > wi:
avail = wi
cij = int32(ci)
if avail != 32:
cij &= (1 << avail) - 1
packed[j] |= cij << used
ci >>= avail
wi -= avail
pos += avail
@portable(flags={"fast-math"})
def coeff_to_mu(self, coeff, coeff64): # TList(TFloat), TList(TInt64)
"""Convert a floating point list of coefficients into a 64 bit
integer (preallocated).
:param coeff: TList(TFloat) list of coefficients in physical units.
:param coeff64: TList(TInt64) preallocated list of coefficients in
machine units.
"""
for i in range(len(coeff)):
vi = coeff[i] * self.scale
for j in range(i):
vi *= self.time_scale
ci = int64(round(vi))
coeff64[i] = ci
# artiq.wavesynth.coefficients.discrete_compensate:
if i == 2:
coeff64[1] += ci >> self.time_width + 1
elif i == 3:
coeff64[2] += ci >> self.time_width
coeff64[1] += ci // 6 >> 2*self.time_width
def coeff_as_packed_mu(self, coeff64):
"""Pack 64 bit integer machine units coefficients into 32 bit integer
RTIO data list.
This is a host-only method that can be used to generate packed
spline coefficient data to be frozen into kernels at compile time.
"""
n = len(coeff64)
width = n*self.width + (n - 1)*n//2*self.time_width
packed = [int32(0)] * ((width + 31)//32)
self.pack_coeff_mu(coeff64, packed)
return packed
def coeff_as_packed(self, coeff):
"""Convert floating point spline coefficients into 32 bit integer
packed data.
This is a host-only method that can be used to generate packed
spline coefficient data to be frozen into kernels at compile time.
"""
coeff64 = [int64(0)] * len(coeff)
self.coeff_to_mu(coeff, coeff64)
return self.coeff_as_packed_mu(coeff64)
@kernel(flags={"fast-math"})
def set_coeff(self, coeff): # TList(TFloat)
"""Set spline coefficients.
Missing coefficients (high order) are zero-extended byt the RTIO
gateware.
If more coefficients are supplied than the gateware supports the extra
coefficients are ignored.
:param value: List of floating point spline coefficients,
lowest order (constant) coefficient first. Units are the
unit of this spline's value times increasing powers of 1/s.
"""
n = len(coeff)
coeff64 = [int64(0)] * n
self.coeff_to_mu(coeff, coeff64)
width = n*self.width + (n - 1)*n//2*self.time_width
packed = [int32(0)] * ((width + 31)//32)
self.pack_coeff_mu(coeff64, packed)
self.set_coeff_mu(packed)
@kernel(flags={"fast-math"})
def smooth(self, start: TFloat, stop: TFloat, duration: TFloat,
order: TInt32):
"""Initiate an interpolated value change.
For zeroth order (step) interpolation, the step is at
``start + duration/2``.
First order interpolation corresponds to a linear value ramp from
``start`` to ``stop`` over ``duration``.
The third order interpolation is constrained to have zero first
order derivative at both `start` and `stop`.
For first order and third order interpolation (linear and cubic)
the interpolator needs to be stopped explicitly at the stop time
(e.g. by setting spline coefficient data or starting a new
:meth:`smooth` interpolation).
This method advances the timeline by ``duration``.
:param start: Initial value of the change. In physical units.
:param stop: Final value of the change. In physical units.
:param duration: Duration of the interpolation. In physical units.
:param order: Order of the interpolation. Only 0, 1,
and 3 are valid: step, linear, cubic.
"""
if order == 0:
delay(duration/2.)
self.set_coeff([stop])
delay(duration/2.)
elif order == 1:
self.set_coeff([start, (stop - start)/duration])
delay(duration)
elif order == 3:
v2 = 6.*(stop - start)/(duration*duration)
self.set_coeff([start, 0., v2, -2.*v2/duration])
delay(duration)
else:
raise ValueError("Invalid interpolation order. "
"Supported orders are: 0, 1, 3.")

View File

@ -1,184 +0,0 @@
core_addr = "192.168.1.70"
device_db = {
"core": {
"type": "local",
"module": "artiq.coredevice.core",
"class": "Core",
"arguments": {"host": core_addr, "ref_period": 1/(8*150e6)}
},
"core_log": {
"type": "controller",
"host": "::1",
"port": 1068,
"command": "aqctl_corelog -p {port} --bind {bind} " + core_addr
},
"core_moninj": {
"type": "controller",
"host": "::1",
"port_proxy": 1383,
"port": 1384,
"command": "aqctl_moninj_proxy --port-proxy {port_proxy} --port-control {port} --bind {bind} " + core_addr
},
"core_cache": {
"type": "local",
"module": "artiq.coredevice.cache",
"class": "CoreCache"
},
"core_dma": {
"type": "local",
"module": "artiq.coredevice.dma",
"class": "CoreDMA"
},
}
device_db.update(
spi_urukul0={
"type": "local",
"module": "artiq.coredevice.spi2",
"class": "SPIMaster",
"arguments": {"channel": 0}
},
ttl_urukul0_io_update={
"type": "local",
"module": "artiq.coredevice.ttl",
"class": "TTLOut",
"arguments": {"channel": 1}
},
ttl_urukul0_sw0={
"type": "local",
"module": "artiq.coredevice.ttl",
"class": "TTLOut",
"arguments": {"channel": 2}
},
ttl_urukul0_sw1={
"type": "local",
"module": "artiq.coredevice.ttl",
"class": "TTLOut",
"arguments": {"channel": 3}
},
ttl_urukul0_sw2={
"type": "local",
"module": "artiq.coredevice.ttl",
"class": "TTLOut",
"arguments": {"channel": 4}
},
ttl_urukul0_sw3={
"type": "local",
"module": "artiq.coredevice.ttl",
"class": "TTLOut",
"arguments": {"channel": 5}
},
urukul0_cpld={
"type": "local",
"module": "artiq.coredevice.urukul",
"class": "CPLD",
"arguments": {
"spi_device": "spi_urukul0",
"io_update_device": "ttl_urukul0_io_update",
"refclk": 150e6,
"clk_sel": 2
}
}
)
for i in range(4):
device_db["urukul0_ch" + str(i)] = {
"type": "local",
"module": "artiq.coredevice.ad9910",
"class": "AD9910",
"arguments": {
"pll_n": 16, # 600MHz sample rate
"pll_vco": 2,
"chip_select": 4 + i,
"cpld_device": "urukul0_cpld",
"sw_device": "ttl_urukul0_sw" + str(i)
}
}
"""
artiq_route routing.bin init
artiq_route routing.bin set 0 0
artiq_route routing.bin set 1 1 0
artiq_route routing.bin set 2 1 1 0
artiq_route routing.bin set 3 2 0
artiq_route routing.bin set 4 2 1 0
artiq_coremgmt -D kasli config write -f routing_table routing.bin
"""
for sayma in range(2):
amc_base = 0x010000 + sayma*0x020000
rtm_base = 0x020000 + sayma*0x020000
for i in range(4):
device_db["led" + str(4*sayma+i)] = {
"type": "local",
"module": "artiq.coredevice.ttl",
"class": "TTLOut",
"arguments": {"channel": amc_base + i}
}
for i in range(2):
device_db["ttl_mcx" + str(2*sayma+i)] = {
"type": "local",
"module": "artiq.coredevice.ttl",
"class": "TTLInOut",
"arguments": {"channel": amc_base + 4 + i}
}
for i in range(8):
device_db["sawg" + str(8*sayma+i)] = {
"type": "local",
"module": "artiq.coredevice.sawg",
"class": "SAWG",
"arguments": {"channel_base": amc_base + 6 + i*10, "parallelism": 4}
}
for basemod in range(2):
for i in range(4):
device_db["sawg_sw" + str(8*sayma+4*basemod+i)] = {
"type": "local",
"module": "artiq.coredevice.ttl",
"class": "TTLOut",
"arguments": {"channel": rtm_base + basemod*9 + i}
}
att_idx = 2*sayma + basemod
device_db["basemod_att_rst_n"+str(att_idx)] = {
"type": "local",
"module": "artiq.coredevice.ttl",
"class": "TTLOut",
"arguments": {"channel": rtm_base + basemod*9 + 4}
}
device_db["basemod_att_clk"+str(att_idx)] = {
"type": "local",
"module": "artiq.coredevice.ttl",
"class": "TTLOut",
"arguments": {"channel": rtm_base + basemod*9 + 5}
}
device_db["basemod_att_le"+str(att_idx)] = {
"type": "local",
"module": "artiq.coredevice.ttl",
"class": "TTLOut",
"arguments": {"channel": rtm_base + basemod*9 + 6}
}
device_db["basemod_att_mosi"+str(att_idx)] = {
"type": "local",
"module": "artiq.coredevice.ttl",
"class": "TTLOut",
"arguments": {"channel": rtm_base + basemod*9 + 7}
}
device_db["basemod_att_miso"+str(att_idx)] = {
"type": "local",
"module": "artiq.coredevice.ttl",
"class": "TTLInOut",
"arguments": {"channel": rtm_base + basemod*9 + 8}
}
device_db["basemod_att"+str(att_idx)] = {
"type": "local",
"module": "artiq.coredevice.basemod_att",
"class": "BaseModAtt",
"arguments": {
"rst_n": "basemod_att_rst_n"+str(att_idx),
"clk": "basemod_att_clk"+str(att_idx),
"le": "basemod_att_le"+str(att_idx),
"mosi": "basemod_att_mosi"+str(att_idx),
"miso": "basemod_att_miso"+str(att_idx),
}
}

View File

@ -1,25 +0,0 @@
from artiq.experiment import *
class BaseMod(EnvExperiment):
def build(self):
self.setattr_device("core")
self.basemods = [self.get_device("basemod_att0"), self.get_device("basemod_att1")]
self.rfsws = [self.get_device("sawg_sw"+str(i)) for i in range(8)]
@kernel
def run(self):
self.core.reset()
for basemod in self.basemods:
self.core.break_realtime()
delay(10*ms)
basemod.reset()
delay(10*ms)
basemod.set(0.0, 0.0, 0.0, 0.0)
delay(10*ms)
print(basemod.get_mu())
self.core.break_realtime()
for rfsw in self.rfsws:
rfsw.on()
delay(1*ms)

View File

@ -1,37 +0,0 @@
from artiq.experiment import *
class Sines2Sayma(EnvExperiment):
def build(self):
self.setattr_device("core")
self.sawgs = [self.get_device("sawg"+str(i)) for i in range(16)]
@kernel
def drtio_is_up(self):
for i in range(5):
if not self.core.get_rtio_destination_status(i):
return False
return True
@kernel
def run(self):
while True:
print("waiting for DRTIO ready...")
while not self.drtio_is_up():
pass
print("OK")
self.core.reset()
for sawg in self.sawgs:
delay(1*ms)
sawg.reset()
for sawg in self.sawgs:
delay(1*ms)
sawg.amplitude1.set(.4)
# Do not use a sub-multiple of oscilloscope sample rates.
sawg.frequency0.set(9*MHz)
while self.drtio_is_up():
pass

View File

@ -1,89 +0,0 @@
from artiq.experiment import *
class SinesUrukulSayma(EnvExperiment):
def build(self):
self.setattr_device("core")
self.setattr_device("urukul0_cpld")
# Urukul clock output syntonized to the RTIO clock.
# Can be used as HMC830 reference on Sayma RTM.
# When using this reference, Sayma must be recalibrated every time Urukul
# is rebooted, as Urukul is not synchronized to the Kasli.
self.urukul_hmc_ref = self.get_device("urukul0_ch3")
# Urukul measurement channels - compare with SAWG outputs.
# When testing sync, do not reboot Urukul, as it is not
# synchronized to the Kasli.
self.urukul_meas = [self.get_device("urukul0_ch" + str(i)) for i in range(3)]
# The same waveform is output on all first 4 SAWG channels (first DAC).
self.sawgs = [self.get_device("sawg"+str(i)) for i in range(4)]
self.basemod = self.get_device("basemod_att0")
self.rfsws = [self.get_device("sawg_sw"+str(i)) for i in range(4)]
# DRTIO destinations:
# 0: local
# 1: Sayma AMC
# 2: Sayma RTM
@kernel
def drtio_is_up(self):
for i in range(3):
if not self.core.get_rtio_destination_status(i):
return False
return True
@kernel
def run(self):
f = 9*MHz
dds_ftw = self.urukul_meas[0].frequency_to_ftw(f)
sawg_ftw = self.sawgs[0].frequency0.to_mu(f)
if dds_ftw != sawg_ftw:
print("DDS and SAWG FTWs do not match:", dds_ftw, sawg_ftw)
return
self.core.reset()
self.urukul0_cpld.init()
delay(1*ms)
self.urukul_hmc_ref.init()
self.urukul_hmc_ref.set_mu(0x40000000, asf=self.urukul_hmc_ref.amplitude_to_asf(0.6))
self.urukul_hmc_ref.set_att(6.)
self.urukul_hmc_ref.sw.on()
for urukul_ch in self.urukul_meas:
delay(1*ms)
urukul_ch.init()
urukul_ch.set_mu(dds_ftw, asf=urukul_ch.amplitude_to_asf(0.5))
urukul_ch.set_att(6.)
urukul_ch.sw.on()
while True:
print("waiting for DRTIO ready...")
while not self.drtio_is_up():
pass
print("OK")
self.core.reset()
delay(10*ms)
self.basemod.reset()
delay(10*ms)
self.basemod.set(3.0, 3.0, 3.0, 3.0)
delay(10*ms)
for rfsw in self.rfsws:
delay(1*ms)
rfsw.on()
for sawg in self.sawgs:
delay(1*ms)
sawg.reset()
for sawg in self.sawgs:
delay(1*ms)
sawg.amplitude1.set(.4)
sawg.frequency0.set_mu(sawg_ftw)
sawg.phase0.set_mu(sawg_ftw*now_mu() >> 17)
while self.drtio_is_up():
pass

View File

@ -1,102 +0,0 @@
core_addr = "192.168.1.65"
device_db = {
"core": {
"type": "local",
"module": "artiq.coredevice.core",
"class": "Core",
"arguments": {"host": core_addr, "ref_period": 1/(8*150e6)}
},
"core_log": {
"type": "controller",
"host": "::1",
"port": 1068,
"command": "aqctl_corelog -p {port} --bind {bind} " + core_addr
},
"core_moninj": {
"type": "controller",
"host": "::1",
"port_proxy": 1383,
"port": 1384,
"command": "aqctl_moninj_proxy --port-proxy {port_proxy} --port-control {port} --bind {bind} " + core_addr
},
"core_cache": {
"type": "local",
"module": "artiq.coredevice.cache",
"class": "CoreCache"
},
"core_dma": {
"type": "local",
"module": "artiq.coredevice.dma",
"class": "CoreDMA"
}
}
# master peripherals
for i in range(4):
device_db["led" + str(i)] = {
"type": "local",
"module": "artiq.coredevice.ttl",
"class": "TTLOut",
"arguments": {"channel": i},
}
# DEST#1 peripherals
amc_base = 0x070000
rtm_base = 0x020000
for i in range(4):
device_db["led" + str(4+i)] = {
"type": "local",
"module": "artiq.coredevice.ttl",
"class": "TTLOut",
"arguments": {"channel": amc_base + i},
}
#DIO (EEM0) starting at RTIO channel 0x000056
for i in range(8):
device_db["ttl" + str(i)] = {
"type": "local",
"module": "artiq.coredevice.ttl",
"class": "TTLOut",
"arguments": {"channel": amc_base + 0x000056 + i},
}
#DIO (EEM1) starting at RTIO channel 0x00005e
for i in range(8):
device_db["ttl" + str(8+i)] = {
"type": "local",
"module": "artiq.coredevice.ttl",
"class": "TTLOut",
"arguments": {"channel": amc_base + 0x00005e + i},
}
device_db["fmcdio_dirctl_clk"] = {
"type": "local",
"module": "artiq.coredevice.ttl",
"class": "TTLOut",
"arguments": {"channel": amc_base + 0x000066}
}
device_db["fmcdio_dirctl_ser"] = {
"type": "local",
"module": "artiq.coredevice.ttl",
"class": "TTLOut",
"arguments": {"channel": amc_base + 0x000067}
}
device_db["fmcdio_dirctl_latch"] = {
"type": "local",
"module": "artiq.coredevice.ttl",
"class": "TTLOut",
"arguments": {"channel": amc_base + 0x000068}
}
device_db["fmcdio_dirctl"] = {
"type": "local",
"module": "artiq.coredevice.shiftreg",
"class": "ShiftReg",
"arguments": {"clk": "fmcdio_dirctl_clk",
"ser": "fmcdio_dirctl_ser",
"latch": "fmcdio_dirctl_latch"}
}

View File

@ -1,129 +0,0 @@
import sys
import os
import select
from artiq.experiment import *
from artiq.coredevice.fmcdio_vhdci_eem import *
def chunker(seq, size):
res = []
for el in seq:
res.append(el)
if len(res) == size:
yield res
res = []
if res:
yield res
def is_enter_pressed() -> TBool:
if os.name == "nt":
if msvcrt.kbhit() and msvcrt.getch() == b"\r":
return True
else:
return False
else:
if select.select([sys.stdin, ], [], [], 0.0)[0]:
sys.stdin.read(1)
return True
else:
return False
class Demo(EnvExperiment):
def build(self):
self.setattr_device("core")
self.setattr_device("fmcdio_dirctl")
self.leds = dict()
self.ttl_outs = dict()
ddb = self.get_device_db()
for name, desc in ddb.items():
if isinstance(desc, dict) and desc["type"] == "local":
module, cls = desc["module"], desc["class"]
if (module, cls) == ("artiq.coredevice.ttl", "TTLOut"):
dev = self.get_device(name)
if "led" in name: # guess
self.leds[name] = dev
elif "ttl" in name: # to exclude fmcdio_dirctl
self.ttl_outs[name] = dev
self.leds = sorted(self.leds.items(), key=lambda x: x[1].channel)
self.ttl_outs = sorted(self.ttl_outs.items(), key=lambda x: x[1].channel)
self.dirctl_word = (
shiftreg_bits(0, dio_bank0_out_pins | dio_bank1_out_pins) |
shiftreg_bits(1, dio_bank0_out_pins | dio_bank1_out_pins)
)
@kernel
def init(self):
self.core.break_realtime()
print("*** Waiting for DRTIO ready...")
drtio_indices = [7]
for i in drtio_indices:
while not self.drtio_is_up(i):
pass
self.fmcdio_dirctl.set(self.dirctl_word)
@kernel
def drtio_is_up(self, drtio_index):
if not self.core.get_rtio_destination_status(drtio_index):
return False
print("DRTIO #", drtio_index, "is ready\n")
return True
@kernel
def test_led(self, led):
while not is_enter_pressed():
self.core.break_realtime()
# do not fill the FIFOs too much to avoid long response times
t = now_mu() - self.core.seconds_to_mu(0.2)
while self.core.get_rtio_counter_mu() < t:
pass
for i in range(3):
led.pulse(100*ms)
delay(100*ms)
def test_leds(self):
print("*** Testing LEDs.")
print("Check for blinking. Press ENTER when done.")
for led_name, led_dev in self.leds:
print("Testing LED: {}".format(led_name))
self.test_led(led_dev)
@kernel
def test_ttl_out_chunk(self, ttl_chunk):
while not is_enter_pressed():
self.core.break_realtime()
for _ in range(50000):
i = 0
for ttl in ttl_chunk:
i += 1
for _ in range(i):
ttl.pulse(1*us)
delay(1*us)
delay(10*us)
def test_ttl_outs(self):
print("*** Testing TTL outputs.")
print("Outputs are tested in groups of 4. Touch each TTL connector")
print("with the oscilloscope probe tip, and check that the number of")
print("pulses corresponds to its number in the group.")
print("Press ENTER when done.")
for ttl_chunk in chunker(self.ttl_outs, 4):
print("Testing TTL outputs: {}.".format(", ".join(name for name, dev in ttl_chunk)))
self.test_ttl_out_chunk([dev for name, dev in ttl_chunk])
def run(self):
self.core.reset()
if self.leds:
self.test_leds()
if self.ttl_outs:
self.test_ttl_outs()

View File

@ -1,173 +0,0 @@
core_addr = "192.168.1.60"
device_db = {
"core": {
"type": "local",
"module": "artiq.coredevice.core",
"class": "Core",
"arguments": {"host": core_addr, "ref_period": 1/(8*150e6)}
},
"core_log": {
"type": "controller",
"host": "::1",
"port": 1068,
"command": "aqctl_corelog -p {port} --bind {bind} " + core_addr
},
"core_moninj": {
"type": "controller",
"host": "::1",
"port_proxy": 1383,
"port": 1384,
"command": "aqctl_moninj_proxy --port-proxy {port_proxy} --port-control {port} --bind {bind} " + core_addr
},
"core_cache": {
"type": "local",
"module": "artiq.coredevice.cache",
"class": "CoreCache"
},
"core_dma": {
"type": "local",
"module": "artiq.coredevice.dma",
"class": "CoreDMA"
},
}
for i in range(4):
device_db["led" + str(i)] = {
"type": "local",
"module": "artiq.coredevice.ttl",
"class": "TTLOut",
"arguments": {"channel": i},
}
for i in range(2):
device_db["ttl" + str(i)] = {
"type": "local",
"module": "artiq.coredevice.ttl",
"class": "TTLInOut",
"arguments": {"channel": 4 + i},
}
device_db.update(
fmcdio_dirctl_clk={
"type": "local",
"module": "artiq.coredevice.ttl",
"class": "TTLOut",
"arguments": {"channel": 6}
},
fmcdio_dirctl_ser={
"type": "local",
"module": "artiq.coredevice.ttl",
"class": "TTLOut",
"arguments": {"channel": 7}
},
fmcdio_dirctl_latch={
"type": "local",
"module": "artiq.coredevice.ttl",
"class": "TTLOut",
"arguments": {"channel": 8}
},
fmcdio_dirctl={
"type": "local",
"module": "artiq.coredevice.shiftreg",
"class": "ShiftReg",
"arguments": {"clk": "fmcdio_dirctl_clk",
"ser": "fmcdio_dirctl_ser",
"latch": "fmcdio_dirctl_latch"}
}
)
device_db.update(
spi_urukul0={
"type": "local",
"module": "artiq.coredevice.spi2",
"class": "SPIMaster",
"arguments": {"channel": 17}
},
ttl_urukul0_io_update={
"type": "local",
"module": "artiq.coredevice.ttl",
"class": "TTLOut",
"arguments": {"channel": 18}
},
ttl_urukul0_sw0={
"type": "local",
"module": "artiq.coredevice.ttl",
"class": "TTLOut",
"arguments": {"channel": 19}
},
ttl_urukul0_sw1={
"type": "local",
"module": "artiq.coredevice.ttl",
"class": "TTLOut",
"arguments": {"channel": 20}
},
ttl_urukul0_sw2={
"type": "local",
"module": "artiq.coredevice.ttl",
"class": "TTLOut",
"arguments": {"channel": 21}
},
ttl_urukul0_sw3={
"type": "local",
"module": "artiq.coredevice.ttl",
"class": "TTLOut",
"arguments": {"channel": 22}
},
urukul0_cpld={
"type": "local",
"module": "artiq.coredevice.urukul",
"class": "CPLD",
"arguments": {
"spi_device": "spi_urukul0",
"io_update_device": "ttl_urukul0_io_update",
"refclk": 125e6,
"clk_sel": 0
}
}
)
for i in range(4):
device_db["urukul0_ch" + str(i)] = {
"type": "local",
"module": "artiq.coredevice.ad9910",
"class": "AD9910",
"arguments": {
"pll_n": 32,
"chip_select": 4 + i,
"cpld_device": "urukul0_cpld",
"sw_device": "ttl_urukul0_sw" + str(i)
}
}
device_db["spi_zotino0"] = {
"type": "local",
"module": "artiq.coredevice.spi2",
"class": "SPIMaster",
"arguments": {"channel": 23}
}
device_db["ttl_zotino0_ldac"] = {
"type": "local",
"module": "artiq.coredevice.ttl",
"class": "TTLOut",
"arguments": {"channel": 24}
}
device_db["ttl_zotino0_clr"] = {
"type": "local",
"module": "artiq.coredevice.ttl",
"class": "TTLOut",
"arguments": {"channel": 25}
}
device_db["zotino0"] = {
"type": "local",
"module": "artiq.coredevice.zotino",
"class": "Zotino",
"arguments": {
"spi_device": "spi_zotino0",
"ldac_device": "ttl_zotino0_ldac",
"clr_device": "ttl_zotino0_clr"
}
}

View File

@ -1,41 +0,0 @@
from artiq.experiment import *
from artiq.coredevice.fmcdio_vhdci_eem import *
class Demo(EnvExperiment):
def build(self):
self.setattr_device("core")
self.setattr_device("fmcdio_dirctl")
self.ttls = [self.get_device("ttl" + str(i)) for i in range(8)]
self.setattr_device("urukul0_cpld")
self.urukul_chs = [self.get_device("urukul0_ch" + str(i)) for i in range(4)]
self.setattr_device("zotino0")
self.dirctl_word = (
shiftreg_bits(1, urukul_out_pins) |
shiftreg_bits(0, urukul_aux_out_pins) |
shiftreg_bits(2, dio_bank0_out_pins | dio_bank1_out_pins) |
shiftreg_bits(3, zotino_out_pins))
@kernel
def run(self):
self.core.reset()
delay(10*ms)
self.fmcdio_dirctl.set(self.dirctl_word)
delay(10*ms)
self.urukul0_cpld.init()
delay(10*ms)
self.zotino0.init()
delay(1*ms)
for i in range(32):
self.zotino0.write_dac(i, i/4)
delay(1*ms)
while True:
for ttl in self.ttls:
ttl.pulse(100*ms)
for urukul_ch in self.urukul_chs:
urukul_ch.sw.pulse(100*ms)

View File

@ -1,549 +0,0 @@
use board_misoc::{csr, clock};
use ad9154_reg;
fn spi_setup(dacno: u8) {
unsafe {
while csr::converter_spi::idle_read() == 0 {}
csr::converter_spi::offline_write(0);
csr::converter_spi::end_write(1);
csr::converter_spi::cs_polarity_write(0b0001);
csr::converter_spi::clk_polarity_write(0);
csr::converter_spi::clk_phase_write(0);
csr::converter_spi::lsb_first_write(0);
csr::converter_spi::half_duplex_write(0);
csr::converter_spi::length_write(24 - 1);
csr::converter_spi::div_write(16 - 2);
csr::converter_spi::cs_write(1 << (csr::CONFIG_CONVERTER_SPI_FIRST_AD9154_CS + dacno as u32));
}
}
fn write(addr: u16, data: u8) {
unsafe {
while csr::converter_spi::writable_read() == 0 {}
csr::converter_spi::data_write(
((addr as u32) << 16) | ((data as u32) << 8));
}
}
fn read(addr: u16) -> u8 {
unsafe {
write((1 << 15) | addr, 0);
while csr::converter_spi::writable_read() == 0 {}
csr::converter_spi::data_read() as u8
}
}
// ad9154 mode 1
// linerate 5Gbps or 6Gbps
// deviceclock_fpga 125MHz or 150MHz
// deviceclock_dac 500MHz or 600MHz
struct JESDSettings {
did: u8,
bid: u8,
l: u8, // lanes
m: u8, // converters
n: u8, // bits/converter
np: u8, // bits/sample
f: u8, // octets/(lane and frame)
s: u8, // samples/(converter and frame)
k: u8, // frames/multiframe
cs: u8, // control bits/sample
subclassv: u8,
jesdv: u8
}
fn jesd_checksum(settings: &JESDSettings) -> u8 {
let mut r: u8 = 0;
for field in [
settings.did,
settings.bid,
settings.l - 1,
settings.f - 1,
settings.k - 1,
settings.m - 1,
settings.n - 1,
settings.cs,
settings.np - 1,
settings.subclassv,
settings.s - 1,
settings.jesdv,
].iter() {
r = r.overflowing_add(*field).0;
}
r
}
const JESD_SETTINGS: JESDSettings = JESDSettings {
did: 0x5a,
bid: 0x5,
l: 8,
m: 4,
n: 16,
np: 16,
f: 2,
s: 2,
k: 16,
cs: 0,
subclassv: 1,
jesdv: 1
};
pub fn reset_and_detect(dacno: u8) -> Result<(), &'static str> {
spi_setup(dacno);
// reset
write(ad9154_reg::SPI_INTFCONFA,
1*ad9154_reg::SOFTRESET_M | 1*ad9154_reg::SOFTRESET |
0*ad9154_reg::LSBFIRST_M | 0*ad9154_reg::LSBFIRST |
0*ad9154_reg::ADDRINC_M | 0*ad9154_reg::ADDRINC |
1*ad9154_reg::SDOACTIVE_M | 1*ad9154_reg::SDOACTIVE);
clock::spin_us(100);
write(ad9154_reg::SPI_INTFCONFA,
0*ad9154_reg::SOFTRESET_M | 0*ad9154_reg::SOFTRESET |
0*ad9154_reg::LSBFIRST_M | 0*ad9154_reg::LSBFIRST |
0*ad9154_reg::ADDRINC_M | 0*ad9154_reg::ADDRINC |
1*ad9154_reg::SDOACTIVE_M | 1*ad9154_reg::SDOACTIVE);
clock::spin_us(100);
if (read(ad9154_reg::PRODIDH) as u16) << 8 | (read(ad9154_reg::PRODIDL) as u16) != 0x9154 {
return Err("invalid AD9154 identification");
} else {
info!("AD9154-{} found", dacno);
}
Ok(())
}
pub fn setup(dacno: u8, linerate: u64) -> Result<(), &'static str> {
spi_setup(dacno);
info!("AD9154-{} initializing...", dacno);
write(ad9154_reg::PWRCNTRL0,
0*ad9154_reg::PD_DAC0 | 0*ad9154_reg::PD_DAC1 |
0*ad9154_reg::PD_DAC2 | 0*ad9154_reg::PD_DAC3 |
0*ad9154_reg::PD_BG);
clock::spin_us(100);
write(ad9154_reg::TXENMASK1, 0*ad9154_reg::DACA_MASK |
0*ad9154_reg::DACB_MASK); // DAC PD not controlled by TXEN pins
write(ad9154_reg::PWRCNTRL3, 1*ad9154_reg::ENA_SPI_TXEN |
1*ad9154_reg::SPI_TXEN);
write(ad9154_reg::CLKCFG0,
0*ad9154_reg::REF_CLKDIV_EN | 1*ad9154_reg::RF_SYNC_EN |
1*ad9154_reg::DUTY_EN | 0*ad9154_reg::PD_CLK_REC |
0*ad9154_reg::PD_SERDES_PCLK | 0*ad9154_reg::PD_CLK_DIG |
0*ad9154_reg::PD_CLK23 | 0*ad9154_reg::PD_CLK01);
write(ad9154_reg::DACPLLCNTRL,
0*ad9154_reg::ENABLE_DACPLL | 0*ad9154_reg::RECAL_DACPLL);
write(ad9154_reg::SYSREF_ACTRL0, // jesd204b subclass 1
0*ad9154_reg::HYS_CNTRL1 | 0*ad9154_reg::SYSREF_RISE |
0*ad9154_reg::HYS_ON | 0*ad9154_reg::PD_SYSREF_BUFFER);
write(ad9154_reg::DEVICE_CONFIG_REG_0, 0x8b); // magic
write(ad9154_reg::DEVICE_CONFIG_REG_1, 0x01); // magic
write(ad9154_reg::DEVICE_CONFIG_REG_2, 0x01); // magic
write(ad9154_reg::SPI_PAGEINDX, 0x3); // A and B dual
write(ad9154_reg::INTERP_MODE, 0x03); // 4x
write(ad9154_reg::MIX_MODE, 0);
write(ad9154_reg::DATA_FORMAT, 0*ad9154_reg::BINARY_FORMAT); // s16
write(ad9154_reg::DATAPATH_CTRL,
0*ad9154_reg::I_TO_Q | 0*ad9154_reg::SEL_SIDEBAND |
0*ad9154_reg::MODULATION_TYPE | 0*ad9154_reg::PHASE_ADJ_ENABLE |
1*ad9154_reg::DIG_GAIN_ENABLE | 0*ad9154_reg::INVSINC_ENABLE);
write(ad9154_reg::IDAC_DIG_GAIN0, 0x00);
write(ad9154_reg::IDAC_DIG_GAIN1, 0x8);
write(ad9154_reg::QDAC_DIG_GAIN0, 0x00);
write(ad9154_reg::QDAC_DIG_GAIN1, 0x8);
write(ad9154_reg::DC_OFFSET_CTRL, 0);
write(ad9154_reg::IPATH_DC_OFFSET_1PART0, 0x00);
write(ad9154_reg::IPATH_DC_OFFSET_1PART1, 0x00);
write(ad9154_reg::IPATH_DC_OFFSET_2PART, 0x00);
write(ad9154_reg::QPATH_DC_OFFSET_1PART0, 0x00);
write(ad9154_reg::QPATH_DC_OFFSET_1PART1, 0x00);
write(ad9154_reg::QPATH_DC_OFFSET_2PART, 0x00);
write(ad9154_reg::PHASE_ADJ0, 0);
write(ad9154_reg::PHASE_ADJ1, 0);
write(ad9154_reg::GROUP_DLY, 0x8*ad9154_reg::COARSE_GROUP_DELAY |
0x8*ad9154_reg::GROUP_DELAY_RESERVED);
write(ad9154_reg::GROUPDELAY_COMP_BYP,
1*ad9154_reg::GROUPCOMP_BYPQ |
1*ad9154_reg::GROUPCOMP_BYPI);
write(ad9154_reg::GROUPDELAY_COMP_I, 0);
write(ad9154_reg::GROUPDELAY_COMP_Q, 0);
write(ad9154_reg::PDP_AVG_TIME, 0*ad9154_reg::PDP_ENABLE);
write(ad9154_reg::MASTER_PD, 0);
write(ad9154_reg::PHY_PD, 0x00); // lanes 0-7 enabled
write(ad9154_reg::GENERIC_PD,
0*ad9154_reg::PD_SYNCOUT0B |
1*ad9154_reg::PD_SYNCOUT1B);
write(ad9154_reg::GENERAL_JRX_CTRL_0,
0x0*ad9154_reg::LINK_EN | 0*ad9154_reg::LINK_PAGE |
0*ad9154_reg::LINK_MODE | 0*ad9154_reg::CHECKSUM_MODE);
write(ad9154_reg::ILS_DID, JESD_SETTINGS.did);
write(ad9154_reg::ILS_BID, JESD_SETTINGS.bid);
write(ad9154_reg::ILS_LID0, 0x00); // lane id
write(ad9154_reg::ILS_SCR_L,
(JESD_SETTINGS.l - 1)*ad9154_reg::L_1 |
1*ad9154_reg::SCR);
write(ad9154_reg::ILS_F, JESD_SETTINGS.f - 1);
write(ad9154_reg::ILS_K, JESD_SETTINGS.k - 1);
write(ad9154_reg::ILS_M, JESD_SETTINGS.m - 1);
write(ad9154_reg::ILS_CS_N,
(JESD_SETTINGS.n - 1)*ad9154_reg::N_1 |
0*ad9154_reg::CS);
write(ad9154_reg::ILS_NP,
(JESD_SETTINGS.np - 1)*ad9154_reg::NP_1 |
JESD_SETTINGS.subclassv*ad9154_reg::SUBCLASSV);
write(ad9154_reg::ILS_S,
(JESD_SETTINGS.s - 1)*ad9154_reg::S_1 |
JESD_SETTINGS.jesdv*ad9154_reg::JESDV);
write(ad9154_reg::ILS_HD_CF,
0*ad9154_reg::HD | 0*ad9154_reg::CF);
write(ad9154_reg::ILS_CHECKSUM, jesd_checksum(&JESD_SETTINGS));
write(ad9154_reg::LANEDESKEW, 0xff);
for i in 0..8 {
write(ad9154_reg::BADDISPARITY, 0*ad9154_reg::RST_IRQ_DIS |
0*ad9154_reg::DISABLE_ERR_CNTR_DIS |
1*ad9154_reg::RST_ERR_CNTR_DIS | i*ad9154_reg::LANE_ADDR_DIS);
write(ad9154_reg::BADDISPARITY, 0*ad9154_reg::RST_IRQ_DIS |
0*ad9154_reg::DISABLE_ERR_CNTR_DIS |
0*ad9154_reg::RST_ERR_CNTR_DIS | i*ad9154_reg::LANE_ADDR_DIS);
write(ad9154_reg::NIT_W, 0*ad9154_reg::RST_IRQ_NIT |
0*ad9154_reg::DISABLE_ERR_CNTR_NIT |
1*ad9154_reg::RST_ERR_CNTR_NIT | i*ad9154_reg::LANE_ADDR_NIT);
write(ad9154_reg::NIT_W, 0*ad9154_reg::RST_IRQ_NIT |
0*ad9154_reg::DISABLE_ERR_CNTR_NIT |
0*ad9154_reg::RST_ERR_CNTR_NIT | i*ad9154_reg::LANE_ADDR_NIT);
write(ad9154_reg::UNEXPECTEDCONTROL_W, 0*ad9154_reg::RST_IRQ_UCC |
0*ad9154_reg::DISABLE_ERR_CNTR_UCC |
1*ad9154_reg::RST_ERR_CNTR_UCC | i*ad9154_reg::LANE_ADDR_UCC);
write(ad9154_reg::BADDISPARITY, 0*ad9154_reg::RST_IRQ_UCC |
0*ad9154_reg::DISABLE_ERR_CNTR_UCC |
0*ad9154_reg::RST_ERR_CNTR_UCC | i*ad9154_reg::LANE_ADDR_UCC);
}
write(ad9154_reg::CTRLREG1, JESD_SETTINGS.f);
write(ad9154_reg::CTRLREG2, 0*ad9154_reg::ILAS_MODE |
0*ad9154_reg::THRESHOLD_MASK_EN);
write(ad9154_reg::KVAL, 1); // *4*K multiframes during ILAS
write(ad9154_reg::LANEENABLE, 0xff); // CGS _after_ this
write(ad9154_reg::TERM_BLK1_CTRLREG0, 1);
write(ad9154_reg::TERM_BLK2_CTRLREG0, 1);
write(ad9154_reg::SERDES_SPI_REG, 1);
if linerate > 5_650_000_000 {
write(ad9154_reg::CDR_OPERATING_MODE_REG_0,
0*ad9154_reg::CDR_OVERSAMP | 0x2*ad9154_reg::CDR_RESERVED |
1*ad9154_reg::ENHALFRATE);
} else {
write(ad9154_reg::CDR_OPERATING_MODE_REG_0,
0*ad9154_reg::CDR_OVERSAMP | 0x2*ad9154_reg::CDR_RESERVED |
0*ad9154_reg::ENHALFRATE);
}
write(ad9154_reg::CDR_RESET, 0);
write(ad9154_reg::CDR_RESET, 1);
if linerate > 5_650_000_000 {
write(ad9154_reg::REF_CLK_DIVIDER_LDO,
0*ad9154_reg::SPI_CDR_OVERSAMP |
1*ad9154_reg::SPI_LDO_BYPASS_FILT |
0*ad9154_reg::SPI_LDO_REF_SEL);
} else {
write(ad9154_reg::REF_CLK_DIVIDER_LDO,
1*ad9154_reg::SPI_CDR_OVERSAMP |
1*ad9154_reg::SPI_LDO_BYPASS_FILT |
0*ad9154_reg::SPI_LDO_REF_SEL);
}
write(ad9154_reg::LDO_FILTER_1, 0x62); // magic
write(ad9154_reg::LDO_FILTER_2, 0xc9); // magic
write(ad9154_reg::LDO_FILTER_3, 0x0e); // magic
write(ad9154_reg::CP_CURRENT_SPI,
0x12*ad9154_reg::SPI_CP_CURRENT |
0*ad9154_reg::SPI_SERDES_LOGEN_POWER_MODE);
write(ad9154_reg::VCO_LDO, 0x7b); // magic
write(ad9154_reg::PLL_RD_REG,
0*ad9154_reg::SPI_SERDES_LOGEN_PD_CORE |
0*ad9154_reg::SPI_SERDES_LDO_PD | 0*ad9154_reg::SPI_SYN_PD |
0*ad9154_reg::SPI_VCO_PD_ALC | 0*ad9154_reg::SPI_VCO_PD_PTAT |
0*ad9154_reg::SPI_VCO_PD);
write(ad9154_reg::ALC_VARACTOR,
0x9*ad9154_reg::SPI_VCO_VARACTOR |
0x8*ad9154_reg::SPI_INIT_ALC_VALUE);
write(ad9154_reg::VCO_OUTPUT,
0xc*ad9154_reg::SPI_VCO_OUTPUT_LEVEL |
0x4*ad9154_reg::SPI_VCO_OUTPUT_RESERVED);
write(ad9154_reg::CP_CONFIG,
0*ad9154_reg::SPI_CP_TEST |
1*ad9154_reg::SPI_CP_CAL_EN |
0*ad9154_reg::SPI_CP_FORCE_CALBITS |
0*ad9154_reg::SPI_CP_OFFSET_OFF |
1*ad9154_reg::SPI_CP_ENABLE_MACHINE |
0*ad9154_reg::SPI_CP_DITHER_MODE |
0*ad9154_reg::SPI_CP_HALF_VCO_CAL_CLK);
write(ad9154_reg::VCO_BIAS_1,
0x3*ad9154_reg::SPI_VCO_BIAS_REF |
0x3*ad9154_reg::SPI_VCO_BIAS_TCF);
write(ad9154_reg::VCO_BIAS_2,
0x1*ad9154_reg::SPI_PRESCALE_BIAS |
1*ad9154_reg::SPI_LAST_ALC_EN |
0x1*ad9154_reg::SPI_PRESCALE_BYPASS_R |
0*ad9154_reg::SPI_VCO_COMP_BYPASS_BIASR |
0*ad9154_reg::SPI_VCO_BYPASS_DAC_R);
write(ad9154_reg::VCO_PD_OVERRIDES,
0*ad9154_reg::SPI_VCO_PD_OVERRIDE_VCO_BUF |
1*ad9154_reg::SPI_VCO_PD_OVERRIDE_CAL_TCF |
0*ad9154_reg::SPI_VCO_PD_OVERRIDE_VAR_REF_TCF |
0*ad9154_reg::SPI_VCO_PD_OVERRIDE_VAR_REF);
write(ad9154_reg::VCO_CAL,
0x2*ad9154_reg::SPI_FB_CLOCK_ADV |
0x3*ad9154_reg::SPI_VCO_CAL_COUNT |
0*ad9154_reg::SPI_VCO_CAL_ALC_WAIT |
1*ad9154_reg::SPI_VCO_CAL_EN);
write(ad9154_reg::CP_LEVEL_DETECT,
0x2*ad9154_reg::SPI_CP_LEVEL_THRESHOLD_HIGH |
0x5*ad9154_reg::SPI_CP_LEVEL_THRESHOLD_LOW |
0*ad9154_reg::SPI_CP_LEVEL_DET_PD);
write(ad9154_reg::VCO_VARACTOR_CTRL_0,
0xe*ad9154_reg::SPI_VCO_VARACTOR_OFFSET |
0x7*ad9154_reg::SPI_VCO_VARACTOR_REF_TCF);
write(ad9154_reg::VCO_VARACTOR_CTRL_1,
0x6*ad9154_reg::SPI_VCO_VARACTOR_REF);
// ensure link is txing
//write(ad9154_reg::SERDESPLL_ENABLE_CNTRL,
// 1*ad9154_reg::ENABLE_SERDESPLL | 1*ad9154_reg::RECAL_SERDESPLL)
write(ad9154_reg::SERDESPLL_ENABLE_CNTRL,
1*ad9154_reg::ENABLE_SERDESPLL | 0*ad9154_reg::RECAL_SERDESPLL);
let t = clock::get_ms();
while read(ad9154_reg::PLL_STATUS) & ad9154_reg::SERDES_PLL_LOCK_RB == 0 {
if clock::get_ms() > t + 200 {
return Err("SERDES PLL lock timeout");
}
}
write(ad9154_reg::EQ_BIAS_REG, 0x22*ad9154_reg::EQ_BIAS_RESERVED |
1*ad9154_reg::EQ_POWER_MODE);
write(ad9154_reg::GENERAL_JRX_CTRL_1, 1); // subclass 1
write(ad9154_reg::LMFC_DELAY_0, 0);
write(ad9154_reg::LMFC_DELAY_1, 0);
write(ad9154_reg::LMFC_VAR_0, 0x0a); // receive buffer delay
write(ad9154_reg::LMFC_VAR_1, 0x0a);
write(ad9154_reg::SYNC_ERRWINDOW, 0); // +- 1/2 DAC clock
// datasheet seems to say ENABLE and ARM should be separate steps,
// so enable now so it can be armed in sync().
write(ad9154_reg::SYNC_CONTROL,
0x1*ad9154_reg::SYNCMODE | 1*ad9154_reg::SYNCENABLE |
0*ad9154_reg::SYNCARM | 0*ad9154_reg::SYNCCLRSTKY);
write(ad9154_reg::XBAR_LN_0_1,
0*ad9154_reg::LOGICAL_LANE0_SRC | 1*ad9154_reg::LOGICAL_LANE1_SRC);
write(ad9154_reg::XBAR_LN_2_3,
2*ad9154_reg::LOGICAL_LANE2_SRC | 3*ad9154_reg::LOGICAL_LANE3_SRC);
write(ad9154_reg::XBAR_LN_4_5,
4*ad9154_reg::LOGICAL_LANE4_SRC | 5*ad9154_reg::LOGICAL_LANE5_SRC);
write(ad9154_reg::XBAR_LN_6_7,
6*ad9154_reg::LOGICAL_LANE6_SRC | 7*ad9154_reg::LOGICAL_LANE7_SRC);
write(ad9154_reg::JESD_BIT_INVERSE_CTRL, 0x00);
write(ad9154_reg::GENERAL_JRX_CTRL_0,
0x1*ad9154_reg::LINK_EN | 0*ad9154_reg::LINK_PAGE |
0*ad9154_reg::LINK_MODE | 0*ad9154_reg::CHECKSUM_MODE);
info!(" ...done");
Ok(())
}
pub fn status(dacno: u8) {
spi_setup(dacno);
info!("Printing status of AD9154-{}", dacno);
info!("PRODID: 0x{:04x}", (read(ad9154_reg::PRODIDH) as u16) << 8 | (read(ad9154_reg::PRODIDL) as u16));
info!("SERDES_PLL_LOCK: {}",
(read(ad9154_reg::PLL_STATUS) & ad9154_reg::SERDES_PLL_LOCK_RB));
info!("");
info!("CODEGRPSYNC: 0x{:02x}", read(ad9154_reg::CODEGRPSYNCFLG));
info!("FRAMESYNC: 0x{:02x}", read(ad9154_reg::FRAMESYNCFLG));
info!("GOODCHECKSUM: 0x{:02x}", read(ad9154_reg::GOODCHKSUMFLG));
info!("INITLANESYNC: 0x{:02x}", read(ad9154_reg::INITLANESYNCFLG));
info!("");
info!("DID_REG: 0x{:02x}", read(ad9154_reg::DID_REG));
info!("BID_REG: 0x{:02x}", read(ad9154_reg::BID_REG));
info!("SCR_L_REG: 0x{:02x}", read(ad9154_reg::SCR_L_REG));
info!("F_REG: 0x{:02x}", read(ad9154_reg::F_REG));
info!("K_REG: 0x{:02x}", read(ad9154_reg::K_REG));
info!("M_REG: 0x{:02x}", read(ad9154_reg::M_REG));
info!("CS_N_REG: 0x{:02x}", read(ad9154_reg::CS_N_REG));
info!("NP_REG: 0x{:02x}", read(ad9154_reg::NP_REG));
info!("S_REG: 0x{:02x}", read(ad9154_reg::S_REG));
info!("HD_CF_REG: 0x{:02x}", read(ad9154_reg::HD_CF_REG));
info!("RES1_REG: 0x{:02x}", read(ad9154_reg::RES1_REG));
info!("RES2_REG: 0x{:02x}", read(ad9154_reg::RES2_REG));
info!("LIDx_REG: 0x{:02x} 0x{:02x} 0x{:02x} 0x{:02x} 0x{:02x} 0x{:02x} 0x{:02x} 0x{:02x}",
read(ad9154_reg::LID0_REG),
read(ad9154_reg::LID1_REG),
read(ad9154_reg::LID2_REG),
read(ad9154_reg::LID3_REG),
read(ad9154_reg::LID4_REG),
read(ad9154_reg::LID5_REG),
read(ad9154_reg::LID6_REG),
read(ad9154_reg::LID7_REG));
info!("CHECKSUMx_REG: 0x{:02x} 0x{:02x} 0x{:02x} 0x{:02x} 0x{:02x} 0x{:02x} 0x{:02x} 0x{:02x}",
read(ad9154_reg::CHECKSUM0_REG),
read(ad9154_reg::CHECKSUM1_REG),
read(ad9154_reg::CHECKSUM2_REG),
read(ad9154_reg::CHECKSUM3_REG),
read(ad9154_reg::CHECKSUM4_REG),
read(ad9154_reg::CHECKSUM5_REG),
read(ad9154_reg::CHECKSUM6_REG),
read(ad9154_reg::CHECKSUM7_REG));
info!("COMPSUMx_REG: 0x{:02x} 0x{:02x} 0x{:02x} 0x{:02x} 0x{:02x} 0x{:02x} 0x{:02x} 0x{:02x}",
read(ad9154_reg::COMPSUM0_REG),
read(ad9154_reg::COMPSUM1_REG),
read(ad9154_reg::COMPSUM2_REG),
read(ad9154_reg::COMPSUM3_REG),
read(ad9154_reg::COMPSUM4_REG),
read(ad9154_reg::COMPSUM5_REG),
read(ad9154_reg::COMPSUM6_REG),
read(ad9154_reg::COMPSUM7_REG));
info!("BADDISPARITY: 0x{:02x}", read(ad9154_reg::BADDISPARITY));
info!("NITDISPARITY: 0x{:02x}", read(ad9154_reg::NIT_W));
}
pub fn prbs(dacno: u8) -> Result<(), &'static str> {
let mut prbs_errors: u32 = 0;
spi_setup(dacno);
/* follow phy prbs testing (p58 of ad9154 datasheet) */
info!("AD9154-{} running PRBS test...", dacno);
/* step 2: select prbs mode */
write(ad9154_reg::PHY_PRBS_TEST_CTRL,
0b00*ad9154_reg::PHY_PRBS_PAT_SEL);
/* step 3: enable test for all lanes */
write(ad9154_reg::PHY_PRBS_TEST_EN, 0xff);
/* step 4: reset */
write(ad9154_reg::PHY_PRBS_TEST_CTRL,
0b00*ad9154_reg::PHY_PRBS_PAT_SEL |
1*ad9154_reg::PHY_TEST_RESET);
write(ad9154_reg::PHY_PRBS_TEST_CTRL,
0b00*ad9154_reg::PHY_PRBS_PAT_SEL);
/* step 5: prbs threshold */
write(ad9154_reg::PHY_PRBS_TEST_THRESHOLD_LOBITS, 0);
write(ad9154_reg::PHY_PRBS_TEST_THRESHOLD_MIDBITS, 0);
write(ad9154_reg::PHY_PRBS_TEST_THRESHOLD_HIBITS, 0);
/* step 6: start */
write(ad9154_reg::PHY_PRBS_TEST_CTRL,
0b00*ad9154_reg::PHY_PRBS_PAT_SEL);
write(ad9154_reg::PHY_PRBS_TEST_CTRL,
0b00*ad9154_reg::PHY_PRBS_PAT_SEL |
1*ad9154_reg::PHY_TEST_START);
/* step 7: wait 500 ms */
clock::spin_us(500000);
/* step 8 : stop */
write(ad9154_reg::PHY_PRBS_TEST_CTRL,
0b00*ad9154_reg::PHY_PRBS_PAT_SEL);
for i in 0..8 {
/* step 9.a: select src err */
write(ad9154_reg::PHY_PRBS_TEST_CTRL,
i*ad9154_reg::PHY_SRC_ERR_CNT);
/* step 9.b: retrieve number of errors */
let lane_errors = (read(ad9154_reg::PHY_PRBS_TEST_ERRCNT_LOBITS) as u32) |
((read(ad9154_reg::PHY_PRBS_TEST_ERRCNT_MIDBITS) as u32) << 8) |
((read(ad9154_reg::PHY_PRBS_TEST_ERRCNT_HIBITS) as u32) << 16);
if lane_errors > 0 {
warn!(" PRBS errors on lane{}: {:06x}", i, lane_errors);
}
prbs_errors += lane_errors
}
if prbs_errors > 0 {
return Err("PRBS failed")
}
info!(" ...passed");
Ok(())
}
pub fn stpl(dacno: u8, m: u8, s: u8) -> Result<(), &'static str> {
spi_setup(dacno);
info!("AD9154-{} running STPL test...", dacno);
fn prng(seed: u32) -> u32 {
return ((seed + 1)*0x31415979 + 1) & 0xffff;
}
for i in 0..m {
let mut data: u32;
let mut errors: u8 = 0;
for j in 0..s {
/* select converter */
write(ad9154_reg::SHORT_TPL_TEST_0,
0b0*ad9154_reg::SHORT_TPL_TEST_EN |
0b0*ad9154_reg::SHORT_TPL_TEST_RESET |
i*ad9154_reg::SHORT_TPL_DAC_SEL |
j*ad9154_reg::SHORT_TPL_SP_SEL);
/* set expected value */
data = prng(((i as u32) << 8) | (j as u32));
write(ad9154_reg::SHORT_TPL_TEST_1, (data & 0x00ff) as u8);
write(ad9154_reg::SHORT_TPL_TEST_2, ((data & 0xff00) >> 8) as u8);
/* enable stpl */
write(ad9154_reg::SHORT_TPL_TEST_0,
0b1*ad9154_reg::SHORT_TPL_TEST_EN |
0b0*ad9154_reg::SHORT_TPL_TEST_RESET |
i*ad9154_reg::SHORT_TPL_DAC_SEL |
j*ad9154_reg::SHORT_TPL_SP_SEL);
/* reset stpl */
write(ad9154_reg::SHORT_TPL_TEST_0,
0b1*ad9154_reg::SHORT_TPL_TEST_EN |
0b1*ad9154_reg::SHORT_TPL_TEST_RESET |
i*ad9154_reg::SHORT_TPL_DAC_SEL |
j*ad9154_reg::SHORT_TPL_SP_SEL);
/* release reset stpl */
write(ad9154_reg::SHORT_TPL_TEST_0,
0b1*ad9154_reg::SHORT_TPL_TEST_EN |
0b0*ad9154_reg::SHORT_TPL_TEST_RESET |
i*ad9154_reg::SHORT_TPL_DAC_SEL |
j*ad9154_reg::SHORT_TPL_SP_SEL);
errors += read(ad9154_reg::SHORT_TPL_TEST_3);
}
info!(" c{} errors: {}", i, errors);
if errors > 0 {
return Err("STPL failed")
}
}
info!(" ...passed");
Ok(())
}
pub fn sync(dacno: u8) -> Result<bool, &'static str> {
spi_setup(dacno);
write(ad9154_reg::SYNC_CONTROL,
0x1*ad9154_reg::SYNCMODE | 1*ad9154_reg::SYNCENABLE |
1*ad9154_reg::SYNCARM | 1*ad9154_reg::SYNCCLRSTKY);
clock::spin_us(1000); // ensure at least one sysref edge
let sync_status = read(ad9154_reg::SYNC_STATUS);
if sync_status & ad9154_reg::SYNC_BUSY != 0 {
return Err("sync logic busy");
}
if sync_status & ad9154_reg::SYNC_LOCK == 0 {
return Err("no sync lock");
}
if sync_status & ad9154_reg::SYNC_TRIP == 0 {
return Err("no sysref edge");
}
let realign_occured = sync_status & ad9154_reg::SYNC_ROTATE != 0;
Ok(realign_occured)
}

View File

@ -1,826 +0,0 @@
#![allow(dead_code)]
pub const SPI_INTFCONFA : u16 = 0x000;
pub const SOFTRESET : u8 = 1 << 0;
pub const LSBFIRST : u8 = 1 << 1;
pub const ADDRINC : u8 = 1 << 2;
pub const SDOACTIVE : u8 = 1 << 3;
pub const SDOACTIVE_M : u8 = 1 << 4;
pub const ADDRINC_M : u8 = 1 << 5;
pub const LSBFIRST_M : u8 = 1 << 6;
pub const SOFTRESET_M : u8 = 1 << 7;
pub const CHIPTYPE : u16 = 0x003;
pub const PRODIDL : u16 = 0x004;
pub const PRODIDH : u16 = 0x005;
pub const CHIPGRADE : u16 = 0x006;
pub const DEV_REVISION : u8 = 1 << 0;
pub const PROD_GRADE : u8 = 1 << 4;
pub const SPI_PAGEINDX : u16 = 0x008;
pub const PWRCNTRL0 : u16 = 0x011;
pub const PD_DAC3 : u8 = 1 << 3;
pub const PD_DAC2 : u8 = 1 << 4;
pub const PD_DAC1 : u8 = 1 << 5;
pub const PD_DAC0 : u8 = 1 << 6;
pub const PD_BG : u8 = 1 << 7;
pub const TXENMASK1 : u16 = 0x012;
pub const DACA_MASK : u8 = 1 << 6;
pub const DACB_MASK : u8 = 1 << 7;
pub const PWRCNTRL3 : u16 = 0x013;
pub const SPI_TXEN : u8 = 1 << 0;
pub const ENA_SPI_TXEN : u8 = 1 << 1;
pub const SPI_PA_CTRL : u8 = 1 << 2;
pub const ENA_PA_CTRL_FROM_SPI : u8 = 1 << 3;
pub const ENA_PA_CTRL_FROM_BLSM : u8 = 1 << 4;
pub const ENA_PA_CTRL_FROM_TXENSM : u8 = 1 << 5;
pub const ENA_PA_CTRL_FROM_PARROT_ERR : u8 = 1 << 6;
pub const GROUP_DLY : u16 = 0x014;
pub const COARSE_GROUP_DELAY : u8 = 1 << 0;
pub const GROUP_DELAY_RESERVED : u8 = 1 << 4;
pub const IRQEN_STATUSMODE0 : u16 = 0x01f;
pub const IRQEN_SMODE_LANEFIFOERR : u8 = 1 << 1;
pub const IRQEN_SMODE_SERPLLLOCK : u8 = 1 << 2;
pub const IRQEN_SMODE_SERPLLLOST : u8 = 1 << 3;
pub const IRQEN_SMODE_DACPLLLOCK : u8 = 1 << 4;
pub const IRQEN_SMODE_DACPLLLOST : u8 = 1 << 5;
pub const IRQEN_STATUSMODE1 : u16 = 0x020;
pub const IRQEN_SMODE_PRBS0 : u8 = 1 << 0;
pub const IRQEN_SMODE_PRBS1 : u8 = 1 << 1;
pub const IRQEN_SMODE_PRBS2 : u8 = 1 << 2;
pub const IRQEN_SMODE_PRBS3 : u8 = 1 << 3;
pub const IRQEN_STATUSMODE2 : u16 = 0x021;
pub const IRQEN_SMODE_SYNC_TRIP0 : u8 = 1 << 0;
pub const IRQEN_SMODE_SYNC_WLIM0 : u8 = 1 << 1;
pub const IRQEN_SMODE_SYNC_ROTATE0 : u8 = 1 << 2;
pub const IRQEN_SMODE_SYNC_LOCK0 : u8 = 1 << 3;
pub const IRQEN_SMODE_NCO_ALIGN0 : u8 = 1 << 4;
pub const IRQEN_SMODE_BLNKDONE0 : u8 = 1 << 5;
pub const IRQEN_SMODE_PDPERR0 : u8 = 1 << 7;
pub const IRQEN_STATUSMODE3 : u16 = 0x022;
pub const IRQEN_SMODE_SYNC_TRIP1 : u8 = 1 << 0;
pub const IRQEN_SMODE_SYNC_WLIM1 : u8 = 1 << 1;
pub const IRQEN_SMODE_SYNC_ROTATE1 : u8 = 1 << 2;
pub const IRQEN_SMODE_SYNC_LOCK1 : u8 = 1 << 3;
pub const IRQEN_SMODE_NCO_ALIGN1 : u8 = 1 << 4;
pub const IRQEN_SMODE_BLNKDONE1 : u8 = 1 << 5;
pub const IRQEN_SMODE_PDPERR1 : u8 = 1 << 7;
pub const IRQ_STATUS0 : u16 = 0x023;
pub const LANEFIFOERR : u8 = 1 << 1;
pub const SERPLLLOCK : u8 = 1 << 2;
pub const SERPLLLOST : u8 = 1 << 3;
pub const DACPLLLOCK : u8 = 1 << 4;
pub const DACPLLLOST : u8 = 1 << 5;
pub const IRQ_STATUS1 : u16 = 0x024;
pub const PRBS0 : u8 = 1 << 0;
pub const PRBS1 : u8 = 1 << 1;
pub const PRBS2 : u8 = 1 << 2;
pub const PRBS3 : u8 = 1 << 3;
pub const IRQ_STATUS2 : u16 = 0x025;
pub const SYNC_TRIP0 : u8 = 1 << 0;
pub const SYNC_WLIM0 : u8 = 1 << 1;
pub const SYNC_ROTATE0 : u8 = 1 << 2;
pub const SYNC_LOCK0 : u8 = 1 << 3;
pub const NCO_ALIGN0 : u8 = 1 << 4;
pub const BLNKDONE0 : u8 = 1 << 5;
pub const PDPERR0 : u8 = 1 << 7;
pub const IRQ_STATUS3 : u16 = 0x026;
pub const SYNC_TRIP1 : u8 = 1 << 0;
pub const SYNC_WLIM1 : u8 = 1 << 1;
pub const SYNC_ROTATE1 : u8 = 1 << 2;
pub const SYNC_LOCK1 : u8 = 1 << 3;
pub const NCO_ALIGN1 : u8 = 1 << 4;
pub const BLNKDONE1 : u8 = 1 << 5;
pub const PDPERR1 : u8 = 1 << 7;
pub const JESD_CHECKS : u16 = 0x030;
pub const ERR_INTSUPP : u8 = 1 << 0;
pub const ERR_SUBCLASS : u8 = 1 << 1;
pub const ERR_KUNSUPP : u8 = 1 << 2;
pub const ERR_JESDBAD : u8 = 1 << 3;
pub const ERR_WINLIMIT : u8 = 1 << 4;
pub const ERR_DLYOVER : u8 = 1 << 5;
pub const SYNC_ERRWINDOW : u16 = 0x034;
pub const SYNC_LASTERR_L : u16 = 0x038;
pub const SYNC_LASTERR_H : u16 = 0x039;
pub const LASTERROR_H : u8 = 1 << 0;
pub const LASTOVER : u8 = 1 << 6;
pub const LASTUNDER : u8 = 1 << 7;
pub const SYNC_CONTROL : u16 = 0x03a;
pub const SYNCMODE : u8 = 1 << 0;
pub const SYNCCLRLAST : u8 = 1 << 4;
pub const SYNCCLRSTKY : u8 = 1 << 5;
pub const SYNCARM : u8 = 1 << 6;
pub const SYNCENABLE : u8 = 1 << 7;
pub const SYNC_STATUS : u16 = 0x03b;
pub const SYNC_TRIP : u8 = 1 << 0;
pub const SYNC_WLIM : u8 = 1 << 1;
pub const SYNC_ROTATE : u8 = 1 << 2;
pub const SYNC_LOCK : u8 = 1 << 3;
pub const SYNC_BUSY : u8 = 1 << 7;
pub const SYNC_CURRERR_L : u16 = 0x03c;
pub const SYNC_CURRERR_H : u16 = 0x03d;
pub const CURRERROR_H : u8 = 1 << 0;
pub const CURROVER : u8 = 1 << 6;
pub const CURRUNDER : u8 = 1 << 7;
pub const DACGAIN0_I : u16 = 0x040;
pub const DACGAIN1_I : u16 = 0x041;
pub const DACGAIN0_Q : u16 = 0x042;
pub const DACGAIN1_Q : u16 = 0x043;
pub const GROUPDELAY_COMP_I : u16 = 0x044;
pub const GROUPDELAY_COMP_Q : u16 = 0x045;
pub const GROUPDELAY_COMP_BYP : u16 = 0x046;
pub const GROUPCOMP_BYPQ : u8 = 1 << 0;
pub const GROUPCOMP_BYPI : u8 = 1 << 1;
pub const MIX_MODE : u16 = 0x04a;
pub const NCOALIGN_MODE : u16 = 0x050;
pub const NCO_ALIGN_MODE : u8 = 1 << 0;
pub const NCO_ALIGN_FAIL : u8 = 1 << 3;
pub const NCO_ALIGN_PASS : u8 = 1 << 4;
pub const NCO_ALIGN_MTCH : u8 = 1 << 5;
pub const NCO_ALIGN_ARM : u8 = 1 << 7;
pub const NCOKEY_ILSB : u16 = 0x051;
pub const NCOKEY_IMSB : u16 = 0x052;
pub const NCOKEY_QLSB : u16 = 0x053;
pub const NCOKEY_QMSB : u16 = 0x054;
pub const PDP_THRES0 : u16 = 0x060;
pub const PDP_THRES1 : u16 = 0x061;
pub const PDP_AVG_TIME : u16 = 0x062;
pub const PDP_AVG_TIME_ : u8 = 1 << 0;
pub const PA_BUS_SWAP : u8 = 1 << 6;
pub const PDP_ENABLE : u8 = 1 << 7;
pub const PDP_POWER0 : u16 = 0x063;
pub const PDP_POWER1 : u16 = 0x064;
pub const CLKCFG0 : u16 = 0x080;
pub const REF_CLKDIV_EN : u8 = 1 << 0;
pub const RF_SYNC_EN : u8 = 1 << 1;
pub const DUTY_EN : u8 = 1 << 2;
pub const PD_CLK_REC : u8 = 1 << 3;
pub const PD_SERDES_PCLK : u8 = 1 << 4;
pub const PD_CLK_DIG : u8 = 1 << 5;
pub const PD_CLK23 : u8 = 1 << 6;
pub const PD_CLK01 : u8 = 1 << 7;
pub const SYSREF_ACTRL0 : u16 = 0x081;
pub const HYS_CNTRL1 : u8 = 1 << 0;
pub const SYSREF_RISE : u8 = 1 << 2;
pub const HYS_ON : u8 = 1 << 3;
pub const PD_SYSREF_BUFFER : u8 = 1 << 4;
pub const SYSREF_ACTRL1 : u16 = 0x082;
pub const DACPLLCNTRL : u16 = 0x083;
pub const ENABLE_DACPLL : u8 = 1 << 4;
pub const RECAL_DACPLL : u8 = 1 << 7;
pub const DACPLLSTATUS : u16 = 0x084;
pub const DACPLL_LOCK : u8 = 1 << 1;
pub const VCO_CAL_PROGRESS : u8 = 1 << 3;
pub const CP_CAL_VALID : u8 = 1 << 4;
pub const CP_OVERRANGE_L : u8 = 1 << 5;
pub const CP_OVERRANGE_H : u8 = 1 << 6;
pub const DACINTEGERWORD0 : u16 = 0x085;
pub const DACLOOPFILT1 : u16 = 0x087;
pub const LF_C1_WORD : u8 = 1 << 0;
pub const LF_C2_WORD : u8 = 1 << 4;
pub const DACLOOPFILT2 : u16 = 0x088;
pub const LF_C3_WORD : u8 = 1 << 0;
pub const LF_R1_WORD : u8 = 1 << 4;
pub const DACLOOPFILT3 : u16 = 0x089;
pub const LF_R3_WORD : u8 = 1 << 0;
pub const LF_BYPASS_C1 : u8 = 1 << 4;
pub const LF_BYPASS_C2 : u8 = 1 << 5;
pub const LF_BYPASS_R1 : u8 = 1 << 6;
pub const LF_BYPASS_R3 : u8 = 1 << 7;
pub const DACCPCNTRL : u16 = 0x08a;
pub const CP_CURRENT : u8 = 1 << 0;
pub const VT_FORCE : u8 = 1 << 6;
pub const DACLOGENCNTRL : u16 = 0x08b;
pub const LODIVMODE : u8 = 1 << 0;
pub const LO_POWER_MODE : u8 = 1 << 4;
pub const DACLDOCNTRL1 : u16 = 0x08c;
pub const REFDIVMODE : u8 = 1 << 0;
pub const LDO_BYPASS_FLT : u8 = 1 << 6;
pub const LDO_REF_SEL : u8 = 1 << 7;
pub const DACLDOCNTRL2 : u16 = 0x08d;
pub const LDO_VDROP : u8 = 1 << 0;
pub const LDO_SEL : u8 = 1 << 2;
pub const LDO_INRUSH : u8 = 1 << 5;
pub const LDO_BYPASS : u8 = 1 << 7;
pub const DATA_FORMAT : u16 = 0x110;
pub const BINARY_FORMAT : u8 = 1 << 7;
pub const DATAPATH_CTRL : u16 = 0x111;
pub const I_TO_Q : u8 = 1 << 0;
pub const SEL_SIDEBAND : u8 = 1 << 1;
pub const MODULATION_TYPE : u8 = 1 << 2;
pub const PHASE_ADJ_ENABLE : u8 = 1 << 4;
pub const DIG_GAIN_ENABLE : u8 = 1 << 5;
pub const INVSINC_ENABLE : u8 = 1 << 7;
pub const INTERP_MODE : u16 = 0x112;
pub const NCO_FTW_UPDATE : u16 = 0x113;
pub const FTW_UPDATE_REQ : u8 = 1 << 0;
pub const FTW_UPDATE_ACK : u8 = 1 << 1;
pub const FTW0 : u16 = 0x114;
pub const FTW1 : u16 = 0x115;
pub const FTW2 : u16 = 0x116;
pub const FTW3 : u16 = 0x117;
pub const FTW4 : u16 = 0x118;
pub const FTW5 : u16 = 0x119;
pub const NCO_PHASE_OFFSET0 : u16 = 0x11a;
pub const NCO_PHASE_OFFSET1 : u16 = 0x11b;
pub const PHASE_ADJ0 : u16 = 0x11c;
pub const PHASE_ADJ1 : u16 = 0x11d;
pub const TXEN_SM_0 : u16 = 0x11f;
pub const TXEN_SM_EN : u8 = 1 << 0;
pub const GP_PA_CTRL : u8 = 1 << 1;
pub const GP_PA_ON_INVERT : u8 = 1 << 2;
pub const RISE_COUNTERS : u8 = 1 << 4;
pub const FALL_COUNTERS : u8 = 1 << 6;
pub const TXEN_RISE_COUNT_0 : u16 = 0x121;
pub const TXEN_RISE_COUNT_1 : u16 = 0x122;
pub const TXEN_FALL_COUNT_0 : u16 = 0x123;
pub const TXEN_FALL_COUNT_1 : u16 = 0x124;
pub const DEVICE_CONFIG_REG_0 : u16 = 0x12d;
pub const DIE_TEMP_CTRL0 : u16 = 0x12f;
pub const AUXADC_ENABLE : u8 = 1 << 0;
pub const AUXADC_RESERVED : u8 = 1 << 1;
pub const DIE_TEMP0 : u16 = 0x132;
pub const DIE_TEMP1 : u16 = 0x133;
pub const DIE_TEMP_UPDATE : u16 = 0x134;
pub const DC_OFFSET_CTRL : u16 = 0x135;
pub const IPATH_DC_OFFSET_1PART0 : u16 = 0x136;
pub const IPATH_DC_OFFSET_1PART1 : u16 = 0x137;
pub const QPATH_DC_OFFSET_1PART0 : u16 = 0x138;
pub const QPATH_DC_OFFSET_1PART1 : u16 = 0x139;
pub const IPATH_DC_OFFSET_2PART : u16 = 0x13a;
pub const QPATH_DC_OFFSET_2PART : u16 = 0x13b;
pub const IDAC_DIG_GAIN0 : u16 = 0x13c;
pub const IDAC_DIG_GAIN1 : u16 = 0x13d;
pub const QDAC_DIG_GAIN0 : u16 = 0x13e;
pub const QDAC_DIG_GAIN1 : u16 = 0x13f;
pub const GAIN_RAMP_UP_STEP0 : u16 = 0x140;
pub const GAIN_RAMP_UP_STEP1 : u16 = 0x141;
pub const GAIN_RAMP_DOWN_STEP0 : u16 = 0x142;
pub const GAIN_RAMP_DOWN_STEP1 : u16 = 0x143;
pub const DEVICE_CONFIG_REG_1 : u16 = 0x146;
pub const BSM_STAT : u16 = 0x147;
pub const SOFTBLANKRB : u8 = 1 << 6;
pub const PRBS : u16 = 0x14b;
pub const PRBS_EN : u8 = 1 << 0;
pub const PRBS_RESET : u8 = 1 << 1;
pub const PRBS_MODE : u8 = 1 << 2;
pub const PRBS_GOOD_I : u8 = 1 << 6;
pub const PRBS_GOOD_Q : u8 = 1 << 7;
pub const PRBS_ERROR_I : u16 = 0x14c;
pub const PRBS_ERROR_Q : u16 = 0x14d;
pub const DACPLLT0 : u16 = 0x1b0;
pub const LOGEN_PD : u8 = 1 << 1;
pub const LDO_PD : u8 = 1 << 3;
pub const SYNTH_PD : u8 = 1 << 4;
pub const VCO_PD_ALC : u8 = 1 << 5;
pub const VCO_PD_PTAT : u8 = 1 << 6;
pub const VCO_PD_IN : u8 = 1 << 7;
pub const DACPLLT1 : u16 = 0x1b1;
pub const PFD_EDGE : u8 = 1 << 1;
pub const PFD_DELAY : u8 = 1 << 2;
pub const DACPLLT2 : u16 = 0x1b2;
pub const EXT_ALC_WORD : u8 = 1 << 0;
pub const EXT_ALC_WORD_EN : u8 = 1 << 7;
pub const DACPLLT3 : u16 = 0x1b3;
pub const EXT_BAND1 : u8 = 1 << 0;
pub const DACPLLT4 : u16 = 0x1b4;
pub const EXT_BAND2 : u8 = 1 << 0;
pub const EXT_BAND_EN : u8 = 1 << 1;
pub const VCO_CAL_OFFSET : u8 = 1 << 3;
pub const BYP_LOAD_DELAY : u8 = 1 << 7;
pub const DACPLLT5 : u16 = 0x1b5;
pub const DACPLLT6 : u16 = 0x1b6;
pub const DACPLLT7 : u16 = 0x1b7;
pub const DACPLLT8 : u16 = 0x1b8;
pub const DACPLLT9 : u16 = 0x1b9;
pub const DACPLLTA : u16 = 0x1ba;
pub const DACPLLTB : u16 = 0x1bb;
pub const VCO_BIAS_REF : u8 = 1 << 0;
pub const VCO_BIAS_TCF : u8 = 1 << 3;
pub const DACPLLTC : u16 = 0x1bc;
pub const DACPLLTD : u16 = 0x1bd;
pub const DACPLLTE : u16 = 0x1be;
pub const DACPLLTF : u16 = 0x1bf;
pub const DACPLLT10 : u16 = 0x1c0;
pub const DACPLLT11 : u16 = 0x1c1;
pub const DACPLLT15 : u16 = 0x1c2;
pub const DACPLLT16 : u16 = 0x1c3;
pub const DACPLLT17 : u16 = 0x1c4;
pub const DACPLLT18 : u16 = 0x1c5;
pub const MASTER_PD : u16 = 0x200;
pub const PHY_PD : u16 = 0x201;
pub const GENERIC_PD : u16 = 0x203;
pub const PD_SYNCOUT1B : u8 = 1 << 0;
pub const PD_SYNCOUT0B : u8 = 1 << 1;
pub const CDR_RESET : u16 = 0x206;
pub const CDR_OPERATING_MODE_REG_0 : u16 = 0x230;
pub const CDR_OVERSAMP : u8 = 1 << 1;
pub const CDR_RESERVED : u8 = 1 << 2;
pub const ENHALFRATE : u8 = 1 << 5;
pub const EQ_BIAS_REG : u16 = 0x268;
pub const EQ_BIAS_RESERVED : u8 = 1 << 0;
pub const EQ_POWER_MODE : u8 = 1 << 6;
pub const SERDESPLL_ENABLE_CNTRL : u16 = 0x280;
pub const ENABLE_SERDESPLL : u8 = 1 << 0;
pub const RECAL_SERDESPLL : u8 = 1 << 2;
pub const PLL_STATUS : u16 = 0x281;
pub const SERDES_PLL_LOCK_RB : u8 = 1 << 0;
pub const SERDES_CURRENTS_READY_RB : u8 = 1 << 1;
pub const SERDES_VCO_CAL_IN_PROGRESS_RB : u8 = 1 << 2;
pub const SERDES_PLL_CAL_VALID_RB : u8 = 1 << 3;
pub const SERDES_PLL_OVERRANGE_L_RB : u8 = 1 << 4;
pub const SERDES_PLL_OVERRANGE_H_RB : u8 = 1 << 5;
pub const LDO_FILTER_1 : u16 = 0x284;
pub const LDO_FILTER_2 : u16 = 0x285;
pub const LDO_FILTER_3 : u16 = 0x286;
pub const CP_CURRENT_SPI : u16 = 0x287;
pub const SPI_CP_CURRENT : u8 = 1 << 0;
pub const SPI_SERDES_LOGEN_POWER_MODE : u8 = 1 << 6;
pub const REF_CLK_DIVIDER_LDO : u16 = 0x289;
pub const SPI_CDR_OVERSAMP : u8 = 1 << 0;
pub const SPI_LDO_BYPASS_FILT : u8 = 1 << 2;
pub const SPI_LDO_REF_SEL : u8 = 1 << 3;
pub const VCO_LDO : u16 = 0x28a;
pub const PLL_RD_REG : u16 = 0x28b;
pub const SPI_SERDES_LOGEN_PD_CORE : u8 = 1 << 0;
pub const SPI_SERDES_LDO_PD : u8 = 1 << 2;
pub const SPI_SYN_PD : u8 = 1 << 3;
pub const SPI_VCO_PD_ALC : u8 = 1 << 4;
pub const SPI_VCO_PD_PTAT : u8 = 1 << 5;
pub const SPI_VCO_PD : u8 = 1 << 6;
pub const ALC_VARACTOR : u16 = 0x290;
pub const SPI_VCO_VARACTOR : u8 = 1 << 0;
pub const SPI_INIT_ALC_VALUE : u8 = 1 << 4;
pub const VCO_OUTPUT : u16 = 0x291;
pub const SPI_VCO_OUTPUT_LEVEL : u8 = 1 << 0;
pub const SPI_VCO_OUTPUT_RESERVED : u8 = 1 << 4;
pub const CP_CONFIG : u16 = 0x294;
pub const SPI_CP_TEST : u8 = 1 << 0;
pub const SPI_CP_CAL_EN : u8 = 1 << 2;
pub const SPI_CP_FORCE_CALBITS : u8 = 1 << 3;
pub const SPI_CP_OFFSET_OFF : u8 = 1 << 4;
pub const SPI_CP_ENABLE_MACHINE : u8 = 1 << 5;
pub const SPI_CP_DITHER_MODE : u8 = 1 << 6;
pub const SPI_CP_HALF_VCO_CAL_CLK : u8 = 1 << 7;
pub const VCO_BIAS_1 : u16 = 0x296;
pub const SPI_VCO_BIAS_REF : u8 = 1 << 0;
pub const SPI_VCO_BIAS_TCF : u8 = 1 << 3;
pub const VCO_BIAS_2 : u16 = 0x297;
pub const SPI_PRESCALE_BIAS : u8 = 1 << 0;
pub const SPI_LAST_ALC_EN : u8 = 1 << 2;
pub const SPI_PRESCALE_BYPASS_R : u8 = 1 << 3;
pub const SPI_VCO_COMP_BYPASS_BIASR : u8 = 1 << 4;
pub const SPI_VCO_BYPASS_DAC_R : u8 = 1 << 5;
pub const VCO_PD_OVERRIDES : u16 = 0x299;
pub const SPI_VCO_PD_OVERRIDE_VCO_BUF : u8 = 1 << 0;
pub const SPI_VCO_PD_OVERRIDE_CAL_TCF : u8 = 1 << 1;
pub const SPI_VCO_PD_OVERRIDE_VAR_REF_TCF : u8 = 1 << 2;
pub const SPI_VCO_PD_OVERRIDE_VAR_REF : u8 = 1 << 3;
pub const VCO_CAL : u16 = 0x29a;
pub const SPI_FB_CLOCK_ADV : u8 = 1 << 0;
pub const SPI_VCO_CAL_COUNT : u8 = 1 << 2;
pub const SPI_VCO_CAL_ALC_WAIT : u8 = 1 << 4;
pub const SPI_VCO_CAL_EN : u8 = 1 << 7;
pub const CP_LEVEL_DETECT : u16 = 0x29c;
pub const SPI_CP_LEVEL_THRESHOLD_HIGH : u8 = 1 << 0;
pub const SPI_CP_LEVEL_THRESHOLD_LOW : u8 = 1 << 3;
pub const SPI_CP_LEVEL_DET_PD : u8 = 1 << 6;
pub const VCO_VARACTOR_CTRL_0 : u16 = 0x29f;
pub const SPI_VCO_VARACTOR_OFFSET : u8 = 1 << 0;
pub const SPI_VCO_VARACTOR_REF_TCF : u8 = 1 << 4;
pub const VCO_VARACTOR_CTRL_1 : u16 = 0x2a0;
pub const SPI_VCO_VARACTOR_REF : u8 = 1 << 0;
pub const TERM_BLK1_CTRLREG0 : u16 = 0x2a7;
pub const TERM_BLK2_CTRLREG0 : u16 = 0x2ae;
pub const GENERAL_JRX_CTRL_0 : u16 = 0x300;
pub const LINK_EN : u8 = 1 << 0;
pub const LINK_PAGE : u8 = 1 << 2;
pub const LINK_MODE : u8 = 1 << 3;
pub const CHECKSUM_MODE : u8 = 1 << 6;
pub const GENERAL_JRX_CTRL_1 : u16 = 0x301;
pub const DYN_LINK_LATENCY_0 : u16 = 0x302;
pub const DYN_LINK_LATENCY_1 : u16 = 0x303;
pub const LMFC_DELAY_0 : u16 = 0x304;
pub const LMFC_DELAY_1 : u16 = 0x305;
pub const LMFC_VAR_0 : u16 = 0x306;
pub const LMFC_VAR_1 : u16 = 0x307;
pub const XBAR_LN_0_1 : u16 = 0x308;
pub const LOGICAL_LANE0_SRC : u8 = 1 << 0;
pub const LOGICAL_LANE1_SRC : u8 = 1 << 3;
pub const XBAR_LN_2_3 : u16 = 0x309;
pub const LOGICAL_LANE2_SRC : u8 = 1 << 0;
pub const LOGICAL_LANE3_SRC : u8 = 1 << 3;
pub const XBAR_LN_4_5 : u16 = 0x30a;
pub const LOGICAL_LANE4_SRC : u8 = 1 << 0;
pub const LOGICAL_LANE5_SRC : u8 = 1 << 3;
pub const XBAR_LN_6_7 : u16 = 0x30b;
pub const LOGICAL_LANE6_SRC : u8 = 1 << 0;
pub const LOGICAL_LANE7_SRC : u8 = 1 << 3;
pub const FIFO_STATUS_REG_0 : u16 = 0x30c;
pub const FIFO_STATUS_REG_1 : u16 = 0x30d;
pub const SYNCB_GEN_1 : u16 = 0x312;
pub const SYNCB_ERR_DUR : u8 = 1 << 4;
pub const SERDES_SPI_REG : u16 = 0x314;
pub const PHY_PRBS_TEST_EN : u16 = 0x315;
pub const PHY_PRBS_TEST_CTRL : u16 = 0x316;
pub const PHY_TEST_RESET : u8 = 1 << 0;
pub const PHY_TEST_START : u8 = 1 << 1;
pub const PHY_PRBS_PAT_SEL : u8 = 1 << 2;
pub const PHY_SRC_ERR_CNT : u8 = 1 << 4;
pub const PHY_PRBS_TEST_THRESHOLD_LOBITS : u16 = 0x317;
pub const PHY_PRBS_TEST_THRESHOLD_MIDBITS : u16 = 0x318;
pub const PHY_PRBS_TEST_THRESHOLD_HIBITS : u16 = 0x319;
pub const PHY_PRBS_TEST_ERRCNT_LOBITS : u16 = 0x31a;
pub const PHY_PRBS_TEST_ERRCNT_MIDBITS : u16 = 0x31b;
pub const PHY_PRBS_TEST_ERRCNT_HIBITS : u16 = 0x31c;
pub const PHY_PRBS_TEST_STATUS : u16 = 0x31d;
pub const SHORT_TPL_TEST_0 : u16 = 0x32c;
pub const SHORT_TPL_TEST_EN : u8 = 1 << 0;
pub const SHORT_TPL_TEST_RESET : u8 = 1 << 1;
pub const SHORT_TPL_DAC_SEL : u8 = 1 << 2;
pub const SHORT_TPL_SP_SEL : u8 = 1 << 4;
pub const SHORT_TPL_TEST_1 : u16 = 0x32d;
pub const SHORT_TPL_TEST_2 : u16 = 0x32e;
pub const SHORT_TPL_TEST_3 : u16 = 0x32f;
pub const DEVICE_CONFIG_REG_2 : u16 = 0x333;
pub const JESD_BIT_INVERSE_CTRL : u16 = 0x334;
pub const DID_REG : u16 = 0x400;
pub const BID_REG : u16 = 0x401;
pub const BID_RD : u8 = 1 << 0;
pub const ADJCNT_RD : u8 = 1 << 4;
pub const LID0_REG : u16 = 0x402;
pub const LID0_RD : u8 = 1 << 0;
pub const PHADJ_RD : u8 = 1 << 5;
pub const ADJDIR_RD : u8 = 1 << 6;
pub const SCR_L_REG : u16 = 0x403;
pub const L_1_RD : u8 = 1 << 0;
pub const SCR_RD : u8 = 1 << 7;
pub const F_REG : u16 = 0x404;
pub const K_REG : u16 = 0x405;
pub const M_REG : u16 = 0x406;
pub const CS_N_REG : u16 = 0x407;
pub const N_1_RD : u8 = 1 << 0;
pub const CS_RD : u8 = 1 << 6;
pub const NP_REG : u16 = 0x408;
pub const NP_1_RD : u8 = 1 << 0;
pub const SUBCLASSV_RD : u8 = 1 << 5;
pub const S_REG : u16 = 0x409;
pub const S_1_RD : u8 = 1 << 0;
pub const JESDV_RD : u8 = 1 << 5;
pub const HD_CF_REG : u16 = 0x40a;
pub const CF_RD : u8 = 1 << 0;
pub const HD_RD : u8 = 1 << 7;
pub const RES1_REG : u16 = 0x40b;
pub const RES2_REG : u16 = 0x40c;
pub const CHECKSUM0_REG : u16 = 0x40d;
pub const COMPSUM0_REG : u16 = 0x40e;
pub const LID1_REG : u16 = 0x412;
pub const CHECKSUM1_REG : u16 = 0x415;
pub const COMPSUM1_REG : u16 = 0x416;
pub const LID2_REG : u16 = 0x41a;
pub const CHECKSUM2_REG : u16 = 0x41d;
pub const COMPSUM2_REG : u16 = 0x41e;
pub const LID3_REG : u16 = 0x422;
pub const CHECKSUM3_REG : u16 = 0x425;
pub const COMPSUM3_REG : u16 = 0x426;
pub const LID4_REG : u16 = 0x42a;
pub const CHECKSUM4_REG : u16 = 0x42d;
pub const COMPSUM4_REG : u16 = 0x42e;
pub const LID5_REG : u16 = 0x432;
pub const CHECKSUM5_REG : u16 = 0x435;
pub const COMPSUM5_REG : u16 = 0x436;
pub const LID6_REG : u16 = 0x43a;
pub const CHECKSUM6_REG : u16 = 0x43d;
pub const COMPSUM6_REG : u16 = 0x43e;
pub const LID7_REG : u16 = 0x442;
pub const CHECKSUM7_REG : u16 = 0x445;
pub const COMPSUM7_REG : u16 = 0x446;
pub const ILS_DID : u16 = 0x450;
pub const ILS_BID : u16 = 0x451;
pub const BID : u8 = 1 << 0;
pub const ADJCNT : u8 = 1 << 4;
pub const ILS_LID0 : u16 = 0x452;
pub const LID0 : u8 = 1 << 0;
pub const PHADJ : u8 = 1 << 5;
pub const ADJDIR : u8 = 1 << 6;
pub const ILS_SCR_L : u16 = 0x453;
pub const L_1 : u8 = 1 << 0;
pub const SCR : u8 = 1 << 7;
pub const ILS_F : u16 = 0x454;
pub const ILS_K : u16 = 0x455;
pub const ILS_M : u16 = 0x456;
pub const ILS_CS_N : u16 = 0x457;
pub const N_1 : u8 = 1 << 0;
pub const CS : u8 = 1 << 6;
pub const ILS_NP : u16 = 0x458;
pub const NP_1 : u8 = 1 << 0;
pub const SUBCLASSV : u8 = 1 << 5;
pub const ILS_S : u16 = 0x459;
pub const S_1 : u8 = 1 << 0;
pub const JESDV : u8 = 1 << 5;
pub const ILS_HD_CF : u16 = 0x45a;
pub const CF : u8 = 1 << 0;
pub const HD : u8 = 1 << 7;
pub const ILS_RES1 : u16 = 0x45b;
pub const ILS_RES2 : u16 = 0x45c;
pub const ILS_CHECKSUM : u16 = 0x45d;
pub const ERRCNTRMON : u16 = 0x46b;
pub const CNTRSEL : u8 = 1 << 0;
pub const LANESEL : u8 = 1 << 4;
pub const LANEDESKEW : u16 = 0x46c;
pub const BADDISPARITY : u16 = 0x46d;
pub const LANE_ADDR_DIS : u8 = 1 << 0;
pub const RST_ERR_CNTR_DIS : u8 = 1 << 5;
pub const DISABLE_ERR_CNTR_DIS : u8 = 1 << 6;
pub const RST_IRQ_DIS : u8 = 1 << 7;
pub const NIT_W : u16 = 0x46e;
pub const LANE_ADDR_NIT : u8 = 1 << 0;
pub const RST_ERR_CNTR_NIT : u8 = 1 << 5;
pub const DISABLE_ERR_CNTR_NIT : u8 = 1 << 6;
pub const RST_IRQ_NIT : u8 = 1 << 7;
pub const UNEXPECTEDCONTROL_W : u16 = 0x46f;
pub const LANE_ADDR_UCC : u8 = 1 << 0;
pub const RST_ERR_CNTR_UCC : u8 = 1 << 5;
pub const DISABLE_ERR_CNTR_UCC : u8 = 1 << 6;
pub const RST_IRQ_UCC : u8 = 1 << 7;
pub const CODEGRPSYNCFLG : u16 = 0x470;
pub const FRAMESYNCFLG : u16 = 0x471;
pub const GOODCHKSUMFLG : u16 = 0x472;
pub const INITLANESYNCFLG : u16 = 0x473;
pub const CTRLREG1 : u16 = 0x476;
pub const CTRLREG2 : u16 = 0x477;
pub const THRESHOLD_MASK_EN : u8 = 1 << 3;
pub const ILAS_MODE : u8 = 1 << 7;
pub const KVAL : u16 = 0x478;
pub const IRQVECTOR_MASK : u16 = 0x47a;
pub const CODEGRPSYNC_MASK : u8 = 1 << 0;
pub const BADCHECKSUM_MASK : u8 = 1 << 2;
pub const INITIALLANESYNC_MASK : u8 = 1 << 3;
pub const UCC_MASK : u8 = 1 << 5;
pub const NIT_MASK : u8 = 1 << 6;
pub const BADDIS_MASK : u8 = 1 << 7;
pub const SYNCASSERTIONMASK : u16 = 0x47b;
pub const CMM_ENABLE : u8 = 1 << 3;
pub const CMM : u8 = 1 << 4;
pub const UCC_S : u8 = 1 << 5;
pub const NIT_S : u8 = 1 << 6;
pub const BADDIS_S : u8 = 1 << 7;
pub const ERRORTHRES : u16 = 0x47c;
pub const LANEENABLE : u16 = 0x47d;
pub const RAMP_ENA : u16 = 0x47e;
pub const DIG_TEST0 : u16 = 0x520;
pub const DC_TEST_MODE : u8 = 1 << 1;
pub const DC_TEST_VALUEI0 : u16 = 0x521;
pub const DC_TEST_VALUEI1 : u16 = 0x522;
pub const DC_TEST_VALUEQ0 : u16 = 0x523;
pub const DC_TEST_VALUEQ1 : u16 = 0x524;

View File

@ -1,419 +0,0 @@
mod hmc830 {
use board_misoc::{csr, clock};
fn spi_setup() {
unsafe {
while csr::converter_spi::idle_read() == 0 {}
csr::converter_spi::offline_write(0);
csr::converter_spi::end_write(1);
csr::converter_spi::cs_polarity_write(0b0001);
csr::converter_spi::clk_polarity_write(0);
csr::converter_spi::clk_phase_write(0);
csr::converter_spi::lsb_first_write(0);
csr::converter_spi::half_duplex_write(0);
csr::converter_spi::length_write(32 - 1);
csr::converter_spi::div_write(16 - 2);
csr::converter_spi::cs_write(1 << csr::CONFIG_CONVERTER_SPI_HMC830_CS);
}
}
pub fn select_spi_mode() {
spi_setup();
unsafe {
// rising egde on CS since cs_polarity still 0
// selects "HMC Mode"
// do a dummy cycle with cs still high to clear CS
csr::converter_spi::length_write(0);
csr::converter_spi::data_write(0);
while csr::converter_spi::writable_read() == 0 {}
csr::converter_spi::length_write(32 - 1);
}
}
fn write(addr: u8, data: u32) {
let val = ((addr as u32) << 24) | data;
unsafe {
while csr::converter_spi::writable_read() == 0 {}
csr::converter_spi::data_write(val << 1); // last clk cycle loads data
}
}
fn read(addr: u8) -> u32 {
// SDO (miso/read bits) is technically CPHA=1, while SDI is CPHA=0
// trust that the 8.2ns+0.2ns/pF provide enough hold time on top of
// the SPI round trip delay and stick with CPHA=0
write((1 << 6) | addr, 0);
unsafe {
while csr::converter_spi::writable_read() == 0 {}
csr::converter_spi::data_read() & 0xffffff
}
}
pub fn detect() -> Result<(), &'static str> {
spi_setup();
let id = read(0x00);
if id != 0xa7975 {
error!("invalid HMC830 ID: 0x{:08x}", id);
return Err("invalid HMC830 identification");
}
Ok(())
}
pub fn init() {
// Configure HMC830 for integer-N operation
// See "PLLs with integrated VCO- RF Applications Product & Operating
// Guide"
spi_setup();
info!("loading HMC830 configuration...");
write(0x0, 0x20); // software reset
write(0x0, 0x00); // normal operation
write(0x6, 0x307ca); // integer-N mode (NB data sheet table 5.8 not self-consistent)
write(0x7, 0x4d); // digital lock detect, 1/2 cycle window (6.5ns window)
write(0x9, 0x2850); // charge pump: 1.6mA, no offset
write(0xa, 0x2045); // for wideband devices like the HMC830
write(0xb, 0x7c061); // for HMC830
// VCO subsystem registers
// NB software reset does not seem to reset these registers, so always
// program them all!
write(0x5, 0xf88); // 1: defaults
write(0x5, 0x6010); // 2: mute output until output divider set
write(0x5, 0x2818); // 3: wideband PLL defaults
write(0x5, 0x60a0); // 4: HMC830 magic value
write(0x5, 0x1628); // 5: HMC830 magic value
write(0x5, 0x7fb0); // 6: HMC830 magic value
write(0x5, 0x0); // ready for VCO auto-cal
info!(" ...done");
}
pub fn set_dividers(r_div: u32, n_div: u32, m_div: u32, out_div: u32) {
// VCO frequency: f_vco = (f_ref/r_div)*(n_int + n_frac/2**24)
// VCO frequency range [1.5GHz, 3GHz]
// Output frequency: f_out = f_vco/out_div
// Max PFD frequency: 125MHz for integer-N, 100MHz for fractional
// (mode B)
// Max reference frequency: 350MHz, however f_ref >= 200MHz requires
// setting 0x08[21]=1
//
// Warning: Output divider is not synchronized! Set to 1 for deterministic
// phase at the output.
//
// :param r_div: reference divider [1, 16383]
// :param n_div: VCO divider, integer part. Integer-N mode: [16, 2**19-1]
// fractional mode: [20, 2**19-4]
// :param m_div: VCO divider, fractional part [0, 2**24-1]
// :param out_div: output divider [1, 62] (0 mutes output)
info!("setting HMC830 dividers...");
write(0x5, 0x6010 + (out_div << 7) + (((out_div <= 2) as u32) << 15));
write(0x5, 0x0); // ready for VCO auto-cal
write(0x2, r_div);
write(0x4, m_div);
write(0x3, n_div);
info!(" ...done");
}
pub fn check_locked() -> Result<(), &'static str> {
info!("waiting for HMC830 lock...");
let t = clock::get_ms();
while read(0x12) & 0x02 == 0 {
if clock::get_ms() > t + 2000 {
error!("lock timeout. Register dump:");
for addr in 0x00..0x14 {
// These registers don't exist (in the data sheet at least)
if addr == 0x0d || addr == 0x0e { continue; }
error!(" [0x{:02x}] = 0x{:04x}", addr, read(addr));
}
return Err("lock timeout");
}
}
info!(" ...locked");
Ok(())
}
}
pub mod hmc7043 {
use board_misoc::{csr, clock};
// Warning: dividers are not synchronized with HMC830 clock input!
// Set DAC_CLK_DIV to 1 or 0 for deterministic phase.
// (0 bypasses the divider and reduces noise)
const DAC_CLK_DIV: u16 = 0;
const FPGA_CLK_DIV: u16 = 16; // Keep in sync with jdcg.rs
const SYSREF_DIV: u16 = 256; // Keep in sync with jdcg.rs
const HMC_SYSREF_DIV: u16 = SYSREF_DIV*8; // must be <= 4MHz
// enabled, divider, output config, is sysref
const OUTPUT_CONFIG: [(bool, u16, u8, bool); 14] = [
(true, DAC_CLK_DIV, 0x08, false), // 0: DAC1_CLK
(true, SYSREF_DIV, 0x01, true), // 1: DAC1_SYSREF
(true, DAC_CLK_DIV, 0x08, false), // 2: DAC0_CLK
(true, SYSREF_DIV, 0x01, true), // 3: DAC0_SYSREF
(true, SYSREF_DIV, 0x10, true), // 4: AMC_FPGA_SYSREF0
(false, FPGA_CLK_DIV, 0x10, true), // 5: AMC_FPGA_SYSREF1
(false, 0, 0x10, false), // 6: unused
(true, FPGA_CLK_DIV, 0x10, true), // 7: RTM_FPGA_SYSREF0
(true, FPGA_CLK_DIV, 0x08, false), // 8: GTP_CLK0_IN
(false, 0, 0x10, false), // 9: unused
(false, 0, 0x10, false), // 10: unused
(false, 0, 0x08, false), // 11: unused / uFL
(false, 0, 0x10, false), // 12: unused
(false, FPGA_CLK_DIV, 0x10, true), // 13: RTM_FPGA_SYSREF1
];
fn spi_setup() {
unsafe {
while csr::converter_spi::idle_read() == 0 {}
csr::converter_spi::offline_write(0);
csr::converter_spi::end_write(1);
csr::converter_spi::cs_polarity_write(0b0001);
csr::converter_spi::clk_polarity_write(0);
csr::converter_spi::clk_phase_write(0);
csr::converter_spi::lsb_first_write(0);
csr::converter_spi::half_duplex_write(0); // change mid-transaction for reads
csr::converter_spi::length_write(24 - 1);
csr::converter_spi::div_write(16 - 2);
csr::converter_spi::cs_write(1 << csr::CONFIG_CONVERTER_SPI_HMC7043_CS);
}
}
fn spi_wait_idle() {
unsafe {
while csr::converter_spi::idle_read() == 0 {}
}
}
fn write(addr: u16, data: u8) {
let cmd = (0 << 15) | addr;
let val = ((cmd as u32) << 8) | data as u32;
unsafe {
while csr::converter_spi::writable_read() == 0 {}
csr::converter_spi::data_write(val << 8);
}
}
fn read(addr: u16) -> u8 {
let cmd = (1 << 15) | addr;
let val = cmd as u32;
unsafe {
while csr::converter_spi::writable_read() == 0 {}
csr::converter_spi::end_write(0);
csr::converter_spi::length_write(16 - 1);
csr::converter_spi::data_write(val << 16);
while csr::converter_spi::writable_read() == 0 {}
csr::converter_spi::end_write(1);
csr::converter_spi::half_duplex_write(1);
csr::converter_spi::length_write(8 - 1);
csr::converter_spi::data_write(0);
while csr::converter_spi::writable_read() == 0 {}
csr::converter_spi::half_duplex_write(0);
csr::converter_spi::length_write(24 - 1);
csr::converter_spi::data_read() as u8
}
}
pub const CHIP_ID: u32 = 0xf17904;
pub fn get_id() -> u32 {
spi_setup();
(read(0x78) as u32) << 16 | (read(0x79) as u32) << 8 | read(0x7a) as u32
}
pub fn detect() -> Result<(), &'static str> {
let id = get_id();
if id != CHIP_ID {
error!("invalid HMC7043 ID: 0x{:08x}", id);
return Err("invalid HMC7043 identification");
}
Ok(())
}
pub fn enable() {
info!("enabling HMC7043");
unsafe {
csr::hmc7043_reset::out_write(0);
}
clock::spin_us(10_000);
spi_setup();
write(0x0, 0x1); // Software reset
write(0x0, 0x0); // Normal operation
write(0x1, 0x48); // mute all outputs
}
const GPO_MUX_CLK_OUT_PHASE: u8 = 3;
const GPO_MUX_FORCE1: u8 = 10;
const GPO_MUX_FORCE0: u8 = 11;
/* Read an HMC7043 internal status bit through the GPO interface.
* This method is required to work around bugs in the register interface.
*/
fn gpo_indirect_read(mux_setting: u8) -> bool {
write(0x50, (mux_setting << 2) | 0x3);
spi_wait_idle();
unsafe {
csr::hmc7043_gpo::in_read() == 1
}
}
pub fn init() {
spi_setup();
info!("loading configuration...");
write(0x3, 0x14); // Disable the RFSYNCIN reseeder
write(0xA, 0x06); // Disable the RFSYNCIN input buffer
write(0xB, 0x07); // Enable the CLKIN input as LVPECL
write(0x9F, 0x4d); // Unexplained high-performance mode
write(0xA0, 0xdf); // Unexplained high-performance mode
// Enable required output groups
let mut output_group_en = 0;
for channel in 0..OUTPUT_CONFIG.len() {
let enabled = OUTPUT_CONFIG[channel].0;
if enabled {
let group = channel/2;
output_group_en |= 1 << group;
}
}
write(0x4, output_group_en);
// Set SYSREF timer divider.
// We don't need this "feature", but the HMC7043 won't work without.
write(0x5c, (HMC_SYSREF_DIV & 0xff) as u8);
write(0x5d, ((HMC_SYSREF_DIV & 0xf00) >> 8) as u8);
for channel in 0..OUTPUT_CONFIG.len() {
let channel_base = 0xc8 + 0x0a*(channel as u16);
let (enabled, divider, outcfg, is_sysref) = OUTPUT_CONFIG[channel];
if enabled {
if !is_sysref {
// DCLK channel: enable high-performance mode
write(channel_base, 0xd1);
} else {
// SYSREF channel: disable hi-perf mode, enable slip
write(channel_base, 0x71);
}
} else {
write(channel_base, 0x10);
}
write(channel_base + 0x1, (divider & 0xff) as u8);
write(channel_base + 0x2, ((divider & 0xf00) >> 8) as u8);
// bypass analog phase shift on DCLK channels to reduce noise
if !is_sysref {
if divider != 0 {
write(channel_base + 0x7, 0x00); // enable divider
} else {
write(channel_base + 0x7, 0x03); // bypass divider for lowest noise
}
} else {
write(channel_base + 0x7, 0x01);
}
write(channel_base + 0x8, outcfg)
}
write(0x1, 0x4a); // Reset dividers and FSMs
write(0x1, 0x48);
write(0x1, 0xc8); // Synchronize dividers
write(0x1, 0x40); // Unmute, high-performance/low-noise mode
clock::spin_us(10_000);
info!(" ...done");
}
pub fn test_gpo() -> Result<(), &'static str> {
info!("testing GPO...");
for trial in 0..10 {
if !gpo_indirect_read(GPO_MUX_FORCE1) {
info!(" ...failed. GPO I/O did not go high (#{})", trial + 1);
return Err("GPO is not functioning");
}
if gpo_indirect_read(GPO_MUX_FORCE0) {
info!(" ...failed. GPO I/O did not return low (#{})", trial + 1);
return Err("GPO is not functioning");
}
}
info!(" ...passed");
Ok(())
}
pub fn check_phased() -> Result<(), &'static str> {
if !gpo_indirect_read(GPO_MUX_CLK_OUT_PHASE) {
return Err("GPO reported phases did not align");
}
// Should be the same as the GPO read
let sysref_fsm_status = read(0x91);
if sysref_fsm_status != 0x2 {
error!("Bad SYSREF FSM status: {:02x}", sysref_fsm_status);
return Err("Bad SYSREF FSM status");
}
Ok(())
}
pub fn unmute() {
/*
* Never missing an opportunity to be awful, the HMC7043 produces broadband noise
* prior to intialization, which can upset the AMC FPGA.
* External circuitry mutes it.
*/
unsafe {
csr::hmc7043_out_en::out_write(1);
}
}
pub fn sysref_delay_dac(dacno: u8, phase_offset: u8) {
spi_setup();
if dacno == 0 {
write(0x00e9, phase_offset);
} else if dacno == 1 {
write(0x00d5, phase_offset);
} else {
unimplemented!();
}
clock::spin_us(100);
}
pub fn sysref_slip() {
spi_setup();
write(0x0002, 0x02);
write(0x0002, 0x00);
clock::spin_us(100);
}
}
pub fn init() -> Result<(), &'static str> {
#[cfg(all(hmc830_ref = "125", rtio_frequency = "125.0"))]
const DIV: (u32, u32, u32, u32) = (2, 32, 0, 1); // 125MHz -> 2.0GHz
#[cfg(all(hmc830_ref = "150", rtio_frequency = "150.0"))]
const DIV: (u32, u32, u32, u32) = (2, 32, 0, 1); // 150MHz -> 2.4GHz
/* do not use other SPI devices before HMC830 SPI mode selection */
hmc830::select_spi_mode();
hmc830::detect()?;
hmc830::init();
hmc830::set_dividers(DIV.0, DIV.1, DIV.2, DIV.3);
hmc830::check_locked()?;
if hmc7043::get_id() == hmc7043::CHIP_ID {
error!("HMC7043 detected while in reset (board rework missing?)");
}
hmc7043::enable();
hmc7043::detect()?;
hmc7043::init();
hmc7043::test_gpo()?;
hmc7043::check_phased()?;
hmc7043::unmute();
Ok(())
}

View File

@ -22,15 +22,6 @@ pub mod rpc_queue;
#[cfg(has_si5324)]
pub mod si5324;
#[cfg(has_wrpll)]
pub mod wrpll;
#[cfg(has_hmc830_7043)]
pub mod hmc830_7043;
#[cfg(has_ad9154)]
mod ad9154_reg;
#[cfg(has_ad9154)]
pub mod ad9154;
#[cfg(has_grabber)]
pub mod grabber;

View File

@ -182,12 +182,6 @@ fn init() -> Result<()> {
i2c::switch_select(BUSNO, 0x70, 0)?;
i2c::switch_select(BUSNO, 0x71, 1 << 3)?;
}
#[cfg(soc_platform = "sayma_amc")]
i2c::switch_select(BUSNO, 0x70, 1 << 4)?;
#[cfg(soc_platform = "sayma_rtm")]
i2c::switch_select(BUSNO, 0x77, 1 << 5)?;
#[cfg(soc_platform = "metlino")]
i2c::switch_select(BUSNO, 0x70, 1 << 4)?;
#[cfg(soc_platform = "kc705")]
i2c::switch_select(BUSNO, 0x74, 1 << 7)?;

View File

@ -1,538 +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(any(soc_platform = "metlino", soc_platform = "sayma_amc", soc_platform = "sayma_rtm"))]
pub const ADDRESS: u8 = 0x55;
#[cfg(soc_platform = "kasli")]
pub const ADDRESS: u8 = 0x67;
pub fn write(dcxo: i2c::Dcxo, reg: u8, val: u8) -> Result<(), &'static str> {
i2c::start(dcxo);
if !i2c::write(dcxo, ADDRESS << 1) {
return Err("Si549 failed to ack write address")
}
if !i2c::write(dcxo, reg) {
return Err("Si549 failed to ack register")
}
if !i2c::write(dcxo, val) {
return Err("Si549 failed to ack value")
}
i2c::stop(dcxo);
Ok(())
}
pub fn write_no_ack_value(dcxo: i2c::Dcxo, reg: u8, val: u8) -> Result<(), &'static str> {
i2c::start(dcxo);
if !i2c::write(dcxo, ADDRESS << 1) {
return Err("Si549 failed to ack write address")
}
if !i2c::write(dcxo, reg) {
return Err("Si549 failed to ack register")
}
i2c::write(dcxo, val);
i2c::stop(dcxo);
Ok(())
}
pub fn read(dcxo: i2c::Dcxo, reg: u8) -> Result<u8, &'static str> {
i2c::start(dcxo);
if !i2c::write(dcxo, ADDRESS << 1) {
return Err("Si549 failed to ack write address")
}
if !i2c::write(dcxo, reg) {
return Err("Si549 failed to ack register")
}
i2c::stop(dcxo);
i2c::start(dcxo);
if !i2c::write(dcxo, (ADDRESS << 1) | 1) {
return Err("Si549 failed to ack read address")
}
let val = i2c::read(dcxo, false);
i2c::stop(dcxo);
Ok(val)
}
pub fn program(dcxo: i2c::Dcxo, hsdiv: u16, lsdiv: u8, fbdiv: u64) -> Result<(), &'static str> {
i2c::init(dcxo)?;
write(dcxo, 255, 0x00)?; // PAGE
write_no_ack_value(dcxo, 7, 0x80)?; // RESET
clock::spin_us(100_000); // required? not specified in datasheet.
write(dcxo, 255, 0x00)?; // PAGE
write(dcxo, 69, 0x00)?; // Disable FCAL override.
// Note: Value 0x00 from Table 5.6 is inconsistent with Table 5.7,
// which shows bit 0 as reserved and =1.
write(dcxo, 17, 0x00)?; // Synchronously disable output
// The Si549 has no ID register, so we check that it responds correctly
// by writing values to a RAM-like register and reading them back.
for test_value in 0..255 {
write(dcxo, 23, test_value)?;
let readback = read(dcxo, 23)?;
if readback != test_value {
return Err("Si549 detection failed");
}
}
write(dcxo, 23, hsdiv as u8)?;
write(dcxo, 24, (hsdiv >> 8) as u8 | (lsdiv << 4))?;
write(dcxo, 26, fbdiv as u8)?;
write(dcxo, 27, (fbdiv >> 8) as u8)?;
write(dcxo, 28, (fbdiv >> 16) as u8)?;
write(dcxo, 29, (fbdiv >> 24) as u8)?;
write(dcxo, 30, (fbdiv >> 32) as u8)?;
write(dcxo, 31, (fbdiv >> 40) as u8)?;
write(dcxo, 7, 0x08)?; // Start FCAL
write(dcxo, 17, 0x01)?; // Synchronously enable output
Ok(())
}
// Si549 digital frequency trim ("all-digital PLL" register)
// ∆ f_out = adpll * 0.0001164e-6 (0.1164 ppb/lsb)
// max trim range is +- 950 ppm
pub fn set_adpll(dcxo: i2c::Dcxo, adpll: i32) -> Result<(), &'static str> {
write(dcxo, 231, adpll as u8)?;
write(dcxo, 232, (adpll >> 8) as u8)?;
write(dcxo, 233, (adpll >> 16) as u8)?;
clock::spin_us(100);
Ok(())
}
pub fn get_adpll(dcxo: i2c::Dcxo) -> Result<i32, &'static str> {
let b1 = read(dcxo, 231)? as i32;
let b2 = read(dcxo, 232)? as i32;
let b3 = read(dcxo, 233)? as i8 as i32;
Ok(b3 << 16 | b2 << 8 | b1)
}
}
// to do: load from gateware config
const DDMTD_COUNTER_N: u32 = 15;
const DDMTD_COUNTER_M: u32 = (1 << DDMTD_COUNTER_N);
const F_SYS: f64 = csr::CONFIG_CLOCK_FREQUENCY as f64;
const F_MAIN: f64 = 125.0e6;
const F_HELPER: f64 = F_MAIN * DDMTD_COUNTER_M as f64 / (DDMTD_COUNTER_M + 1) as f64;
const F_BEAT: f64 = F_MAIN - F_HELPER;
const TIME_STEP: f32 = 1./F_BEAT as f32;
fn ddmtd_tag_to_s(mu: f32) -> f32 {
return (mu as f32)*TIME_STEP;
}
fn get_frequencies() -> (u32, u32, u32) {
unsafe {
csr::wrpll::frequency_counter_update_en_write(1);
// wait for at least one full update cycle (> 2 timer periods)
clock::spin_us(200_000);
csr::wrpll::frequency_counter_update_en_write(0);
let helper = csr::wrpll::frequency_counter_counter_helper_read();
let main = csr::wrpll::frequency_counter_counter_rtio_read();
let cdr = csr::wrpll::frequency_counter_counter_rtio_rx0_read();
(helper, main, cdr)
}
}
fn log_frequencies() -> (u32, u32, u32) {
let (f_helper, f_main, f_cdr) = get_frequencies();
let conv_khz = |f| 4*(f as u64)*(csr::CONFIG_CLOCK_FREQUENCY as u64)/(1000*(1 << 23));
info!("helper clock frequency: {}kHz ({})", conv_khz(f_helper), f_helper);
info!("main clock frequency: {}kHz ({})", conv_khz(f_main), f_main);
info!("CDR clock frequency: {}kHz ({})", conv_khz(f_cdr), f_cdr);
(f_helper, f_main, f_cdr)
}
fn get_tags() -> (i32, i32, u16, u16) {
unsafe {
csr::wrpll::tag_arm_write(1);
while csr::wrpll::tag_arm_read() != 0 {}
let main_diff = csr::wrpll::main_diff_tag_read() as i32;
let helper_diff = csr::wrpll::helper_diff_tag_read() as i32;
let ref_tag = csr::wrpll::ref_tag_read();
let main_tag = csr::wrpll::main_tag_read();
(main_diff, helper_diff, ref_tag, main_tag)
}
}
fn print_tags() {
const NUM_TAGS: usize = 30;
let mut main_diffs = [0; NUM_TAGS]; // input to main loop filter
let mut helper_diffs = [0; NUM_TAGS]; // input to helper loop filter
let mut ref_tags = [0; NUM_TAGS];
let mut main_tags = [0; NUM_TAGS];
let mut jitter = [0 as f32; NUM_TAGS];
for i in 0..NUM_TAGS {
let (main_diff, helper_diff, ref_tag, main_tag) = get_tags();
main_diffs[i] = main_diff;
helper_diffs[i] = helper_diff;
ref_tags[i] = ref_tag;
main_tags[i] = main_tag;
}
info!("DDMTD ref tags: {:?}", ref_tags);
info!("DDMTD main tags: {:?}", main_tags);
info!("DDMTD main diffs: {:?}", main_diffs);
info!("DDMTD helper diffs: {:?}", helper_diffs);
// look at the difference between the main DCXO and reference...
let t0 = main_diffs[0];
main_diffs.iter_mut().for_each(|x| *x -= t0);
// crude estimate of the max difference across our sample set (assumes no unwrapping issues...)
let delta = main_diffs[main_diffs.len()-1] as f32 / (main_diffs.len()-1) as f32;
info!("detla: {:?} tags", delta);
let delta_f: f32 = delta/DDMTD_COUNTER_M as f32 * F_BEAT as f32;
info!("MAIN <-> ref frequency difference: {:?} Hz ({:?} ppm)", delta_f, delta_f/F_HELPER as f32 * 1e6);
jitter.iter_mut().enumerate().for_each(|(i, x)| *x = main_diffs[i] as f32 - delta*(i as f32));
info!("jitter: {:?} tags", jitter);
let var = jitter.iter().map(|x| x*x).fold(0 as f32, |acc, x| acc + x as f32) / NUM_TAGS as f32;
info!("variance: {:?} tags^2", var);
}
pub fn init() {
info!("initializing WR PLL...");
unsafe { csr::wrpll::helper_reset_write(1); }
unsafe {
csr::wrpll::helper_dcxo_i2c_address_write(si549::ADDRESS);
csr::wrpll::main_dcxo_i2c_address_write(si549::ADDRESS);
}
#[cfg(rtio_frequency = "125.0")]
let (h_hsdiv, h_lsdiv, h_fbdiv) = (0x05c, 0, 0x04b5badb98a);
#[cfg(rtio_frequency = "125.0")]
let (m_hsdiv, m_lsdiv, m_fbdiv) = (0x05c, 0, 0x04b5c447213);
si549::program(i2c::Dcxo::Main, m_hsdiv, m_lsdiv, m_fbdiv)
.expect("cannot initialize main Si549");
si549::program(i2c::Dcxo::Helper, h_hsdiv, h_lsdiv, h_fbdiv)
.expect("cannot initialize helper Si549");
// Si549 Settling Time for Large Frequency Change.
// Datasheet said 10ms but it lied.
clock::spin_us(50_000);
unsafe { csr::wrpll::helper_reset_write(0); }
clock::spin_us(1);
}
pub fn diagnostics() {
info!("WRPLL diagnostics...");
info!("Untrimmed oscillator frequencies:");
log_frequencies();
info!("Increase helper DCXO frequency by +10ppm (1.25kHz):");
si549::set_adpll(i2c::Dcxo::Helper, 85911).expect("ADPLL write failed");
// to do: add check on frequency?
log_frequencies();
}
fn trim_dcxos(f_helper: u32, f_main: u32, f_cdr: u32) -> Result<(i32, i32), &'static str> {
info!("Trimming oscillator frequencies...");
const DCXO_STEP: i64 = (1.0e6/0.0001164) as i64;
const ADPLL_MAX: i64 = (950.0/0.0001164) as i64;
const TIMER_WIDTH: u32 = 23;
const COUNTER_DIV: u32 = 2;
// how many counts we expect to measure
const SYS_COUNTS: i64 = (1 << (TIMER_WIDTH - COUNTER_DIV)) as i64;
const EXP_MAIN_COUNTS: i64 = ((SYS_COUNTS as f64) * (F_MAIN/F_SYS)) as i64;
const EXP_HELPER_COUNTS: i64 = ((SYS_COUNTS as f64) * (F_HELPER/F_SYS)) as i64;
// calibrate the SYS clock to the CDR clock and correct the measured counts
// assume frequency errors are small so we can make an additive correction
// positive error means sys clock is too fast
let sys_err: i64 = EXP_MAIN_COUNTS - (f_cdr as i64);
let main_err: i64 = EXP_MAIN_COUNTS - (f_main as i64) - sys_err;
let helper_err: i64 = EXP_HELPER_COUNTS - (f_helper as i64) - sys_err;
info!("sys count err {}", sys_err);
info!("main counts err {}", main_err);
info!("helper counts err {}", helper_err);
// calculate required adjustment to the ADPLL register see
// https://www.silabs.com/documents/public/data-sheets/si549-datasheet.pdf
// section 5.6
let helper_adpll: i64 = helper_err*DCXO_STEP/EXP_HELPER_COUNTS;
let main_adpll: i64 = main_err*DCXO_STEP/EXP_MAIN_COUNTS;
if helper_adpll.abs() > ADPLL_MAX {
return Err("helper DCXO offset too large");
}
if main_adpll.abs() > ADPLL_MAX {
return Err("main DCXO offset too large");
}
info!("ADPLL offsets: helper={} main={}", helper_adpll, main_adpll);
Ok((helper_adpll as i32, main_adpll as i32))
}
fn statistics(data: &[u16]) -> (f32, f32) {
let sum = data.iter().fold(0 as u32, |acc, x| acc + *x as u32);
let mean = sum as f32 / data.len() as f32;
let squared_sum = data.iter().fold(0 as u32, |acc, x| acc + (*x as u32).pow(2));
let variance = (squared_sum as f32 / data.len() as f32) - mean;
return (mean, variance)
}
fn select_recovered_clock_int(rc: bool) -> Result<(), &'static str> {
info!("Untrimmed oscillator frequencies:");
let (f_helper, f_main, f_cdr) = log_frequencies();
if rc {
let (helper_adpll, main_adpll) = trim_dcxos(f_helper, f_main, f_cdr)?;
// to do: add assertion on max frequency shift here?
si549::set_adpll(i2c::Dcxo::Helper, helper_adpll).expect("ADPLL write failed");
si549::set_adpll(i2c::Dcxo::Main, main_adpll).expect("ADPLL write failed");
log_frequencies();
clock::spin_us(100_000); // TO DO: remove/reduce!
print_tags();
info!("increasing main DCXO by 1ppm (125Hz):");
si549::set_adpll(i2c::Dcxo::Main, main_adpll + 8591).expect("ADPLL write failed");
clock::spin_us(100_000);
print_tags();
si549::set_adpll(i2c::Dcxo::Main, main_adpll).expect("ADPLL write failed");
unsafe {
csr::wrpll::adpll_offset_helper_write(helper_adpll as u32);
csr::wrpll::adpll_offset_main_write(main_adpll as u32);
csr::wrpll::helper_dcxo_gpio_enable_write(0);
csr::wrpll::main_dcxo_gpio_enable_write(0);
csr::wrpll::helper_dcxo_errors_write(0xff);
csr::wrpll::main_dcxo_errors_write(0xff);
csr::wrpll::collector_reset_write(0);
}
clock::spin_us(1_000); // wait for the collector to produce meaningful output
unsafe {
csr::wrpll::filter_reset_write(0);
}
clock::spin_us(100_000);
print_tags();
// let mut tags = [0; 10];
// for i in 0..tags.len() {
// tags[i] = get_ddmtd_helper_tag();
// }
// info!("DDMTD helper tags: {:?}", tags);
unsafe {
csr::wrpll::filter_reset_write(1);
csr::wrpll::collector_reset_write(1);
}
clock::spin_us(50_000);
unsafe {
csr::wrpll::helper_dcxo_gpio_enable_write(1);
csr::wrpll::main_dcxo_gpio_enable_write(1);
}
unsafe {
info!("error {} {}",
csr::wrpll::helper_dcxo_errors_read(),
csr::wrpll::main_dcxo_errors_read());
}
info!("new ADPLL: {} {}",
si549::get_adpll(i2c::Dcxo::Helper)?,
si549::get_adpll(i2c::Dcxo::Main)?);
} else {
si549::set_adpll(i2c::Dcxo::Helper, 0).expect("ADPLL write failed");
si549::set_adpll(i2c::Dcxo::Main, 0).expect("ADPLL write failed");
}
Ok(())
}
pub fn select_recovered_clock(rc: bool) {
if rc {
info!("switching to recovered clock");
} else {
info!("switching to local XO clock");
}
match select_recovered_clock_int(rc) {
Ok(()) => info!("clock transition completed"),
Err(e) => error!("clock transition failed: {}", e)
}
}

View File

@ -67,10 +67,6 @@ pub fn get_adresses() -> NetAddresses {
.map(|addr_buf| EthernetAddress(addr_buf))
.unwrap_or_else(|_e| EthernetAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x21]));
}
#[cfg(soc_platform = "sayma_amc")]
{ hardware_addr = EthernetAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x11]); }
#[cfg(soc_platform = "metlino")]
{ hardware_addr = EthernetAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x19]); }
#[cfg(soc_platform = "kc705")]
{ hardware_addr = EthernetAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x01]); }
}

View File

@ -114,7 +114,7 @@ pub unsafe fn write(mut addr: usize, mut data: &[u8]) {
}
}
#[cfg(any(soc_platform = "kasli", soc_platform = "metlino", soc_platform = "kc705"))]
#[cfg(any(soc_platform = "kasli", soc_platform = "kc705"))]
pub unsafe fn reload () -> ! {
csr::icap::iprog_write(1);
loop {}

View File

@ -341,7 +341,7 @@ pub fn panic_impl(info: &core::panic::PanicInfo) -> ! {
});
if config::read_str("panic_reset", |r| r == Ok("1")) &&
cfg!(any(soc_platform = "kasli", soc_platform = "metlino", soc_platform = "kc705")) {
cfg!(any(soc_platform = "kasli", soc_platform = "kc705")) {
println!("restarting...");
unsafe {
kernel::stop();

View File

@ -1,7 +1,6 @@
use board_misoc::config;
#[cfg(si5324_as_synthesizer)]
use board_artiq::si5324;
#[cfg(has_drtio)]
use board_misoc::{csr, clock};
#[derive(Debug, PartialEq)]
@ -10,7 +9,6 @@ pub enum RtioClock {
Default,
Int_125,
Int_100,
Int_150,
Ext0_Bypass,
Ext0_Synth0_10to125,
Ext0_Synth0_100to125,
@ -23,7 +21,6 @@ fn get_rtio_clock_cfg() -> RtioClock {
let res = match result {
Ok("int_125") => RtioClock::Int_125,
Ok("int_100") => RtioClock::Int_100,
Ok("int_150") => RtioClock::Int_150,
Ok("ext0_bypass") => RtioClock::Ext0_Bypass,
Ok("ext0_bypass_125") => RtioClock::Ext0_Bypass,
Ok("ext0_bypass_100") => RtioClock::Ext0_Bypass,
@ -54,9 +51,7 @@ fn get_rtio_clock_cfg() -> RtioClock {
return RtioClock::Ext0_Synth0_125to125;
#[cfg(all(rtio_frequency = "125.0", not(si5324_ext_ref)))]
return RtioClock::Int_125;
#[cfg(all(rtio_frequency = "150.0", not(si5324_ext_ref)))]
return RtioClock::Int_150;
#[cfg(all(rtio_frequency = "100.0", not(si5324_ext_ref)))]
#[cfg(all(rtio_frequency = "100.0", not(si5324_ext_ref), not(soc_platform = "kasli")))]
return RtioClock::Int_100;
//in case nothing is set
return RtioClock::Int_125;
@ -68,40 +63,12 @@ fn get_rtio_clock_cfg() -> RtioClock {
#[cfg(has_rtio_crg)]
pub mod crg {
#[cfg(has_rtio_clock_switch)]
use super::RtioClock;
use board_misoc::{clock, csr};
pub fn check() -> bool {
unsafe { csr::rtio_crg::pll_locked_read() != 0 }
}
#[cfg(has_rtio_clock_switch)]
pub fn init(clk: RtioClock) -> bool {
let clk_sel: u8 = match clk {
RtioClock::Ext0_Bypass => {
info!("Using external clock");
1
},
RtioClock::Int_125 => {
info!("Using internal RTIO clock");
0
},
_ => {
warn!("rtio_clock setting '{:?}' is not supported. Using default internal RTIO clock instead", clk);
0
}
};
unsafe {
csr::rtio_crg::pll_reset_write(1);
csr::rtio_crg::clock_sel_write(clk_sel);
csr::rtio_crg::pll_reset_write(0);
}
clock::spin_us(150);
return check()
}
#[cfg(not(has_rtio_clock_switch))]
pub fn init() -> bool {
info!("Using internal RTIO clock");
unsafe {
@ -123,8 +90,6 @@ pub mod crg {
const SI5324_EXT_INPUT: si5324::Input = si5324::Input::Ckin1;
#[cfg(all(si5324_as_synthesizer, soc_platform = "kasli", not(hw_rev = "v2.0")))]
const SI5324_EXT_INPUT: si5324::Input = si5324::Input::Ckin2;
#[cfg(all(si5324_as_synthesizer, soc_platform = "metlino"))]
const SI5324_EXT_INPUT: si5324::Input = si5324::Input::Ckin2;
#[cfg(all(si5324_as_synthesizer, soc_platform = "kc705"))]
const SI5324_EXT_INPUT: si5324::Input = si5324::Input::Ckin2;
@ -179,22 +144,6 @@ fn setup_si5324_as_synthesizer(cfg: RtioClock) {
SI5324_EXT_INPUT
)
},
RtioClock::Int_150 => { // 150MHz output, from crystal
info!("using internal 150MHz RTIO clock");
(
si5324::FrequencySettings {
n1_hs : 9,
nc1_ls : 4,
n2_hs : 10,
n2_ls : 33732,
n31 : 7139,
n32 : 7139,
bwsel : 3,
crystal_as_ckin2: true
},
si5324::Input::Ckin2
)
},
RtioClock::Int_100 => { // 100MHz output, from crystal
info!("using internal 100MHz RTIO clock");
(
@ -247,35 +196,66 @@ fn setup_si5324_as_synthesizer(cfg: RtioClock) {
si5324::setup(&si5324_settings, si5324_ref_input).expect("cannot initialize Si5324");
}
pub fn init() {
let clock_cfg = get_rtio_clock_cfg();
#[cfg(si5324_as_synthesizer)]
fn setup_si5324(clock_cfg: RtioClock) {
let switched = unsafe {
csr::crg::switch_done_read()
};
if switched == 1 {
info!("Clocking has already been set up.");
return;
}
match clock_cfg {
RtioClock::Ext0_Bypass => {
info!("using external RTIO clock with PLL bypass");
si5324::bypass(SI5324_EXT_INPUT).expect("cannot bypass Si5324")
},
_ => setup_si5324_as_synthesizer(clock_cfg),
}
// switch sysclk source to si5324
#[cfg(not(has_drtio))]
{
match clock_cfg {
RtioClock::Ext0_Bypass => {
info!("using external RTIO clock with PLL bypass");
si5324::bypass(SI5324_EXT_INPUT).expect("cannot bypass Si5324")
},
_ => setup_si5324_as_synthesizer(clock_cfg),
info!("Switching sys clock, rebooting...");
// delay for clean UART log, wait until UART FIFO is empty
clock::spin_us(1300);
unsafe {
csr::crg::clock_sel_write(1);
loop {}
}
}
}
pub fn init() {
let clock_cfg = get_rtio_clock_cfg();
setup_si5324(clock_cfg);
#[cfg(has_drtio)]
{
unsafe {
csr::drtio_transceiver::stable_clkin_write(1);
let switched = unsafe {
csr::crg::switch_done_read()
};
if switched == 0 {
info!("Switching sys clock, rebooting...");
clock::spin_us(500); // delay for clean UART log
unsafe {
// clock switch and reboot will begin after TX is initialized
// and TX will be initialized after this
csr::drtio_transceiver::stable_clkin_write(1);
}
loop {}
}
clock::spin_us(1500); // wait for CPLL/QPLL lock
unsafe {
csr::drtio_transceiver::txenable_write(0xffffffffu32 as _);
else {
// enable TX after the reboot, with stable clock
unsafe {
csr::drtio_transceiver::txenable_write(0xffffffffu32 as _);
}
}
}
#[cfg(has_rtio_crg)]
{
#[cfg(has_rtio_clock_switch)]
let result = crg::init(clock_cfg);
#[cfg(not(has_rtio_clock_switch))]
let result = crg::init();
if !result {
error!("RTIO clock failed");

View File

@ -1,74 +0,0 @@
pub const INIT: u8 = 0x00;
pub const PRINT_STATUS: u8 = 0x01;
pub const PRBS: u8 = 0x02;
pub const STPL: u8 = 0x03;
pub const SYSREF_DELAY_DAC: u8 = 0x10;
pub const SYSREF_SLIP: u8 = 0x11;
pub const SYNC: u8 = 0x12;
pub const DDMTD_SYSREF_RAW: u8 = 0x20;
pub const DDMTD_SYSREF: u8 = 0x21;
fn average_2phases(a: i32, b: i32, modulo: i32) -> i32 {
let diff = ((a - b + modulo/2 + modulo) % modulo) - modulo/2;
return (modulo + b + diff/2) % modulo;
}
pub fn average_phases(phases: &[i32], modulo: i32) -> i32 {
if phases.len() == 1 {
panic!("input array length must be a power of 2");
} else if phases.len() == 2 {
average_2phases(phases[0], phases[1], modulo)
} else {
let cut = phases.len()/2;
average_2phases(
average_phases(&phases[..cut], modulo),
average_phases(&phases[cut..], modulo),
modulo)
}
}
pub const RAW_DDMTD_N_SHIFT: i32 = 6;
pub const RAW_DDMTD_N: i32 = 1 << RAW_DDMTD_N_SHIFT;
pub const DDMTD_DITHER_BITS: i32 = 1;
pub const DDMTD_N_SHIFT: i32 = RAW_DDMTD_N_SHIFT + DDMTD_DITHER_BITS;
pub const DDMTD_N: i32 = 1 << DDMTD_N_SHIFT;
#[cfg(has_ad9154)]
use board_misoc::{clock, csr};
#[cfg(has_ad9154)]
pub fn init_ddmtd() -> Result<(), &'static str> {
unsafe {
csr::sysref_ddmtd::reset_write(1);
clock::spin_us(1);
csr::sysref_ddmtd::reset_write(0);
clock::spin_us(100);
if csr::sysref_ddmtd::locked_read() != 0 {
Ok(())
} else {
Err("DDMTD helper PLL failed to lock")
}
}
}
#[cfg(has_ad9154)]
pub fn measure_ddmdt_phase_raw() -> i32 {
unsafe { csr::sysref_ddmtd::dt_read() as i32 }
}
#[cfg(has_ad9154)]
pub fn measure_ddmdt_phase() -> i32 {
const AVG_PRECISION_SHIFT: i32 = 6;
const AVG_PRECISION: i32 = 1 << AVG_PRECISION_SHIFT;
const AVG_MOD: i32 = 1 << (RAW_DDMTD_N_SHIFT + AVG_PRECISION_SHIFT + DDMTD_DITHER_BITS);
let mut measurements = [0; AVG_PRECISION as usize];
for i in 0..AVG_PRECISION {
measurements[i as usize] = measure_ddmdt_phase_raw() << (AVG_PRECISION_SHIFT + DDMTD_DITHER_BITS);
clock::spin_us(10);
}
average_phases(&measurements, AVG_MOD) >> AVG_PRECISION_SHIFT
}

View File

@ -1,589 +0,0 @@
pub mod jesd {
use board_misoc::{csr, clock};
pub fn reset(reset: bool) {
unsafe {
csr::jesd_crg::jreset_write(if reset {1} else {0});
}
}
pub fn enable(dacno: u8, en: bool) {
unsafe {
(csr::JDCG[dacno as usize].jesd_control_enable_write)(if en {1} else {0})
}
}
pub fn phy_done(dacno: u8) -> bool {
unsafe {
(csr::JDCG[dacno as usize].jesd_control_phy_done_read)() != 0
}
}
pub fn ready(dacno: u8) -> bool {
unsafe {
(csr::JDCG[dacno as usize].jesd_control_ready_read)() != 0
}
}
pub fn prbs(dacno: u8, en: bool) {
unsafe {
(csr::JDCG[dacno as usize].jesd_control_prbs_config_write)(if en {0b01} else {0b00})
}
clock::spin_us(5000);
}
pub fn stpl(dacno: u8, en: bool) {
unsafe {
(csr::JDCG[dacno as usize].jesd_control_stpl_enable_write)(if en {1} else {0})
}
clock::spin_us(5000);
}
pub fn jsync(dacno: u8) -> bool {
unsafe {
(csr::JDCG[dacno as usize].jesd_control_jsync_read)() != 0
}
}
}
pub mod jdac {
use board_misoc::{csr, clock};
use board_artiq::drtioaux;
use super::jesd;
use super::super::jdac_common;
pub fn basic_request(dacno: u8, reqno: u8, param: u8) -> Result<u8, &'static str> {
if let Err(e) = drtioaux::send(1, &drtioaux::Packet::JdacBasicRequest {
destination: 0,
dacno: dacno,
reqno: reqno,
param: param
}) {
error!("aux packet error ({})", e);
return Err("aux packet error while sending for JESD DAC basic request");
}
match drtioaux::recv_timeout(1, Some(1000)) {
Ok(drtioaux::Packet::JdacBasicReply { succeeded, retval }) => {
if succeeded {
Ok(retval)
} else {
error!("JESD DAC basic request failed (dacno={}, reqno={})", dacno, reqno);
Err("remote error status to JESD DAC basic request")
}
},
Ok(packet) => {
error!("received unexpected aux packet: {:?}", packet);
Err("unexpected aux packet in reply to JESD DAC basic request")
},
Err(e) => {
error!("aux packet error ({})", e);
Err("aux packet error while waiting for JESD DAC basic reply")
}
}
}
pub fn init() -> Result<(), &'static str> {
for dacno in 0..csr::JDCG.len() {
let dacno = dacno as u8;
info!("DAC-{} initializing...", dacno);
jesd::enable(dacno, true);
clock::spin_us(10_000);
if !jesd::phy_done(dacno) {
error!("JESD core PHY not done");
return Err("JESD core PHY not done");
}
basic_request(dacno, jdac_common::INIT, 0)?;
// JESD ready depends on JSYNC being valid, so DAC init needs to happen first
if !jesd::ready(dacno) {
error!("JESD core reported not ready, sending DAC status print request");
basic_request(dacno, jdac_common::PRINT_STATUS, 0)?;
return Err("JESD core reported not ready");
}
jesd::prbs(dacno, true);
basic_request(dacno, jdac_common::PRBS, 0)?;
jesd::prbs(dacno, false);
basic_request(dacno, jdac_common::INIT, 0)?;
clock::spin_us(5000);
if !jesd::jsync(dacno) {
error!("JESD core reported bad SYNC");
return Err("JESD core reported bad SYNC");
}
info!(" ...done initializing");
}
Ok(())
}
pub fn stpl() -> Result<(), &'static str> {
for dacno in 0..csr::JDCG.len() {
let dacno = dacno as u8;
info!("Running STPL test on DAC-{}...", dacno);
jesd::stpl(dacno, true);
basic_request(dacno, jdac_common::STPL, 0)?;
jesd::stpl(dacno, false);
info!(" ...done STPL test");
}
Ok(())
}
}
pub mod jesd204sync {
use board_misoc::{csr, clock, config};
use super::jdac;
use super::super::jdac_common;
const HMC7043_ANALOG_DELAY_RANGE: u8 = 24;
const FPGA_CLK_DIV: u16 = 16; // Keep in sync with hmc830_7043.rs
const SYSREF_DIV: u16 = 256; // Keep in sync with hmc830_7043.rs
fn hmc7043_sysref_delay_dac(dacno: u8, phase_offset: u8) -> Result<(), &'static str> {
match jdac::basic_request(dacno, jdac_common::SYSREF_DELAY_DAC, phase_offset) {
Ok(_) => Ok(()),
Err(e) => Err(e)
}
}
fn hmc7043_sysref_slip() -> Result<(), &'static str> {
match jdac::basic_request(0, jdac_common::SYSREF_SLIP, 0) {
Ok(_) => Ok(()),
Err(e) => Err(e)
}
}
fn ad9154_sync(dacno: u8) -> Result<bool, &'static str> {
match jdac::basic_request(dacno, jdac_common::SYNC, 0) {
Ok(0) => Ok(false),
Ok(_) => Ok(true),
Err(e) => Err(e)
}
}
fn measure_ddmdt_phase_raw() -> Result<i32, &'static str> {
Ok(jdac::basic_request(0, jdac_common::DDMTD_SYSREF_RAW, 0)? as i32)
}
fn measure_ddmdt_phase() -> Result<i32, &'static str> {
Ok(jdac::basic_request(0, jdac_common::DDMTD_SYSREF, 0)? as i32)
}
fn test_ddmtd_stability(raw: bool, tolerance: i32) -> Result<(), &'static str> {
info!("testing DDMTD stability (raw={}, tolerance={})...", raw, tolerance);
let modulo = if raw { jdac_common::RAW_DDMTD_N } else { jdac_common::DDMTD_N };
let measurement = if raw { measure_ddmdt_phase_raw } else { measure_ddmdt_phase };
let ntests = if raw { 150 } else { 15 };
let mut max_pkpk = 0;
for _ in 0..32 {
// If we are near the edges, wraparound can throw off the simple min/max computation.
// In this case, add an offset to get near the center.
let quadrant = measure_ddmdt_phase()?;
let center_offset =
if quadrant < jdac_common::DDMTD_N/4 || quadrant > 3*jdac_common::DDMTD_N/4 {
modulo/2
} else {
0
};
let mut min = modulo;
let mut max = 0;
for _ in 0..ntests {
let m = (measurement()? + center_offset) % modulo;
if m < min {
min = m;
}
if m > max {
max = m;
}
}
let pkpk = max - min;
if pkpk > max_pkpk {
max_pkpk = pkpk;
}
if pkpk > tolerance {
error!(" ...excessive peak-peak jitter: {} (min={} max={} center_offset={})", pkpk,
min, max, center_offset);
return Err("excessive DDMTD peak-peak jitter");
}
hmc7043_sysref_slip();
}
info!(" ...passed, peak-peak jitter: {}", max_pkpk);
Ok(())
}
fn test_slip_ddmtd() -> Result<(), &'static str> {
// expected_step = (RTIO clock frequency)*(DDMTD N)/(HMC7043 CLKIN frequency)
let expected_step = 8;
let tolerance = 1;
info!("testing HMC7043 SYSREF slip against DDMTD...");
let mut old_phase = measure_ddmdt_phase()?;
for _ in 0..1024 {
hmc7043_sysref_slip();
let phase = measure_ddmdt_phase()?;
let step = (jdac_common::DDMTD_N + old_phase - phase) % jdac_common::DDMTD_N;
if (step - expected_step).abs() > tolerance {
error!(" ...got unexpected step: {} ({} -> {})", step, old_phase, phase);
return Err("HMC7043 SYSREF slip produced unexpected DDMTD step");
}
old_phase = phase;
}
info!(" ...passed");
Ok(())
}
fn sysref_sh_error() -> bool {
unsafe {
csr::sysref_sampler::sh_error_reset_write(1);
clock::spin_us(1);
csr::sysref_sampler::sh_error_reset_write(0);
clock::spin_us(10);
csr::sysref_sampler::sh_error_read() != 0
}
}
const SYSREF_SH_PRECISION_SHIFT: i32 = 5;
const SYSREF_SH_PRECISION: i32 = 1 << SYSREF_SH_PRECISION_SHIFT;
const SYSREF_SH_MOD: i32 = 1 << (jdac_common::DDMTD_N_SHIFT + SYSREF_SH_PRECISION_SHIFT);
#[derive(Default)]
struct SysrefShLimits {
rising_phases: [i32; SYSREF_SH_PRECISION as usize],
falling_phases: [i32; SYSREF_SH_PRECISION as usize],
}
fn measure_sysref_sh_limits() -> Result<SysrefShLimits, &'static str> {
let mut ret = SysrefShLimits::default();
let mut nslips = 0;
let mut rising_n = 0;
let mut falling_n = 0;
let mut previous = sysref_sh_error();
while rising_n < SYSREF_SH_PRECISION || falling_n < SYSREF_SH_PRECISION {
hmc7043_sysref_slip();
nslips += 1;
if nslips > 1024 {
return Err("too many slips and not enough SYSREF S/H error transitions");
}
let current = sysref_sh_error();
let phase = measure_ddmdt_phase()?;
if current && !previous && rising_n < SYSREF_SH_PRECISION {
ret.rising_phases[rising_n as usize] = phase << SYSREF_SH_PRECISION_SHIFT;
rising_n += 1;
}
if !current && previous && falling_n < SYSREF_SH_PRECISION {
ret.falling_phases[falling_n as usize] = phase << SYSREF_SH_PRECISION_SHIFT;
falling_n += 1;
}
previous = current;
}
Ok(ret)
}
fn max_phase_deviation(average: i32, phases: &[i32]) -> i32 {
let mut ret = 0;
for phase in phases.iter() {
let deviation = (phase - average + jdac_common::DDMTD_N) % jdac_common::DDMTD_N;
if deviation > ret {
ret = deviation;
}
}
return ret;
}
fn reach_sysref_ddmtd_target(target: i32, tolerance: i32) -> Result<i32, &'static str> {
for _ in 0..1024 {
let delta = (measure_ddmdt_phase()? - target + jdac_common::DDMTD_N) % jdac_common::DDMTD_N;
if delta <= tolerance {
return Ok(delta)
}
hmc7043_sysref_slip();
}
Err("failed to reach SYSREF DDMTD phase target")
}
fn calibrate_sysref_target(rising_average: i32, falling_average: i32) -> Result<i32, &'static str> {
info!("calibrating SYSREF DDMTD target phase...");
let coarse_target =
if rising_average < falling_average {
(rising_average + falling_average)/2
} else {
((falling_average - (jdac_common::DDMTD_N - rising_average))/2 + jdac_common::DDMTD_N) % jdac_common::DDMTD_N
};
info!(" SYSREF calibration coarse target: {}", coarse_target);
reach_sysref_ddmtd_target(coarse_target, 8)?;
let target = measure_ddmdt_phase()?;
info!(" ...done, target={}", target);
Ok(target)
}
fn sysref_get_tsc_phase_raw() -> Result<u8, &'static str> {
if sysref_sh_error() {
return Err("SYSREF failed S/H timing");
}
let ret = unsafe { csr::sysref_sampler::sysref_phase_read() };
Ok(ret)
}
// Note: the code below assumes RTIO/SYSREF frequency ratio is a power of 2
fn sysref_get_tsc_phase() -> Result<i32, &'static str> {
let mask = (SYSREF_DIV/FPGA_CLK_DIV - 1) as u8;
Ok((sysref_get_tsc_phase_raw()? & mask) as i32)
}
pub fn test_sysref_frequency() -> Result<(), &'static str> {
info!("testing SYSREF frequency against raw TSC phase bit toggles...");
let mut all_toggles = 0;
let initial_phase = sysref_get_tsc_phase_raw()?;
for _ in 0..20000 {
clock::spin_us(1);
all_toggles |= sysref_get_tsc_phase_raw()? ^ initial_phase;
}
let ratio = (SYSREF_DIV/FPGA_CLK_DIV) as u8;
let expected_toggles = 0xff ^ (ratio - 1);
if all_toggles == expected_toggles {
info!(" ...done (0x{:02x})", all_toggles);
Ok(())
} else {
error!(" ...unexpected toggles: got 0x{:02x}, expected 0x{:02x}",
all_toggles, expected_toggles);
Err("unexpected toggles")
}
}
fn sysref_slip_rtio_cycle() {
for _ in 0..FPGA_CLK_DIV {
hmc7043_sysref_slip();
}
}
pub fn test_slip_tsc() -> Result<(), &'static str> {
info!("testing HMC7043 SYSREF slip against TSC phase...");
let initial_phase = sysref_get_tsc_phase()?;
let modulo = (SYSREF_DIV/FPGA_CLK_DIV) as i32;
for i in 0..128 {
sysref_slip_rtio_cycle();
let expected_phase = (initial_phase + i + 1) % modulo;
let phase = sysref_get_tsc_phase()?;
if phase != expected_phase {
error!(" ...unexpected TSC phase: got {}, expected {} ", phase, expected_phase);
return Err("HMC7043 SYSREF slip produced unexpected TSC phase");
}
}
info!(" ...done");
Ok(())
}
pub fn sysref_rtio_align() -> Result<(), &'static str> {
info!("aligning SYSREF with RTIO TSC...");
let mut nslips = 0;
loop {
sysref_slip_rtio_cycle();
if sysref_get_tsc_phase()? == 0 {
info!(" ...done");
return Ok(())
}
nslips += 1;
if nslips > SYSREF_DIV/FPGA_CLK_DIV {
return Err("failed to find SYSREF transition aligned with RTIO TSC");
}
}
}
pub fn sysref_auto_rtio_align() -> Result<(), &'static str> {
test_ddmtd_stability(true, 4)?;
test_ddmtd_stability(false, 1)?;
test_slip_ddmtd()?;
info!("determining SYSREF S/H limits...");
let sysref_sh_limits = measure_sysref_sh_limits()?;
let rising_average = jdac_common::average_phases(&sysref_sh_limits.rising_phases, SYSREF_SH_MOD);
let falling_average = jdac_common::average_phases(&sysref_sh_limits.falling_phases, SYSREF_SH_MOD);
let rising_max_deviation = max_phase_deviation(rising_average, &sysref_sh_limits.rising_phases);
let falling_max_deviation = max_phase_deviation(falling_average, &sysref_sh_limits.falling_phases);
let rising_average = rising_average >> SYSREF_SH_PRECISION_SHIFT;
let falling_average = falling_average >> SYSREF_SH_PRECISION_SHIFT;
let rising_max_deviation = rising_max_deviation >> SYSREF_SH_PRECISION_SHIFT;
let falling_max_deviation = falling_max_deviation >> SYSREF_SH_PRECISION_SHIFT;
info!(" SYSREF S/H average limits (DDMTD phases): {} {}", rising_average, falling_average);
info!(" SYSREF S/H maximum limit deviation: {} {}", rising_max_deviation, falling_max_deviation);
if rising_max_deviation > 8 || falling_max_deviation > 8 {
return Err("excessive SYSREF S/H limit deviation");
}
info!(" ...done");
let entry = config::read_str("sysref_ddmtd_phase_fpga", |r| r.map(|s| s.parse()));
let target_phase = match entry {
Ok(Ok(phase)) => {
info!("using FPGA SYSREF DDMTD phase target from config: {}", phase);
phase
}
_ => {
let phase = calibrate_sysref_target(rising_average, falling_average)?;
if let Err(e) = config::write_int("sysref_ddmtd_phase_fpga", phase as u32) {
error!("failed to update FPGA SYSREF DDMTD phase target in config: {}", e);
}
phase
}
};
info!("aligning SYSREF with RTIO clock...");
let delta = reach_sysref_ddmtd_target(target_phase, 3)?;
if sysref_sh_error() {
return Err("SYSREF does not meet S/H timing at DDMTD phase target");
}
info!(" ...done, delta={}", delta);
test_sysref_frequency()?;
test_slip_tsc()?;
sysref_rtio_align()?;
Ok(())
}
fn sysref_cal_dac(dacno: u8) -> Result<u8, &'static str> {
info!("calibrating SYSREF delay at DAC-{}...", dacno);
// Allocate for more than expected as jitter may create spurious entries.
let mut limits_buf = [0; 8];
let mut n_limits = 0;
limits_buf[n_limits] = -1;
n_limits += 1;
// avoid spurious rotation at delay=0
hmc7043_sysref_delay_dac(dacno, 0);
ad9154_sync(dacno)?;
for scan_delay in 0..HMC7043_ANALOG_DELAY_RANGE {
hmc7043_sysref_delay_dac(dacno, scan_delay);
if ad9154_sync(dacno)? {
limits_buf[n_limits] = scan_delay as i16;
n_limits += 1;
if n_limits >= limits_buf.len() - 1 {
break;
}
}
}
limits_buf[n_limits] = HMC7043_ANALOG_DELAY_RANGE as i16;
n_limits += 1;
info!(" using limits: {:?}", &limits_buf[..n_limits]);
let mut delay = 0;
let mut best_margin = 0;
for i in 0..(n_limits-1) {
let margin = limits_buf[i+1] - limits_buf[i];
if margin > best_margin {
best_margin = margin;
delay = ((limits_buf[i+1] + limits_buf[i])/2) as u8;
}
}
info!(" ...done, delay={}", delay);
Ok(delay)
}
fn sysref_dac_align(dacno: u8, delay: u8) -> Result<(), &'static str> {
let tolerance = 5;
info!("verifying SYSREF margins at DAC-{}...", dacno);
// avoid spurious rotation at delay=0
hmc7043_sysref_delay_dac(dacno, 0);
ad9154_sync(dacno)?;
let mut rotation_seen = false;
for scan_delay in 0..HMC7043_ANALOG_DELAY_RANGE {
hmc7043_sysref_delay_dac(dacno, scan_delay);
if ad9154_sync(dacno)? {
rotation_seen = true;
let distance = (scan_delay as i16 - delay as i16).abs();
if distance < tolerance {
error!(" rotation at delay={} is {} delay steps from target (FAIL)", scan_delay, distance);
return Err("insufficient SYSREF margin at DAC");
} else {
info!(" rotation at delay={} is {} delay steps from target (PASS)", scan_delay, distance);
}
}
}
if !rotation_seen {
return Err("no rotation seen when scanning DAC SYSREF delay");
}
info!(" ...done");
// We tested that the value is correct - now use it
info!("synchronizing DAC-{}", dacno);
hmc7043_sysref_delay_dac(dacno, delay);
ad9154_sync(dacno)?;
Ok(())
}
pub fn sysref_auto_dac_align() -> Result<(), &'static str> {
// We assume that DAC SYSREF traces are length-matched so only one delay
// value is needed, and we use DAC-0 as calibration reference.
let entry = config::read_str("sysref_7043_delay_dac", |r| r.map(|s| s.parse()));
let delay = match entry {
Ok(Ok(delay)) => {
info!("using DAC SYSREF delay from config: {}", delay);
delay
},
_ => {
let delay = sysref_cal_dac(0)?;
if let Err(e) = config::write_int("sysref_7043_delay_dac", delay as u32) {
error!("failed to update DAC SYSREF delay in config: {}", e);
}
delay
}
};
for dacno in 0..csr::JDCG.len() {
sysref_dac_align(dacno as u8, delay)?;
}
Ok(())
}
pub fn sysref_auto_align() {
if let Err(e) = sysref_auto_rtio_align() {
error!("failed to align SYSREF at FPGA: {}", e);
}
if let Err(e) = sysref_auto_dac_align() {
error!("failed to align SYSREF at DAC: {}", e);
}
}
pub fn resync_dacs() -> Result<(), &'static str> {
for dacno in 0..csr::JDCG.len() {
info!("resynchronizing DAC-{}", dacno);
ad9154_sync(dacno as u8)?;
}
Ok(())
}
}

View File

@ -12,19 +12,11 @@ use core::convert::TryFrom;
use board_misoc::{csr, ident, clock, uart_logger, i2c, pmp};
#[cfg(has_si5324)]
use board_artiq::si5324;
#[cfg(has_wrpll)]
use board_artiq::wrpll;
use board_artiq::{spi, drtioaux};
use board_artiq::drtio_routing;
#[cfg(has_hmc830_7043)]
use board_artiq::hmc830_7043;
use riscv::register::{mcause, mepc, mtval};
mod repeater;
#[cfg(has_jdcg)]
mod jdcg;
#[cfg(any(has_ad9154, has_jdcg))]
pub mod jdac_common;
fn drtiosat_reset(reset: bool) {
unsafe {
@ -303,43 +295,6 @@ fn process_aux_packet(_repeaters: &mut [repeater::Repeater],
}
}
drtioaux::Packet::JdacBasicRequest { destination: _destination, dacno: _dacno,
reqno: _reqno, param: _param } => {
forward!(_routing_table, _destination, *_rank, _repeaters, &packet);
#[cfg(has_ad9154)]
let (succeeded, retval) = {
#[cfg(rtio_frequency = "125.0")]
const LINERATE: u64 = 5_000_000_000;
#[cfg(rtio_frequency = "150.0")]
const LINERATE: u64 = 6_000_000_000;
match _reqno {
jdac_common::INIT => (board_artiq::ad9154::setup(_dacno, LINERATE).is_ok(), 0),
jdac_common::PRINT_STATUS => { board_artiq::ad9154::status(_dacno); (true, 0) },
jdac_common::PRBS => (board_artiq::ad9154::prbs(_dacno).is_ok(), 0),
jdac_common::STPL => (board_artiq::ad9154::stpl(_dacno, 4, 2).is_ok(), 0),
jdac_common::SYSREF_DELAY_DAC => { board_artiq::hmc830_7043::hmc7043::sysref_delay_dac(_dacno, _param); (true, 0) },
jdac_common::SYSREF_SLIP => { board_artiq::hmc830_7043::hmc7043::sysref_slip(); (true, 0) },
jdac_common::SYNC => {
match board_artiq::ad9154::sync(_dacno) {
Ok(false) => (true, 0),
Ok(true) => (true, 1),
Err(e) => {
error!("DAC sync failed: {}", e);
(false, 0)
}
}
},
jdac_common::DDMTD_SYSREF_RAW => (true, jdac_common::measure_ddmdt_phase_raw() as u8),
jdac_common::DDMTD_SYSREF => (true, jdac_common::measure_ddmdt_phase() as u8),
_ => (false, 0)
}
};
#[cfg(not(has_ad9154))]
let (succeeded, retval) = (false, 0);
drtioaux::send(0,
&drtioaux::Packet::JdacBasicReply { succeeded: succeeded, retval: retval })
}
_ => {
warn!("received unexpected aux packet");
Ok(())
@ -426,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_as_ckin2: true
};
#[cfg(all(has_si5324, rtio_frequency = "125.0"))]
const SI5324_SETTINGS: si5324::FrequencySettings
= si5324::FrequencySettings {
@ -465,6 +407,28 @@ const SI5324_SETTINGS: si5324::FrequencySettings
crystal_as_ckin2: true
};
fn sysclk_setup() {
let switched = unsafe {
csr::crg::switch_done_read()
};
if switched == 1 {
info!("Clocking has already been set up.");
return;
}
else {
#[cfg(has_si5324)]
si5324::setup(&SI5324_SETTINGS, si5324::Input::Ckin1).expect("cannot initialize Si5324");
info!("Switching sys clock, rebooting...");
// delay for clean UART log, wait until UART FIFO is empty
clock::spin_us(1300);
unsafe {
csr::drtio_transceiver::stable_clkin_write(1);
}
loop {}
}
}
#[no_mangle]
pub extern fn main() -> i32 {
extern {
@ -492,17 +456,6 @@ pub extern fn main() -> i32 {
io_expander1 = board_misoc::io_expander::IoExpander::new(1).unwrap();
io_expander0.init().expect("I2C I/O expander #0 initialization failed");
io_expander1.init().expect("I2C I/O expander #1 initialization failed");
#[cfg(has_wrpll)]
{
io_expander0.set_oe(1, 1 << 7).unwrap();
io_expander0.set(1, 7, true);
io_expander0.service().unwrap();
io_expander1.set_oe(0, 1 << 7).unwrap();
io_expander1.set_oe(1, 1 << 7).unwrap();
io_expander1.set(0, 7, true);
io_expander1.set(1, 7, true);
io_expander1.service().unwrap();
}
// Actively drive TX_DISABLE to false on SFP0..3
io_expander0.set_oe(0, 1 << 1).unwrap();
@ -517,33 +470,13 @@ pub extern fn main() -> i32 {
io_expander1.service().unwrap();
}
#[cfg(has_si5324)]
si5324::setup(&SI5324_SETTINGS, si5324::Input::Ckin1).expect("cannot initialize Si5324");
#[cfg(has_wrpll)]
wrpll::init();
sysclk_setup();
unsafe {
csr::drtio_transceiver::stable_clkin_write(1);
}
clock::spin_us(1500); // wait for CPLL/QPLL lock
#[cfg(not(has_jdcg))]
unsafe {
csr::drtio_transceiver::txenable_write(0xffffffffu32 as _);
}
#[cfg(has_wrpll)]
wrpll::diagnostics();
init_rtio_crg();
#[cfg(has_hmc830_7043)]
/* must be the first SPI init because of HMC830 SPI mode selection */
hmc830_7043::init().expect("cannot initialize HMC830/7043");
#[cfg(has_ad9154)]
{
jdac_common::init_ddmtd().expect("failed to initialize SYSREF DDMTD core");
for dacno in 0..csr::CONFIG_AD9154_COUNT {
board_artiq::ad9154::reset_and_detect(dacno as u8).expect("AD9154 DAC not detected");
}
}
init_rtio_crg();
#[cfg(has_drtio_routing)]
let mut repeaters = [repeater::Repeater::default(); csr::DRTIOREP.len()];
@ -558,11 +491,6 @@ pub extern fn main() -> i32 {
let mut hardware_tick_ts = 0;
loop {
#[cfg(has_jdcg)]
unsafe {
// Hide from uplink until RTM is ready
csr::drtio_transceiver::txenable_write(0xfffffffeu32 as _);
}
while !drtiosat_link_rx_up() {
drtiosat_process_errors();
for rep in repeaters.iter_mut() {
@ -582,15 +510,11 @@ pub extern fn main() -> i32 {
si5324::siphaser::select_recovered_clock(true).expect("failed to switch clocks");
si5324::siphaser::calibrate_skew().expect("failed to calibrate skew");
}
#[cfg(has_wrpll)]
wrpll::select_recovered_clock(true);
drtioaux::reset(0);
drtiosat_reset(false);
drtiosat_reset_phy(false);
#[cfg(has_jdcg)]
let mut was_up = false;
while drtiosat_link_rx_up() {
drtiosat_process_errors();
process_aux_packets(&mut repeaters, &mut routing_table, &mut rank);
@ -605,18 +529,6 @@ pub extern fn main() -> i32 {
hardware_tick(&mut hardware_tick_ts);
if drtiosat_tsc_loaded() {
info!("TSC loaded from uplink");
#[cfg(has_jdcg)]
{
// We assume that the RTM on repeater0 is up.
// Uplink should not send a TSC load command unless the link is
// up, and we are hiding when the RTM is down.
if let Err(e) = jdcg::jesd204sync::sysref_rtio_align() {
error!("failed to align SYSREF with TSC ({})", e);
}
if let Err(e) = jdcg::jesd204sync::resync_dacs() {
error!("DAC resync failed after SYSREF/TSC realignment ({})", e);
}
}
for rep in repeaters.iter() {
if let Err(e) = rep.sync_tsc() {
error!("failed to sync TSC ({})", e);
@ -626,46 +538,14 @@ pub extern fn main() -> i32 {
error!("aux packet error: {}", e);
}
}
#[cfg(has_jdcg)]
{
let is_up = repeaters[0].is_up();
if is_up && !was_up {
/*
* One side of the JESD204 elastic buffer is clocked by the jitter filter
* (Si5324 or WRPLL), the other by the RTM.
* The elastic buffer can operate only when those two clocks are derived from
* the same oscillator.
* This is the case when either of those conditions is true:
* (1) The DRTIO master and the RTM are clocked directly from a common external
* source, *and* the jitter filter has locked to the recovered clock.
* This clocking scheme may provide less noise and phase drift at the DACs.
* (2) The RTM clock is connected to the jitter filter output.
* To handle those cases, we simply keep the JESD204 core in reset unless the
* jitter filter is locked to the recovered clock.
*/
jdcg::jesd::reset(false);
let _ = jdcg::jdac::init();
jdcg::jesd204sync::sysref_auto_align();
jdcg::jdac::stpl();
unsafe {
csr::drtio_transceiver::txenable_write(0xffffffffu32 as _); // unhide
}
}
was_up = is_up;
}
}
#[cfg(has_jdcg)]
jdcg::jesd::reset(true);
drtiosat_reset_phy(true);
drtiosat_reset(true);
drtiosat_tsc_loaded();
info!("uplink is down, switching to local oscillator clock");
#[cfg(has_si5324)]
si5324::siphaser::select_recovered_clock(false).expect("failed to switch clocks");
#[cfg(has_wrpll)]
wrpll::select_recovered_clock(false);
}
}

View File

@ -25,12 +25,10 @@ def get_argparser():
Valid actions:
* gateware: write main gateware bitstream to flash
* rtm_gateware: write RTM gateware bitstream to flash
* bootloader: write bootloader to flash
* storage: write storage image to flash
* firmware: write firmware to flash
* load: load main gateware bitstream into device (volatile but fast)
* rtm_load: load RTM gateware bitstream into device
* erase: erase flash memory
* start: trigger the target to (re)load its gateware bitstream from flash
@ -61,7 +59,7 @@ Prerequisites:
help="SSH host to jump through")
parser.add_argument("-t", "--target", default="kasli",
help="target board, default: %(default)s, one of: "
"kasli sayma metlino kc705")
"kasli kc705")
parser.add_argument("-I", "--preinit-command", default=[], action="append",
help="add a pre-initialization OpenOCD command. "
"Useful for selecting a board when several are connected.")
@ -69,8 +67,6 @@ Prerequisites:
parser.add_argument("-d", "--dir", default=None, help="look for board binaries in this directory")
parser.add_argument("--srcbuild", help="board binaries directory is laid out as a source build tree",
default=False, action="store_true")
parser.add_argument("--no-rtm-jtag", help="do not attempt JTAG to the RTM",
default=False, action="store_true")
parser.add_argument("action", metavar="ACTION", nargs="*",
default=[],
help="actions to perform, default: flash everything")
@ -234,76 +230,6 @@ class ProgrammerXC7(Programmer):
"xc7_program xc7.tap")
class ProgrammerAMCRTM(Programmer):
_sector_size = 0x10000
def __init__(self, client, preinit_script):
Programmer.__init__(self, client, preinit_script)
add_commands(self._board_script,
"source {}".format(self._transfer_script("fpga/xilinx-xadc.cfg")),
"interface ftdi",
"ftdi_device_desc \"Quad RS232-HS\"",
"ftdi_vid_pid 0x0403 0x6011",
"ftdi_channel 0",
# EN_USB_JTAG on ADBUS7: out, high
# nTRST on ADBUS4: out, high, but R46 is DNP
"ftdi_layout_init 0x0098 0x008b",
"reset_config none",
"adapter_khz 5000",
"transport select jtag",
# tap 0, pld 0
"source {}".format(self._transfer_script("cpld/xilinx-xc7.cfg")),
# tap 1, pld 1
"set CHIP XCKU040",
"source {}".format(self._transfer_script("cpld/xilinx-xcu.cfg")))
self.add_flash_bank("spi0", "xcu", index=0)
self.add_flash_bank("spi1", "xcu", index=1)
add_commands(self._script, "echo \"RTM FPGA XADC:\"", "xadc_report xc7.tap")
add_commands(self._script, "echo \"AMC FPGA XADC:\"", "xadc_report xcu.tap")
def load_proxy(self):
self.load(find_proxy_bitfile("bscan_spi_xcku040.bit"), pld=1)
def start(self):
add_commands(self._script, "xcu_program xcu.tap")
class ProgrammerAMC(Programmer):
_sector_size = 0x10000
def __init__(self, client, preinit_script):
Programmer.__init__(self, client, preinit_script)
add_commands(self._board_script,
"source {}".format(self._transfer_script("fpga/xilinx-xadc.cfg")),
"interface ftdi",
"ftdi_device_desc \"Quad RS232-HS\"",
"ftdi_vid_pid 0x0403 0x6011",
"ftdi_channel 0",
# EN_USB_JTAG on ADBUS7: out, high
# nTRST on ADBUS4: out, high, but R46 is DNP
"ftdi_layout_init 0x0098 0x008b",
"reset_config none",
"adapter_khz 5000",
"transport select jtag",
"set CHIP XCKU040",
"source {}".format(self._transfer_script("cpld/xilinx-xcu.cfg")))
self.add_flash_bank("spi0", "xcu", index=0)
self.add_flash_bank("spi1", "xcu", index=1)
add_commands(self._script, "echo \"AMC FPGA XADC:\"", "xadc_report xcu.tap")
def load_proxy(self):
self.load(find_proxy_bitfile("bscan_spi_xcku040.bit"), pld=0)
def start(self):
add_commands(self._script, "xcu_program xcu.tap")
def main():
args = get_argparser().parse_args()
common_args.init_logger_from_args(args)
@ -316,21 +242,6 @@ def main():
"storage": ("spi0", 0x440000),
"firmware": ("spi0", 0x450000),
},
"sayma": {
"programmer": ProgrammerAMCRTM,
"gateware": ("spi0", 0x000000),
"bootloader": ("spi1", 0x000000),
"storage": ("spi1", 0x040000),
"firmware": ("spi1", 0x050000),
"rtm_gateware": ("spi1", 0x200000),
},
"metlino": {
"programmer": ProgrammerAMC,
"gateware": ("spi0", 0x000000),
"bootloader": ("spi1", 0x000000),
"storage": ("spi1", 0x040000),
"firmware": ("spi1", 0x050000),
},
"kc705": {
"programmer": partial(ProgrammerXC7, board="kc705", proxy="bscan_spi_xc7k325t.bit"),
"gateware": ("spi0", 0x000000),
@ -341,32 +252,21 @@ def main():
}[args.target]
if not args.action:
if args.target == "sayma":
args.action = "gateware rtm_gateware bootloader firmware start".split()
else:
args.action = "gateware bootloader firmware start".split()
args.action = "gateware bootloader firmware start".split()
needs_artifacts = any(
action in args.action
for action in ["gateware", "rtm_gateware", "bootloader", "firmware", "load", "rtm_load"])
for action in ["gateware", "bootloader", "firmware", "load"])
if needs_artifacts and args.dir is None:
raise ValueError("the directory containing the binaries need to be specified using -d.")
binary_dir = args.dir
if binary_dir is not None:
rtm_binary_dir = os.path.join(binary_dir, "rtm")
else:
rtm_binary_dir = None
if args.host is None:
client = LocalClient()
else:
client = SSHClient(args.host, args.jump)
if args.target == "sayma" and args.no_rtm_jtag:
programmer_cls = ProgrammerAMC
else:
programmer_cls = config["programmer"]
programmer = programmer_cls(client, preinit_script=args.preinit_command)
programmer = config["programmer"](client, preinit_script=args.preinit_command)
def artifact_path(this_binary_dir, *path_filename):
if args.srcbuild:
@ -377,20 +277,11 @@ def main():
*_, filename = path_filename
return os.path.join(this_binary_dir, filename)
def convert_gateware(bit_filename, header=False):
def convert_gateware(bit_filename):
bin_handle, bin_filename = tempfile.mkstemp(
prefix="artiq_", suffix="_" + os.path.basename(bit_filename))
with open(bit_filename, "rb") as bit_file, \
open(bin_handle, "wb") as bin_file:
if header:
bin_file.write(b"\x00"*8)
with open(bit_filename, "rb") as bit_file, open(bin_handle, "wb") as bin_file:
bit2bin(bit_file, bin_file)
if header:
magic = 0x5352544d # "SRTM", see sayma_rtm target
length = bin_file.tell() - 8
bin_file.seek(0)
bin_file.write(magic.to_bytes(4, byteorder="little"))
bin_file.write(length.to_bytes(4, byteorder="little"))
atexit.register(lambda: os.unlink(bin_filename))
return bin_filename
@ -399,11 +290,6 @@ def main():
gateware_bin = convert_gateware(
artifact_path(binary_dir, "gateware", "top.bit"))
programmer.write_binary(*config["gateware"], gateware_bin)
elif action == "rtm_gateware":
rtm_gateware_bin = convert_gateware(
artifact_path(rtm_binary_dir, "gateware", "top.bit"), header=True)
programmer.write_binary(*config["rtm_gateware"],
rtm_gateware_bin)
elif action == "bootloader":
bootloader_bin = artifact_path(binary_dir, "software", "bootloader", "bootloader.bin")
programmer.write_binary(*config["bootloader"], bootloader_bin)
@ -423,23 +309,12 @@ def main():
"Found firmware files: {}".format(" ".join(firmware_fbis)))
programmer.write_binary(*config["firmware"], firmware_fbis[0])
elif action == "load":
if args.target == "sayma":
gateware_bit = artifact_path(binary_dir, "gateware", "top.bit")
programmer.load(gateware_bit, 1)
else:
gateware_bit = artifact_path(binary_dir, "gateware", "top.bit")
programmer.load(gateware_bit, 0)
elif action == "rtm_load":
rtm_gateware_bit = artifact_path(rtm_binary_dir, "gateware", "top.bit")
programmer.load(rtm_gateware_bit, 0)
gateware_bit = artifact_path(binary_dir, "gateware", "top.bit")
programmer.load(gateware_bit, 0)
elif action == "start":
programmer.start()
elif action == "erase":
if args.target == "sayma" or args.target == "metlino":
programmer.erase_flash("spi0")
programmer.erase_flash("spi1")
else:
programmer.erase_flash("spi0")
programmer.erase_flash("spi0")
else:
raise ValueError("invalid action", action)

View File

@ -22,8 +22,7 @@ class Transmitter(Module, AutoCSR):
self.aux_tx = CSR()
self.specials.mem = Memory(mem_dw, max_packet//(mem_dw//8))
converter = ClockDomainsRenamer("rtio")(
stream.Converter(mem_dw, ll_dw))
converter = stream.Converter(mem_dw, ll_dw)
self.submodules += converter
# when continuously fed, the Converter outputs data continuously
@ -36,7 +35,7 @@ class Transmitter(Module, AutoCSR):
seen_eop_rst = Signal()
frame_r = Signal()
seen_eop = Signal()
self.sync.rtio += [
self.sync += [
If(link_layer.tx_aux_ack,
frame_r.eq(link_layer.tx_aux_frame),
If(frame_r & ~link_layer.tx_aux_frame, seen_eop.eq(1))
@ -44,13 +43,9 @@ class Transmitter(Module, AutoCSR):
If(seen_eop_rst, seen_eop.eq(0))
]
mem_port = self.mem.get_port(clock_domain="rtio")
mem_port = self.mem.get_port()
self.specials += mem_port
self.aux_tx_length.storage.attr.add("no_retiming")
tx_length = Signal(bits_for(max_packet))
self.specials += MultiReg(self.aux_tx_length.storage, tx_length, "rtio")
frame_counter_nbits = bits_for(max_packet) - log2_int(mem_dw//8)
frame_counter = Signal(frame_counter_nbits)
frame_counter_next = Signal(frame_counter_nbits)
@ -66,35 +61,33 @@ class Transmitter(Module, AutoCSR):
mem_port.adr.eq(frame_counter_next),
converter.sink.data.eq(mem_port.dat_r)
]
self.sync.rtio += frame_counter.eq(frame_counter_next)
start_tx = PulseSynchronizer("sys", "rtio")
tx_done = PulseSynchronizer("rtio", "sys")
self.submodules += start_tx, tx_done
self.comb += start_tx.i.eq(self.aux_tx.re)
tx_done = Signal()
self.sync += [
If(tx_done.o, self.aux_tx.w.eq(0)),
If(self.aux_tx.re, self.aux_tx.w.eq(1))
frame_counter.eq(frame_counter_next),
If(self.aux_tx.re, self.aux_tx.w.eq(1)),
If(tx_done, self.aux_tx.w.eq(0))
]
fsm = ClockDomainsRenamer("rtio")(FSM(reset_state="IDLE"))
fsm = FSM(reset_state="IDLE")
self.submodules += fsm
fsm.act("IDLE",
frame_counter_rst.eq(1),
seen_eop_rst.eq(1),
If(start_tx.o, NextState("TRANSMIT"))
If(self.aux_tx.re, NextState("TRANSMIT"))
)
fsm.act("TRANSMIT",
converter.sink.stb.eq(1),
If(converter.sink.ack,
frame_counter_ce.eq(1)
),
If(frame_counter_next == tx_length, NextState("WAIT_INTERFRAME"))
If(frame_counter_next == self.aux_tx_length.storage,
NextState("WAIT_INTERFRAME"))
)
fsm.act("WAIT_INTERFRAME",
If(seen_eop,
tx_done.i.eq(1),
tx_done.eq(1),
NextState("IDLE")
)
)

View File

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

View File

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

View File

@ -108,7 +108,7 @@ class RTController(Module):
cond_underflow = Signal()
self.comb += cond_underflow.eq((self.cri.o_timestamp[tsc.glbl_fine_ts_width:]
- self.csrs.underflow_margin.storage[tsc.glbl_fine_ts_width:]) < tsc.coarse_ts_sys)
- self.csrs.underflow_margin.storage[tsc.glbl_fine_ts_width:]) < tsc.coarse_ts)
# buffer space
buffer_space = Memory(16, 256)

View File

@ -15,15 +15,11 @@ class RTController(Module, AutoCSR):
self.command_missed_chan_sel = CSRStatus(24)
self.buffer_space_timeout_dest = CSRStatus(8)
self.specials += MultiReg(self.reset.storage, rt_packet.reset, "rtio")
self.sync += rt_packet.reset.eq(self.reset.storage)
set_time_stb = Signal()
set_time_ack = Signal()
self.submodules += CrossDomainRequest("rtio",
set_time_stb, set_time_ack, None,
rt_packet.set_time_stb, rt_packet.set_time_ack, None)
self.sync += [
If(set_time_ack, set_time_stb.eq(0)),
If(rt_packet.set_time_stb, set_time_stb.eq(0)),
If(self.set_time.re, set_time_stb.eq(1))
]
self.comb += self.set_time.w.eq(set_time_stb)
@ -31,10 +27,10 @@ class RTController(Module, AutoCSR):
errors = [
(rt_packet.err_unknown_packet_type, "rtio_rx", None, None),
(rt_packet.err_packet_truncated, "rtio_rx", None, None),
(rt_packet.err_command_missed, "rtio",
(rt_packet.err_command_missed, "sys",
Cat(rt_packet.command_missed_cmd, rt_packet.command_missed_chan_sel),
Cat(self.command_missed_cmd.status, self.command_missed_chan_sel.status)),
(rt_packet.err_buffer_space_timeout, "rtio",
(rt_packet.err_buffer_space_timeout, "sys",
rt_packet.buffer_space_destination, self.buffer_space_timeout_dest.status)
]

View File

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

View File

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

View File

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

View File

@ -1,23 +1,23 @@
from migen import *
from migen.genlib.cdc import MultiReg, PulseSynchronizer
from migen.genlib.cdc import MultiReg
from misoc.interconnect.csr import *
# This code assumes 125/62.5MHz reference clock and 100MHz, 125MHz or 150MHz RTIO
# This code assumes 125/62.5MHz reference clock and 100MHz or 125MHz RTIO
# frequency.
class SiPhaser7Series(Module, AutoCSR):
def __init__(self, si5324_clkin, rx_synchronizer,
ref_clk=None, ref_div2=False, ultrascale=False, rtio_clk_freq=150e6):
ref_clk=None, ref_div2=False, ultrascale=False, rtio_clk_freq=125e6):
self.switch_clocks = CSRStorage()
self.phase_shift = CSR()
self.phase_shift_done = CSRStatus(reset=1)
self.error = CSR()
assert rtio_clk_freq in (100e6, 125e6, 150e6)
assert rtio_clk_freq in (100e6, 125e6)
# 125MHz/62.5MHz reference clock to 100MHz/125MHz/150MHz. VCO @ 750MHz.
# 125MHz/62.5MHz reference clock to 100MHz/125MHz. VCO @ 750MHz.
# Used to provide a startup clock to the transceiver through the Si,
# we do not use the crystal reference so that the PFD (f3) frequency
# can be high.
@ -97,17 +97,14 @@ class SiPhaser7Series(Module, AutoCSR):
toggle_out = rx_synchronizer.resync(toggle_in)
toggle_out_expected = Signal()
self.sync.rtio += toggle_out_expected.eq(~toggle_out)
self.sync += toggle_out_expected.eq(~toggle_out)
error = Signal()
error_clear = PulseSynchronizer("sys", "rtio")
self.submodules += error_clear
self.sync.rtio += [
self.sync += [
If(toggle_out != toggle_out_expected, error.eq(1)),
If(error_clear.o, error.eq(0))
If(self.error.re, error.eq(0))
]
self.specials += MultiReg(error, self.error.w)
self.comb += error_clear.i.eq(self.error.re)
# expose MMCM outputs - used for clock constraints
self.mmcm_freerun_output = mmcm_freerun_output

View File

@ -33,7 +33,7 @@ class BruteforceClockAligner(Module):
check_counter = Signal(max=check_max_val+1)
check = Signal()
reset_check_counter = Signal()
self.sync.rtio_tx += [
self.sync += [
check.eq(0),
If(reset_check_counter,
check_counter.eq(check_max_val)
@ -47,7 +47,7 @@ class BruteforceClockAligner(Module):
)
]
checks_reset = PulseSynchronizer("rtio_tx", "rtio_rx")
checks_reset = PulseSynchronizer("sys", "rtio_rx")
self.submodules += checks_reset
comma_n = ~comma & 0b1111111111
@ -76,7 +76,7 @@ class BruteforceClockAligner(Module):
)
]
fsm = ClockDomainsRenamer("rtio_tx")(FSM(reset_state="WAIT_COMMA"))
fsm = FSM(reset_state="WAIT_COMMA")
self.submodules += fsm
fsm.act("WAIT_COMMA",

View File

@ -1,704 +0,0 @@
from functools import reduce
from operator import or_, and_
from migen import *
from migen.genlib.resetsync import AsyncResetSynchronizer
from misoc.cores.code_8b10b import Encoder, Decoder
from microscope import *
from artiq.gateware.drtio.core import TransceiverInterface, ChannelInterface
from artiq.gateware.drtio.transceiver.clock_aligner import BruteforceClockAligner
from artiq.gateware.drtio.transceiver.gth_ultrascale_init import *
class GTHSingle(Module):
def __init__(self, refclk, pads, sys_clk_freq, rtio_clk_freq, rtiox_mul, dw, mode):
assert (dw == 20) or (dw == 40)
assert mode in ["single", "master", "slave"]
self.mode = mode
# phase alignment
self.txsyncallin = Signal()
self.txphaligndone = Signal()
self.txsyncallin = Signal()
self.txsyncin = Signal()
self.txsyncout = Signal()
self.txdlysreset = Signal()
# # #
self.txenable = Signal()
nwords = dw//10
self.submodules.encoder = encoder = ClockDomainsRenamer("rtio_tx")(
Encoder(nwords, True))
self.submodules.decoders = decoders = [ClockDomainsRenamer("rtio_rx")(
(Decoder(True))) for _ in range(nwords)]
self.rx_ready = Signal()
# transceiver direct clock outputs
# for OBUFDS_GTE3
self.rxrecclkout = Signal()
# useful to specify clock constraints in a way palatable to Vivado
self.txoutclk = Signal()
self.rxoutclk = Signal()
# # #
# TX generates RTIO clock, init must be in system domain
self.submodules.tx_init = tx_init = GTHInit(sys_clk_freq, False, mode)
# RX receives restart commands from RTIO domain
rx_init = ClockDomainsRenamer("rtio_tx")(GTHInit(rtio_clk_freq, True))
self.submodules += rx_init
cpll_reset = Signal()
cpll_lock = Signal()
self.comb += [
cpll_reset.eq(tx_init.pllreset),
tx_init.plllock.eq(cpll_lock),
rx_init.plllock.eq(cpll_lock)
]
txdata = Signal(dw)
rxdata = Signal(dw)
rxphaligndone = Signal()
gth_params = dict(
p_ACJTAG_DEBUG_MODE =0b0,
p_ACJTAG_MODE =0b0,
p_ACJTAG_RESET =0b0,
p_ADAPT_CFG0 =0b1111100000000000,
p_ADAPT_CFG1 =0b0000000000000000,
p_ALIGN_COMMA_DOUBLE ="FALSE",
p_ALIGN_COMMA_ENABLE =0b0000000000,
p_ALIGN_COMMA_WORD =1,
p_ALIGN_MCOMMA_DET ="FALSE",
p_ALIGN_MCOMMA_VALUE =0b1010000011,
p_ALIGN_PCOMMA_DET ="FALSE",
p_ALIGN_PCOMMA_VALUE =0b0101111100,
p_A_RXOSCALRESET =0b0,
p_A_RXPROGDIVRESET =0b0,
p_A_TXPROGDIVRESET =0b0,
p_CBCC_DATA_SOURCE_SEL ="ENCODED",
p_CDR_SWAP_MODE_EN =0b0,
p_CHAN_BOND_KEEP_ALIGN ="FALSE",
p_CHAN_BOND_MAX_SKEW =1,
p_CHAN_BOND_SEQ_1_1 =0b0000000000,
p_CHAN_BOND_SEQ_1_2 =0b0000000000,
p_CHAN_BOND_SEQ_1_3 =0b0000000000,
p_CHAN_BOND_SEQ_1_4 =0b0000000000,
p_CHAN_BOND_SEQ_1_ENABLE =0b1111,
p_CHAN_BOND_SEQ_2_1 =0b0000000000,
p_CHAN_BOND_SEQ_2_2 =0b0000000000,
p_CHAN_BOND_SEQ_2_3 =0b0000000000,
p_CHAN_BOND_SEQ_2_4 =0b0000000000,
p_CHAN_BOND_SEQ_2_ENABLE =0b1111,
p_CHAN_BOND_SEQ_2_USE ="FALSE",
p_CHAN_BOND_SEQ_LEN =1,
p_CLK_CORRECT_USE ="FALSE",
p_CLK_COR_KEEP_IDLE ="FALSE",
p_CLK_COR_MAX_LAT =20,
p_CLK_COR_MIN_LAT =18,
p_CLK_COR_PRECEDENCE ="TRUE",
p_CLK_COR_REPEAT_WAIT =0,
p_CLK_COR_SEQ_1_1 =0b0000000000,
p_CLK_COR_SEQ_1_2 =0b0000000000,
p_CLK_COR_SEQ_1_3 =0b0000000000,
p_CLK_COR_SEQ_1_4 =0b0000000000,
p_CLK_COR_SEQ_1_ENABLE =0b1111,
p_CLK_COR_SEQ_2_1 =0b0000000000,
p_CLK_COR_SEQ_2_2 =0b0000000000,
p_CLK_COR_SEQ_2_3 =0b0000000000,
p_CLK_COR_SEQ_2_4 =0b0000000000,
p_CLK_COR_SEQ_2_ENABLE =0b1111,
p_CLK_COR_SEQ_2_USE ="FALSE",
p_CLK_COR_SEQ_LEN =1,
p_CPLL_CFG0 =0b0110011111111000,
p_CPLL_CFG1 =0b1010010010101100,
p_CPLL_CFG2 =0b0000000000000111,
p_CPLL_CFG3 =0b000000,
p_CPLL_FBDIV =5,
p_CPLL_FBDIV_45 =4,
p_CPLL_INIT_CFG0 =0b0000001010110010,
p_CPLL_INIT_CFG1 =0b00000000,
p_CPLL_LOCK_CFG =0b0000000111101000,
p_CPLL_REFCLK_DIV =1,
p_DDI_CTRL =0b00,
p_DDI_REALIGN_WAIT =15,
p_DEC_MCOMMA_DETECT ="FALSE",
p_DEC_PCOMMA_DETECT ="FALSE",
p_DEC_VALID_COMMA_ONLY ="FALSE",
p_DFE_D_X_REL_POS =0b0,
p_DFE_VCM_COMP_EN =0b0,
p_DMONITOR_CFG0 =0b0000000000,
p_DMONITOR_CFG1 =0b00000000,
p_ES_CLK_PHASE_SEL =0b0,
p_ES_CONTROL =0b000000,
p_ES_ERRDET_EN ="FALSE",
p_ES_EYE_SCAN_EN ="FALSE",
p_ES_HORZ_OFFSET =0b000000000000,
p_ES_PMA_CFG =0b0000000000,
p_ES_PRESCALE =0b00000,
p_ES_QUALIFIER0 =0b0000000000000000,
p_ES_QUALIFIER1 =0b0000000000000000,
p_ES_QUALIFIER2 =0b0000000000000000,
p_ES_QUALIFIER3 =0b0000000000000000,
p_ES_QUALIFIER4 =0b0000000000000000,
p_ES_QUAL_MASK0 =0b0000000000000000,
p_ES_QUAL_MASK1 =0b0000000000000000,
p_ES_QUAL_MASK2 =0b0000000000000000,
p_ES_QUAL_MASK3 =0b0000000000000000,
p_ES_QUAL_MASK4 =0b0000000000000000,
p_ES_SDATA_MASK0 =0b0000000000000000,
p_ES_SDATA_MASK1 =0b0000000000000000,
p_ES_SDATA_MASK2 =0b0000000000000000,
p_ES_SDATA_MASK3 =0b0000000000000000,
p_ES_SDATA_MASK4 =0b0000000000000000,
p_EVODD_PHI_CFG =0b00000000000,
p_EYE_SCAN_SWAP_EN =0b0,
p_FTS_DESKEW_SEQ_ENABLE =0b1111,
p_FTS_LANE_DESKEW_CFG =0b1111,
p_FTS_LANE_DESKEW_EN ="FALSE",
p_GEARBOX_MODE =0b00000,
p_GM_BIAS_SELECT =0b0,
p_LOCAL_MASTER =0b1,
p_OOBDIVCTL =0b00,
p_OOB_PWRUP =0b0,
p_PCI3_AUTO_REALIGN ="OVR_1K_BLK",
p_PCI3_PIPE_RX_ELECIDLE =0b0,
p_PCI3_RX_ASYNC_EBUF_BYPASS =0b00,
p_PCI3_RX_ELECIDLE_EI2_ENABLE =0b0,
p_PCI3_RX_ELECIDLE_H2L_COUNT =0b000000,
p_PCI3_RX_ELECIDLE_H2L_DISABLE =0b000,
p_PCI3_RX_ELECIDLE_HI_COUNT =0b000000,
p_PCI3_RX_ELECIDLE_LP4_DISABLE =0b0,
p_PCI3_RX_FIFO_DISABLE =0b0,
p_PCIE_BUFG_DIV_CTRL =0b0001000000000000,
p_PCIE_RXPCS_CFG_GEN3 =0b0000001010100100,
p_PCIE_RXPMA_CFG =0b0000000000001010,
p_PCIE_TXPCS_CFG_GEN3 =0b0010010010100100,
p_PCIE_TXPMA_CFG =0b0000000000001010,
p_PCS_PCIE_EN ="FALSE",
p_PCS_RSVD0 =0b0000000000000000,
p_PCS_RSVD1 =0b000,
p_PD_TRANS_TIME_FROM_P2 =0b000000111100,
p_PD_TRANS_TIME_NONE_P2 =0b00011001,
p_PD_TRANS_TIME_TO_P2 =0b01100100,
p_PLL_SEL_MODE_GEN12 =0b00,
p_PLL_SEL_MODE_GEN3 =0b11,
p_PMA_RSV1 =0b1111000000000000,
p_PROCESS_PAR =0b010,
p_RATE_SW_USE_DRP =0b1,
p_RESET_POWERSAVE_DISABLE =0b0,
)
gth_params.update(
p_RXBUFRESET_TIME =0b00011,
p_RXBUF_ADDR_MODE ="FAST",
p_RXBUF_EIDLE_HI_CNT =0b1000,
p_RXBUF_EIDLE_LO_CNT =0b0000,
p_RXBUF_EN ="FALSE",
p_RXBUF_RESET_ON_CB_CHANGE ="TRUE",
p_RXBUF_RESET_ON_COMMAALIGN ="FALSE",
p_RXBUF_RESET_ON_EIDLE ="FALSE",
p_RXBUF_RESET_ON_RATE_CHANGE ="TRUE",
p_RXBUF_THRESH_OVFLW =0,
p_RXBUF_THRESH_OVRD ="FALSE",
p_RXBUF_THRESH_UNDFLW =0,
p_RXCDRFREQRESET_TIME =0b00001,
p_RXCDRPHRESET_TIME =0b00001,
p_RXCDR_CFG0 =0b0000000000000000,
p_RXCDR_CFG0_GEN3 =0b0000000000000000,
p_RXCDR_CFG1 =0b0000000000000000,
p_RXCDR_CFG1_GEN3 =0b0000000000000000,
p_RXCDR_CFG2 =0b0000011111010110,
p_RXCDR_CFG2_GEN3 =0b0000011111100110,
p_RXCDR_CFG3 =0b0000000000000000,
p_RXCDR_CFG3_GEN3 =0b0000000000000000,
p_RXCDR_CFG4 =0b0000000000000000,
p_RXCDR_CFG4_GEN3 =0b0000000000000000,
p_RXCDR_CFG5 =0b0000000000000000,
p_RXCDR_CFG5_GEN3 =0b0000000000000000,
p_RXCDR_FR_RESET_ON_EIDLE =0b0,
p_RXCDR_HOLD_DURING_EIDLE =0b0,
p_RXCDR_LOCK_CFG0 =0b0100010010000000,
p_RXCDR_LOCK_CFG1 =0b0101111111111111,
p_RXCDR_LOCK_CFG2 =0b0111011111000011,
p_RXCDR_PH_RESET_ON_EIDLE =0b0,
p_RXCFOK_CFG0 =0b0100000000000000,
p_RXCFOK_CFG1 =0b0000000001100101,
p_RXCFOK_CFG2 =0b0000000000101110,
p_RXDFELPMRESET_TIME =0b0001111,
p_RXDFELPM_KL_CFG0 =0b0000000000000000,
p_RXDFELPM_KL_CFG1 =0b0000000000000010,
p_RXDFELPM_KL_CFG2 =0b0000000000000000,
p_RXDFE_CFG0 =0b0000101000000000,
p_RXDFE_CFG1 =0b0000000000000000,
p_RXDFE_GC_CFG0 =0b0000000000000000,
p_RXDFE_GC_CFG1 =0b0111100001110000,
p_RXDFE_GC_CFG2 =0b0000000000000000,
p_RXDFE_H2_CFG0 =0b0000000000000000,
p_RXDFE_H2_CFG1 =0b0000000000000000,
p_RXDFE_H3_CFG0 =0b0100000000000000,
p_RXDFE_H3_CFG1 =0b0000000000000000,
p_RXDFE_H4_CFG0 =0b0010000000000000,
p_RXDFE_H4_CFG1 =0b0000000000000011,
p_RXDFE_H5_CFG0 =0b0010000000000000,
p_RXDFE_H5_CFG1 =0b0000000000000011,
p_RXDFE_H6_CFG0 =0b0010000000000000,
p_RXDFE_H6_CFG1 =0b0000000000000000,
p_RXDFE_H7_CFG0 =0b0010000000000000,
p_RXDFE_H7_CFG1 =0b0000000000000000,
p_RXDFE_H8_CFG0 =0b0010000000000000,
p_RXDFE_H8_CFG1 =0b0000000000000000,
p_RXDFE_H9_CFG0 =0b0010000000000000,
p_RXDFE_H9_CFG1 =0b0000000000000000,
p_RXDFE_HA_CFG0 =0b0010000000000000,
p_RXDFE_HA_CFG1 =0b0000000000000000,
p_RXDFE_HB_CFG0 =0b0010000000000000,
p_RXDFE_HB_CFG1 =0b0000000000000000,
p_RXDFE_HC_CFG0 =0b0000000000000000,
p_RXDFE_HC_CFG1 =0b0000000000000000,
p_RXDFE_HD_CFG0 =0b0000000000000000,
p_RXDFE_HD_CFG1 =0b0000000000000000,
p_RXDFE_HE_CFG0 =0b0000000000000000,
p_RXDFE_HE_CFG1 =0b0000000000000000,
p_RXDFE_HF_CFG0 =0b0000000000000000,
p_RXDFE_HF_CFG1 =0b0000000000000000,
p_RXDFE_OS_CFG0 =0b1000000000000000,
p_RXDFE_OS_CFG1 =0b0000000000000000,
p_RXDFE_UT_CFG0 =0b1000000000000000,
p_RXDFE_UT_CFG1 =0b0000000000000011,
p_RXDFE_VP_CFG0 =0b1010101000000000,
p_RXDFE_VP_CFG1 =0b0000000000110011,
p_RXDLY_CFG =0b0000000000011111,
p_RXDLY_LCFG =0b0000000000110000,
p_RXELECIDLE_CFG ="SIGCFG_4",
p_RXGBOX_FIFO_INIT_RD_ADDR =4,
p_RXGEARBOX_EN ="FALSE",
p_RXISCANRESET_TIME =0b00001,
p_RXLPM_CFG =0b0000000000000000,
p_RXLPM_GC_CFG =0b0001000000000000,
p_RXLPM_KH_CFG0 =0b0000000000000000,
p_RXLPM_KH_CFG1 =0b0000000000000010,
p_RXLPM_OS_CFG0 =0b1000000000000000,
p_RXLPM_OS_CFG1 =0b0000000000000010,
p_RXOOB_CFG =0b000000110,
p_RXOOB_CLK_CFG ="PMA",
p_RXOSCALRESET_TIME =0b00011,
p_RXOUT_DIV =2,
p_RXPCSRESET_TIME =0b00011,
p_RXPHBEACON_CFG =0b0000000000000000,
p_RXPHDLY_CFG =0b0010000000100000,
p_RXPHSAMP_CFG =0b0010000100000000,
p_RXPHSLIP_CFG =0b0110011000100010,
p_RXPH_MONITOR_SEL =0b00000,
p_RXPI_CFG0 =0b00,
p_RXPI_CFG1 =0b00,
p_RXPI_CFG2 =0b00,
p_RXPI_CFG3 =0b00,
p_RXPI_CFG4 =0b1,
p_RXPI_CFG5 =0b1,
p_RXPI_CFG6 =0b000,
p_RXPI_LPM =0b0,
p_RXPI_VREFSEL =0b0,
p_RXPMACLK_SEL ="DATA",
p_RXPMARESET_TIME =0b00011,
p_RXPRBS_ERR_LOOPBACK =0b0,
p_RXPRBS_LINKACQ_CNT =15,
p_RXSLIDE_AUTO_WAIT =7,
p_RXSLIDE_MODE ="OFF",
p_RXSYNC_MULTILANE =0b0,
p_RXSYNC_OVRD =0b0,
p_RXSYNC_SKIP_DA =0b0,
p_RX_AFE_CM_EN =0b0,
p_RX_BIAS_CFG0 =0b0000101010110100,
p_RX_BUFFER_CFG =0b000000,
p_RX_CAPFF_SARC_ENB =0b0,
p_RX_CLK25_DIV =6,
p_RX_CLKMUX_EN =0b1,
p_RX_CLK_SLIP_OVRD =0b00000,
p_RX_CM_BUF_CFG =0b1010,
p_RX_CM_BUF_PD =0b0,
p_RX_CM_SEL =0b11,
p_RX_CM_TRIM =0b1010,
p_RX_CTLE3_LPF =0b00000001,
p_RX_DATA_WIDTH =dw,
p_RX_DDI_SEL =0b000000,
p_RX_DEFER_RESET_BUF_EN ="TRUE",
p_RX_DFELPM_CFG0 =0b0110,
p_RX_DFELPM_CFG1 =0b1,
p_RX_DFELPM_KLKH_AGC_STUP_EN =0b1,
p_RX_DFE_AGC_CFG0 =0b10,
p_RX_DFE_AGC_CFG1 =0b100,
p_RX_DFE_KL_LPM_KH_CFG0 =0b01,
p_RX_DFE_KL_LPM_KH_CFG1 =0b100,
p_RX_DFE_KL_LPM_KL_CFG0 =0b01,
p_RX_DFE_KL_LPM_KL_CFG1 =0b100,
p_RX_DFE_LPM_HOLD_DURING_EIDLE =0b0,
p_RX_DISPERR_SEQ_MATCH ="TRUE",
p_RX_DIVRESET_TIME =0b00001,
p_RX_EN_HI_LR =0b0,
p_RX_EYESCAN_VS_CODE =0b0000000,
p_RX_EYESCAN_VS_NEG_DIR =0b0,
p_RX_EYESCAN_VS_RANGE =0b00,
p_RX_EYESCAN_VS_UT_SIGN =0b0,
p_RX_FABINT_USRCLK_FLOP =0b0,
p_RX_INT_DATAWIDTH =dw==40,
p_RX_PMA_POWER_SAVE =0b0,
p_RX_PROGDIV_CFG =0.0,
p_RX_SAMPLE_PERIOD =0b111,
p_RX_SIG_VALID_DLY =11,
p_RX_SUM_DFETAPREP_EN =0b0,
p_RX_SUM_IREF_TUNE =0b0000,
p_RX_SUM_RES_CTRL =0b00,
p_RX_SUM_VCMTUNE =0b0000,
p_RX_SUM_VCM_OVWR =0b0,
p_RX_SUM_VREF_TUNE =0b000,
p_RX_TUNE_AFE_OS =0b10,
p_RX_WIDEMODE_CDR =0b0,
p_RX_XCLK_SEL ="RXUSR",
p_SAS_MAX_COM =64,
p_SAS_MIN_COM =36,
p_SATA_BURST_SEQ_LEN =0b1110,
p_SATA_CPLL_CFG ="VCO_3000MHZ",
p_SATA_MAX_BURST =8,
p_SATA_MAX_INIT =21,
p_SATA_MAX_WAKE =7,
p_SATA_MIN_BURST =4,
p_SATA_MIN_INIT =12,
p_SATA_MIN_WAKE =4,
p_SHOW_REALIGN_COMMA ="TRUE",
p_SIM_RECEIVER_DETECT_PASS ="TRUE",
p_SIM_RESET_SPEEDUP ="TRUE",
p_SIM_TX_EIDLE_DRIVE_LEVEL =0b0,
p_SIM_VERSION =2,
p_TAPDLY_SET_TX =0b00,
p_TEMPERATUR_PAR =0b0010,
p_TERM_RCAL_CFG =0b100001000010000,
p_TERM_RCAL_OVRD =0b000,
p_TRANS_TIME_RATE =0b00001110,
p_TST_RSV0 =0b00000000,
p_TST_RSV1 =0b00000000,
)
gth_params.update(
p_TXBUF_EN ="FALSE",
p_TXBUF_RESET_ON_RATE_CHANGE ="TRUE",
p_TXDLY_CFG =0b0000000000001001,
p_TXDLY_LCFG =0b0000000001010000,
p_TXDRVBIAS_N =0b1010,
p_TXDRVBIAS_P =0b1010,
p_TXFIFO_ADDR_CFG ="LOW",
p_TXGBOX_FIFO_INIT_RD_ADDR =4,
p_TXGEARBOX_EN ="FALSE",
p_TXOUT_DIV =2,
p_TXPCSRESET_TIME =0b00011,
p_TXPHDLY_CFG0 =0b0010000000100000,
p_TXPHDLY_CFG1 =0b0000000001110101,
p_TXPH_CFG =0b0000100110000000,
p_TXPH_MONITOR_SEL =0b00000,
p_TXPI_CFG0 =0b00,
p_TXPI_CFG1 =0b00,
p_TXPI_CFG2 =0b00,
p_TXPI_CFG3 =0b1,
p_TXPI_CFG4 =0b1,
p_TXPI_CFG5 =0b000,
p_TXPI_GRAY_SEL =0b0,
p_TXPI_INVSTROBE_SEL =0b0,
p_TXPI_LPM =0b0,
p_TXPI_PPMCLK_SEL ="TXUSRCLK2",
p_TXPI_PPM_CFG =0b00000000,
p_TXPI_SYNFREQ_PPM =0b001,
p_TXPI_VREFSEL =0b0,
p_TXPMARESET_TIME =0b00011,
p_TXSYNC_MULTILANE =0 if mode == "single" else 1,
p_TXSYNC_OVRD =0b0,
p_TXSYNC_SKIP_DA =0b0,
p_TX_CLK25_DIV =6,
p_TX_CLKMUX_EN =0b1,
p_TX_DATA_WIDTH =dw,
p_TX_DCD_CFG =0b000010,
p_TX_DCD_EN =0b0,
p_TX_DEEMPH0 =0b000000,
p_TX_DEEMPH1 =0b000000,
p_TX_DIVRESET_TIME =0b00001,
p_TX_DRIVE_MODE ="DIRECT",
p_TX_EIDLE_ASSERT_DELAY =0b100,
p_TX_EIDLE_DEASSERT_DELAY =0b011,
p_TX_EML_PHI_TUNE =0b0,
p_TX_FABINT_USRCLK_FLOP =0b0,
p_TX_IDLE_DATA_ZERO =0b0,
p_TX_INT_DATAWIDTH =dw==40,
p_TX_LOOPBACK_DRIVE_HIZ ="FALSE",
p_TX_MAINCURSOR_SEL =0b0,
p_TX_MARGIN_FULL_0 =0b1001111,
p_TX_MARGIN_FULL_1 =0b1001110,
p_TX_MARGIN_FULL_2 =0b1001100,
p_TX_MARGIN_FULL_3 =0b1001010,
p_TX_MARGIN_FULL_4 =0b1001000,
p_TX_MARGIN_LOW_0 =0b1000110,
p_TX_MARGIN_LOW_1 =0b1000101,
p_TX_MARGIN_LOW_2 =0b1000011,
p_TX_MARGIN_LOW_3 =0b1000010,
p_TX_MARGIN_LOW_4 =0b1000000,
p_TX_MODE_SEL =0b000,
p_TX_PMADATA_OPT =0b0,
p_TX_PMA_POWER_SAVE =0b0,
p_TX_PROGCLK_SEL ="PREPI",
p_TX_PROGDIV_CFG =dw/rtiox_mul,
p_TX_QPI_STATUS_EN =0b0,
p_TX_RXDETECT_CFG =0b00000000110010,
p_TX_RXDETECT_REF =0b100,
p_TX_SAMPLE_PERIOD =0b111,
p_TX_SARC_LPBK_ENB =0b0,
p_TX_XCLK_SEL ="TXUSR",
p_USE_PCS_CLK_PHASE_SEL =0b0,
p_WB_MODE =0b00,
)
gth_params.update(
# Reset modes
i_GTRESETSEL=0,
i_RESETOVRD=0,
i_CPLLRESET=0,
i_CPLLPD=cpll_reset,
o_CPLLLOCK=cpll_lock,
i_CPLLLOCKEN=1,
i_CPLLREFCLKSEL=0b001,
i_TSTIN=2**20-1,
i_GTREFCLK0=refclk,
# TX clock
o_TXOUTCLK=self.txoutclk,
i_TXSYSCLKSEL=0b00,
i_TXPLLCLKSEL=0b00,
i_TXOUTCLKSEL=0b101,
# TX Startup/Reset
i_GTTXRESET=tx_init.gtXxreset,
o_TXRESETDONE=tx_init.Xxresetdone,
i_TXDLYSRESET=tx_init.Xxdlysreset if mode != "slave" else self.txdlysreset,
o_TXDLYSRESETDONE=tx_init.Xxdlysresetdone,
o_TXPHALIGNDONE=tx_init.Xxphaligndone,
i_TXUSERRDY=tx_init.Xxuserrdy,
i_TXSYNCMODE=mode != "slave",
i_TXSYNCALLIN=self.txsyncallin,
i_TXSYNCIN=self.txsyncin,
o_TXSYNCOUT=self.txsyncout,
# TX data
i_TXINHIBIT=~self.txenable,
i_TXCTRL0=Cat(*[txdata[10*i+8] for i in range(nwords)]),
i_TXCTRL1=Cat(*[txdata[10*i+9] for i in range(nwords)]),
i_TXDATA=Cat(*[txdata[10*i:10*i+8] for i in range(nwords)]),
i_TXUSRCLK=ClockSignal("rtio_tx"),
i_TXUSRCLK2=ClockSignal("rtio_tx"),
# TX electrical
i_TXPD=0b00,
i_TXBUFDIFFCTRL=0b000,
i_TXDIFFCTRL=0b1100,
# RX Startup/Reset
i_GTRXRESET=rx_init.gtXxreset,
o_RXRESETDONE=rx_init.Xxresetdone,
i_RXDLYSRESET=rx_init.Xxdlysreset,
o_RXPHALIGNDONE=rxphaligndone,
i_RXSYNCALLIN=rxphaligndone,
i_RXUSERRDY=rx_init.Xxuserrdy,
i_RXSYNCIN=0,
i_RXSYNCMODE=1,
o_RXSYNCDONE=rx_init.Xxsyncdone,
# RX AFE
i_RXDFEAGCCTRL=1,
i_RXDFEXYDEN=1,
i_RXLPMEN=1,
i_RXOSINTCFG=0xd,
i_RXOSINTEN=1,
# RX clock
i_RXRATE=0,
i_RXDLYBYPASS=0,
i_RXSYSCLKSEL=0b00,
i_RXOUTCLKSEL=0b010,
i_RXPLLCLKSEL=0b00,
o_RXRECCLKOUT=self.rxrecclkout,
o_RXOUTCLK=self.rxoutclk,
i_RXUSRCLK=ClockSignal("rtio_rx"),
i_RXUSRCLK2=ClockSignal("rtio_rx"),
# RX data
o_RXCTRL0=Cat(*[rxdata[10*i+8] for i in range(nwords)]),
o_RXCTRL1=Cat(*[rxdata[10*i+9] for i in range(nwords)]),
o_RXDATA=Cat(*[rxdata[10*i:10*i+8] for i in range(nwords)]),
# RX electrical
i_RXPD=Replicate(rx_init.restart, 2),
i_RXELECIDLEMODE=0b11,
# Pads
i_GTHRXP=pads.rxp,
i_GTHRXN=pads.rxn,
o_GTHTXP=pads.txp,
o_GTHTXN=pads.txn
)
self.specials += Instance("GTHE3_CHANNEL", **gth_params)
self.comb += self.txphaligndone.eq(tx_init.Xxphaligndone)
self.submodules += [
add_probe_async("drtio_gth", "cpll_lock", cpll_lock),
add_probe_async("drtio_gth", "txuserrdy", tx_init.Xxuserrdy),
add_probe_async("drtio_gth", "tx_init_done", tx_init.done),
add_probe_async("drtio_gth", "rxuserrdy", rx_init.Xxuserrdy),
add_probe_async("drtio_gth", "rx_init_done", rx_init.done),
add_probe_buffer("drtio_gth", "txdata", txdata, clock_domain="rtio_tx"),
add_probe_buffer("drtio_gth", "rxdata", rxdata, clock_domain="rtio_rx")
]
# tx clocking
tx_reset_deglitched = Signal()
tx_reset_deglitched.attr.add("no_retiming")
self.sync += tx_reset_deglitched.eq(~tx_init.done)
self.clock_domains.cd_rtio_tx = ClockDomain()
self.clock_domains.cd_rtiox_tx = ClockDomain()
if mode == "master" or mode == "single":
self.specials += [
Instance("BUFG_GT", i_I=self.txoutclk, o_O=self.cd_rtiox_tx.clk, i_DIV=0),
Instance("BUFG_GT", i_I=self.txoutclk, o_O=self.cd_rtio_tx.clk, i_DIV=rtiox_mul-1)
]
self.specials += AsyncResetSynchronizer(self.cd_rtio_tx, tx_reset_deglitched)
# rx clocking
rx_reset_deglitched = Signal()
rx_reset_deglitched.attr.add("no_retiming")
self.sync.rtio_tx += rx_reset_deglitched.eq(~rx_init.done)
self.clock_domains.cd_rtio_rx = ClockDomain()
self.specials += [
Instance("BUFG_GT", i_I=self.rxoutclk, o_O=self.cd_rtio_rx.clk),
AsyncResetSynchronizer(self.cd_rtio_rx, rx_reset_deglitched)
]
# tx data
self.comb += txdata.eq(Cat(*[encoder.output[i] for i in range(nwords)]))
# rx data
for i in range(nwords):
self.comb += decoders[i].input.eq(rxdata[10*i:10*(i+1)])
# clock alignment
clock_aligner = BruteforceClockAligner(0b0101111100, rtio_clk_freq)
self.submodules += clock_aligner
self.comb += [
clock_aligner.rxdata.eq(rxdata),
rx_init.restart.eq(clock_aligner.restart),
self.rx_ready.eq(clock_aligner.ready)
]
self.submodules += add_probe_async("drtio_gth", "clock_aligner_ready", clock_aligner.ready)
class GTHTXPhaseAlignement(Module):
# TX Buffer Bypass in Single-Lane/Multi-Lane Auto Mode (ug576)
def __init__(self, gths):
txsyncallin = Signal()
txsync = Signal()
txphaligndone = Signal(len(gths))
txdlysreset = Signal()
ready_for_align = Signal(len(gths))
all_ready_for_align = Signal()
for i, gth in enumerate(gths):
# Common to all transceivers
self.comb += [
ready_for_align[i].eq(1),
gth.txsyncin.eq(txsync),
gth.txsyncallin.eq(txsyncallin),
txphaligndone[i].eq(gth.txphaligndone)
]
# Specific to Master or Single transceivers
if gth.mode == "master" or gth.mode == "single":
self.comb += [
gth.tx_init.all_ready_for_align.eq(all_ready_for_align),
txsync.eq(gth.txsyncout),
txdlysreset.eq(gth.tx_init.Xxdlysreset)
]
# Specific to Slave transceivers
else:
self.comb += [
ready_for_align[i].eq(gth.tx_init.ready_for_align),
gth.txdlysreset.eq(txdlysreset),
]
self.comb += [
txsyncallin.eq(reduce(and_, [txphaligndone[i] for i in range(len(gths))])),
all_ready_for_align.eq(reduce(and_, [ready_for_align[i] for i in range(len(gths))]))
]
class GTH(Module, TransceiverInterface):
def __init__(self, clock_pads, data_pads, sys_clk_freq, rtio_clk_freq, rtiox_mul=2, dw=20, master=0, clock_recout_pads=None):
self.nchannels = nchannels = len(data_pads)
self.gths = []
# # #
create_buf = hasattr(clock_pads, "p")
if create_buf:
refclk = Signal()
ibufds_ceb = Signal()
self.specials += Instance("IBUFDS_GTE3",
i_CEB=ibufds_ceb,
i_I=clock_pads.p,
i_IB=clock_pads.n,
o_O=refclk)
else:
refclk = clock_pads
rtio_tx_clk = Signal()
channel_interfaces = []
for i in range(nchannels):
if nchannels == 1:
mode = "single"
else:
mode = "master" if i == master else "slave"
gth = GTHSingle(refclk, data_pads[i], sys_clk_freq, rtio_clk_freq, rtiox_mul, dw, mode)
if mode == "master":
self.comb += rtio_tx_clk.eq(gth.cd_rtio_tx.clk)
elif mode == "slave":
self.comb += gth.cd_rtio_tx.clk.eq(rtio_tx_clk)
self.gths.append(gth)
setattr(self.submodules, "gth"+str(i), gth)
channel_interface = ChannelInterface(gth.encoder, gth.decoders)
self.comb += channel_interface.rx_ready.eq(gth.rx_ready)
channel_interfaces.append(channel_interface)
self.submodules.tx_phase_alignment = GTHTXPhaseAlignement(self.gths)
TransceiverInterface.__init__(self, channel_interfaces)
for n, gth in enumerate(self.gths):
self.comb += gth.txenable.eq(self.txenable.storage[n])
self.clock_domains.cd_rtiox = ClockDomain(reset_less=True)
if create_buf:
# GTH PLLs recover on their own from an interrupted clock input,
# but be paranoid about HMC7043 noise.
self.stable_clkin.storage.attr.add("no_retiming")
self.comb += ibufds_ceb.eq(~self.stable_clkin.storage)
self.comb += [
self.cd_rtio.clk.eq(self.gths[master].cd_rtio_tx.clk),
self.cd_rtiox.clk.eq(self.gths[master].cd_rtiox_tx.clk),
self.cd_rtio.rst.eq(reduce(or_, [gth.cd_rtio_tx.rst for gth in self.gths]))
]
for i in range(nchannels):
self.comb += [
getattr(self, "cd_rtio_rx" + str(i)).clk.eq(self.gths[i].cd_rtio_rx.clk),
getattr(self, "cd_rtio_rx" + str(i)).rst.eq(self.gths[i].cd_rtio_rx.rst)
]
if clock_recout_pads is not None:
self.specials += Instance("OBUFDS_GTE3",
p_REFCLK_EN_TX_PATH=0b1,
p_REFCLK_ICNTL_TX=0b00111,
i_I=self.gths[0].rxrecclkout,
i_CEB=0,
o_O=clock_recout_pads.p, o_OB=clock_recout_pads.n)

View File

@ -1,157 +0,0 @@
from math import ceil
from migen import *
from migen.genlib.cdc import MultiReg
from migen.genlib.misc import WaitTimer
__all__ = ["GTHInit"]
class GTHInit(Module):
def __init__(self, sys_clk_freq, rx, mode="master"):
assert not (rx and mode != "master")
self.done = Signal()
self.restart = Signal()
# GTH signals
self.plllock = Signal()
self.pllreset = Signal()
self.gtXxreset = Signal()
self.Xxresetdone = Signal()
self.Xxdlysreset = Signal()
self.Xxdlysresetdone = Signal()
self.Xxphaligndone = Signal()
self.Xxsyncdone = Signal()
self.Xxuserrdy = Signal()
self.all_ready_for_align = Signal(reset=1)
self.ready_for_align = Signal()
# # #
# Double-latch transceiver asynch outputs
plllock = Signal()
Xxresetdone = Signal()
Xxdlysresetdone = Signal()
Xxphaligndone = Signal()
Xxsyncdone = Signal()
self.specials += [
MultiReg(self.plllock, plllock),
MultiReg(self.Xxresetdone, Xxresetdone),
MultiReg(self.Xxdlysresetdone, Xxdlysresetdone),
MultiReg(self.Xxphaligndone, Xxphaligndone),
MultiReg(self.Xxsyncdone, Xxsyncdone)
]
# Deglitch FSM outputs driving transceiver asynch inputs
gtXxreset = Signal()
Xxdlysreset = Signal()
Xxuserrdy = Signal()
self.sync += [
self.gtXxreset.eq(gtXxreset),
self.Xxdlysreset.eq(Xxdlysreset),
self.Xxuserrdy.eq(Xxuserrdy)
]
# PLL reset must be at least 2us
pll_reset_cycles = ceil(2000*sys_clk_freq/1000000000)
pll_reset_timer = WaitTimer(pll_reset_cycles)
self.submodules += pll_reset_timer
startup_fsm = ResetInserter()(FSM(reset_state="RESET_ALL"))
self.submodules += startup_fsm
ready_timer = WaitTimer(int(sys_clk_freq/1000))
self.submodules += ready_timer
self.comb += [
ready_timer.wait.eq(~self.done & ~startup_fsm.reset),
startup_fsm.reset.eq(self.restart | ready_timer.done)
]
if rx:
cdr_stable_timer = WaitTimer(1024)
self.submodules += cdr_stable_timer
Xxphaligndone_r = Signal(reset=1)
Xxphaligndone_rising = Signal()
self.sync += Xxphaligndone_r.eq(Xxphaligndone)
self.comb += Xxphaligndone_rising.eq(Xxphaligndone & ~Xxphaligndone_r)
startup_fsm.act("RESET_ALL",
gtXxreset.eq(1),
self.pllreset.eq(1),
pll_reset_timer.wait.eq(1),
If(pll_reset_timer.done,
NextState("RELEASE_PLL_RESET")
)
)
startup_fsm.act("RELEASE_PLL_RESET",
gtXxreset.eq(1),
If(plllock, NextState("RELEASE_GTH_RESET"))
)
# Release GTH reset and wait for GTH resetdone
# (from UG476, GTH is reset on falling edge
# of gtXxreset)
if rx:
startup_fsm.act("RELEASE_GTH_RESET",
Xxuserrdy.eq(1),
cdr_stable_timer.wait.eq(1),
If(Xxresetdone & cdr_stable_timer.done, NextState("ALIGN"))
)
else:
startup_fsm.act("RELEASE_GTH_RESET",
Xxuserrdy.eq(1),
If(Xxresetdone,
If(mode == "slave",
NextState("WAIT_ALIGN")
).Else(
NextState("ALIGN")
)
)
)
# Start delay alignment (pulse)
startup_fsm.act("ALIGN",
Xxuserrdy.eq(1),
If(self.all_ready_for_align,
Xxdlysreset.eq(1),
NextState("WAIT_ALIGN")
)
)
if rx:
# Wait for delay alignment
startup_fsm.act("WAIT_ALIGN",
Xxuserrdy.eq(1),
If(Xxsyncdone,
NextState("READY")
)
)
else:
# Wait for delay alignment
startup_fsm.act("WAIT_ALIGN",
Xxuserrdy.eq(1),
self.ready_for_align.eq(1),
If(Xxdlysresetdone,
If(mode == "slave",
NextState("WAIT_LAST_ALIGN_DONE")
).Else(
NextState("WAIT_FIRST_ALIGN_DONE")
)
)
)
# Wait 2 rising edges of Xxphaligndone
# (from UG576 in TX Buffer Bypass in Single-Lane Auto Mode)
startup_fsm.act("WAIT_FIRST_ALIGN_DONE",
Xxuserrdy.eq(1),
If(Xxphaligndone_rising, NextState("WAIT_LAST_ALIGN_DONE"))
)
startup_fsm.act("WAIT_LAST_ALIGN_DONE",
Xxuserrdy.eq(1),
If(Xxphaligndone_rising, NextState("READY"))
)
startup_fsm.act("READY",
Xxuserrdy.eq(1),
self.done.eq(1),
If(self.restart, NextState("RESET_ALL"))
)

View File

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

View File

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

View File

@ -11,11 +11,13 @@ class GTXInit(Module):
# Choose between Auto Mode and Manual Mode for TX/RX phase alignment with buffer bypassed:
# * Auto Mode: When only single lane is involved, as suggested by Xilinx (AR59612)
# * Manual Mode: When only multi-lane is involved, as suggested by Xilinx (AR59612)
def __init__(self, sys_clk_freq, rx, mode="single"):
def __init__(self, clk_freq, rx, mode="single"):
assert isinstance(rx, bool)
assert mode in ["single", "master", "slave"]
self.mode = mode
self.stable_clkin = Signal()
self.done = Signal()
self.restart = Signal()
@ -83,13 +85,13 @@ class GTXInit(Module):
# After configuration, transceiver resets have to stay low for
# at least 500ns (see AR43482)
startup_cycles = ceil(500*sys_clk_freq/1000000000)
startup_cycles = ceil(500*clk_freq/1000000000)
startup_timer = WaitTimer(startup_cycles)
self.submodules += startup_timer
# PLL reset should be 1 period of refclk
# (i.e. 1/(125MHz) for the case of RTIO @ 125MHz)
pll_reset_cycles = ceil(sys_clk_freq/125e6)
pll_reset_cycles = ceil(clk_freq/125e6)
pll_reset_timer = WaitTimer(pll_reset_cycles)
self.submodules += pll_reset_timer
@ -108,7 +110,7 @@ class GTXInit(Module):
startup_fsm.act("INITIAL",
startup_timer.wait.eq(1),
If(startup_timer.done, NextState("RESET_ALL"))
If(startup_timer.done & self.stable_clkin, NextState("RESET_ALL"))
)
startup_fsm.act("RESET_ALL",
gtXxreset.eq(1),

View File

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

View File

@ -1,156 +0,0 @@
from migen import *
from migen.genlib.resetsync import AsyncResetSynchronizer
from migen.genlib.cdc import MultiReg, PulseSynchronizer
from misoc.interconnect.csr import *
from artiq.gateware.drtio.wrpll.si549 import Si549
from artiq.gateware.drtio.wrpll.ddmtd import DDMTD, Collector
from artiq.gateware.drtio.wrpll import thls, filters
class FrequencyCounter(Module, AutoCSR):
def __init__(self, timer_width=23, counter_width=23, domains=["helper", "rtio", "rtio_rx0"]):
for domain in domains:
name = "counter_" + domain
counter = CSRStatus(counter_width, name=name)
setattr(self, name, counter)
self.update_en = CSRStorage()
timer = Signal(timer_width)
timer_tick = Signal()
self.sync += Cat(timer, timer_tick).eq(timer + 1)
for domain in domains:
sync_domain = getattr(self.sync, domain)
divider = Signal(2)
sync_domain += divider.eq(divider + 1)
divided = Signal()
divided.attr.add("no_retiming")
sync_domain += divided.eq(divider[-1])
divided_sys = Signal()
self.specials += MultiReg(divided, divided_sys)
divided_sys_r = Signal()
divided_tick = Signal()
self.sync += divided_sys_r.eq(divided_sys)
self.comb += divided_tick.eq(divided_sys & ~divided_sys_r)
counter = Signal(counter_width)
counter_csr = getattr(self, "counter_" + domain)
self.sync += [
If(timer_tick,
If(self.update_en.storage, counter_csr.status.eq(counter)),
counter.eq(0),
).Else(
If(divided_tick, counter.eq(counter + 1))
)
]
class WRPLL(Module, AutoCSR):
def __init__(self, helper_clk_pads, main_dcxo_i2c, helper_dxco_i2c, ddmtd_inputs, N=15):
self.helper_reset = CSRStorage(reset=1)
self.collector_reset = CSRStorage(reset=1)
self.filter_reset = CSRStorage(reset=1)
self.adpll_offset_helper = CSRStorage(24)
self.adpll_offset_main = CSRStorage(24)
self.tag_arm = CSR()
self.main_diff_tag = CSRStatus(32)
self.helper_diff_tag = CSRStatus(32)
self.ref_tag = CSRStatus(N)
self.main_tag = CSRStatus(N)
main_diff_tag_32 = Signal((32, True))
helper_diff_tag_32 = Signal((32, True))
self.comb += [
self.main_diff_tag.status.eq(main_diff_tag_32),
self.helper_diff_tag.status.eq(helper_diff_tag_32)
]
self.clock_domains.cd_helper = ClockDomain()
self.clock_domains.cd_collector = ClockDomain()
self.clock_domains.cd_filter = ClockDomain()
self.helper_reset.storage.attr.add("no_retiming")
self.filter_reset.storage.attr.add("no_retiming")
self.specials += Instance("IBUFGDS",
i_I=helper_clk_pads.p, i_IB=helper_clk_pads.n,
o_O=self.cd_helper.clk)
self.comb += [
self.cd_collector.clk.eq(self.cd_collector.clk),
self.cd_filter.clk.eq(self.cd_helper.clk),
]
self.specials += [
AsyncResetSynchronizer(self.cd_helper, self.helper_reset.storage),
AsyncResetSynchronizer(self.cd_collector, self.collector_reset.storage),
AsyncResetSynchronizer(self.cd_filter, self.filter_reset.storage)
]
self.submodules.helper_dcxo = Si549(helper_dxco_i2c)
self.submodules.main_dcxo = Si549(main_dcxo_i2c)
# for diagnostics and PLL initialization
self.submodules.frequency_counter = FrequencyCounter()
ddmtd_counter = Signal(N)
self.sync.helper += ddmtd_counter.eq(ddmtd_counter + 1)
self.submodules.ddmtd_ref = DDMTD(ddmtd_counter, ddmtd_inputs.rec_clk)
self.submodules.ddmtd_main = DDMTD(ddmtd_counter, ddmtd_inputs.main_xo)
collector_cd = ClockDomainsRenamer("collector")
filter_cd = ClockDomainsRenamer("filter")
self.submodules.collector = collector_cd(Collector(N))
self.submodules.filter_helper = filter_cd(
thls.make(filters.helper, data_width=48))
self.submodules.filter_main = filter_cd(
thls.make(filters.main, data_width=48))
self.comb += [
self.collector.tag_ref.eq(self.ddmtd_ref.h_tag),
self.collector.ref_stb.eq(self.ddmtd_ref.h_tag_update),
self.collector.tag_main.eq(self.ddmtd_main.h_tag),
self.collector.main_stb.eq(self.ddmtd_main.h_tag_update)
]
collector_stb_ps = PulseSynchronizer("helper", "sys")
self.submodules += collector_stb_ps
self.sync.helper += collector_stb_ps.i.eq(self.collector.out_stb)
collector_stb_sys = Signal()
self.sync += collector_stb_sys.eq(collector_stb_ps.o)
main_diff_tag_sys = Signal((N+2, True))
helper_diff_tag_sys = Signal((N+2, True))
ref_tag_sys = Signal(N)
main_tag_sys = Signal(N)
self.specials += MultiReg(self.collector.out_main, main_diff_tag_sys)
self.specials += MultiReg(self.collector.out_helper, helper_diff_tag_sys)
self.specials += MultiReg(self.collector.tag_ref, ref_tag_sys)
self.specials += MultiReg(self.collector.tag_main, main_tag_sys)
self.sync += [
If(self.tag_arm.re & self.tag_arm.r, self.tag_arm.w.eq(1)),
If(collector_stb_sys,
self.tag_arm.w.eq(0),
If(self.tag_arm.w,
main_diff_tag_32.eq(main_diff_tag_sys),
helper_diff_tag_32.eq(helper_diff_tag_sys),
self.ref_tag.status.eq(ref_tag_sys),
self.main_tag.status.eq(main_tag_sys)
)
)
]
self.comb += [
self.filter_helper.input.eq(self.collector.out_helper << 22),
self.filter_helper.input_stb.eq(self.collector.out_stb),
self.filter_main.input.eq(self.collector.out_main),
self.filter_main.input_stb.eq(self.collector.out_stb)
]
self.sync.helper += [
self.helper_dcxo.adpll_stb.eq(self.filter_helper.output_stb),
self.helper_dcxo.adpll.eq(self.filter_helper.output + self.adpll_offset_helper.storage),
self.main_dcxo.adpll_stb.eq(self.filter_main.output_stb),
self.main_dcxo.adpll.eq(self.filter_main.output + self.adpll_offset_main.storage)
]

View File

@ -1,221 +0,0 @@
from migen import *
from migen.genlib.cdc import PulseSynchronizer, MultiReg
from migen.genlib.fsm import FSM
from misoc.interconnect.csr import *
class DDMTDSamplerExtFF(Module):
def __init__(self, ddmtd_inputs):
self.rec_clk = Signal()
self.main_xo = Signal()
# # #
# TODO: s/h timing at FPGA pads
if hasattr(ddmtd_inputs, "rec_clk"):
rec_clk_1 = ddmtd_inputs.rec_clk
else:
rec_clk_1 = Signal()
self.specials += Instance("IBUFDS",
i_I=ddmtd_inputs.rec_clk_p, i_IB=ddmtd_inputs.rec_clk_n,
o_O=rec_clk_1)
if hasattr(ddmtd_inputs, "main_xo"):
main_xo_1 = ddmtd_inputs.main_xo
else:
main_xo_1 = Signal()
self.specials += Instance("IBUFDS",
i_I=ddmtd_inputs.main_xo_p, i_IB=ddmtd_inputs.main_xo_n,
o_O=main_xo_1)
self.specials += [
Instance("FD", i_C=ClockSignal("helper"),
i_D=rec_clk_1, o_Q=self.rec_clk,
attr={("IOB", "TRUE")}),
Instance("FD", i_C=ClockSignal("helper"),
i_D=main_xo_1, o_Q=self.main_xo,
attr={("IOB", "TRUE")}),
]
class DDMTDSamplerGTP(Module):
def __init__(self, gtp, main_xo_pads):
self.rec_clk = Signal()
self.main_xo = Signal()
# # #
# Getting the main XO signal from IBUFDS_GTE2 is problematic because
# the transceiver PLL craps out if an improper clock signal is applied,
# so we are disabling the buffer until the clock is stable.
main_xo_se = Signal()
rec_clk_1 = Signal()
main_xo_1 = Signal()
self.specials += [
Instance("IBUFDS",
i_I=main_xo_pads.p, i_IB=main_xo_pads.n,
o_O=main_xo_se),
Instance("FD", i_C=ClockSignal("helper"),
i_D=gtp.cd_rtio_rx0.clk, o_Q=rec_clk_1,
attr={("DONT_TOUCH", "TRUE")}),
Instance("FD", i_C=ClockSignal("helper"),
i_D=rec_clk_1, o_Q=self.rec_clk,
attr={("DONT_TOUCH", "TRUE")}),
Instance("FD", i_C=ClockSignal("helper"),
i_D=main_xo_se, o_Q=main_xo_1,
attr={("IOB", "TRUE")}),
Instance("FD", i_C=ClockSignal("helper"),
i_D=main_xo_1, o_Q=self.main_xo,
attr={("DONT_TOUCH", "TRUE")}),
]
class DDMTDDeglitcherFirstEdge(Module):
def __init__(self, input_signal, blind_period=128):
self.detect = Signal()
self.tag_correction = 0
rising = Signal()
input_signal_r = Signal()
self.sync.helper += [
input_signal_r.eq(input_signal),
rising.eq(input_signal & ~input_signal_r)
]
blind_counter = Signal(max=blind_period)
self.sync.helper += [
If(blind_counter != 0, blind_counter.eq(blind_counter - 1)),
If(input_signal_r, blind_counter.eq(blind_period - 1)),
self.detect.eq(rising & (blind_counter == 0))
]
class DDMTD(Module):
def __init__(self, counter, input_signal):
# in helper clock domain
self.h_tag = Signal(len(counter))
self.h_tag_update = Signal()
# # #
deglitcher = DDMTDDeglitcherFirstEdge(input_signal)
self.submodules += deglitcher
self.sync.helper += [
self.h_tag_update.eq(0),
If(deglitcher.detect,
self.h_tag_update.eq(1),
self.h_tag.eq(counter + deglitcher.tag_correction)
)
]
class Collector(Module):
"""Generates loop filter inputs from DDMTD outputs.
The input to the main DCXO lock loop filter is the difference between the
reference and main tags after unwrapping (see below).
The input to the helper DCXO lock loop filter is the difference between the
current reference tag and the previous reference tag after unwrapping.
When the WR PLL is locked, the following ideally (no noise/jitter) obtain:
- f_main = f_ref
- f_helper = f_ref * 2^N/(2^N+1)
- f_beat = f_ref - f_helper = f_ref / (2^N + 1) (cycle time is: dt=1/f_beat)
- the reference and main DCXO tags are equal to each other at every cycle
(the main DCXO lock drives this difference to 0)
- the reference and main DCXO tags both have the same value at each cycle
(the tag difference for each DDMTD is given by
f_helper*dt = f_helper/f_beat = 2^N, which causes the N-bit DDMTD counter
to wrap around and come back to its previous value)
Note that we currently lock the frequency of the helper DCXO to the
reference clock, not it's phase. As a result, while the tag differences are
controlled, their absolute values are arbitrary. We could consider moving
the helper lock to a phase lock at some point in the future...
Since the DDMTD counter is only N bits, it is possible for tag values to
wrap around. This will happen frequently if the locked tags happens to be
near the edges of the counter, so that jitter can easily cause a phase wrap.
But, it can also easily happen during lock acquisition or other transients.
To avoid glitches in the output, we unwrap the tag differences. Currently
we do this in hardware, but we should consider extending the processor to
allow us to do it inside the filters. Since the processor uses wider
signals, this would significantly extend the overall glitch-free
range of the PLL and may aid lock acquisition.
"""
def __init__(self, N):
self.ref_stb = Signal()
self.main_stb = Signal()
self.tag_ref = Signal(N)
self.tag_main = Signal(N)
self.out_stb = Signal()
self.out_main = Signal((N+2, True))
self.out_helper = Signal((N+2, True))
self.out_tag_ref = Signal(N)
self.out_tag_main = Signal(N)
tag_ref_r = Signal(N)
tag_main_r = Signal(N)
main_tag_diff = Signal((N+2, True))
helper_tag_diff = Signal((N+2, True))
# # #
fsm = FSM(reset_state="IDLE")
self.submodules += fsm
fsm.act("IDLE",
NextValue(self.out_stb, 0),
If(self.ref_stb & self.main_stb,
NextValue(tag_ref_r, self.tag_ref),
NextValue(tag_main_r, self.tag_main),
NextState("DIFF")
).Elif(self.ref_stb,
NextValue(tag_ref_r, self.tag_ref),
NextState("WAITMAIN")
).Elif(self.main_stb,
NextValue(tag_main_r, self.tag_main),
NextState("WAITREF")
)
)
fsm.act("WAITREF",
If(self.ref_stb,
NextValue(tag_ref_r, self.tag_ref),
NextState("DIFF")
)
)
fsm.act("WAITMAIN",
If(self.main_stb,
NextValue(tag_main_r, self.tag_main),
NextState("DIFF")
)
)
fsm.act("DIFF",
NextValue(main_tag_diff, tag_main_r - tag_ref_r),
NextValue(helper_tag_diff, tag_ref_r - self.out_tag_ref),
NextState("UNWRAP")
)
fsm.act("UNWRAP",
If(main_tag_diff - self.out_main > 2**(N-1),
NextValue(main_tag_diff, main_tag_diff - 2**N)
).Elif(self.out_main - main_tag_diff > 2**(N-1),
NextValue(main_tag_diff, main_tag_diff + 2**N)
),
If(helper_tag_diff - self.out_helper > 2**(N-1),
NextValue(helper_tag_diff, helper_tag_diff - 2**N)
).Elif(self.out_helper - helper_tag_diff > 2**(N-1),
NextValue(helper_tag_diff, helper_tag_diff + 2**N)
),
NextState("OUTPUT")
)
fsm.act("OUTPUT",
NextValue(self.out_tag_ref, tag_ref_r),
NextValue(self.out_tag_main, tag_main_r),
NextValue(self.out_main, main_tag_diff),
NextValue(self.out_helper, helper_tag_diff),
NextValue(self.out_stb, 1),
NextState("IDLE")
)

View File

@ -1,61 +0,0 @@
helper_xn1 = 0
helper_xn2 = 0
helper_yn0 = 0
helper_yn1 = 0
helper_yn2 = 0
helper_out = 0
main_xn1 = 0
main_xn2 = 0
main_yn0 = 0
main_yn1 = 0
main_yn2 = 0
def helper(tag_diff):
global helper_xn1, helper_xn2, helper_yn0, \
helper_yn1, helper_yn2, helper_out
helper_xn0 = 0 - tag_diff # *(2**22)
helper_yr = 4294967296
helper_yn2 = helper_yn1
helper_yn1 = helper_yn0
helper_yn0 = (284885690 * (helper_xn0
+ (217319150 * helper_xn1 >> 44)
- (17591968725108 * helper_xn2 >> 44)
) >> 44
) + (35184372088832*helper_yn1 >> 44) - helper_yn2
helper_xn2 = helper_xn1
helper_xn1 = helper_xn0
helper_out = 268435456*helper_yn0 >> 44
helper_out = min(helper_out, helper_yr)
helper_out = max(helper_out, 0 - helper_yr)
return helper_out
def main(main_xn0):
global main_xn1, main_xn2, main_yn0, main_yn1, main_yn2
main_yr = 4294967296
main_yn2 = main_yn1
main_yn1 = main_yn0
main_yn0 = (
((133450380908*(((35184372088832*main_xn0) >> 44) +
((17592186044417*main_xn1) >> 44))) >> 44) +
((29455872930889*main_yn1) >> 44) -
((12673794781453*main_yn2) >> 44))
main_xn2 = main_xn1
main_xn1 = main_xn0
main_yn0 = min(main_yn0, main_yr)
main_yn0 = max(main_yn0, 0 - main_yr)
return main_yn0

View File

@ -1,340 +0,0 @@
from migen import *
from migen.genlib.fsm import *
from migen.genlib.cdc import MultiReg, PulseSynchronizer, BlindTransfer
from misoc.interconnect.csr import *
class I2CClockGen(Module):
def __init__(self, width):
self.load = Signal(width)
self.clk2x = Signal()
cnt = Signal.like(self.load)
self.comb += [
self.clk2x.eq(cnt == 0),
]
self.sync += [
If(self.clk2x,
cnt.eq(self.load),
).Else(
cnt.eq(cnt - 1),
)
]
class I2CMasterMachine(Module):
def __init__(self, clock_width):
self.scl = Signal(reset=1)
self.sda_o = Signal(reset=1)
self.sda_i = Signal()
self.submodules.cg = CEInserter()(I2CClockGen(clock_width))
self.start = Signal()
self.stop = Signal()
self.write = Signal()
self.ack = Signal()
self.data = Signal(8)
self.ready = Signal()
###
bits = Signal(4)
data = Signal(8)
fsm = CEInserter()(FSM("IDLE"))
self.submodules += fsm
fsm.act("IDLE",
self.ready.eq(1),
If(self.start,
NextState("START0"),
).Elif(self.stop,
NextState("STOP0"),
).Elif(self.write,
NextValue(bits, 8),
NextValue(data, self.data),
NextState("WRITE0")
)
)
fsm.act("START0",
NextValue(self.scl, 1),
NextState("START1")
)
fsm.act("START1",
NextValue(self.sda_o, 0),
NextState("IDLE")
)
fsm.act("STOP0",
NextValue(self.scl, 0),
NextState("STOP1")
)
fsm.act("STOP1",
NextValue(self.sda_o, 0),
NextState("STOP2")
)
fsm.act("STOP2",
NextValue(self.scl, 1),
NextState("STOP3")
)
fsm.act("STOP3",
NextValue(self.sda_o, 1),
NextState("IDLE")
)
fsm.act("WRITE0",
NextValue(self.scl, 0),
NextState("WRITE1")
)
fsm.act("WRITE1",
If(bits == 0,
NextValue(self.sda_o, 1),
NextState("READACK0"),
).Else(
NextValue(self.sda_o, data[7]),
NextState("WRITE2"),
)
)
fsm.act("WRITE2",
NextValue(self.scl, 1),
NextValue(data[1:], data[:-1]),
NextValue(bits, bits - 1),
NextState("WRITE0"),
)
fsm.act("READACK0",
NextValue(self.scl, 1),
NextState("READACK1"),
)
fsm.act("READACK1",
NextValue(self.ack, ~self.sda_i),
NextState("IDLE")
)
run = Signal()
idle = Signal()
self.comb += [
run.eq((self.start | self.stop | self.write) & self.ready),
idle.eq(~run & fsm.ongoing("IDLE")),
self.cg.ce.eq(~idle),
fsm.ce.eq(run | self.cg.clk2x),
]
class ADPLLProgrammer(Module):
def __init__(self):
self.i2c_divider = Signal(16)
self.i2c_address = Signal(7)
self.adpll = Signal(24)
self.stb = Signal()
self.busy = Signal()
self.nack = Signal()
self.scl = Signal()
self.sda_i = Signal()
self.sda_o = Signal()
self.scl.attr.add("no_retiming")
self.sda_o.attr.add("no_retiming")
# # #
master = I2CMasterMachine(16)
self.submodules += master
self.comb += [
master.cg.load.eq(self.i2c_divider),
self.scl.eq(master.scl),
master.sda_i.eq(self.sda_i),
self.sda_o.eq(master.sda_o)
]
fsm = FSM()
self.submodules += fsm
adpll = Signal.like(self.adpll)
fsm.act("IDLE",
If(self.stb,
NextValue(adpll, self.adpll),
NextState("START")
)
)
fsm.act("START",
master.start.eq(1),
If(master.ready, NextState("DEVADDRESS"))
)
fsm.act("DEVADDRESS",
master.data.eq(self.i2c_address << 1),
master.write.eq(1),
If(master.ready, NextState("REGADRESS"))
)
fsm.act("REGADRESS",
master.data.eq(231),
master.write.eq(1),
If(master.ready,
If(master.ack,
NextState("DATA0")
).Else(
self.nack.eq(1),
NextState("STOP")
)
)
)
fsm.act("DATA0",
master.data.eq(adpll[0:8]),
master.write.eq(1),
If(master.ready,
If(master.ack,
NextState("DATA1")
).Else(
self.nack.eq(1),
NextState("STOP")
)
)
)
fsm.act("DATA1",
master.data.eq(adpll[8:16]),
master.write.eq(1),
If(master.ready,
If(master.ack,
NextState("DATA2")
).Else(
self.nack.eq(1),
NextState("STOP")
)
)
)
fsm.act("DATA2",
master.data.eq(adpll[16:24]),
master.write.eq(1),
If(master.ready,
If(~master.ack, self.nack.eq(1)),
NextState("STOP")
)
)
fsm.act("STOP",
master.stop.eq(1),
If(master.ready,
If(~master.ack, self.nack.eq(1)),
NextState("IDLE")
)
)
self.comb += self.busy.eq(~fsm.ongoing("IDLE"))
def simulate_programmer():
from migen.sim.core import run_simulation
dut = ADPLLProgrammer()
def generator():
yield dut.i2c_divider.eq(4)
yield dut.i2c_address.eq(0x55)
yield
yield dut.adpll.eq(0x123456)
yield dut.stb.eq(1)
yield
yield dut.stb.eq(0)
yield
while (yield dut.busy):
yield
for _ in range(20):
yield
run_simulation(dut, generator(), vcd_name="tb.vcd")
class Si549(Module, AutoCSR):
def __init__(self, pads):
self.gpio_enable = CSRStorage(reset=1)
self.gpio_in = CSRStatus(2)
self.gpio_out = CSRStorage(2)
self.gpio_oe = CSRStorage(2)
self.i2c_divider = CSRStorage(16, reset=75)
self.i2c_address = CSRStorage(7)
self.errors = CSR(2)
# in helper clock domain
self.adpll = Signal(24)
self.adpll_stb = Signal()
# # #
programmer = ClockDomainsRenamer("helper")(ADPLLProgrammer())
self.submodules += programmer
self.i2c_divider.storage.attr.add("no_retiming")
self.i2c_address.storage.attr.add("no_retiming")
self.specials += [
MultiReg(self.i2c_divider.storage, programmer.i2c_divider, "helper"),
MultiReg(self.i2c_address.storage, programmer.i2c_address, "helper")
]
self.comb += [
programmer.adpll.eq(self.adpll),
programmer.stb.eq(self.adpll_stb)
]
self.gpio_enable.storage.attr.add("no_retiming")
self.gpio_out.storage.attr.add("no_retiming")
self.gpio_oe.storage.attr.add("no_retiming")
# SCL GPIO and mux
ts_scl = TSTriple(1)
self.specials += ts_scl.get_tristate(pads.scl)
status = Signal()
self.comb += self.gpio_in.status[0].eq(status)
self.specials += MultiReg(ts_scl.i, status)
self.comb += [
If(self.gpio_enable.storage,
ts_scl.o.eq(self.gpio_out.storage[0]),
ts_scl.oe.eq(self.gpio_oe.storage[0])
).Else(
ts_scl.o.eq(0),
ts_scl.oe.eq(~programmer.scl)
)
]
# SDA GPIO and mux
ts_sda = TSTriple(1)
self.specials += ts_sda.get_tristate(pads.sda)
status = Signal()
self.comb += self.gpio_in.status[1].eq(status)
self.specials += MultiReg(ts_sda.i, status)
self.comb += [
If(self.gpio_enable.storage,
ts_sda.o.eq(self.gpio_out.storage[1]),
ts_sda.oe.eq(self.gpio_oe.storage[1])
).Else(
ts_sda.o.eq(0),
ts_sda.oe.eq(~programmer.sda_o)
)
]
self.specials += MultiReg(ts_sda.i, programmer.sda_i, "helper")
# Error reporting
collision_cdc = BlindTransfer("helper", "sys")
self.submodules += collision_cdc
self.comb += collision_cdc.i.eq(programmer.stb & programmer.busy)
nack_cdc = PulseSynchronizer("helper", "sys")
self.submodules += nack_cdc
self.comb += nack_cdc.i.eq(programmer.nack)
for n, trig in enumerate([collision_cdc.o, nack_cdc.o]):
self.sync += [
If(self.errors.re & self.errors.r[n], self.errors.w[n].eq(0)),
If(trig, self.errors.w[n].eq(1))
]
if __name__ == "__main__":
simulate_programmer()

View File

@ -1,618 +0,0 @@
import inspect
import ast
from copy import copy
import operator
from functools import reduce
from collections import OrderedDict
from migen import *
from migen.genlib.fsm import *
class Isn:
def __init__(self, immediate=None, inputs=None, outputs=None):
if inputs is None:
inputs = []
if outputs is None:
outputs = []
self.immediate = immediate
self.inputs = inputs
self.outputs = outputs
def __repr__(self):
r = "<"
r += self.__class__.__name__
if self.immediate is not None:
r += " (" + str(self.immediate) + ")"
for inp in self.inputs:
r += " r" + str(inp)
if self.outputs:
r += " ->"
for outp in self.outputs:
r += " r" + str(outp)
r += ">"
return r
class NopIsn(Isn):
opcode = 0
class AddIsn(Isn):
opcode = 1
class SubIsn(Isn):
opcode = 2
class MulShiftIsn(Isn):
opcode = 3
# opcode = 4: MulShift with alternate shift
class MinIsn(Isn):
opcode = 5
class MaxIsn(Isn):
opcode = 6
class CopyIsn(Isn):
opcode = 7
class InputIsn(Isn):
opcode = 8
class OutputIsn(Isn):
opcode = 9
class EndIsn(Isn):
opcode = 10
class ASTCompiler:
def __init__(self):
self.program = []
self.data = []
self.next_ssa_reg = -1
self.constants = dict()
self.names = dict()
self.globals = OrderedDict()
def get_ssa_reg(self):
r = self.next_ssa_reg
self.next_ssa_reg -= 1
return r
def add_global(self, name):
if name not in self.globals:
r = len(self.data)
self.data.append(0)
self.names[name] = r
self.globals[name] = r
def input(self, name):
target = self.get_ssa_reg()
self.program.append(InputIsn(outputs=[target]))
self.names[name] = target
def emit(self, node):
if isinstance(node, ast.BinOp):
if isinstance(node.op, ast.RShift):
if not isinstance(node.left, ast.BinOp) or not isinstance(node.left.op, ast.Mult):
raise NotImplementedError
if not isinstance(node.right, ast.Num):
raise NotImplementedError
left = self.emit(node.left.left)
right = self.emit(node.left.right)
cons = lambda **kwargs: MulShiftIsn(immediate=node.right.n, **kwargs)
else:
left = self.emit(node.left)
right = self.emit(node.right)
if isinstance(node.op, ast.Add):
cons = AddIsn
elif isinstance(node.op, ast.Sub):
cons = SubIsn
elif isinstance(node.op, ast.Mult):
cons = lambda **kwargs: MulShiftIsn(immediate=0, **kwargs)
else:
raise NotImplementedError
output = self.get_ssa_reg()
self.program.append(cons(inputs=[left, right], outputs=[output]))
return output
elif isinstance(node, ast.Call):
if not isinstance(node.func, ast.Name):
raise NotImplementedError
funcname = node.func.id
if node.keywords:
raise NotImplementedError
inputs = [self.emit(x) for x in node.args]
if funcname == "min":
cons = MinIsn
elif funcname == "max":
cons = MaxIsn
else:
raise NotImplementedError
output = self.get_ssa_reg()
self.program.append(cons(inputs=inputs, outputs=[output]))
return output
elif isinstance(node, (ast.Num, ast.UnaryOp)):
if isinstance(node, ast.UnaryOp):
if not isinstance(node.operand, ast.Num):
raise NotImplementedError
if isinstance(node.op, ast.UAdd):
transform = lambda x: x
elif isinstance(node.op, ast.USub):
transform = operator.neg
elif isinstance(node.op, ast.Invert):
transform = operator.invert
else:
raise NotImplementedError
node = node.operand
else:
transform = lambda x: x
n = transform(node.n)
if n in self.constants:
return self.constants[n]
else:
r = len(self.data)
self.data.append(n)
self.constants[n] = r
return r
elif isinstance(node, ast.Name):
return self.names[node.id]
elif isinstance(node, ast.Assign):
output = self.emit(node.value)
for target in node.targets:
assert isinstance(target, ast.Name)
self.names[target.id] = output
elif isinstance(node, ast.Return):
value = self.emit(node.value)
self.program.append(OutputIsn(inputs=[value]))
elif isinstance(node, ast.Global):
pass
else:
raise NotImplementedError
class Processor:
def __init__(self, data_width=32, multiplier_stages=2):
self.data_width = data_width
self.multiplier_stages = multiplier_stages
self.multiplier_shifts = []
self.program_rom_size = None
self.data_ram_size = None
self.opcode_bits = 4
self.reg_bits = None
def get_instruction_latency(self, isn):
return {
AddIsn: 2,
SubIsn: 2,
MulShiftIsn: 1 + self.multiplier_stages,
MinIsn: 2,
MaxIsn: 2,
CopyIsn: 1,
InputIsn: 1
}[isn.__class__]
def encode_instruction(self, isn, exit):
opcode = isn.opcode
if isn.immediate is not None and not isinstance(isn, MulShiftIsn):
r0 = isn.immediate
if len(isn.inputs) >= 1:
r1 = isn.inputs[0]
else:
r1 = 0
else:
if len(isn.inputs) >= 1:
r0 = isn.inputs[0]
else:
r0 = 0
if len(isn.inputs) >= 2:
r1 = isn.inputs[1]
else:
r1 = 0
r = 0
for value, bits in ((exit, self.reg_bits), (r1, self.reg_bits), (r0, self.reg_bits), (opcode, self.opcode_bits)):
r <<= bits
r |= value
return r
def instruction_bits(self):
return 3*self.reg_bits + self.opcode_bits
def implement(self, program, data):
return ProcessorImpl(self, program, data)
class Scheduler:
def __init__(self, processor, reserved_data, program):
self.processor = processor
self.reserved_data = reserved_data
self.used_registers = set(range(self.reserved_data))
self.exits = dict()
self.program = program
self.remaining = copy(program)
self.output = []
def allocate_register(self):
r = min(set(range(max(self.used_registers) + 2)) - self.used_registers)
self.used_registers.add(r)
return r
def free_register(self, r):
assert r >= self.reserved_data
self.used_registers.discard(r)
def find_inputs(self, cycle, isn):
mapped_inputs = []
for inp in isn.inputs:
if inp >= 0:
mapped_inputs.append(inp)
else:
found = False
for i in range(cycle):
if i in self.exits:
r, rm = self.exits[i]
if r == inp:
mapped_inputs.append(rm)
found = True
break
if not found:
return None
return mapped_inputs
def schedule_one(self, isn):
cycle = len(self.output)
mapped_inputs = self.find_inputs(cycle, isn)
if mapped_inputs is None:
return False
if isn.outputs:
# check that exit slot is free
latency = self.processor.get_instruction_latency(isn)
exit = cycle + latency
if exit in self.exits:
return False
# avoid RAW hazard with global writeback
for output in isn.outputs:
if output >= 0:
for risn in self.remaining:
for inp in risn.inputs:
if inp == output:
return False
# Instruction can be scheduled
self.remaining.remove(isn)
for inp, minp in zip(isn.inputs, mapped_inputs):
can_free = inp < 0 and all(inp != rinp for risn in self.remaining for rinp in risn.inputs)
if can_free:
self.free_register(minp)
if isn.outputs:
assert len(isn.outputs) == 1
if isn.outputs[0] < 0:
output = self.allocate_register()
else:
output = isn.outputs[0]
self.exits[exit] = (isn.outputs[0], output)
self.output.append(isn.__class__(immediate=isn.immediate, inputs=mapped_inputs))
return True
def schedule(self):
while self.remaining:
success = False
for isn in self.remaining:
if self.schedule_one(isn):
success = True
break
if not success:
self.output.append(NopIsn())
self.output += [NopIsn()]*(max(self.exits.keys()) - len(self.output) + 1)
return self.output
class CompiledProgram:
def __init__(self, processor, program, exits, data, glbs):
self.processor = processor
self.program = program
self.exits = exits
self.data = data
self.globals = glbs
def pretty_print(self):
for cycle, isn in enumerate(self.program):
l = "{:4d} {:15}".format(cycle, str(isn))
if cycle in self.exits:
l += " -> r{}".format(self.exits[cycle])
print(l)
def dimension_processor(self):
self.processor.program_rom_size = len(self.program)
self.processor.data_ram_size = len(self.data)
self.processor.reg_bits = (self.processor.data_ram_size - 1).bit_length()
for isn in self.program:
if isinstance(isn, MulShiftIsn) and isn.immediate not in self.processor.multiplier_shifts:
self.processor.multiplier_shifts.append(isn.immediate)
def encode(self):
r = []
for i, isn in enumerate(self.program):
exit = self.exits.get(i, 0)
r.append(self.processor.encode_instruction(isn, exit))
return r
def compile(processor, function):
node = ast.parse(inspect.getsource(function))
assert isinstance(node, ast.Module)
assert len(node.body) == 1
node = node.body[0]
assert isinstance(node, ast.FunctionDef)
assert len(node.args.args) == 1
arg = node.args.args[0].arg
body = node.body
astcompiler = ASTCompiler()
for node in body:
if isinstance(node, ast.Global):
for name in node.names:
astcompiler.add_global(name)
arg_r = astcompiler.input(arg)
for node in body:
astcompiler.emit(node)
if isinstance(node, ast.Return):
break
for glbl, location in astcompiler.globals.items():
new_location = astcompiler.names[glbl]
if new_location != location:
astcompiler.program.append(CopyIsn(inputs=[new_location], outputs=[location]))
scheduler = Scheduler(processor, len(astcompiler.data), astcompiler.program)
scheduler.schedule()
program = copy(scheduler.output)
program.append(EndIsn())
max_reg = max(max(max(isn.inputs + [0]) for isn in program), max(v[1] for k, v in scheduler.exits.items()))
return CompiledProgram(
processor=processor,
program=program,
exits={k: v[1] for k, v in scheduler.exits.items()},
data=astcompiler.data + [0]*(max_reg - len(astcompiler.data) + 1),
glbs=astcompiler.globals)
class BaseUnit(Module):
def __init__(self, data_width):
self.stb_i = Signal()
self.i0 = Signal((data_width, True))
self.i1 = Signal((data_width, True))
self.stb_o = Signal()
self.o = Signal((data_width, True))
class NopUnit(BaseUnit):
pass
class OpUnit(BaseUnit):
def __init__(self, op, data_width, stages, op_data_width=None):
BaseUnit.__init__(self, data_width)
# work around Migen's mishandling of Verilog's cretinous operator width rules
if op_data_width is None:
op_data_width = data_width
if stages > 1:
# Vivado backward retiming for DSP does not work correctly if DSP inputs
# are not registered.
i0 = Signal.like(self.i0)
i1 = Signal.like(self.i1)
stb_i = Signal()
self.sync += [
i0.eq(self.i0),
i1.eq(self.i1),
stb_i.eq(self.stb_i)
]
output_stages = stages - 1
else:
i0, i1, stb_i = self.i0, self.i1, self.stb_i
output_stages = stages
o = Signal((op_data_width, True))
self.comb += o.eq(op(i0, i1))
stb_o = stb_i
for i in range(output_stages):
n_o = Signal((data_width, True))
if stages > 1:
n_o.attr.add(("retiming_backward", 1))
n_stb_o = Signal()
self.sync += [
n_o.eq(o),
n_stb_o.eq(stb_o)
]
o = n_o
stb_o = n_stb_o
self.comb += [
self.o.eq(o),
self.stb_o.eq(stb_o)
]
class SelectUnit(BaseUnit):
def __init__(self, op, data_width):
BaseUnit.__init__(self, data_width)
self.sync += [
self.stb_o.eq(self.stb_i),
If(op(self.i0, self.i1),
self.o.eq(self.i0)
).Else(
self.o.eq(self.i1)
)
]
class CopyUnit(BaseUnit):
def __init__(self, data_width):
BaseUnit.__init__(self, data_width)
self.comb += [
self.stb_o.eq(self.stb_i),
self.o.eq(self.i0)
]
class InputUnit(BaseUnit):
def __init__(self, data_width, input_stb, input):
BaseUnit.__init__(self, data_width)
self.buffer = Signal(data_width)
self.comb += [
self.stb_o.eq(self.stb_i),
self.o.eq(self.buffer)
]
class OutputUnit(BaseUnit):
def __init__(self, data_width, output_stb, output):
BaseUnit.__init__(self, data_width)
self.sync += [
output_stb.eq(self.stb_i),
output.eq(self.i0)
]
class ProcessorImpl(Module):
def __init__(self, pd, program, data):
self.input_stb = Signal()
self.input = Signal((pd.data_width, True))
self.output_stb = Signal()
self.output = Signal((pd.data_width, True))
self.busy = Signal()
# # #
program_mem = Memory(pd.instruction_bits(), pd.program_rom_size, init=program)
data_mem0 = Memory(pd.data_width, pd.data_ram_size, init=data)
data_mem1 = Memory(pd.data_width, pd.data_ram_size, init=data)
self.specials += program_mem, data_mem0, data_mem1
pc = Signal(pd.instruction_bits())
pc_next = Signal.like(pc)
pc_en = Signal()
self.sync += pc.eq(pc_next)
self.comb += [
If(pc_en,
pc_next.eq(pc + 1)
).Else(
pc_next.eq(0)
)
]
program_mem_port = program_mem.get_port()
self.specials += program_mem_port
self.comb += program_mem_port.adr.eq(pc_next)
s = 0
opcode = Signal(pd.opcode_bits)
self.comb += opcode.eq(program_mem_port.dat_r[s:s+pd.opcode_bits])
s += pd.opcode_bits
r0 = Signal(pd.reg_bits)
self.comb += r0.eq(program_mem_port.dat_r[s:s+pd.reg_bits])
s += pd.reg_bits
r1 = Signal(pd.reg_bits)
self.comb += r1.eq(program_mem_port.dat_r[s:s+pd.reg_bits])
s += pd.reg_bits
exit = Signal(pd.reg_bits)
self.comb += exit.eq(program_mem_port.dat_r[s:s+pd.reg_bits])
data_read_port0 = data_mem0.get_port()
data_read_port1 = data_mem1.get_port()
self.specials += data_read_port0, data_read_port1
self.comb += [
data_read_port0.adr.eq(r0),
data_read_port1.adr.eq(r1)
]
data_write_port = data_mem0.get_port(write_capable=True)
data_write_port_dup = data_mem1.get_port(write_capable=True)
self.specials += data_write_port, data_write_port_dup
self.comb += [
data_write_port_dup.we.eq(data_write_port.we),
data_write_port_dup.adr.eq(data_write_port.adr),
data_write_port_dup.dat_w.eq(data_write_port.dat_w),
data_write_port.adr.eq(exit)
]
nop = NopUnit(pd.data_width)
adder = OpUnit(operator.add, pd.data_width, 1)
subtractor = OpUnit(operator.sub, pd.data_width, 1)
if pd.multiplier_shifts:
if len(pd.multiplier_shifts) != 1:
raise NotImplementedError
multiplier = OpUnit(lambda a, b: a * b >> pd.multiplier_shifts[0],
pd.data_width, pd.multiplier_stages, op_data_width=2*pd.data_width)
else:
multiplier = NopUnit(pd.data_width)
minu = SelectUnit(operator.lt, pd.data_width)
maxu = SelectUnit(operator.gt, pd.data_width)
copier = CopyUnit(pd.data_width)
inu = InputUnit(pd.data_width, self.input_stb, self.input)
outu = OutputUnit(pd.data_width, self.output_stb, self.output)
units = [nop, adder, subtractor, multiplier, minu, maxu, copier, inu, outu]
self.submodules += units
for unit in units:
self.sync += unit.stb_i.eq(0)
self.comb += [
unit.i0.eq(data_read_port0.dat_r),
unit.i1.eq(data_read_port1.dat_r),
If(unit.stb_o,
data_write_port.we.eq(1),
data_write_port.dat_w.eq(unit.o)
)
]
decode_table = [
(NopIsn.opcode, nop),
(AddIsn.opcode, adder),
(SubIsn.opcode, subtractor),
(MulShiftIsn.opcode, multiplier),
(MulShiftIsn.opcode + 1, multiplier),
(MinIsn.opcode, minu),
(MaxIsn.opcode, maxu),
(CopyIsn.opcode, copier),
(InputIsn.opcode, inu),
(OutputIsn.opcode, outu)
]
for allocated_opcode, unit in decode_table:
self.sync += If(pc_en & (opcode == allocated_opcode), unit.stb_i.eq(1))
fsm = FSM()
self.submodules += fsm
fsm.act("IDLE",
pc_en.eq(0),
NextValue(inu.buffer, self.input),
If(self.input_stb, NextState("PROCESSING"))
)
fsm.act("PROCESSING",
self.busy.eq(1),
pc_en.eq(1),
If(opcode == EndIsn.opcode,
pc_en.eq(0),
NextState("IDLE")
)
)
def make(function, **kwargs):
proc = Processor(**kwargs)
cp = compile(proc, function)
cp.dimension_processor()
return proc.implement(cp.encode(), cp.data)

View File

@ -1,112 +0,0 @@
from migen import *
from misoc.interconnect.stream import Endpoint
class Accu(Module):
def __init__(self, width, meta=[]):
self.i = Endpoint([("p", width), ("f", width), ("clr", 1)])
self.o = Endpoint([("z", width)])
self.latency = 1
###
f = Signal.like(self.i.f)
p = Signal.like(self.i.p)
self.comb += self.i.ack.eq(~self.o.stb | self.o.ack)
self.sync += [
If(self.o.ack,
self.o.stb.eq(0),
),
If(self.i.ack,
self.o.stb.eq(1),
If(self.i.stb,
self.o.z.eq(self.i.p + Mux(self.i.clr, 0, self.o.z + p)),
f.eq(self.i.f),
p.eq(self.i.f - self.i.p),
).Else(
self.o.z.eq(self.o.z + f),
)
)
]
class MCM(Module):
def __init__(self, width, constants):
n = len(constants)
self.i = i = Signal(width)
self.o = o = [Signal.like(self.i) for i in range(n)]
###
# TODO: improve MCM
assert range(n) == constants
assert n <= 9
if n > 0:
self.comb += o[0].eq(0)
if n > 1:
self.comb += o[1].eq(i)
if n > 2:
self.comb += o[2].eq(i << 1)
if n > 3:
self.comb += o[3].eq(i + (i << 1))
if n > 4:
self.comb += o[4].eq(i << 2)
if n > 5:
self.comb += o[5].eq(i + (i << 2))
if n > 6:
self.comb += o[6].eq(o[3] << 1)
if n > 7:
self.comb += o[7].eq((i << 3) - i)
if n > 8:
self.comb += o[8].eq(i << 3)
class PhasedAccu(Module):
def __init__(self, width, parallelism=8):
self.i = Endpoint([("p", width), ("f", width), ("clr", 1)])
self.o = Endpoint([("z{}".format(i), width) for i in
range(parallelism)])
self.parallelism = parallelism
self.latency = 2
###
a = MCM(width, range(parallelism + 1))
self.submodules += a
z = [Signal(width, reset_less=True) for i in range(parallelism)]
o = self.o.payload.flatten()
load = Signal(reset_less=True)
clr = Signal(reset_less=True)
p = Signal.like(self.i.p)
f = Signal.like(self.i.f, reset_less=True)
fp = Signal.like(self.i.f)
self.comb += [
self.i.ack.eq(self.o.ack),
a.i.eq(self.i.f),
]
self.sync += [
If(self.o.ack,
self.o.stb.eq(0),
),
If(~self.o.stb | self.o.ack,
self.o.stb.eq(1),
If(load,
load.eq(0),
[oi.eq(Mux(clr, 0, o[0] + fp) + zi)
for oi, zi in zip(o, z)],
fp.eq(f),
).Else(
[oi.eq(oi + fp) for oi in o],
),
),
If(self.i.stb & self.i.ack,
[zi.eq(self.i.p - Mux(self.i.clr, 0, p) + aoi)
for zi, aoi in zip(z, a.o)],
clr.eq(self.i.clr),
p.eq(self.i.p),
f.eq(a.o[parallelism]),
load.eq(1),
),
]

View File

@ -1,172 +0,0 @@
from math import floor
from operator import add
from functools import reduce
from collections import namedtuple
import numpy as np
from migen import *
def halfgen4(width, n, df=1e-3):
"""
http://recycle.lbl.gov/~ldoolitt/halfband
params:
* `up` is the passband/stopband width, as a fraction of
input sampling rate
* `n is the order of half-band filter to generate
returns:
* `a` is the full set of FIR coefficients, `4*n-1` long.
implement wisely.
"""
npt = n*40
wmax = 2*np.pi*width
wfit = (1 - np.linspace(0, 1, npt)[:, None]**2)*wmax
target = .5*np.ones_like(wfit)
basis = np.cos(wfit*np.arange(1, 2*n, 2))
weight = np.ones_like(wfit)
f0 = None
for i in range(40):
l = np.linalg.pinv(basis*weight)@(target*weight)
err = np.fabs(basis@l - .5)
f = np.max(err)/np.mean(err)
if f0 and (f0 - f)/(f0 + f) < df/2:
break
f0 = f
weight[err > (1 - df)*np.max(err)] *= 1 + 1.5/(i + 11)
a = np.c_[l, np.zeros_like(l)].ravel()[:-1]
a = np.r_[a[::-1], 1, a]/2
return a
_Widths = namedtuple("_Widths", "A B P")
_widths = {
"DSP48E1": _Widths(25, 18, 48),
}
class ParallelFIR(Module):
"""Full-rate parallelized finite impulse response filter.
Tries to use transposed form as much as possible.
:param coefficients: tap coefficients (normalized to 1.),
increasing delay.
:param parallelism: number of samples per cycle.
:param width: bit width of input and output.
:param arch: architecture (default: "DSP48E1").
"""
def __init__(self, coefficients, parallelism, width=16,
arch="DSP48E1", cull_delays=()):
self.width = width
self.parallelism = p = parallelism
n = len(coefficients)
# input and output: old to new, decreasing delay
self.i = [Signal((width, True)) for i in range(p)]
self.o = [Signal((width, True), reset_less=True) for i in range(p)]
self.latency = (n + 1)//2//p + 2
w = _widths[arch]
c_max = max(abs(c) for c in coefficients)
c_shift = bits_for(floor((1 << w.B - 2) / c_max))
self.coefficients = cs = [int(round(c*(1 << c_shift)))
for c in coefficients]
assert max(bits_for(c) for c in cs) <= w.B
max_out = sum(abs(c)*(1 << w.A - 1) for c in cs)
assert max_out <= (1 << w.P - 1) - 1, (bits_for(max_out), w)
###
# Delay line: increasing delay
x = [Signal((w.A, True), reset_less=True) for _ in range(n + p - 1)]
for xi, xj in zip(x, self.i[::-1]):
self.comb += xi.eq(xj)
for xi, xj in zip(x[len(self.i):], x):
self.sync += xi.eq(xj)
for delay in range(p):
o = Signal((w.P, True), reset_less=True)
self.sync += self.o[delay].eq(o >> c_shift)
# Make products
tap = delay
for i, c in enumerate(cs):
# simplify for halfband and symmetric filters
if not c or c in cs[:i]:
continue
js = [j + p - 1 for j, cj in enumerate(cs) if cj == c]
m = Signal.like(o)
o0, o = o, Signal.like(o)
q = Signal.like(x[0])
if tap + p <= js[0]:
self.sync += o0.eq(o + m)
tap += p
else:
self.comb += o0.eq(o + m)
assert min(js) - tap >= 0
js = [j for j in js
if (p - 1 - j - tap) % p not in cull_delays]
if not js:
continue
self.comb += q.eq(reduce(add, [x[j - tap] for j in js]))
self.sync += m.eq(c*q)
# symmetric rounding
if c_shift > 1 and delay not in cull_delays:
self.comb += o.eq((1 << c_shift - 1) - 1)
class FIR(ParallelFIR):
def __init__(self, *args, **kwargs):
super().__init__(self, *args, parallelism=1, **kwargs)
self.i = self.i[0]
self.o = self.o[0]
def halfgen4_cascade(rate, width, order=None):
"""Generate coefficients for cascaded half-band filters.
Coefficients are normalized to a gain of two per stage to compensate for
the zero stuffing.
:param rate: upsampling rate. power of two
:param width: passband/stopband width in units of input sampling rate.
:param order: highest order, defaults to :param:`rate`"""
if order is None:
order = rate
coeff = []
p = 1
while p < rate:
p *= 2
coeff.append(2*halfgen4(width*p/rate/2, order*p//rate))
return coeff
class ParallelHBFUpsampler(Module):
"""Parallel, power-of-two, half-band, cascading upsampler.
Coefficients should be normalized to overall gain of 2
(highest/center coefficient being 1)."""
def __init__(self, coefficients, width=16, **kwargs):
self.parallelism = 1 # accumulate
self.latency = 0 # accumulate
self.width = width
self.i = Signal((width, True))
###
i = [self.i]
for coeff in coefficients:
self.parallelism *= 2
hbf = ParallelFIR(coeff, self.parallelism, width=width,
cull_delays={0}, **kwargs)
self.submodules += hbf
self.comb += [a.eq(b) for a, b in zip(hbf.i[1::2], i)]
i = hbf.o
self.latency += hbf.latency
self.o = i

View File

@ -1,220 +0,0 @@
from collections import namedtuple
from migen import *
from misoc.interconnect.stream import Endpoint
from misoc.cores.cordic import Cordic
from .accu import PhasedAccu
from .tools import eqh, SatAddMixin
from .spline import Spline
from .fir import ParallelHBFUpsampler, halfgen4_cascade
_Widths = namedtuple("_Widths", "t a p f")
_Orders = namedtuple("_Orders", "a p f")
class SplineParallelDUC(Module):
def __init__(self, widths, orders, parallelism=1, **kwargs):
self.parallelism = parallelism
self.widths = widths
p = Spline(order=orders.p, width=widths.p)
f = Spline(order=orders.f, width=widths.f)
self.f = f.tri(widths.t)
self.p = p.tri(widths.t)
self.submodules += p, f
self.ce = Signal(reset=1)
self.clr = Signal()
###
accu = PhasedAccu(len(self.f.a0), parallelism)
cordic = [Cordic(width=widths.a, widthz=len(self.p.a0), guard=None,
eval_mode="pipelined") for i in range(parallelism)]
self.submodules += accu, cordic
self.xi = [c.xi for c in cordic]
self.yi = [c.yi for c in cordic]
self.xo = [c.xo for c in cordic]
self.yo = [c.yo for c in cordic]
self.latency = cordic[0].latency
self.gain = cordic[0].gain
self.f.latency += accu.latency + self.latency
self.p.latency += accu.latency + self.latency
###
assert p.latency == f.latency
self.comb += [
p.o.ack.eq(self.ce),
f.o.ack.eq(self.ce),
eqh(accu.i.f, f.o.a0),
eqh(accu.i.p, p.o.a0),
accu.i.stb.eq(p.o.stb | f.o.stb),
accu.o.ack.eq(1),
[eqh(c.zi, zi) for c, zi in
zip(cordic, accu.o.payload.flatten())]
]
assert p.latency == 1
accu.i.clr.reset_less = True
self.sync += [
accu.i.clr.eq(0),
If(p.i.stb,
accu.i.clr.eq(self.clr),
),
]
class SplineParallelDDS(SplineParallelDUC):
def __init__(self, widths, orders, **kwargs):
a = Spline(order=orders.a, width=widths.a)
self.a = a.tri(widths.t)
self.submodules += a
super().__init__(widths._replace(a=len(self.a.a0)), orders, **kwargs)
self.a.latency += self.latency
###
self.comb += [
a.o.ack.eq(self.ce),
[eqh(x, a.o.a0) for x in self.xi],
[y.eq(0) for y in self.yi],
]
del self.xi
del self.yi
class Config(Module):
def __init__(self, width, cordic_gain):
self.clr = Signal(3, reset=0b111)
self.iq_en = Signal(2, reset=0b01)
self.limits = [[Signal((width, True)), Signal((width, True))]
for i in range(2)]
limit = (1 << width - 1) - 1
limit_cordic = int(limit/cordic_gain)
self.limits[0][0].reset = Constant(-limit, (width, True))
self.limits[0][1].reset = Constant(limit, (width, True))
self.limits[1][0].reset = Constant(-limit_cordic, (width, True))
self.limits[1][1].reset = Constant(limit_cordic, (width, True))
# TODO make persistent, add read-out/notification/clear
self.clipped = [Signal(2) for i in range(2)]
self.i = Endpoint([("addr", bits_for(4 + 2*len(self.limits) - 1)),
("data", width)])
assert len(self.i.addr) == 3
self.ce = Signal()
###
div = Signal(16, reset=0)
n = Signal.like(div)
self.comb += self.ce.eq(n == 0)
self.sync += [
n.eq(n - 1),
If(self.ce,
n.eq(div),
)
]
pad = Signal()
reg = Array(sum(self.limits,
[Cat(div, n), self.clr, self.iq_en, pad]))
self.comb += self.i.ack.eq(1)
self.sync += [
If(self.i.stb,
reg[self.i.addr].eq(self.i.data),
),
]
class Channel(Module, SatAddMixin):
def __init__(self, width=16, parallelism=4, widths=None, orders=None):
if orders is None:
orders = _Orders(a=4, f=2, p=1)
if widths is None:
widths = _Widths(t=width, a=orders.a*width, p=orders.p*width,
f=(orders.f + 2)*width)
self.submodules.a1 = a1 = SplineParallelDDS(widths, orders)
self.submodules.a2 = a2 = SplineParallelDDS(widths, orders)
coeff = halfgen4_cascade(parallelism, width=.4, order=8)
hbf = [ParallelHBFUpsampler(coeff, width=width + 1) for i in range(2)]
self.submodules.b = b = SplineParallelDUC(
widths._replace(a=len(hbf[0].o[0]), f=widths.f - width), orders,
parallelism=parallelism)
cfg = Config(width, b.gain)
u = Spline(width=widths.a, order=orders.a)
self.submodules += cfg, u, hbf
self.u = u.tri(widths.t)
self.i = [cfg.i, self.u, a1.a, a1.f, a1.p, a2.a, a2.f, a2.p, b.f, b.p]
self.i_names = "cfg u a1 f1 p1 a2 f2 p2 f0 p0".split()
self.i_named = dict(zip(self.i_names, self.i))
self.y_in = [Signal.like(b.yo[0]) for i in range(parallelism)]
self.o = [Signal((width, True), reset_less=True)
for i in range(parallelism)]
self.widths = widths
self.orders = orders
self.parallelism = parallelism
self.cordic_gain = a2.gain*b.gain
self.u.latency += 1 # self.o
b.p.latency += 1 # self.o
b.f.latency += 1 # self.o
a_latency_delta = hbf[0].latency + b.latency + 2 # hbf.i, self.o
for a in a1, a2:
a.a.latency += a_latency_delta
a.p.latency += a_latency_delta
a.f.latency += a_latency_delta
self.latency = max(_.latency for _ in self.i[1:])
for i in self.i[1:]:
i.latency -= self.latency
assert i.latency <= 0
cfg.i.latency = 0
###
self.comb += [
a1.ce.eq(cfg.ce),
a2.ce.eq(cfg.ce),
b.ce.eq(cfg.ce),
u.o.ack.eq(cfg.ce),
Cat(b.clr, a1.clr, a2.clr).eq(cfg.clr),
[i.eq(j) for i, j in zip(b.xi, hbf[0].o)],
[i.eq(j) for i, j in zip(b.yi, hbf[1].o)],
]
hbf[0].i.reset_less = True
hbf[1].i.reset_less = True
self.sync += [
hbf[0].i.eq(self.sat_add((a1.xo[0], a2.xo[0]),
width=len(hbf[0].i),
limits=cfg.limits[1], clipped=cfg.clipped[1])),
hbf[1].i.eq(self.sat_add((a1.yo[0], a2.yo[0]),
width=len(hbf[1].i),
limits=cfg.limits[1])),
]
# wire up outputs and q_{i,o} exchange
for o, x, y in zip(self.o, b.xo, self.y_in):
o_offset = Signal.like(o)
o_x = Signal.like(x)
o_y = Signal.like(y)
self.comb += [
o_offset.eq(u.o.a0[-len(o):]),
If(cfg.iq_en[0],
o_x.eq(x)
),
If(cfg.iq_en[1],
o_y.eq(y)
),
]
self.sync += [
o.eq(self.sat_add((o_offset, o_x, o_y),
width=len(o),
limits=cfg.limits[0], clipped=cfg.clipped[0])),
]
def connect_y(self, buddy):
self.comb += [i.eq(j) for i, j in zip(buddy.y_in, self.b.yo)]

View File

@ -1,47 +0,0 @@
from migen import *
from misoc.interconnect.stream import Endpoint
class Spline(Module):
def __init__(self, order, width, step=1, time_width=None):
if not (step == 1 or order <= 2):
raise ValueError("For non-linear splines, "
"`step` needs to be one.")
layout = [("a{}".format(i), (width, True)) for i in range(order)]
self.i = Endpoint(layout)
self.o = Endpoint(layout)
self.latency = 1
###
o = self.o.payload.flatten()
self.comb += self.i.ack.eq(~self.o.stb | self.o.ack)
self.sync += [
If(self.o.ack,
self.o.stb.eq(0),
),
If(self.i.ack,
self.o.stb.eq(1),
[o[i].eq(o[i] + (o[i + 1] << log2_int(step)))
for i in range(order - 1)],
If(self.i.stb,
self.o.payload.eq(self.i.payload),
),
),
]
def tri(self, time_width):
layout = [(name, (length - i*time_width, signed))
for i, (name, (length, signed), dir) in
enumerate(self.i.payload.layout[::-1])]
layout.reverse()
i = Endpoint(layout)
i.latency = self.latency
self.comb += [
self.i.stb.eq(i.stb),
i.ack.eq(self.i.ack),
[i0[-len(i1):].eq(i1) for i0, i1 in
zip(self.i.payload.flatten(), i.payload.flatten())]
]
return i

View File

@ -1,69 +0,0 @@
from operator import add
from functools import reduce
from migen import *
class Delay(Module):
def __init__(self, i, delay, o=None):
if isinstance(i, (int, tuple)):
z = [Signal(i) for j in range(delay + 1)]
elif isinstance(i, list):
z = [Record(i) for j in range(delay + 1)]
elif isinstance(i, Record):
z = [Record(i.layout) for j in range(delay + 1)]
else:
z = [Signal.like(i) for j in range(delay + 1)]
self.i = z[0]
self.o = z[-1]
if not isinstance(i, (int, list, tuple)):
self.comb += self.i.eq(i)
if o is not None:
self.comb += o.eq(self.o)
self.latency = delay
self.sync += [z[j + 1].eq(z[j]) for j in range(delay)]
def eqh(a, b):
return a[-len(b):].eq(b[-len(a):])
class SatAddMixin:
"""Signed saturating addition mixin"""
def sat_add(self, a, width, limits=None, clipped=None):
a = list(a)
# assert all(value_bits_sign(ai)[1] for ai in a)
max_width = max(value_bits_sign(ai)[0] for ai in a)
carry = log2_int(len(a), need_pow2=False)
full = Signal((max_width + carry, True))
limited = Signal((width, True))
carry = len(full) - width
assert carry >= 0
clip = Signal(2)
sign = Signal()
if clipped is not None:
self.comb += clipped.eq(clip)
self.comb += [
full.eq(reduce(add, a)),
sign.eq(full[-1]),
limited.eq(full)
]
if limits is None:
self.comb += [
If(full[-1-carry:] != Replicate(sign, carry + 1),
clip.eq(Cat(sign, ~sign)),
limited.eq(Cat(Replicate(~sign, width - 1), sign)),
)
]
else:
self.comb += [
If(full < limits[0],
clip.eq(0b01),
limited.eq(limits[0])
),
If(full > limits[1],
clip.eq(0b10),
limited.eq(limits[1]),
)
]
return limited

View File

@ -1,29 +0,0 @@
from migen.build.generic_platform import *
from artiq.coredevice.fmcdio_vhdci_eem import *
io = [
("fmcdio_dirctl", 0,
Subsignal("clk", Pins("LPC:LA32_N")),
Subsignal("ser", Pins("LPC:LA33_P")),
Subsignal("latch", Pins("LPC:LA32_P")),
IOStandard("LVCMOS18")
),
]
def _get_connectors():
connectors = []
for i in range(4):
connections = dict()
for j, pair in enumerate(eem_fmc_connections[i]):
for pn in "n", "p":
cc = "cc_" if j == 0 else ""
lpc_cc = "CC_" if eem_fmc_connections[i][j] in (0, 1, 17, 18) else ""
connections["d{}_{}{}".format(j, cc, pn)] = \
"LPC:LA{:02d}_{}{}".format(pair, lpc_cc, pn.upper())
connectors.append(("eem{}".format(i), connections))
return connectors
connectors = _get_connectors()

View File

@ -1,290 +0,0 @@
from collections import namedtuple
from migen import *
from migen.genlib.cdc import MultiReg, BusSynchronizer
from migen.genlib.resetsync import AsyncResetSynchronizer
from misoc.interconnect.csr import *
from jesd204b.common import (JESD204BTransportSettings,
JESD204BPhysicalSettings,
JESD204BSettings)
from jesd204b.phy.gth import (GTHChannelPLL as JESD204BGTHChannelPLL,
GTHQuadPLL as JESD204BGTHQuadPLL,
GTHTransmitter as JESD204BGTHTransmitter,
GTHInit as JESD204BGTHInit,
GTHTransmitterInterconnect as JESD204BGTHTransmitterInterconnect)
from jesd204b.phy import JESD204BPhyTX
from jesd204b.core import JESD204BCoreTX
from jesd204b.core import JESD204BCoreTXControl
class UltrascaleCRG(Module, AutoCSR):
linerate = int(6e9) # linerate = 20*data_rate*4/8 = data_rate*10
# data_rate = dac_rate/interp_factor
refclk_freq = int(150e6)
fabric_freq = int(125e6)
def __init__(self, platform, use_rtio_clock=False):
self.jreset = CSRStorage(reset=1)
self.refclk = Signal()
self.clock_domains.cd_jesd = ClockDomain()
refclk2 = Signal()
refclk_pads = platform.request("dac_refclk", 0)
platform.add_period_constraint(refclk_pads.p, 1e9/self.refclk_freq)
self.specials += [
Instance("IBUFDS_GTE3", i_CEB=0, p_REFCLK_HROW_CK_SEL=0b00,
i_I=refclk_pads.p, i_IB=refclk_pads.n,
o_O=self.refclk, o_ODIV2=refclk2),
AsyncResetSynchronizer(self.cd_jesd, self.jreset.storage),
]
if use_rtio_clock:
self.cd_jesd.clk.attr.add("keep")
self.comb += self.cd_jesd.clk.eq(ClockSignal("rtio"))
else:
self.specials += Instance("BUFG_GT", i_I=refclk2, o_O=self.cd_jesd.clk)
PhyPads = namedtuple("PhyPads", "txp txn")
class UltrascaleTX(Module, AutoCSR):
def __init__(self, platform, sys_crg, jesd_crg, dac, pll_type="cpll", tx_half=False):
# Note: In general, the choice between channel and quad PLLs can be made based on the "nominal operating ranges", which are (see UG576, Ch.2):
# CPLL: 2.0 - 6.25 GHz
# QPLL0: 9.8 - 16.375 GHz
# QPLL1: 8.0 - 13.0 GHz
# However, the exact frequency and/or linerate range should be checked according to the model and speed grade from their corresponding datasheets.
pll_cls = {
"cpll": JESD204BGTHChannelPLL,
"qpll": JESD204BGTHQuadPLL
}[pll_type]
ps = JESD204BPhysicalSettings(l=8, m=4, n=16, np=16)
ts = JESD204BTransportSettings(f=2, s=2, k=16, cs=0)
settings = JESD204BSettings(ps, ts, did=0x5a, bid=0x5)
jesd_pads = platform.request("dac_jesd", dac)
plls = []
phys = []
for i in range(len(jesd_pads.txp)):
pll = pll_cls(
jesd_crg.refclk, jesd_crg.refclk_freq, jesd_crg.linerate)
self.submodules += pll
plls.append(pll)
# QPLL quads
if pll_type == "qpll":
gthe3_common_cfgs = []
for i in range(0, len(plls), 4):
# GTHE3_COMMON common signals
qpll_clk = Signal()
qpll_refclk = Signal()
qpll_reset = Signal()
qpll_lock = Signal()
# GTHE3_COMMON
self.specials += pll_cls.get_gthe3_common(
jesd_crg.refclk, jesd_crg.refclk_freq, jesd_crg.linerate,
qpll_clk, qpll_refclk, qpll_reset, qpll_lock)
gthe3_common_cfgs.append({
"clk": qpll_clk,
"refclk": qpll_refclk,
"reset": qpll_reset,
"lock": qpll_lock
})
# Per-channel PLL phys
for i, pll in enumerate(plls):
# PhyTX
phy = JESD204BPhyTX(
pll, jesd_crg.refclk, PhyPads(jesd_pads.txp[i], jesd_pads.txn[i]),
jesd_crg.fabric_freq, transceiver="gth", tx_half=tx_half)
phys.append(phy)
if tx_half:
platform.add_period_constraint(phy.transmitter.cd_tx_half.clk,
80*1e9/jesd_crg.linerate)
platform.add_false_path_constraints(
sys_crg.cd_sys.clk,
jesd_crg.cd_jesd.clk,
phy.transmitter.cd_tx_half.clk)
else:
platform.add_period_constraint(phy.transmitter.cd_tx.clk,
40*1e9/jesd_crg.linerate)
platform.add_false_path_constraints(
sys_crg.cd_sys.clk,
jesd_crg.cd_jesd.clk,
phy.transmitter.cd_tx.clk)
# CHANNEL & init interconnects
for i, (pll, phy) in enumerate(zip(plls, phys)):
# CPLLs: 1 init per channel
if pll_type == "cpll":
phy_channel_cfg = {}
# Connect reset/lock to init
pll_reset = pll.reset
pll_lock = pll.lock
self.submodules += JESD204BGTHTransmitterInterconnect(
pll_reset, pll_lock, phy.transmitter, phy.transmitter.init)
# QPLL: 4 inits and 4 channels per quad
elif pll_type == "qpll":
# Connect clk/refclk to CHANNEL
phy_cfg = gthe3_common_cfgs[int(i//4)]
phy_channel_cfg = {
"qpll_clk": phy_cfg["clk"],
"qpll_refclk": phy_cfg["refclk"]
}
# Connect reset/lock to init
pll_reset = phy_cfg["reset"]
pll_lock = phy_cfg["lock"]
if i % 4 == 0:
self.submodules += JESD204BGTHTransmitterInterconnect(
pll_reset, pll_lock, phy.transmitter,
[phys[j].transmitter.init for j in range(i, min(len(phys), i+4))])
# GTHE3_CHANNEL
self.specials += JESD204BGTHTransmitter.get_gthe3_channel(
pll, phy.transmitter, **phy_channel_cfg)
self.submodules.core = JESD204BCoreTX(
phys, settings, converter_data_width=64)
self.submodules.control = JESD204BCoreTXControl(self.core)
self.core.register_jsync(platform.request("dac_sync", dac))
class DDMTDEdgeDetector(Module):
def __init__(self, i):
self.rising = Signal()
history = Signal(4)
deglitched = Signal()
self.sync.helper += history.eq(Cat(history[1:], i))
self.comb += deglitched.eq(i | history[0] | history[1] | history[2] | history[3])
deglitched_r = Signal()
self.sync.helper += [
deglitched_r.eq(deglitched),
self.rising.eq(deglitched & ~deglitched_r)
]
# See "Digital femtosecond time difference circuit for CERN's timing system"
# by P. Moreira and I. Darwazeh
class DDMTD(Module, AutoCSR):
def __init__(self, input_pads, rtio_clk_freq=150e6):
N = 64
self.reset = CSRStorage(reset=1)
self.locked = CSRStatus()
self.dt = CSRStatus(N.bit_length())
# # #
self.clock_domains.cd_helper = ClockDomain(reset_less=True)
helper_locked = Signal()
helper_fb = Signal()
helper_output = Signal()
input_se = Signal()
beat1 = Signal()
beat2 = Signal()
self.specials += [
Instance("MMCME2_BASE",
p_CLKIN1_PERIOD=1e9/rtio_clk_freq,
i_CLKIN1=ClockSignal("rtio"),
i_RST=self.reset.storage,
o_LOCKED=helper_locked,
# VCO at 1200MHz with 150MHz RTIO frequency
p_CLKFBOUT_MULT_F=8.0,
p_DIVCLK_DIVIDE=1,
o_CLKFBOUT=helper_fb, i_CLKFBIN=helper_fb,
# helper PLL ratio: 64/65 (N=64)
p_CLKOUT0_DIVIDE_F=8.125,
o_CLKOUT0=helper_output,
),
MultiReg(helper_locked, self.locked.status),
Instance("BUFG", i_I=helper_output, o_O=self.cd_helper.clk),
Instance("IBUFDS", i_I=input_pads.p, i_IB=input_pads.n, o_O=input_se),
Instance("FD", i_C=self.cd_helper.clk, i_D=input_se, o_Q=beat1, attr={("IOB", "TRUE")}),
Instance("FD", i_C=self.cd_helper.clk, i_D=ClockSignal("rtio"), o_Q=beat2),
]
ed1 = DDMTDEdgeDetector(beat1)
ed2 = DDMTDEdgeDetector(beat2)
self.submodules += ed1, ed2
counting = Signal()
counter = Signal(N.bit_length())
result = Signal.like(counter)
self.sync.helper += [
If(counting,
counter.eq(counter + 1)
).Else(
result.eq(counter)
),
If(ed1.rising, counting.eq(1), counter.eq(0)),
If(ed2.rising, counting.eq(0))
]
bsync = BusSynchronizer(len(result), "helper", "sys")
self.submodules += bsync
self.comb += [
bsync.i.eq(result),
self.dt.status.eq(bsync.o)
]
# This assumes:
# * fine RTIO frequency (rtiox) = 2*RTIO frequency
# * JESD and coarse RTIO clocks are the same
# (only reset may differ).
class SysrefSampler(Module, AutoCSR):
def __init__(self, sysref_pads, coarse_ts, sysref_phase_bits=8):
self.sh_error = CSRStatus()
self.sh_error_reset = CSRStorage()
# Note: only the lower log2(RTIO frequency / SYSREF frequency) bits are stable
self.sysref_phase = CSRStatus(8)
self.jref = Signal()
# # #
sysref_se = Signal()
sysref_oversample = Signal(4)
self.specials += [
Instance("IBUFDS", i_I=sysref_pads.p, i_IB=sysref_pads.n, o_O=sysref_se),
Instance("ISERDESE3",
p_IS_CLK_INVERTED=0,
p_IS_CLK_B_INVERTED=1,
p_DATA_WIDTH=4,
i_D=sysref_se,
i_RST=ResetSignal("rtio"),
i_FIFO_RD_EN=0,
i_CLK=ClockSignal("rtiox"),
i_CLK_B=ClockSignal("rtiox"), # locally inverted
i_CLKDIV=ClockSignal("rtio"),
o_Q=sysref_oversample)
]
self.comb += self.jref.eq(sysref_oversample[1])
sh_error = Signal()
sh_error_reset = Signal()
self.sync.rtio += [
If(~( (sysref_oversample[0] == sysref_oversample[1])
& (sysref_oversample[1] == sysref_oversample[2])),
sh_error.eq(1)
),
If(sh_error_reset, sh_error.eq(0))
]
self.specials += [
MultiReg(self.sh_error_reset.storage, sh_error_reset, "rtio"),
MultiReg(sh_error, self.sh_error.status)
]
jref_r = Signal()
sysref_phase_rtio = Signal(sysref_phase_bits)
self.sync.rtio += [
jref_r.eq(self.jref),
If(self.jref & ~jref_r, sysref_phase_rtio.eq(coarse_ts))
]
sysref_phase_rtio.attr.add("no_retiming")
self.specials += MultiReg(sysref_phase_rtio, self.sysref_phase.status)

View File

@ -24,7 +24,7 @@ class Core(Module, AutoCSR):
self.sequence_error_channel = CSRStatus(16)
# Clocking/Reset
# Create rsys, rio and rio_phy domains based on sys and rtio
# Create rio and rio_phy domains based on sys
# with reset controlled by CSR.
#
# The `rio` CD contains logic that is reset with `core.reset()`.
@ -40,20 +40,15 @@ class Core(Module, AutoCSR):
cmd_reset.eq(self.reset.re),
cmd_reset_phy.eq(self.reset_phy.re)
]
cmd_reset.attr.add("no_retiming")
cmd_reset_phy.attr.add("no_retiming")
self.clock_domains.cd_rsys = ClockDomain()
self.clock_domains.cd_rio = ClockDomain()
self.clock_domains.cd_rio_phy = ClockDomain()
self.comb += [
self.cd_rsys.clk.eq(ClockSignal()),
self.cd_rsys.rst.eq(cmd_reset),
self.cd_rio.clk.eq(ClockSignal("rtio")),
self.cd_rio_phy.clk.eq(ClockSignal("rtio"))
self.cd_rio.clk.eq(ClockSignal()),
self.cd_rio.rst.eq(cmd_reset),
self.cd_rio_phy.clk.eq(ClockSignal()),
self.cd_rio_phy.rst.eq(cmd_reset_phy)
]
self.specials += AsyncResetSynchronizer(self.cd_rio, cmd_reset)
self.specials += AsyncResetSynchronizer(self.cd_rio_phy, cmd_reset_phy)
# TSC
chan_fine_ts_width = max(max(rtlink.get_fine_ts_width(channel.interface.o)
@ -65,22 +60,22 @@ class Core(Module, AutoCSR):
# Outputs/Inputs
quash_channels = [n for n, c in enumerate(channels) if isinstance(c, LogChannel)]
outputs = SED(channels, tsc.glbl_fine_ts_width, "async",
outputs = SED(channels, tsc.glbl_fine_ts_width,
quash_channels=quash_channels,
lane_count=lane_count, fifo_depth=fifo_depth,
interface=self.cri)
self.submodules += outputs
self.comb += outputs.coarse_timestamp.eq(tsc.coarse_ts)
self.sync += outputs.minimum_coarse_timestamp.eq(tsc.coarse_ts_sys + 16)
self.sync += outputs.minimum_coarse_timestamp.eq(tsc.coarse_ts + 16)
inputs = InputCollector(tsc, channels, "async",
inputs = InputCollector(tsc, channels,
quash_channels=quash_channels,
interface=self.cri)
self.submodules += inputs
# Asychronous output errors
o_collision_sync = BlindTransfer("rio", "rsys", data_width=16)
o_busy_sync = BlindTransfer("rio", "rsys", data_width=16)
o_collision_sync = BlindTransfer("rio", "sys", data_width=16)
o_busy_sync = BlindTransfer("rio", "sys", data_width=16)
self.submodules += o_collision_sync, o_busy_sync
o_collision = Signal()
o_busy = Signal()

View File

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

View File

@ -1,6 +1,6 @@
from migen import *
from migen.genlib.record import Record
from migen.genlib.fifo import *
from migen.genlib.fifo import SyncFIFOBuffered
from migen.genlib.cdc import BlindTransfer
from artiq.gateware.rtio import cri
@ -24,24 +24,13 @@ def get_channel_layout(coarse_ts_width, interface):
class InputCollector(Module):
def __init__(self, tsc, channels, mode, quash_channels=[], interface=None):
def __init__(self, tsc, channels, quash_channels=[], interface=None):
if interface is None:
interface = cri.Interface()
self.cri = interface
# # #
if mode == "sync":
fifo_factory = SyncFIFOBuffered
sync_io = self.sync
sync_cri = self.sync
elif mode == "async":
fifo_factory = lambda *args: ClockDomainsRenamer({"write": "rio", "read": "rsys"})(AsyncFIFO(*args))
sync_io = self.sync.rio
sync_cri = self.sync.rsys
else:
raise ValueError
i_statuses, i_datas, i_timestamps = [], [], []
i_ack = Signal()
sel = self.cri.chan_sel[:16]
@ -55,7 +44,7 @@ class InputCollector(Module):
# FIFO
layout = get_channel_layout(len(tsc.coarse_ts), iif)
fifo = fifo_factory(layout_len(layout), channel.ififo_depth)
fifo = SyncFIFOBuffered(layout_len(layout), channel.ififo_depth)
self.submodules += fifo
fifo_in = Record(layout)
fifo_out = Record(layout)
@ -67,7 +56,7 @@ class InputCollector(Module):
# FIFO write
if iif.delay:
counter_rtio = Signal.like(tsc.coarse_ts, reset_less=True)
sync_io += counter_rtio.eq(tsc.coarse_ts - (iif.delay + 1))
self.sync += counter_rtio.eq(tsc.coarse_ts - (iif.delay + 1))
else:
counter_rtio = tsc.coarse_ts
if hasattr(fifo_in, "data"):
@ -80,17 +69,8 @@ class InputCollector(Module):
self.comb += fifo_in.timestamp.eq(full_ts)
self.comb += fifo.we.eq(iif.stb)
overflow_io = Signal()
self.comb += overflow_io.eq(fifo.we & ~fifo.writable)
if mode == "sync":
overflow_trigger = overflow_io
elif mode == "async":
overflow_transfer = BlindTransfer("rio", "rsys")
self.submodules += overflow_transfer
self.comb += overflow_transfer.i.eq(overflow_io)
overflow_trigger = overflow_transfer.o
else:
raise ValueError
overflow_trigger = Signal()
self.comb += overflow_trigger.eq(fifo.we & ~fifo.writable)
# FIFO read, CRI connection
if hasattr(fifo_out, "data"):
@ -107,7 +87,7 @@ class InputCollector(Module):
self.comb += selected.eq(sel == n)
overflow = Signal()
sync_cri += [
self.sync += [
If(selected & i_ack,
overflow.eq(0)),
If(overflow_trigger,
@ -122,7 +102,7 @@ class InputCollector(Module):
input_pending = Signal()
self.cri.i_data.reset_less = True
self.cri.i_timestamp.reset_less = True
sync_cri += [
self.sync += [
i_ack.eq(0),
If(i_ack,
self.cri.i_status.eq(Cat(~i_status_raw[0], i_status_raw[1], 0)),

View File

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

View File

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

View File

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

View File

@ -1,39 +0,0 @@
from collections import namedtuple
from migen import *
from artiq.gateware.rtio import rtlink
from artiq.gateware.dsp.sawg import Channel as _Channel
_Phy = namedtuple("Phy", "rtlink probes overrides")
_ChannelPHY = ClockDomainsRenamer("rio_phy")(_Channel)
class Channel(_ChannelPHY):
def __init__(self, *args, **kwargs):
_ChannelPHY.__init__(self, *args, **kwargs)
self.phys = []
cfg = self.i[0]
rl = rtlink.Interface(rtlink.OInterface(
data_width=len(cfg.data), address_width=len(cfg.addr),
enable_replace=False))
self.comb += [
cfg.stb.eq(rl.o.stb),
rl.o.busy.eq(~cfg.ack),
cfg.data.eq(rl.o.data),
cfg.addr.eq(rl.o.address),
]
self.phys.append(_Phy(rl, [], []))
for i in self.i[1:]:
rl = rtlink.Interface(rtlink.OInterface(len(i.payload),
delay=-i.latency))
self.comb += [
i.stb.eq(rl.o.stb),
rl.o.busy.eq(~i.ack),
i.payload.raw_bits().eq(rl.o.data),
]
# TODO probes, overrides
self.phys.append(_Phy(rl, [], []))
self.phys_named = dict(zip(self.i_names, self.phys))

View File

@ -19,7 +19,7 @@ class _OSERDESE2_8X(Module):
p_INIT_OQ=0b11111111 if invert else 0b00000000,
o_OQ=self.ser_out, o_TQ=self.t_out,
i_RST=ResetSignal("rio_phy"),
i_CLK=ClockSignal("rtiox4"),
i_CLK=ClockSignal("sys4x"),
i_CLKDIV=ClockSignal("rio_phy"),
i_D1=o[0] ^ invert, i_D2=o[1] ^ invert, i_D3=o[2] ^ invert, i_D4=o[3] ^ invert,
i_D5=o[4] ^ invert, i_D6=o[5] ^ invert, i_D7=o[6] ^ invert, i_D8=o[7] ^ invert,
@ -43,8 +43,8 @@ class _ISERDESE2_8X(Module):
o_Q1=i[7], o_Q2=i[6], o_Q3=i[5], o_Q4=i[4],
o_Q5=i[3], o_Q6=i[2], o_Q7=i[1], o_Q8=i[0],
i_D=self.ser_in,
i_CLK=ClockSignal("rtiox4"),
i_CLKB=~ClockSignal("rtiox4"),
i_CLK=ClockSignal("sys4x"),
i_CLKB=~ClockSignal("sys4x"),
i_CE1=1,
i_RST=ResetSignal("rio_phy"),
i_CLKDIV=ClockSignal("rio_phy"))

View File

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

View File

@ -11,41 +11,25 @@ __all__ = ["SED"]
class SED(Module):
def __init__(self, channels, glbl_fine_ts_width, mode,
def __init__(self, channels, glbl_fine_ts_width,
lane_count=8, fifo_depth=128, enable_spread=True,
quash_channels=[], report_buffer_space=False, interface=None):
if mode == "sync":
lane_dist_cdr = lambda x: x
fifos_cdr = lambda x: x
gates_cdr = lambda x: x
output_driver_cdr = lambda x: x
elif mode == "async":
lane_dist_cdr = ClockDomainsRenamer("rsys")
fifos_cdr = ClockDomainsRenamer({"write": "rsys", "read": "rio"})
gates_cdr = ClockDomainsRenamer("rio")
output_driver_cdr = ClockDomainsRenamer("rio")
else:
raise ValueError
seqn_width = layouts.seqn_width(lane_count, fifo_depth)
self.submodules.lane_dist = lane_dist_cdr(
LaneDistributor(lane_count, seqn_width,
layouts.fifo_payload(channels),
[channel.interface.o.delay for channel in channels],
glbl_fine_ts_width,
enable_spread=enable_spread,
quash_channels=quash_channels,
interface=interface))
self.submodules.fifos = fifos_cdr(
FIFOs(lane_count, fifo_depth,
layouts.fifo_payload(channels), mode, report_buffer_space))
self.submodules.gates = gates_cdr(
Gates(lane_count, seqn_width,
layouts.fifo_payload(channels),
layouts.output_network_payload(channels, glbl_fine_ts_width)))
self.submodules.output_driver = output_driver_cdr(
OutputDriver(channels, glbl_fine_ts_width, lane_count, seqn_width))
self.submodules.lane_dist = LaneDistributor(lane_count, seqn_width,
layouts.fifo_payload(channels),
[channel.interface.o.delay for channel in channels],
glbl_fine_ts_width,
enable_spread=enable_spread,
quash_channels=quash_channels,
interface=interface)
self.submodules.fifos = FIFOs(lane_count, fifo_depth,
layouts.fifo_payload(channels), report_buffer_space)
self.submodules.gates = Gates(lane_count, seqn_width,
layouts.fifo_payload(channels),
layouts.output_network_payload(channels, glbl_fine_ts_width))
self.submodules.output_driver = OutputDriver(channels, glbl_fine_ts_width,
lane_count, seqn_width)
for o, i in zip(self.lane_dist.output, self.fifos.input):
self.comb += o.connect(i)

View File

@ -2,7 +2,7 @@ from operator import or_
from functools import reduce
from migen import *
from migen.genlib.fifo import *
from migen.genlib.fifo import SyncFIFOBuffered
from artiq.gateware.rtio.sed import layouts
@ -11,7 +11,7 @@ __all__ = ["FIFOs"]
class FIFOs(Module):
def __init__(self, lane_count, fifo_depth, layout_payload, mode, report_buffer_space=False):
def __init__(self, lane_count, fifo_depth, layout_payload, report_buffer_space=False):
seqn_width = layouts.seqn_width(lane_count, fifo_depth)
self.input = [Record(layouts.fifo_ingress(seqn_width, layout_payload))
for _ in range(lane_count)]
@ -23,16 +23,9 @@ class FIFOs(Module):
# # #
if mode == "sync":
fifo_cls = SyncFIFOBuffered
elif mode == "async":
fifo_cls = AsyncFIFOBuffered
else:
raise ValueError
fifos = []
for input, output in zip(self.input, self.output):
fifo = fifo_cls(seqn_width + layout_len(layout_payload), fifo_depth)
fifo = SyncFIFOBuffered(seqn_width + layout_len(layout_payload), fifo_depth)
self.submodules += fifo
fifos.append(fifo)
@ -47,9 +40,6 @@ class FIFOs(Module):
]
if report_buffer_space:
if mode != "sync":
raise NotImplementedError
def compute_max(elts):
l = len(elts)
if l == 1:

View File

@ -1,48 +1,26 @@
from migen import *
from artiq.gateware.rtio.cdc import GrayCodeTransfer
class TSC(Module):
def __init__(self, mode, glbl_fine_ts_width=0):
def __init__(self, glbl_fine_ts_width=0):
self.glbl_fine_ts_width = glbl_fine_ts_width
# in rtio domain
self.coarse_ts = Signal(64 - glbl_fine_ts_width)
self.full_ts = Signal(64)
# in sys domain
# monotonic, may lag behind the counter in the IO clock domain, but
# not be ahead of it.
self.coarse_ts_sys = Signal.like(self.coarse_ts)
self.full_ts_sys = Signal(64)
# in rtio domain
self.load = Signal()
self.load_value = Signal.like(self.coarse_ts)
if mode == "async":
self.full_ts_cri = self.full_ts_sys
elif mode == "sync":
self.full_ts_cri = self.full_ts
else:
raise ValueError
self.full_ts_cri = self.full_ts
# # #
self.sync.rtio += If(self.load,
self.sync += If(self.load,
self.coarse_ts.eq(self.load_value)
).Else(
self.coarse_ts.eq(self.coarse_ts + 1)
)
coarse_ts_cdc = GrayCodeTransfer(len(self.coarse_ts)) # from rtio to sys
self.submodules += coarse_ts_cdc
self.comb += [
coarse_ts_cdc.i.eq(self.coarse_ts),
self.coarse_ts_sys.eq(coarse_ts_cdc.o)
]
self.comb += [
self.full_ts.eq(self.coarse_ts << glbl_fine_ts_width),
self.full_ts_sys.eq(self.coarse_ts_sys << glbl_fine_ts_width)
]

View File

@ -1,39 +1,3 @@
from migen import *
from migen.genlib.cdc import MultiReg
from misoc.interconnect.csr import *
class RTIOClockMultiplier(Module, AutoCSR):
def __init__(self, rtio_clk_freq):
self.pll_reset = CSRStorage(reset=1)
self.pll_locked = CSRStatus()
self.clock_domains.cd_rtiox4 = ClockDomain(reset_less=True)
# See "Global Clock Network Deskew Using Two BUFGs" in ug472.
clkfbout = Signal()
clkfbin = Signal()
rtiox4_clk = Signal()
pll_locked = Signal()
self.specials += [
Instance("MMCME2_BASE",
p_CLKIN1_PERIOD=1e9/rtio_clk_freq,
i_CLKIN1=ClockSignal("rtio"),
i_RST=self.pll_reset.storage,
o_LOCKED=pll_locked,
p_CLKFBOUT_MULT_F=8.0, p_DIVCLK_DIVIDE=1,
o_CLKFBOUT=clkfbout, i_CLKFBIN=clkfbin,
p_CLKOUT0_DIVIDE_F=2.0, o_CLKOUT0=rtiox4_clk,
),
Instance("BUFG", i_I=clkfbout, o_O=clkfbin),
Instance("BUFG", i_I=rtiox4_clk, o_O=self.cd_rtiox4.clk),
MultiReg(pll_locked, self.pll_locked.status)
]
def fix_serdes_timing_path(platform):
# ignore timing of path from OSERDESE2 through the pad to ISERDESE2
platform.add_platform_command(

View File

@ -17,69 +17,15 @@ from misoc.integration.builder import builder_args, builder_argdict
from artiq.gateware.amp import AMPSoC
from artiq.gateware import rtio
from artiq.gateware.rtio.phy import ttl_simple, ttl_serdes_7series, edge_counter
from artiq.gateware.rtio.xilinx_clocking import RTIOClockMultiplier, fix_serdes_timing_path
from artiq.gateware.rtio.xilinx_clocking import fix_serdes_timing_path
from artiq.gateware import eem
from artiq.gateware.drtio.transceiver import gtp_7series
from artiq.gateware.drtio.siphaser import SiPhaser7Series
from artiq.gateware.drtio.wrpll import WRPLL, DDMTDSamplerGTP
from artiq.gateware.drtio.rx_synchronizer import XilinxRXSynchronizer
from artiq.gateware.drtio import *
from artiq.build_soc import *
class _RTIOCRG(Module, AutoCSR):
def __init__(self, platform):
self.pll_reset = CSRStorage(reset=1)
self.pll_locked = CSRStatus()
self.clock_domains.cd_rtio = ClockDomain()
self.clock_domains.cd_rtiox4 = ClockDomain(reset_less=True)
if platform.hw_rev == "v2.0":
clk_synth = platform.request("cdr_clk_clean_fabric")
else:
clk_synth = platform.request("si5324_clkout_fabric")
clk_synth_se = Signal()
platform.add_period_constraint(clk_synth.p, 8.0)
self.specials += [
Instance("IBUFGDS",
p_DIFF_TERM="TRUE", p_IBUF_LOW_PWR="FALSE",
i_I=clk_synth.p, i_IB=clk_synth.n, o_O=clk_synth_se),
]
pll_locked = Signal()
rtio_clk = Signal()
rtiox4_clk = Signal()
fb_clk = Signal()
self.specials += [
Instance("PLLE2_ADV",
p_STARTUP_WAIT="FALSE", o_LOCKED=pll_locked,
p_BANDWIDTH="HIGH",
p_REF_JITTER1=0.001,
p_CLKIN1_PERIOD=8.0, p_CLKIN2_PERIOD=8.0,
i_CLKIN2=clk_synth_se,
# Warning: CLKINSEL=0 means CLKIN2 is selected
i_CLKINSEL=0,
# VCO @ 1.5GHz when using 125MHz input
p_CLKFBOUT_MULT=12, p_DIVCLK_DIVIDE=1,
i_CLKFBIN=fb_clk,
i_RST=self.pll_reset.storage,
o_CLKFBOUT=fb_clk,
p_CLKOUT0_DIVIDE=3, p_CLKOUT0_PHASE=0.0,
o_CLKOUT0=rtiox4_clk,
p_CLKOUT1_DIVIDE=12, p_CLKOUT1_PHASE=0.0,
o_CLKOUT1=rtio_clk),
Instance("BUFG", i_I=rtio_clk, o_O=self.cd_rtio.clk),
Instance("BUFG", i_I=rtiox4_clk, o_O=self.cd_rtiox4.clk),
AsyncResetSynchronizer(self.cd_rtio, ~pll_locked),
MultiReg(pll_locked, self.pll_locked.status)
]
class SMAClkinForward(Module):
def __init__(self, platform):
sma_clkin = platform.request("sma_clkin")
@ -118,6 +64,8 @@ class StandaloneBase(MiniSoC, AMPSoC):
integrated_sram_size=8192,
ethmac_nrxslots=4,
ethmac_ntxslots=4,
clk_freq=kwargs.get("rtio_frequency", 125.0e6),
rtio_sys_merge=True,
**kwargs)
AMPSoC.__init__(self)
add_identifier(self, gateware_identifier_str=gateware_identifier_str)
@ -127,6 +75,23 @@ class StandaloneBase(MiniSoC, AMPSoC):
self.platform.request("error_led")))
self.csr_devices.append("error_led")
self.submodules += SMAClkinForward(self.platform)
cdr_clk_out = self.platform.request("cdr_clk_clean")
else:
cdr_clk_out = self.platform.request("si5324_clkout")
cdr_clk = Signal()
cdr_clk_buf = Signal()
self.platform.add_period_constraint(cdr_clk_out, 8.)
self.specials += [
Instance("IBUFDS_GTE2",
i_CEB=0,
i_I=cdr_clk_out.p, i_IB=cdr_clk_out.n,
o_O=cdr_clk),
Instance("BUFG", i_I=cdr_clk, o_O=cdr_clk_buf)
]
self.crg.configure(cdr_clk_buf)
i2c = self.platform.request("i2c")
self.submodules.i2c = gpio.GPIOTristate([i2c.scl, i2c.sda])
@ -136,10 +101,8 @@ class StandaloneBase(MiniSoC, AMPSoC):
self.config["SI5324_SOFT_RESET"] = None
def add_rtio(self, rtio_channels, sed_lanes=8):
self.submodules.rtio_crg = _RTIOCRG(self.platform)
self.csr_devices.append("rtio_crg")
fix_serdes_timing_path(self.platform)
self.submodules.rtio_tsc = rtio.TSC("async", glbl_fine_ts_width=3)
self.submodules.rtio_tsc = rtio.TSC(glbl_fine_ts_width=3)
self.submodules.rtio_core = rtio.Core(self.rtio_tsc, rtio_channels, lane_count=sed_lanes)
self.csr_devices.append("rtio_core")
self.submodules.rtio = rtio.KernelInitiator(self.rtio_tsc)
@ -157,10 +120,6 @@ class StandaloneBase(MiniSoC, AMPSoC):
self.submodules.rtio_moninj = rtio.MonInj(rtio_channels)
self.csr_devices.append("rtio_moninj")
self.platform.add_false_path_constraints(
self.crg.cd_sys.clk,
self.rtio_crg.cd_rtio.clk)
self.submodules.rtio_analyzer = rtio.Analyzer(self.rtio_tsc, self.rtio_core.cri,
self.get_native_sdram_if(), cpu_dw=self.cpu_dw)
self.csr_devices.append("rtio_analyzer")
@ -247,8 +206,6 @@ class SUServo(StandaloneBase):
self.add_rtio(self.rtio_channels)
pads = self.platform.lookup_request("sampler3_adc_data_p")
self.platform.add_false_path_constraints(
pads.clkout, self.rtio_crg.cd_rtio.clk)
self.platform.add_false_path_constraints(
pads.clkout, self.crg.cd_sys.clk)
@ -277,6 +234,8 @@ class MasterBase(MiniSoC, AMPSoC):
integrated_sram_size=8192,
ethmac_nrxslots=4,
ethmac_ntxslots=4,
clk_freq=rtio_clk_freq,
rtio_sys_merge=True,
**kwargs)
AMPSoC.__init__(self)
add_identifier(self, gateware_identifier_str=gateware_identifier_str)
@ -315,8 +274,6 @@ class MasterBase(MiniSoC, AMPSoC):
sys_clk_freq=self.clk_freq,
rtio_clk_freq=rtio_clk_freq)
self.csr_devices.append("drtio_transceiver")
self.sync += self.disable_cdr_clk_ibuf.eq(
~self.drtio_transceiver.stable_clkin.storage)
if enable_sata:
sfp_channels = self.drtio_transceiver.channels[1:]
@ -329,7 +286,7 @@ class MasterBase(MiniSoC, AMPSoC):
self.comb += [self.virtual_leds.get(i + 1).eq(channel.rx_ready)
for i, channel in enumerate(sfp_channels)]
self.submodules.rtio_tsc = rtio.TSC("async", glbl_fine_ts_width=3)
self.submodules.rtio_tsc = rtio.TSC(glbl_fine_ts_width=3)
drtio_csr_group = []
drtioaux_csr_group = []
@ -366,8 +323,14 @@ class MasterBase(MiniSoC, AMPSoC):
rtio_clk_period = 1e9/rtio_clk_freq
gtp = self.drtio_transceiver.gtps[0]
txout_buf = Signal()
self.specials += Instance("BUFG", i_I=gtp.txoutclk, o_O=txout_buf)
self.crg.configure(txout_buf, clk_sw=gtp.tx_init.done)
platform.add_period_constraint(gtp.txoutclk, rtio_clk_period)
platform.add_period_constraint(gtp.rxoutclk, rtio_clk_period)
platform.add_false_path_constraints(
self.crg.cd_sys.clk,
gtp.txoutclk, gtp.rxoutclk)
@ -376,8 +339,6 @@ class MasterBase(MiniSoC, AMPSoC):
platform.add_false_path_constraints(
self.crg.cd_sys.clk, gtp.rxoutclk)
self.submodules.rtio_crg = RTIOClockMultiplier(rtio_clk_freq)
self.csr_devices.append("rtio_crg")
fix_serdes_timing_path(platform)
def add_rtio(self, rtio_channels, sed_lanes=8):
@ -409,19 +370,18 @@ class MasterBase(MiniSoC, AMPSoC):
# Never running out of stupid features, GTs on A7 make you pack
# unrelated transceiver PLLs into one GTPE2_COMMON yourself.
def create_qpll(self):
# The GTP acts up if you send any glitch to its
# clock input, even while the PLL is held in reset.
self.disable_cdr_clk_ibuf = Signal(reset=1)
self.disable_cdr_clk_ibuf.attr.add("no_retiming")
if self.platform.hw_rev == "v2.0":
cdr_clk_clean = self.platform.request("cdr_clk_clean")
cdr_clk_out = self.platform.request("cdr_clk_clean")
else:
cdr_clk_clean = self.platform.request("si5324_clkout")
cdr_clk_clean_buf = Signal()
cdr_clk_out = self.platform.request("si5324_clkout")
cdr_clk = Signal()
self.platform.add_period_constraint(cdr_clk_out, 8.)
self.specials += Instance("IBUFDS_GTE2",
i_CEB=self.disable_cdr_clk_ibuf,
i_I=cdr_clk_clean.p, i_IB=cdr_clk_clean.n,
o_O=cdr_clk_clean_buf)
i_CEB=0,
i_I=cdr_clk_out.p, i_IB=cdr_clk_out.n,
o_O=cdr_clk)
# Note precisely the rules Xilinx made up:
# refclksel=0b001 GTREFCLK0 selected
# refclksel=0b010 GTREFCLK1 selected
@ -436,7 +396,8 @@ class MasterBase(MiniSoC, AMPSoC):
fbdiv=4,
fbdiv_45=5,
refclk_div=1)
qpll = QPLL(cdr_clk_clean_buf, qpll_drtio_settings,
qpll = QPLL(cdr_clk, qpll_drtio_settings,
self.crg.clk125_buf, qpll_eth_settings)
self.submodules += qpll
self.drtio_qpll_channel, self.ethphy_qpll_channel = qpll.channels
@ -448,7 +409,7 @@ class SatelliteBase(BaseSoC):
}
mem_map.update(BaseSoC.mem_map)
def __init__(self, rtio_clk_freq=125e6, enable_sata=False, *, with_wrpll=False, gateware_identifier_str=None, hw_rev="v2.0", **kwargs):
def __init__(self, rtio_clk_freq=125e6, enable_sata=False, *, gateware_identifier_str=None, hw_rev="v2.0", **kwargs):
if hw_rev in ("v1.0", "v1.1"):
cpu_bus_width = 32
else:
@ -459,6 +420,8 @@ class SatelliteBase(BaseSoC):
cpu_bus_width=cpu_bus_width,
sdram_controller_type="minicon",
l2_size=128*1024,
clk_freq=rtio_clk_freq,
rtio_sys_merge=True,
**kwargs)
add_identifier(self, gateware_identifier_str=gateware_identifier_str)
@ -469,23 +432,24 @@ class SatelliteBase(BaseSoC):
self.platform.request("error_led")))
self.csr_devices.append("error_led")
disable_cdr_clk_ibuf = Signal(reset=1)
disable_cdr_clk_ibuf.attr.add("no_retiming")
if self.platform.hw_rev == "v2.0":
cdr_clk_clean = self.platform.request("cdr_clk_clean")
cdr_clk_out = self.platform.request("cdr_clk_clean")
else:
cdr_clk_clean = self.platform.request("si5324_clkout")
cdr_clk_clean_buf = Signal()
cdr_clk_out = self.platform.request("si5324_clkout")
cdr_clk = Signal()
self.platform.add_period_constraint(cdr_clk_out, 8.)
self.specials += Instance("IBUFDS_GTE2",
i_CEB=disable_cdr_clk_ibuf,
i_I=cdr_clk_clean.p, i_IB=cdr_clk_clean.n,
o_O=cdr_clk_clean_buf)
i_CEB=0,
i_I=cdr_clk_out.p, i_IB=cdr_clk_out.n,
o_O=cdr_clk)
qpll_drtio_settings = QPLLSettings(
refclksel=0b001,
fbdiv=4,
fbdiv_45=5,
refclk_div=1)
qpll = QPLL(cdr_clk_clean_buf, qpll_drtio_settings)
qpll = QPLL(cdr_clk, qpll_drtio_settings)
self.submodules += qpll
drtio_data_pads = []
@ -504,8 +468,6 @@ class SatelliteBase(BaseSoC):
sys_clk_freq=self.clk_freq,
rtio_clk_freq=rtio_clk_freq)
self.csr_devices.append("drtio_transceiver")
self.sync += disable_cdr_clk_ibuf.eq(
~self.drtio_transceiver.stable_clkin.storage)
if enable_sata:
sfp_channels = self.drtio_transceiver.channels[1:]
@ -518,7 +480,7 @@ class SatelliteBase(BaseSoC):
self.comb += [self.virtual_leds.get(i).eq(channel.rx_ready)
for i, channel in enumerate(sfp_channels)]
self.submodules.rtio_tsc = rtio.TSC("sync", glbl_fine_ts_width=3)
self.submodules.rtio_tsc = rtio.TSC(glbl_fine_ts_width=3)
drtioaux_csr_group = []
drtioaux_memory_group = []
@ -570,52 +532,34 @@ class SatelliteBase(BaseSoC):
rtio_clk_period = 1e9/rtio_clk_freq
self.config["RTIO_FREQUENCY"] = str(rtio_clk_freq/1e6)
if with_wrpll:
self.submodules.wrpll_sampler = DDMTDSamplerGTP(
self.drtio_transceiver,
platform.request("cdr_clk_clean_fabric"))
helper_clk_pads = platform.request("ddmtd_helper_clk")
self.submodules.wrpll = WRPLL(
helper_clk_pads=helper_clk_pads,
main_dcxo_i2c=platform.request("ddmtd_main_dcxo_i2c"),
helper_dxco_i2c=platform.request("ddmtd_helper_dcxo_i2c"),
ddmtd_inputs=self.wrpll_sampler)
self.csr_devices.append("wrpll")
# note: do not use self.wrpll.cd_helper.clk; otherwise, vivado craps out with:
# critical warning: create_clock attempting to set clock on an unknown port/pin
# command: "create_clock -period 7.920000 -waveform {0.000000 3.960000} -name
# helper_clk [get_xlnx_outside_genome_inst_pin 20 0]
platform.add_period_constraint(helper_clk_pads.p, rtio_clk_period*0.99)
platform.add_false_path_constraints(self.crg.cd_sys.clk, helper_clk_pads.p)
else:
self.submodules.siphaser = SiPhaser7Series(
si5324_clkin=platform.request("cdr_clk") if platform.hw_rev == "v2.0"
else platform.request("si5324_clkin"),
rx_synchronizer=self.rx_synchronizer,
ref_clk=self.crg.clk125_div2, ref_div2=True,
rtio_clk_freq=rtio_clk_freq)
platform.add_false_path_constraints(
self.crg.cd_sys.clk, self.siphaser.mmcm_freerun_output)
self.csr_devices.append("siphaser")
self.config["HAS_SI5324"] = None
self.config["SI5324_SOFT_RESET"] = None
self.submodules.siphaser = SiPhaser7Series(
si5324_clkin=platform.request("cdr_clk") if platform.hw_rev == "v2.0"
else platform.request("si5324_clkin"),
rx_synchronizer=self.rx_synchronizer,
ref_clk=self.crg.clk125_div2, ref_div2=True,
rtio_clk_freq=rtio_clk_freq)
platform.add_false_path_constraints(
self.crg.cd_sys.clk, self.siphaser.mmcm_freerun_output)
self.csr_devices.append("siphaser")
self.config["HAS_SI5324"] = None
self.config["SI5324_SOFT_RESET"] = None
gtp = self.drtio_transceiver.gtps[0]
txout_buf = Signal()
self.specials += Instance("BUFG", i_I=gtp.txoutclk, o_O=txout_buf)
self.crg.configure(txout_buf, clk_sw=gtp.tx_init.done)
platform.add_period_constraint(gtp.txoutclk, rtio_clk_period)
platform.add_period_constraint(gtp.rxoutclk, rtio_clk_period)
platform.add_false_path_constraints(
self.crg.cd_sys.clk,
gtp.txoutclk, gtp.rxoutclk)
if with_wrpll:
platform.add_false_path_constraints(
helper_clk_pads.p, gtp.rxoutclk)
for gtp in self.drtio_transceiver.gtps[1:]:
platform.add_period_constraint(gtp.rxoutclk, rtio_clk_period)
platform.add_false_path_constraints(
self.crg.cd_sys.clk, gtp.rxoutclk)
self.submodules.rtio_crg = RTIOClockMultiplier(rtio_clk_freq)
self.csr_devices.append("rtio_crg")
fix_serdes_timing_path(platform)
def add_rtio(self, rtio_channels, sed_lanes=8):
@ -629,7 +573,7 @@ class SatelliteBase(BaseSoC):
self.submodules.cri_con = rtio.CRIInterconnectShared(
[self.drtiosat.cri],
[self.local_io.cri] + self.drtio_cri,
mode="sync", enable_routing=True)
enable_routing=True)
self.csr_devices.append("cri_con")
self.submodules.routing_table = rtio.RoutingTableAccess(self.cri_con)
self.csr_devices.append("routing_table")
@ -687,7 +631,6 @@ def main():
parser.add_argument("-V", "--variant", default="tester",
help="variant: {} (default: %(default)s)".format(
"/".join(sorted(VARIANTS.keys()))))
parser.add_argument("--with-wrpll", default=False, action="store_true")
parser.add_argument("--tester-dds", default=None,
help="Tester variant DDS type: ad9910/ad9912 "
"(default: ad9910)")
@ -696,8 +639,6 @@ def main():
args = parser.parse_args()
argdict = dict()
if args.with_wrpll:
argdict["with_wrpll"] = True
argdict["gateware_identifier_str"] = args.gateware_identifier_str
argdict["dds"] = args.tester_dds

View File

@ -64,7 +64,7 @@ class GenericStandalone(StandaloneBase):
self.add_csr_group("grabber", self.grabber_csr_group)
for grabber in self.grabber_csr_group:
self.platform.add_false_path_constraints(
self.rtio_crg.cd_rtio.clk, getattr(self, grabber).deserializer.cd_cl.clk)
self.crg.cd_sys.clk, getattr(self, grabber).deserializer.cd_cl.clk)
class GenericMaster(MasterBase):

View File

@ -17,72 +17,27 @@ from misoc.integration.builder import builder_args, builder_argdict
from artiq.gateware.amp import AMPSoC
from artiq.gateware import rtio, nist_clock, nist_qc2
from artiq.gateware.rtio.phy import ttl_simple, ttl_serdes_7series, dds, spi2
from artiq.gateware.rtio.xilinx_clocking import RTIOClockMultiplier, fix_serdes_timing_path
from artiq.gateware.rtio.xilinx_clocking import fix_serdes_timing_path
from artiq.gateware.drtio.transceiver import gtx_7series
from artiq.gateware.drtio.siphaser import SiPhaser7Series
from artiq.gateware.drtio.rx_synchronizer import XilinxRXSynchronizer
from artiq.gateware.drtio import *
from artiq.build_soc import *
class _RTIOCRG(Module, AutoCSR):
def __init__(self, platform, rtio_internal_clk, use_sma=True):
self._clock_sel = CSRStorage()
self._pll_reset = CSRStorage(reset=1)
self._pll_locked = CSRStatus()
self.clock_domains.cd_rtio = ClockDomain()
self.clock_domains.cd_rtiox4 = ClockDomain(reset_less=True)
# 100 MHz when using 125MHz input
self.clock_domains.cd_ext_clkout = ClockDomain(reset_less=True)
platform.add_period_constraint(self.cd_ext_clkout.clk, 5.0)
if use_sma:
ext_clkout = platform.request("user_sma_gpio_p_33")
self.sync.ext_clkout += ext_clkout.eq(~ext_clkout)
rtio_external_clk = Signal()
if use_sma:
user_sma_clock = platform.request("user_sma_clock")
platform.add_period_constraint(user_sma_clock.p, 8.0)
self.specials += Instance("IBUFDS",
i_I=user_sma_clock.p, i_IB=user_sma_clock.n,
o_O=rtio_external_clk)
pll_locked = Signal()
rtio_clk = Signal()
rtiox4_clk = Signal()
ext_clkout_clk = Signal()
class SMAClkinForward(Module):
def __init__(self, platform):
sma_clkin = platform.request("user_sma_clock")
sma_clkin_se = Signal()
sma_clkin_buffered = Signal()
cdr_clk_se = Signal()
cdr_clk = platform.request("si5324_clkin_33")
self.specials += [
Instance("PLLE2_ADV",
p_STARTUP_WAIT="FALSE", o_LOCKED=pll_locked,
p_REF_JITTER1=0.01,
p_CLKIN1_PERIOD=8.0, p_CLKIN2_PERIOD=8.0,
i_CLKIN1=rtio_internal_clk, i_CLKIN2=rtio_external_clk,
# Warning: CLKINSEL=0 means CLKIN2 is selected
i_CLKINSEL=~self._clock_sel.storage,
# VCO @ 1GHz when using 125MHz input
p_CLKFBOUT_MULT=8, p_DIVCLK_DIVIDE=1,
i_CLKFBIN=self.cd_rtio.clk,
i_RST=self._pll_reset.storage,
o_CLKFBOUT=rtio_clk,
p_CLKOUT0_DIVIDE=2, p_CLKOUT0_PHASE=0.0,
o_CLKOUT0=rtiox4_clk,
p_CLKOUT1_DIVIDE=5, p_CLKOUT1_PHASE=0.0,
o_CLKOUT1=ext_clkout_clk),
Instance("BUFG", i_I=rtio_clk, o_O=self.cd_rtio.clk),
Instance("BUFG", i_I=rtiox4_clk, o_O=self.cd_rtiox4.clk),
Instance("BUFG", i_I=ext_clkout_clk, o_O=self.cd_ext_clkout.clk),
AsyncResetSynchronizer(self.cd_rtio, ~pll_locked),
MultiReg(pll_locked, self._pll_locked.status)
Instance("IBUFDS", i_I=sma_clkin.p, i_IB=sma_clkin.n, o_O=sma_clkin_se),
Instance("BUFG", i_I=sma_clkin_se, o_O=sma_clkin_buffered),
Instance("ODDR", i_C=sma_clkin_buffered, i_CE=1, i_D1=0, i_D2=1, o_Q=cdr_clk_se),
Instance("OBUFDS", i_I=cdr_clk_se, o_O=cdr_clk.p, o_OB=cdr_clk.n)
]
# The default voltage for these signals on KC705 is 2.5V, and the Migen platform
# follows this default. But since the SMAs are on the same bank as the DDS,
# which is set to 3.3V by reprogramming the KC705 power ICs, we need to
@ -138,6 +93,7 @@ class _StandaloneBase(MiniSoC, AMPSoC):
integrated_sram_size=8192,
ethmac_nrxslots=4,
ethmac_ntxslots=4,
rtio_sys_merge=True,
**kwargs)
AMPSoC.__init__(self)
add_identifier(self, gateware_identifier_str=gateware_identifier_str)
@ -149,6 +105,31 @@ class _StandaloneBase(MiniSoC, AMPSoC):
if isinstance(self.platform.toolchain, XilinxISEToolchain):
self.platform.toolchain.bitgen_opt += " -g compress"
self.platform.add_extension(_reprogrammed3v3_io)
cdr_clk_out = self.platform.request("si5324_clkout")
cdr_clk = Signal()
cdr_clk_buf = Signal()
self.config["HAS_SI5324"] = None
self.config["SI5324_AS_SYNTHESIZER"] = None
self.submodules.si5324_rst_n = gpio.GPIOOut(self.platform.request("si5324_33").rst_n)
self.csr_devices.append("si5324_rst_n")
self.specials += [
Instance("IBUFDS_GTE2",
i_CEB=0,
i_I=cdr_clk_out.p, i_IB=cdr_clk_out.n,
o_O=cdr_clk,
p_CLKCM_CFG=1,
p_CLKRCV_TRST=1,
p_CLKSWING_CFG="2'b11"),
Instance("BUFG", i_I=cdr_clk, o_O=cdr_clk_buf)
]
self.crg.configure(cdr_clk_buf)
self.submodules += SMAClkinForward(self.platform)
self.submodules.timer1 = timer.Timer()
self.csr_devices.append("timer1")
self.interrupt_devices.append("timer1")
@ -158,7 +139,6 @@ class _StandaloneBase(MiniSoC, AMPSoC):
self.platform.request("user_led", 1)))
self.csr_devices.append("leds")
self.platform.add_extension(_reprogrammed3v3_io)
self.platform.add_extension(_ams101_dac)
i2c = self.platform.request("i2c")
@ -169,10 +149,7 @@ class _StandaloneBase(MiniSoC, AMPSoC):
self.config["HAS_DDS"] = None
def add_rtio(self, rtio_channels):
self.submodules.rtio_crg = _RTIOCRG(self.platform, self.crg.cd_sys.clk)
self.csr_devices.append("rtio_crg")
self.config["HAS_RTIO_CLOCK_SWITCH"] = None
self.submodules.rtio_tsc = rtio.TSC("async", glbl_fine_ts_width=3)
self.submodules.rtio_tsc = rtio.TSC(glbl_fine_ts_width=3)
self.submodules.rtio_core = rtio.Core(self.rtio_tsc, rtio_channels)
self.csr_devices.append("rtio_core")
self.submodules.rtio = rtio.KernelInitiator(self.rtio_tsc)
@ -187,11 +164,6 @@ class _StandaloneBase(MiniSoC, AMPSoC):
self.submodules.rtio_moninj = rtio.MonInj(rtio_channels)
self.csr_devices.append("rtio_moninj")
self.platform.add_period_constraint(self.rtio_crg.cd_rtio.clk, 8.)
self.platform.add_false_path_constraints(
self.crg.cd_sys.clk,
self.rtio_crg.cd_rtio.clk)
self.submodules.rtio_analyzer = rtio.Analyzer(self.rtio_tsc, self.rtio_core.cri,
self.get_native_sdram_if(), cpu_dw=self.cpu_dw)
self.csr_devices.append("rtio_analyzer")
@ -208,6 +180,7 @@ class _MasterBase(MiniSoC, AMPSoC):
mem_map.update(MiniSoC.mem_map)
def __init__(self, gateware_identifier_str=None, drtio_100mhz=False, **kwargs):
clk_freq = 100e6 if drtio_100mhz else 125e6
MiniSoC.__init__(self,
cpu_type="vexriscv",
cpu_bus_width=64,
@ -216,6 +189,8 @@ class _MasterBase(MiniSoC, AMPSoC):
integrated_sram_size=8192,
ethmac_nrxslots=4,
ethmac_ntxslots=4,
clk_freq=clk_freq,
rtio_sys_merge=True,
**kwargs)
AMPSoC.__init__(self)
add_identifier(self, gateware_identifier_str=gateware_identifier_str)
@ -236,17 +211,14 @@ class _MasterBase(MiniSoC, AMPSoC):
platform.request("sfp"), platform.request("user_sma_mgt")
]
rtio_clk_freq = 100e6 if drtio_100mhz else 125e6
# 1000BASE_BX10 Ethernet compatible, 100/125MHz RTIO clock
self.submodules.drtio_transceiver = gtx_7series.GTX(
clock_pads=platform.request("si5324_clkout"),
pads=data_pads,
sys_clk_freq=self.clk_freq,
rtio_clk_freq=rtio_clk_freq)
clk_freq=self.clk_freq)
self.csr_devices.append("drtio_transceiver")
self.submodules.rtio_tsc = rtio.TSC("async", glbl_fine_ts_width=3)
self.submodules.rtio_tsc = rtio.TSC(glbl_fine_ts_width=3)
drtio_csr_group = []
drtioaux_csr_group = []
@ -292,29 +264,31 @@ class _MasterBase(MiniSoC, AMPSoC):
self.config["HAS_SI5324"] = None
self.config["SI5324_AS_SYNTHESIZER"] = None
self.comb += [
platform.request("user_sma_clock_p").eq(ClockSignal("rtio_rx0")),
platform.request("user_sma_clock_n").eq(ClockSignal("rtio"))
]
rtio_clk_period = 1e9/self.drtio_transceiver.rtio_clk_freq
# Constrain TX & RX timing for the first transceiver channel
# (First channel acts as master for phase alignment for all channels' TX)
gtx0 = self.drtio_transceiver.gtxs[0]
txout_buf = Signal()
self.specials += Instance("BUFG", i_I=gtx0.txoutclk, o_O=txout_buf)
self.crg.configure(txout_buf, clk_sw=gtx0.tx_init.done)
self.comb += [
platform.request("user_sma_clock_p").eq(ClockSignal("rtio_rx0")),
platform.request("user_sma_clock_n").eq(gtx0.txoutclk)
]
platform.add_period_constraint(gtx0.txoutclk, rtio_clk_period)
platform.add_period_constraint(gtx0.rxoutclk, rtio_clk_period)
platform.add_false_path_constraints(
self.crg.cd_sys.clk,
gtx0.txoutclk, gtx0.rxoutclk)
self.crg.cd_sys.clk, gtx0.rxoutclk)
# Constrain RX timing for the each transceiver channel
# (Each channel performs single-lane phase alignment for RX)
for gtx in self.drtio_transceiver.gtxs[1:]:
platform.add_period_constraint(gtx.rxoutclk, rtio_clk_period)
platform.add_false_path_constraints(
self.crg.cd_sys.clk, gtx0.txoutclk, gtx.rxoutclk)
self.crg.cd_sys.clk, gtx.rxoutclk)
self.submodules.rtio_crg = RTIOClockMultiplier(self.drtio_transceiver.rtio_clk_freq)
self.csr_devices.append("rtio_crg")
fix_serdes_timing_path(platform)
def add_rtio(self, rtio_channels):
@ -345,12 +319,15 @@ class _SatelliteBase(BaseSoC):
mem_map.update(BaseSoC.mem_map)
def __init__(self, gateware_identifier_str=None, sma_as_sat=False, drtio_100mhz=False, **kwargs):
clk_freq = 100e6 if drtio_100mhz else 125e6
BaseSoC.__init__(self,
cpu_type="vexriscv",
cpu_bus_width=64,
sdram_controller_type="minicon",
l2_size=128*1024,
integrated_sram_size=8192,
clk_freq=clk_freq,
rtio_sys_merge=True,
**kwargs)
add_identifier(self, gateware_identifier_str=gateware_identifier_str)
@ -372,17 +349,16 @@ class _SatelliteBase(BaseSoC):
if sma_as_sat:
data_pads = data_pads[::-1]
rtio_clk_freq = 100e6 if drtio_100mhz else 125e6
rtio_clk_freq = clk_freq
# 1000BASE_BX10 Ethernet compatible, 100/125MHz RTIO clock
self.submodules.drtio_transceiver = gtx_7series.GTX(
clock_pads=platform.request("si5324_clkout"),
pads=data_pads,
sys_clk_freq=self.clk_freq,
rtio_clk_freq=rtio_clk_freq)
clk_freq=self.clk_freq)
self.csr_devices.append("drtio_transceiver")
self.submodules.rtio_tsc = rtio.TSC("sync", glbl_fine_ts_width=3)
self.submodules.rtio_tsc = rtio.TSC(glbl_fine_ts_width=3)
drtioaux_csr_group = []
drtioaux_memory_group = []
@ -432,6 +408,7 @@ class _SatelliteBase(BaseSoC):
self.submodules.siphaser = SiPhaser7Series(
si5324_clkin=platform.request("si5324_clkin_33"),
rx_synchronizer=self.rx_synchronizer,
ref_clk=ClockSignal("bootstrap"),
ultrascale=False,
rtio_clk_freq=self.drtio_transceiver.rtio_clk_freq)
platform.add_false_path_constraints(
@ -445,20 +422,22 @@ class _SatelliteBase(BaseSoC):
self.config["I2C_BUS_COUNT"] = 1
self.config["HAS_SI5324"] = None
self.comb += [
platform.request("user_sma_clock_p").eq(ClockSignal("rtio_rx0")),
platform.request("user_sma_clock_n").eq(ClockSignal("rtio"))
]
rtio_clk_period = 1e9/self.drtio_transceiver.rtio_clk_freq
# Constrain TX & RX timing for the first transceiver channel
# (First channel acts as master for phase alignment for all channels' TX)
gtx0 = self.drtio_transceiver.gtxs[0]
txout_buf = Signal()
self.specials += Instance("BUFG", i_I=gtx0.txoutclk, o_O=txout_buf)
self.crg.configure(txout_buf, clk_sw=gtx0.tx_init.done)
self.comb += [
platform.request("user_sma_clock_p").eq(ClockSignal("rtio_rx0")),
platform.request("user_sma_clock_n").eq(gtx0.txoutclk)
]
platform.add_period_constraint(gtx0.txoutclk, rtio_clk_period)
platform.add_period_constraint(gtx0.rxoutclk, rtio_clk_period)
platform.add_false_path_constraints(
self.crg.cd_sys.clk,
gtx0.txoutclk, gtx0.rxoutclk)
# Constrain RX timing for the each transceiver channel
# (Each channel performs single-lane phase alignment for RX)
for gtx in self.drtio_transceiver.gtxs[1:]:
@ -466,8 +445,6 @@ class _SatelliteBase(BaseSoC):
platform.add_false_path_constraints(
self.crg.cd_sys.clk, gtx.rxoutclk)
self.submodules.rtio_crg = RTIOClockMultiplier(self.drtio_transceiver.rtio_clk_freq)
self.csr_devices.append("rtio_crg")
fix_serdes_timing_path(platform)
def add_rtio(self, rtio_channels):
@ -479,7 +456,7 @@ class _SatelliteBase(BaseSoC):
self.submodules.cri_con = rtio.CRIInterconnectShared(
[self.drtiosat.cri],
[self.local_io.cri] + self.drtio_cri,
mode="sync", enable_routing=True)
enable_routing=True)
self.csr_devices.append("cri_con")
self.submodules.routing_table = rtio.RoutingTableAccess(self.cri_con)
self.csr_devices.append("routing_table")

View File

@ -1,180 +0,0 @@
#!/usr/bin/env python3
import argparse
from functools import partial
from migen import *
from migen.build.generic_platform import IOStandard
from misoc.cores import gpio
from misoc.integration.builder import builder_args, builder_argdict
from misoc.interconnect.csr import *
from misoc.targets.metlino import *
from artiq.gateware.amp import AMPSoC
from artiq.gateware import eem
from artiq.gateware import rtio
from artiq.gateware.rtio.phy import ttl_simple, ttl_serdes_ultrascale
from artiq.gateware.drtio.transceiver import gth_ultrascale
from artiq.gateware.drtio import *
from artiq.build_soc import *
def workaround_us_lvds_tristate(platform):
# Those shoddy Kintex Ultrascale FPGAs take almost a microsecond to change the direction of a
# LVDS I/O buffer. The application has to cope with it and this cannot be handled at static
# timing analysis. Disable the latter for IOBUFDS.
# See:
# https://forums.xilinx.com/t5/Timing-Analysis/Delay-890-ns-in-OBUFTDS-in-Kintex-UltraScale/td-p/868364
platform.add_platform_command(
"set_false_path -through [get_pins -filter {{REF_PIN_NAME == T}} -of [get_cells -filter {{REF_NAME == IOBUFDS}}]]")
class Master(MiniSoC, AMPSoC):
mem_map = {
"cri_con": 0x10000000,
"rtio": 0x11000000,
"rtio_dma": 0x12000000,
"drtioaux": 0x14000000,
"mailbox": 0x70000000
}
mem_map.update(MiniSoC.mem_map)
def __init__(self, gateware_identifier_str=None, **kwargs):
MiniSoC.__init__(self,
cpu_type="vexriscv",
cpu_bus_width=64,
sdram_controller_type="minicon",
l2_size=128*1024,
integrated_sram_size=8192,
ethmac_nrxslots=4,
ethmac_ntxslots=4,
csr_address_width=15,
**kwargs)
AMPSoC.__init__(self)
add_identifier(self, gateware_identifier_str=gateware_identifier_str)
platform = self.platform
rtio_clk_freq = 150e6
self.comb += platform.request("input_clk_sel").eq(1)
self.comb += platform.request("filtered_clk_sel").eq(1)
self.submodules.si5324_rst_n = gpio.GPIOOut(platform.request("si5324").rst_n)
self.csr_devices.append("si5324_rst_n")
i2c = self.platform.request("i2c")
self.submodules.i2c = gpio.GPIOTristate([i2c.scl, i2c.sda])
self.csr_devices.append("i2c")
self.config["I2C_BUS_COUNT"] = 1
self.config["HAS_SI5324"] = None
self.config["SI5324_AS_SYNTHESIZER"] = None
self.config["RTIO_FREQUENCY"] = str(rtio_clk_freq/1e6)
self.submodules.drtio_transceiver = gth_ultrascale.GTH(
clock_pads=platform.request("cdr_clk_clean", 0),
data_pads=[platform.request("mch_fabric_d", i) for i in range(11)],
sys_clk_freq=self.clk_freq,
rtio_clk_freq=rtio_clk_freq)
self.csr_devices.append("drtio_transceiver")
self.submodules.rtio_tsc = rtio.TSC("async", glbl_fine_ts_width=3)
drtio_csr_group = []
drtioaux_csr_group = []
drtioaux_memory_group = []
drtio_cri = []
for i in range(len(self.drtio_transceiver.channels)):
core_name = "drtio" + str(i)
coreaux_name = "drtioaux" + str(i)
memory_name = "drtioaux" + str(i) + "_mem"
drtio_csr_group.append(core_name)
drtioaux_csr_group.append(coreaux_name)
drtioaux_memory_group.append(memory_name)
cdr = ClockDomainsRenamer({"rtio_rx": "rtio_rx" + str(i)})
core = cdr(DRTIOMaster(self.rtio_tsc, self.drtio_transceiver.channels[i]))
setattr(self.submodules, core_name, core)
drtio_cri.append(core.cri)
self.csr_devices.append(core_name)
coreaux = cdr(DRTIOAuxController(core.link_layer, self.cpu_dw))
setattr(self.submodules, coreaux_name, coreaux)
self.csr_devices.append(coreaux_name)
memory_address = self.mem_map["drtioaux"] + 0x800*i
self.add_wb_slave(memory_address, 0x800,
coreaux.bus)
self.add_memory_region(memory_name, memory_address | self.shadow_base, 0x800)
self.config["HAS_DRTIO"] = None
self.config["HAS_DRTIO_ROUTING"] = None
self.add_csr_group("drtio", drtio_csr_group)
self.add_csr_group("drtioaux", drtioaux_csr_group)
self.add_memory_group("drtioaux_mem", drtioaux_memory_group)
rtio_clk_period = 1e9/rtio_clk_freq
gth0 = self.drtio_transceiver.gths[0]
platform.add_period_constraint(gth0.txoutclk, rtio_clk_period/2)
platform.add_period_constraint(gth0.rxoutclk, rtio_clk_period)
platform.add_false_path_constraints(
self.crg.cd_sys.clk,
gth0.txoutclk, gth0.rxoutclk)
for gth in self.drtio_transceiver.gths[1:]:
platform.add_period_constraint(gth.rxoutclk, rtio_clk_period)
platform.add_false_path_constraints(
self.crg.cd_sys.clk, gth0.txoutclk, gth.rxoutclk)
self.rtio_channels = rtio_channels = []
for i in range(4):
phy = ttl_simple.Output(platform.request("user_led", i))
self.submodules += phy
rtio_channels.append(rtio.Channel.from_phy(phy))
output_4x = partial(ttl_serdes_ultrascale.Output, 4)
eem.DIO.add_std(self, 2, output_4x, output_4x,
iostandard=lambda eem: IOStandard("LVDS"))
eem.Urukul.add_std(self, 0, 1, output_4x,
iostandard=lambda eem: IOStandard("LVDS"))
eem.Zotino.add_std(self, 3, output_4x,
iostandard=lambda eem: IOStandard("LVDS"))
workaround_us_lvds_tristate(platform)
self.config["HAS_RTIO_LOG"] = None
self.config["RTIO_LOG_CHANNEL"] = len(rtio_channels)
rtio_channels.append(rtio.LogChannel())
self.submodules.rtio_moninj = rtio.MonInj(rtio_channels)
self.csr_devices.append("rtio_moninj")
self.submodules.rtio_core = rtio.Core(self.rtio_tsc, rtio_channels)
self.csr_devices.append("rtio_core")
self.submodules.rtio = rtio.KernelInitiator(self.rtio_tsc)
self.submodules.rtio_dma = ClockDomainsRenamer("sys_kernel")(
rtio.DMA(self.get_native_sdram_if(), self.cpu_dw))
self.register_kernel_cpu_csrdevice("rtio")
self.register_kernel_cpu_csrdevice("rtio_dma")
self.submodules.cri_con = rtio.CRIInterconnectShared(
[self.rtio.cri, self.rtio_dma.cri],
[self.rtio_core.cri] + drtio_cri,
enable_routing=True)
self.register_kernel_cpu_csrdevice("cri_con")
self.submodules.routing_table = rtio.RoutingTableAccess(self.cri_con)
self.csr_devices.append("routing_table")
def main():
parser = argparse.ArgumentParser(
description="Metlino gateware and firmware builder")
builder_args(parser)
soc_sdram_args(parser)
parser.set_defaults(output_dir="artiq_metlino")
parser.add_argument("--gateware-identifier-str", default=None,
help="Override ROM identifier")
args = parser.parse_args()
args.variant = "master"
soc = Master(gateware_identifier_str=args.gateware_identifier_str, **soc_sdram_argdict(args))
build_artiq_soc(soc, builder_argdict(args))
if __name__ == "__main__":
main()

View File

@ -1,462 +0,0 @@
#!/usr/bin/env python3
import argparse
import os
import warnings
from functools import partial
from migen import *
from migen.build.generic_platform import IOStandard
from misoc.cores import gpio
from misoc.integration.builder import builder_args, builder_argdict
from misoc.interconnect.csr import *
from misoc.targets.sayma_amc import *
from artiq.gateware.amp import AMPSoC
from artiq.gateware import eem
from artiq.gateware import rtio
from artiq.gateware import jesd204_tools
from artiq.gateware import fmcdio_vhdci_eem
from artiq.gateware.rtio.phy import ttl_simple, ttl_serdes_ultrascale, sawg
from artiq.gateware.drtio.transceiver import gth_ultrascale
from artiq.gateware.drtio.siphaser import SiPhaser7Series
from artiq.gateware.drtio.wrpll import WRPLL, DDMTDSamplerExtFF
from artiq.gateware.drtio.rx_synchronizer import XilinxRXSynchronizer
from artiq.gateware.drtio import *
from artiq.build_soc import *
def workaround_us_lvds_tristate(platform):
# Those shoddy Kintex Ultrascale FPGAs take almost a microsecond to change the direction of a
# LVDS I/O buffer. The application has to cope with it and this cannot be handled at static
# timing analysis. Disable the latter for IOBUFDS.
# See:
# https://forums.xilinx.com/t5/Timing-Analysis/Delay-890-ns-in-OBUFTDS-in-Kintex-UltraScale/td-p/868364
platform.add_platform_command(
"set_false_path -through [get_pins -filter {{REF_PIN_NAME == T}} -of [get_cells -filter {{REF_NAME == IOBUFDS}}]]")
class RTMUARTForward(Module):
def __init__(self, platform):
# forward RTM UART to second FTDI UART channel
serial_1 = platform.request("serial", 1)
serial_rtm = platform.request("serial_rtm")
self.comb += [
serial_1.tx.eq(serial_rtm.rx),
serial_rtm.tx.eq(serial_1.rx)
]
class SatelliteBase(MiniSoC):
mem_map = {
"drtioaux": 0x14000000,
}
mem_map.update(MiniSoC.mem_map)
def __init__(self, rtio_clk_freq=125e6, identifier_suffix="", gateware_identifier_str=None, with_sfp=False, *, with_wrpll, **kwargs):
MiniSoC.__init__(self,
cpu_type="vexriscv",
cpu_bus_width=64,
sdram_controller_type="minicon",
l2_size=128*1024,
integrated_sram_size=8192,
ethmac_nrxslots=4,
ethmac_ntxslots=4,
**kwargs)
add_identifier(self, suffix=identifier_suffix, gateware_identifier_str=gateware_identifier_str)
self.rtio_clk_freq = rtio_clk_freq
platform = self.platform
if with_wrpll:
clock_recout_pads = platform.request("ddmtd_rec_clk")
else:
clock_recout_pads = None
if with_sfp:
# Use SFP0 to connect to master (Kasli)
self.comb += platform.request("sfp_tx_disable", 0).eq(0)
drtio_uplink = platform.request("sfp", 0)
else:
drtio_uplink = platform.request("fat_pipe", 0)
self.submodules.drtio_transceiver = gth_ultrascale.GTH(
clock_pads=platform.request("cdr_clk_clean"),
data_pads=[drtio_uplink, platform.request("rtm_amc_link")],
sys_clk_freq=self.clk_freq,
rtio_clk_freq=rtio_clk_freq,
clock_recout_pads=clock_recout_pads)
self.csr_devices.append("drtio_transceiver")
self.submodules.rtio_tsc = rtio.TSC("sync", glbl_fine_ts_width=3)
drtioaux_csr_group = []
drtioaux_memory_group = []
drtiorep_csr_group = []
self.drtio_cri = []
for i in range(len(self.drtio_transceiver.channels)):
coreaux_name = "drtioaux" + str(i)
memory_name = "drtioaux" + str(i) + "_mem"
drtioaux_csr_group.append(coreaux_name)
drtioaux_memory_group.append(memory_name)
cdr = ClockDomainsRenamer({"rtio_rx": "rtio_rx" + str(i)})
if i == 0:
self.submodules.rx_synchronizer = cdr(XilinxRXSynchronizer())
core = cdr(DRTIOSatellite(
self.rtio_tsc, self.drtio_transceiver.channels[i],
self.rx_synchronizer))
self.submodules.drtiosat = core
self.csr_devices.append("drtiosat")
else:
corerep_name = "drtiorep" + str(i-1)
drtiorep_csr_group.append(corerep_name)
core = cdr(DRTIORepeater(
self.rtio_tsc, self.drtio_transceiver.channels[i]))
setattr(self.submodules, corerep_name, core)
self.drtio_cri.append(core.cri)
self.csr_devices.append(corerep_name)
coreaux = cdr(DRTIOAuxController(core.link_layer, self.cpu_dw))
setattr(self.submodules, coreaux_name, coreaux)
self.csr_devices.append(coreaux_name)
memory_address = self.mem_map["drtioaux"] + 0x800*i
self.add_wb_slave(memory_address, 0x800,
coreaux.bus)
self.add_memory_region(memory_name, memory_address | self.shadow_base, 0x800)
self.config["HAS_DRTIO"] = None
self.config["HAS_DRTIO_ROUTING"] = None
self.add_csr_group("drtioaux", drtioaux_csr_group)
self.add_memory_group("drtioaux_mem", drtioaux_memory_group)
self.add_csr_group("drtiorep", drtiorep_csr_group)
rtio_clk_period = 1e9/rtio_clk_freq
self.config["RTIO_FREQUENCY"] = str(rtio_clk_freq/1e6)
if with_wrpll:
self.comb += [
platform.request("filtered_clk_sel").eq(0),
platform.request("ddmtd_main_dcxo_oe").eq(1),
platform.request("ddmtd_helper_dcxo_oe").eq(1)
]
self.submodules.wrpll_sampler = DDMTDSamplerExtFF(
platform.request("ddmtd_inputs"))
self.submodules.wrpll = WRPLL(
helper_clk_pads=platform.request("ddmtd_helper_clk"),
main_dcxo_i2c=platform.request("ddmtd_main_dcxo_i2c"),
helper_dxco_i2c=platform.request("ddmtd_helper_dcxo_i2c"),
ddmtd_inputs=self.wrpll_sampler)
self.csr_devices.append("wrpll")
platform.add_period_constraint(self.wrpll.cd_helper.clk, rtio_clk_period*0.99)
platform.add_false_path_constraints(self.crg.cd_sys.clk, self.wrpll.cd_helper.clk)
else:
self.comb += platform.request("filtered_clk_sel").eq(1)
self.submodules.siphaser = SiPhaser7Series(
si5324_clkin=platform.request("si5324_clkin"),
rx_synchronizer=self.rx_synchronizer,
ultrascale=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.submodules.si5324_rst_n = gpio.GPIOOut(platform.request("si5324").rst_n)
self.csr_devices.append("si5324_rst_n")
i2c = self.platform.request("i2c")
self.submodules.i2c = gpio.GPIOTristate([i2c.scl, i2c.sda])
self.csr_devices.append("i2c")
self.config["I2C_BUS_COUNT"] = 1
self.config["HAS_SI5324"] = None
gth = self.drtio_transceiver.gths[0]
platform.add_period_constraint(gth.txoutclk, rtio_clk_period/2)
platform.add_period_constraint(gth.rxoutclk, rtio_clk_period)
platform.add_false_path_constraints(
self.crg.cd_sys.clk,
gth.txoutclk, gth.rxoutclk)
def add_rtio(self, rtio_channels):
# Only add MonInj core if there is anything to monitor
if any([len(c.probes) for c in rtio_channels]):
self.submodules.rtio_moninj = rtio.MonInj(rtio_channels)
self.csr_devices.append("rtio_moninj")
self.submodules.local_io = SyncRTIO(self.rtio_tsc, rtio_channels)
self.comb += self.drtiosat.async_errors.eq(self.local_io.async_errors)
self.submodules.cri_con = rtio.CRIInterconnectShared(
[self.drtiosat.cri],
[self.local_io.cri] + self.drtio_cri,
mode="sync", enable_routing=True)
self.csr_devices.append("cri_con")
self.submodules.routing_table = rtio.RoutingTableAccess(self.cri_con)
self.csr_devices.append("routing_table")
# JESD204 DAC Channel Group
class JDCGSAWG(Module, AutoCSR):
def __init__(self, platform, sys_crg, jesd_crg, dac):
# Kintex Ultrascale GTH, speed grade -1C:
# CPLL linerate (D=1): 4.0 - 8.5 Gb/s
self.submodules.jesd = jesd204_tools.UltrascaleTX(
platform, sys_crg, jesd_crg, dac)
self.submodules.sawgs = [sawg.Channel(width=16, parallelism=4) for i in range(4)]
for conv, ch in zip(self.jesd.core.sink.flatten(), self.sawgs):
assert len(Cat(ch.o)) == len(conv)
self.sync.jesd += conv.eq(Cat(ch.o))
class JDCGPattern(Module, AutoCSR):
def __init__(self, platform, sys_crg, jesd_crg, dac):
self.submodules.jesd = jesd204_tools.UltrascaleTX(
platform, sys_crg, jesd_crg, dac)
self.sawgs = []
ramp = Signal(4)
self.sync.rtio += ramp.eq(ramp + 1)
samples = [[Signal(16) for i in range(4)] for j in range(4)]
self.comb += [
a.eq(Cat(b)) for a, b in zip(
self.jesd.core.sink.flatten(), samples)
]
# ch0: 16-step ramp with big carry toggles
for i in range(4):
self.comb += [
samples[0][i][-4:].eq(ramp),
samples[0][i][:-4].eq(0x7ff if i % 2 else 0x800)
]
# ch1: 50 MHz
from math import pi, cos
data = [int(round(cos(i/12*2*pi)*((1 << 15) - 1)))
for i in range(12)]
k = Signal(2)
self.sync.rtio += If(k == 2, k.eq(0)).Else(k.eq(k + 1))
self.comb += [
Case(k, {
i: [samples[1][j].eq(data[i*4 + j]) for j in range(4)]
for i in range(3)
})
]
# ch2: ch0, ch3: ch1
self.comb += [
Cat(samples[2]).eq(Cat(samples[0])),
Cat(samples[3]).eq(Cat(samples[1]))
]
class JDCGSyncDDS(Module, AutoCSR):
def __init__(self, platform, sys_crg, jesd_crg, dac):
self.submodules.jesd = jesd204_tools.UltrascaleTX(
platform, sys_crg, jesd_crg, dac)
self.coarse_ts = Signal(32)
self.sawgs = []
ftw = round(2**len(self.coarse_ts)*9e6/600e6)
parallelism = 4
mul_1 = Signal.like(self.coarse_ts)
mul_2 = Signal.like(self.coarse_ts)
mul_3 = Signal.like(self.coarse_ts)
self.sync.rtio += [
mul_1.eq(self.coarse_ts*ftw*parallelism),
mul_2.eq(mul_1),
mul_3.eq(mul_2)
]
phases = [Signal.like(self.coarse_ts) for i in range(parallelism)]
self.sync.rtio += [phases[i].eq(mul_3 + i*ftw) for i in range(parallelism)]
resolution = 10
steps = 2**resolution
from math import pi, cos
data = [(2**16 + round(cos(i/steps*2*pi)*((1 << 15) - 1))) & 0xffff
for i in range(steps)]
samples = [Signal(16) for i in range(4)]
for phase, sample in zip(phases, samples):
table = Memory(16, steps, init=data)
table_port = table.get_port(clock_domain="rtio")
self.specials += table, table_port
self.comb += [
table_port.adr.eq(phase >> (len(self.coarse_ts) - resolution)),
sample.eq(table_port.dat_r)
]
self.sync.rtio += [sink.eq(Cat(samples))
for sink in self.jesd.core.sink.flatten()]
class Satellite(SatelliteBase):
"""
DRTIO satellite with local DAC/SAWG channels, as well as TTL channels via FMC and VHDCI carrier.
"""
def __init__(self, jdcg_type, **kwargs):
SatelliteBase.__init__(self, 150e6,
identifier_suffix="." + jdcg_type,
**kwargs)
platform = self.platform
self.submodules += RTMUARTForward(platform)
# RTM bitstream upload
slave_fpga_cfg = self.platform.request("rtm_fpga_cfg")
self.submodules.slave_fpga_cfg = gpio.GPIOTristate([
slave_fpga_cfg.cclk,
slave_fpga_cfg.din,
slave_fpga_cfg.done,
slave_fpga_cfg.init_b,
slave_fpga_cfg.program_b,
])
self.csr_devices.append("slave_fpga_cfg")
self.config["SLAVE_FPGA_GATEWARE"] = 0x200000
self.rtio_channels = rtio_channels = []
for i in range(4):
phy = ttl_simple.Output(platform.request("user_led", i))
self.submodules += phy
rtio_channels.append(rtio.Channel.from_phy(phy))
mcx_io = platform.request("mcx_io", 0)
phy = ttl_serdes_ultrascale.InOut(4, mcx_io.level)
self.comb += mcx_io.direction.eq(phy.oe)
self.submodules += phy
rtio_channels.append(rtio.Channel.from_phy(phy))
mcx_io = platform.request("mcx_io", 1)
phy = ttl_serdes_ultrascale.InOut(4, mcx_io.level)
self.comb += mcx_io.direction.eq(phy.oe)
self.submodules += phy
rtio_channels.append(rtio.Channel.from_phy(phy))
self.submodules.jesd_crg = jesd204_tools.UltrascaleCRG(
platform, use_rtio_clock=True)
cls = {
"sawg": JDCGSAWG,
"pattern": JDCGPattern,
"syncdds": JDCGSyncDDS
}[jdcg_type]
self.submodules.jdcg_0 = cls(platform, self.crg, self.jesd_crg, 0)
self.submodules.jdcg_1 = cls(platform, self.crg, self.jesd_crg, 1)
self.csr_devices.append("jesd_crg")
self.csr_devices.append("jdcg_0")
self.csr_devices.append("jdcg_1")
self.config["HAS_JDCG"] = None
self.add_csr_group("jdcg", ["jdcg_0", "jdcg_1"])
self.config["RTIO_FIRST_SAWG_CHANNEL"] = len(rtio_channels)
rtio_channels.extend(rtio.Channel.from_phy(phy)
for sawg in self.jdcg_0.sawgs +
self.jdcg_1.sawgs
for phy in sawg.phys)
# FMC-VHDCI-EEM DIOs x 2 (all OUTPUTs)
platform.add_connectors(fmcdio_vhdci_eem.connectors)
output_4x = partial(ttl_serdes_ultrascale.Output, 4)
eem.DIO.add_std(self, 0,
output_4x, output_4x,
iostandard=lambda eem: IOStandard("LVDS"))
eem.DIO.add_std(self, 1,
output_4x, output_4x,
iostandard=lambda eem: IOStandard("LVDS"))
# FMC-DIO-32ch-LVDS-a Direction Control Pins (via shift register) as TTLs x 3
platform.add_extension(fmcdio_vhdci_eem.io)
print("fmcdio_vhdci_eem.[CLK, SER, LATCH] starting at RTIO channel 0x{:06x}"
.format(len(rtio_channels)))
fmcdio_dirctl = platform.request("fmcdio_dirctl", 0)
fmcdio_dirctl_phys = [
ttl_simple.Output(fmcdio_dirctl.clk),
ttl_simple.Output(fmcdio_dirctl.ser),
ttl_simple.Output(fmcdio_dirctl.latch)
]
for phy in fmcdio_dirctl_phys:
self.submodules += phy
rtio_channels.append(rtio.Channel.from_phy(phy))
workaround_us_lvds_tristate(platform)
self.add_rtio(rtio_channels)
self.submodules.sysref_sampler = jesd204_tools.SysrefSampler(
platform.request("amc_fpga_sysref", 0), self.rtio_tsc.coarse_ts)
self.csr_devices.append("sysref_sampler")
self.jdcg_0.jesd.core.register_jref(self.sysref_sampler.jref)
self.jdcg_1.jesd.core.register_jref(self.sysref_sampler.jref)
if jdcg_type == "syncdds":
self.comb += [
self.jdcg_0.coarse_ts.eq(self.rtio_tsc.coarse_ts),
self.jdcg_1.coarse_ts.eq(self.rtio_tsc.coarse_ts),
]
class SimpleSatellite(SatelliteBase):
def __init__(self, **kwargs):
SatelliteBase.__init__(self, **kwargs)
platform = self.platform
self.submodules += RTMUARTForward(platform)
rtio_channels = []
for i in range(4):
phy = ttl_simple.Output(platform.request("user_led", i))
self.submodules += phy
rtio_channels.append(rtio.Channel.from_phy(phy))
mcx_io = platform.request("mcx_io", 0)
phy = ttl_serdes_ultrascale.InOut(4, mcx_io.level)
self.comb += mcx_io.direction.eq(phy.oe)
self.submodules += phy
rtio_channels.append(rtio.Channel.from_phy(phy))
mcx_io = platform.request("mcx_io", 1)
phy = ttl_serdes_ultrascale.InOut(4, mcx_io.level)
self.comb += mcx_io.direction.eq(phy.oe)
self.submodules += phy
rtio_channels.append(rtio.Channel.from_phy(phy))
self.add_rtio(rtio_channels)
def main():
parser = argparse.ArgumentParser(
description="Sayma AMC gateware and firmware builder")
builder_args(parser)
soc_sayma_amc_args(parser)
parser.set_defaults(output_dir="artiq_sayma")
parser.add_argument("-V", "--variant", default="satellite",
help="variant: satellite/simplesatellite "
"(default: %(default)s)")
parser.add_argument("--sfp", default=False, action="store_true",
help="use SFP port for DRTIO instead of uTCA backplane")
parser.add_argument("--rtm-csr-csv",
default=os.path.join("artiq_sayma", "rtm_gateware", "rtm_csr.csv"),
help="CSV file listing remote CSRs on RTM (default: %(default)s)")
parser.add_argument("--jdcg-type",
default="sawg",
help="Change type of signal generator. This is used exclusively for "
"development and debugging.")
parser.add_argument("--with-wrpll", default=False, action="store_true")
parser.add_argument("--gateware-identifier-str", default=None,
help="Override ROM identifier")
args = parser.parse_args()
variant = args.variant.lower()
if variant == "satellite":
soc = Satellite(
with_sfp=args.sfp,
jdcg_type=args.jdcg_type,
with_wrpll=args.with_wrpll,
gateware_identifier_str=args.gateware_identifier_str,
**soc_sayma_amc_argdict(args))
elif variant == "simplesatellite":
soc = SimpleSatellite(
with_sfp=args.sfp,
with_wrpll=args.with_wrpll,
gateware_identifier_str=args.gateware_identifier_str,
**soc_sayma_amc_argdict(args))
else:
raise SystemExit("Invalid variant (-V/--variant)")
build_artiq_soc(soc, builder_argdict(args))
if __name__ == "__main__":
main()

View File

@ -1,280 +0,0 @@
#!/usr/bin/env python3
import argparse
import os
import subprocess
import struct
from migen import *
from migen.genlib.cdc import MultiReg
from misoc.interconnect.csr import *
from misoc.cores import gpio
from misoc.cores import spi2
from misoc.cores.a7_gtp import *
from misoc.targets.sayma_rtm import BaseSoC, soc_sayma_rtm_args, soc_sayma_rtm_argdict
from misoc.integration.builder import Builder, builder_args, builder_argdict
from artiq.gateware import rtio
from artiq.gateware import jesd204_tools
from artiq.gateware.rtio.phy import ttl_simple, ttl_serdes_7series
from artiq.gateware.rtio.xilinx_clocking import RTIOClockMultiplier, fix_serdes_timing_path
from artiq.gateware.drtio.transceiver import gtp_7series
from artiq.gateware.drtio.siphaser import SiPhaser7Series
from artiq.gateware.drtio.wrpll import WRPLL, DDMTDSamplerGTP
from artiq.gateware.drtio.rx_synchronizer import XilinxRXSynchronizer
from artiq.gateware.drtio import *
from artiq.build_soc import add_identifier
from artiq import __artiq_dir__ as artiq_dir
class _SatelliteBase(BaseSoC):
mem_map = {
"drtioaux": 0x50000000,
}
mem_map.update(BaseSoC.mem_map)
def __init__(self, rtio_clk_freq, *, with_wrpll, gateware_identifier_str, **kwargs):
BaseSoC.__init__(self,
cpu_type="vexriscv",
cpu_bus_width=64,
**kwargs)
add_identifier(self, gateware_identifier_str=gateware_identifier_str)
self.rtio_clk_freq = rtio_clk_freq
platform = self.platform
disable_cdrclkc_ibuf = Signal(reset=1)
disable_cdrclkc_ibuf.attr.add("no_retiming")
cdrclkc_clkout = platform.request("cdr_clk_clean")
cdrclkc_clkout_buf = Signal()
self.specials += Instance("IBUFDS_GTE2",
i_CEB=disable_cdrclkc_ibuf,
i_I=cdrclkc_clkout.p, i_IB=cdrclkc_clkout.n,
o_O=cdrclkc_clkout_buf)
qpll_drtio_settings = QPLLSettings(
refclksel=0b001,
fbdiv=4,
fbdiv_45=5,
refclk_div=1)
qpll = QPLL(cdrclkc_clkout_buf, qpll_drtio_settings)
self.submodules += qpll
self.submodules.drtio_transceiver = gtp_7series.GTP(
qpll_channel=qpll.channels[0],
data_pads=[platform.request("rtm_amc_link", 0)],
sys_clk_freq=self.clk_freq,
rtio_clk_freq=rtio_clk_freq)
self.csr_devices.append("drtio_transceiver")
self.sync += disable_cdrclkc_ibuf.eq(
~self.drtio_transceiver.stable_clkin.storage)
self.submodules.rtio_tsc = rtio.TSC("sync", glbl_fine_ts_width=3)
cdr = ClockDomainsRenamer({"rtio_rx": "rtio_rx0"})
self.submodules.rx_synchronizer = cdr(XilinxRXSynchronizer())
core = cdr(DRTIOSatellite(
self.rtio_tsc, self.drtio_transceiver.channels[0],
self.rx_synchronizer))
self.submodules.drtiosat = core
self.csr_devices.append("drtiosat")
coreaux = cdr(DRTIOAuxController(core.link_layer, self.cpu_dw))
self.submodules.drtioaux0 = coreaux
self.csr_devices.append("drtioaux0")
memory_address = self.mem_map["drtioaux"]
self.add_wb_slave(memory_address, 0x800,
coreaux.bus)
self.add_memory_region("drtioaux0_mem", memory_address | self.shadow_base, 0x800)
self.config["HAS_DRTIO"] = None
self.add_csr_group("drtioaux", ["drtioaux0"])
self.add_memory_group("drtioaux_mem", ["drtioaux0_mem"])
gtp = self.drtio_transceiver.gtps[0]
rtio_clk_period = 1e9/rtio_clk_freq
self.config["RTIO_FREQUENCY"] = str(rtio_clk_freq/1e6)
if with_wrpll:
self.comb += [
platform.request("filtered_clk_sel").eq(0),
platform.request("ddmtd_main_dcxo_oe").eq(1),
platform.request("ddmtd_helper_dcxo_oe").eq(1)
]
self.submodules.wrpll_sampler = DDMTDSamplerGTP(
self.drtio_transceiver,
platform.request("cdr_clk_clean_fabric"))
self.submodules.wrpll = WRPLL(
helper_clk_pads=platform.request("ddmtd_helper_clk"),
main_dcxo_i2c=platform.request("ddmtd_main_dcxo_i2c"),
helper_dxco_i2c=platform.request("ddmtd_helper_dcxo_i2c"),
ddmtd_inputs=self.wrpll_sampler)
self.csr_devices.append("wrpll")
platform.add_period_constraint(self.wrpll.cd_helper.clk, rtio_clk_period*0.99)
platform.add_false_path_constraints(self.crg.cd_sys.clk, self.wrpll.cd_helper.clk)
platform.add_false_path_constraints(self.wrpll.cd_helper.clk, gtp.rxoutclk)
else:
self.comb += platform.request("filtered_clk_sel").eq(1)
self.submodules.siphaser = SiPhaser7Series(
si5324_clkin=platform.request("si5324_clkin"),
rx_synchronizer=self.rx_synchronizer,
ref_clk=self.crg.cd_sys.clk, 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.submodules.si5324_rst_n = gpio.GPIOOut(platform.request("si5324").rst_n)
self.csr_devices.append("si5324_rst_n")
i2c = self.platform.request("i2c")
self.submodules.i2c = gpio.GPIOTristate([i2c.scl, i2c.sda])
self.csr_devices.append("i2c")
self.config["I2C_BUS_COUNT"] = 1
self.config["HAS_SI5324"] = None
platform.add_period_constraint(gtp.txoutclk, rtio_clk_period)
platform.add_period_constraint(gtp.rxoutclk, rtio_clk_period)
platform.add_false_path_constraints(
self.crg.cd_sys.clk,
gtp.txoutclk, gtp.rxoutclk)
self.submodules.rtio_crg = RTIOClockMultiplier(rtio_clk_freq)
self.csr_devices.append("rtio_crg")
fix_serdes_timing_path(platform)
def add_rtio(self, rtio_channels):
self.submodules.rtio_moninj = rtio.MonInj(rtio_channels)
self.csr_devices.append("rtio_moninj")
self.submodules.local_io = SyncRTIO(self.rtio_tsc, rtio_channels)
self.comb += self.drtiosat.async_errors.eq(self.local_io.async_errors)
self.comb += self.drtiosat.cri.connect(self.local_io.cri)
class Satellite(_SatelliteBase):
def __init__(self, **kwargs):
_SatelliteBase.__init__(self, **kwargs)
platform = self.platform
rtio_channels = []
for bm in range(2):
print("BaseMod{} RF switches starting at RTIO channel 0x{:06x}"
.format(bm, len(rtio_channels)))
for i in range(4):
phy = ttl_serdes_7series.Output_8X(platform.request("basemod{}_rfsw".format(bm), i),
invert=True)
self.submodules += phy
rtio_channels.append(rtio.Channel.from_phy(phy))
print("BaseMod{} attenuator starting at RTIO channel 0x{:06x}"
.format(bm, len(rtio_channels)))
basemod_att = platform.request("basemod{}_att".format(bm))
for name in "rst_n clk le".split():
signal = getattr(basemod_att, name)
for i in range(len(signal)):
phy = ttl_simple.Output(signal[i])
self.submodules += phy
rtio_channels.append(rtio.Channel.from_phy(phy))
phy = ttl_simple.Output(basemod_att.mosi[0])
self.submodules += phy
rtio_channels.append(rtio.Channel.from_phy(phy))
for i in range(3):
self.comb += basemod_att.mosi[i+1].eq(basemod_att.miso[i])
phy = ttl_simple.InOut(basemod_att.miso[3])
self.submodules += phy
rtio_channels.append(rtio.Channel.from_phy(phy))
self.add_rtio(rtio_channels)
self.comb += platform.request("clk_src_ext_sel").eq(0)
# HMC clock chip and DAC control
self.comb += [
platform.request("ad9154_rst_n", 0).eq(1),
platform.request("ad9154_rst_n", 1).eq(1)
]
self.submodules.converter_spi = spi2.SPIMaster(spi2.SPIInterface(
platform.request("hmc_spi"),
platform.request("ad9154_spi", 0),
platform.request("ad9154_spi", 1)))
self.csr_devices.append("converter_spi")
self.submodules.hmc7043_reset = gpio.GPIOOut(
platform.request("hmc7043_reset"), reset_out=1)
self.csr_devices.append("hmc7043_reset")
self.submodules.hmc7043_gpo = gpio.GPIOIn(
platform.request("hmc7043_gpo"))
self.csr_devices.append("hmc7043_gpo")
self.config["HAS_HMC830_7043"] = None
self.config["HAS_AD9154"] = None
self.config["AD9154_COUNT"] = 2
self.config["CONVERTER_SPI_HMC830_CS"] = 0
self.config["CONVERTER_SPI_HMC7043_CS"] = 1
self.config["CONVERTER_SPI_FIRST_AD9154_CS"] = 2
self.config["HMC830_REF"] = str(int(self.rtio_clk_freq/1e6))
# HMC workarounds
self.comb += platform.request("hmc830_pwr_en").eq(1)
self.submodules.hmc7043_out_en = gpio.GPIOOut(
platform.request("hmc7043_out_en"))
self.csr_devices.append("hmc7043_out_en")
# DDMTD
sysref_pads = platform.request("rtm_fpga_sysref", 0)
self.submodules.sysref_ddmtd = jesd204_tools.DDMTD(sysref_pads, self.rtio_clk_freq)
self.csr_devices.append("sysref_ddmtd")
platform.add_false_path_constraints(
self.sysref_ddmtd.cd_helper.clk, self.drtio_transceiver.gtps[0].txoutclk)
platform.add_false_path_constraints(
self.sysref_ddmtd.cd_helper.clk, self.crg.cd_sys.clk)
class SatmanSoCBuilder(Builder):
def __init__(self, *args, **kwargs):
Builder.__init__(self, *args, **kwargs)
firmware_dir = os.path.join(artiq_dir, "firmware")
self.software_packages = []
self.add_software_package("satman", os.path.join(firmware_dir, "satman"))
def initialize_memory(self):
satman = os.path.join(self.output_dir, "software", "satman",
"satman.bin")
with open(satman, "rb") as boot_file:
boot_data = []
unpack_endian = "<I"
while True:
w = boot_file.read(4)
if not w:
break
boot_data.append(struct.unpack(unpack_endian, w)[0])
self.soc.main_ram.mem.init = boot_data
def main():
parser = argparse.ArgumentParser(
description="Sayma RTM gateware and firmware builder")
builder_args(parser)
soc_sayma_rtm_args(parser)
parser.add_argument("--rtio-clk-freq",
default=150, type=int, help="RTIO clock frequency in MHz")
parser.add_argument("--with-wrpll", default=False, action="store_true")
parser.add_argument("--gateware-identifier-str", default=None,
help="Override ROM identifier")
parser.set_defaults(output_dir=os.path.join("artiq_sayma", "rtm"))
args = parser.parse_args()
soc = Satellite(
rtio_clk_freq=1e6*args.rtio_clk_freq,
with_wrpll=args.with_wrpll,
gateware_identifier_str=args.gateware_identifier_str,
**soc_sayma_rtm_argdict(args))
builder = SatmanSoCBuilder(soc, **builder_argdict(args))
try:
builder.build()
except subprocess.CalledProcessError as e:
raise SystemExit("Command {} failed".format(" ".join(e.cmd)))
if __name__ == "__main__":
main()

View File

@ -36,7 +36,7 @@ class TB(Module):
def __init__(self, nwords, dw):
self.submodules.link_layer = Loopback(nwords)
self.submodules.aux_controller = ClockDomainsRenamer(
{"rtio": "sys", "rtio_rx": "sys"})(DRTIOAuxController(self.link_layer, dw))
{"rtio_rx": "sys"})(DRTIOAuxController(self.link_layer, dw))
class TestAuxController(unittest.TestCase):

View File

@ -52,7 +52,7 @@ class DUT(Module):
self.ttl1 = Signal()
self.transceivers = DummyTransceiverPair(nwords)
self.submodules.tsc_master = rtio.TSC("async")
self.submodules.tsc_master = rtio.TSC()
self.submodules.master = DRTIOMaster(self.tsc_master,
self.transceivers.alice)
self.submodules.master_ki = rtio.KernelInitiator(self.tsc_master,
@ -67,7 +67,7 @@ class DUT(Module):
rtio.Channel.from_phy(self.phy1),
rtio.Channel.from_phy(self.phy2),
]
self.submodules.tsc_satellite = rtio.TSC("sync")
self.submodules.tsc_satellite = rtio.TSC()
self.submodules.satellite = DRTIOSatellite(
self.tsc_satellite, self.transceivers.bob, rx_synchronizer)
self.satellite.reset.storage.reset = 0
@ -144,8 +144,8 @@ class OutputsTestbench:
class TestFullStack(unittest.TestCase):
clocks = {"sys": 8, "rtio": 5, "rtio_rx": 5,
"rio": 5, "rio_phy": 5}
clocks = {"sys": 8, "rtio_rx": 8,
"rio": 8, "rio_phy": 8}
def test_pulses(self):
tb = OutputsTestbench()
@ -169,7 +169,7 @@ class TestFullStack(unittest.TestCase):
yield from tb.sync()
run_simulation(tb.dut,
{"sys": test(), "rtio": tb.check_ttls(ttl_changes)}, self.clocks)
{"sys": test()}, self.clocks)
self.assertEqual(ttl_changes, correct_ttl_changes)
def test_underflow(self):
@ -214,7 +214,7 @@ class TestFullStack(unittest.TestCase):
yield from tb.sync()
run_simulation(tb.dut,
{"sys": test(), "rtio": tb.check_ttls(ttl_changes)}, self.clocks)
{"sys": test()}, self.clocks)
self.assertEqual(ttl_changes, correct_ttl_changes)
def test_write_underflow(self):
@ -284,7 +284,7 @@ class TestFullStack(unittest.TestCase):
yield dut.phy2.rtlink.i.stb.eq(0)
run_simulation(dut,
{"sys": test(), "rtio": generate_input()}, self.clocks)
{"sys": test()}, self.clocks)
def test_echo(self):
dut = DUT(2)

View File

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

View File

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

View File

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

View File

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

View File

@ -22,7 +22,7 @@ class DUT(Module):
rtio.Channel.from_phy(self.phy1)
]
self.submodules.sed = SED(rtio_channels, 0, "sync", **kwargs)
self.submodules.sed = SED(rtio_channels, 0, **kwargs)
self.sync += [
self.sed.coarse_timestamp.eq(self.sed.coarse_timestamp + 1),
self.sed.minimum_coarse_timestamp.eq(self.sed.coarse_timestamp + 16)

View File

@ -1,158 +0,0 @@
import unittest
import numpy as np
from migen import *
from artiq.gateware.drtio.wrpll.ddmtd import Collector
from artiq.gateware.drtio.wrpll import thls, filters
class HelperChainTB(Module):
def __init__(self, N):
self.tag_ref = Signal(N)
self.input_stb = Signal()
self.adpll = Signal((24, True))
self.out_stb = Signal()
###
self.submodules.collector = Collector(N)
self.submodules.loop_filter = thls.make(filters.helper, data_width=48)
self.comb += [
self.collector.tag_ref.eq(self.tag_ref),
self.collector.ref_stb.eq(self.input_stb),
self.collector.main_stb.eq(self.input_stb),
self.loop_filter.input.eq(self.collector.out_helper << 22),
self.loop_filter.input_stb.eq(self.collector.out_stb),
self.adpll.eq(self.loop_filter.output),
self.out_stb.eq(self.loop_filter.output_stb),
]
class TestDSP(unittest.TestCase):
def test_main_collector(self):
N = 2
collector = Collector(N=N)
# check collector phase unwrapping
tags = [(0, 0, 0),
(0, 1, 1),
(2, 1, -1),
(3, 1, -2),
(0, 1, -3),
(1, 1, -4),
(2, 1, -5),
(3, 1, -6),
(3, 3, -4),
(0, 0, -4),
(0, 1, -3),
(0, 2, -2),
(0, 3, -1),
(0, 0, 0)]
for i in range(10):
tags.append((i % (2**N), (i+1) % (2**N), 1))
def generator():
for tag_ref, tag_main, out in tags:
yield collector.tag_ref.eq(tag_ref)
yield collector.tag_main.eq(tag_main)
yield collector.main_stb.eq(1)
yield collector.ref_stb.eq(1)
yield
yield collector.main_stb.eq(0)
yield collector.ref_stb.eq(0)
while not (yield collector.out_stb):
yield
out_main = yield collector.out_main
self.assertEqual(out_main, out)
run_simulation(collector, generator())
def test_helper_collector(self):
N = 3
collector = Collector(N=N)
# check collector phase unwrapping
tags = [((2**N - 1 - tag) % (2**N), -1) for tag in range(20)]
tags += [((tags[-1][0] + 1 + tag) % (2**N), 1) for tag in range(20)]
tags += [((tags[-1][0] - 2 - 2*tag) % (2**N), -2) for tag in range(20)]
def generator():
for tag_ref, out in tags:
yield collector.tag_ref.eq(tag_ref)
yield collector.main_stb.eq(1)
yield collector.ref_stb.eq(1)
yield
yield collector.main_stb.eq(0)
yield collector.ref_stb.eq(0)
while not (yield collector.out_stb):
yield
out_helper = yield collector.out_helper
self.assertEqual(out_helper, out)
run_simulation(collector, generator())
# test helper collector + filter against output from MATLAB model
def test_helper_chain(self):
pll = HelperChainTB(15)
initial_helper_out = -8000
ref_tags = np.array([
24778, 16789, 8801, 814, 25596, 17612, 9628, 1646,
26433, 18453, 10474, 2496, 27287, 19311, 11337, 3364, 28160,
20190, 12221, 4253, 29054, 21088, 13124, 5161, 29966, 22005,
14045, 6087, 30897, 22940, 14985, 7031, 31847, 23895, 15944,
7995, 47, 24869, 16923, 8978, 1035, 25861, 17920, 9981,
2042, 26873, 18937, 11002, 3069, 27904, 19973, 12042, 4113,
28953, 21026, 13100, 5175, 30020, 22098, 14177, 6257, 31106,
23189, 15273, 7358, 32212, 24300, 16388, 8478, 569, 25429,
17522, 9617, 1712, 26577, 18675, 10774, 2875, 27745, 19848,
11951, 4056, 28930, 21038, 13147, 5256, 30135, 22247, 14361,
6475, 31359, 23476, 15595, 7714, 32603, 24725, 16847, 8971,
1096
])
adpll_sim = np.array([
8, 24, 41, 57, 74, 91, 107, 124, 140, 157, 173,
190, 206, 223, 239, 256, 273, 289, 306, 322, 339, 355,
372, 388, 405, 421, 438, 454, 471, 487, 504, 520, 537,
553, 570, 586, 603, 619, 636, 652, 668, 685, 701, 718,
734, 751, 767, 784, 800, 817, 833, 850, 866, 882, 899,
915, 932, 948, 965, 981, 998, 1014, 1030, 1047, 1063, 1080,
1096, 1112, 1129, 1145, 1162, 1178, 1194, 1211, 1227, 1244, 1260,
1276, 1293, 1309, 1326, 1342, 1358, 1375, 1391, 1407, 1424, 1440,
1457, 1473, 1489, 1506, 1522, 1538, 1555, 1571, 1587, 1604, 1620,
1636])
def sim():
yield pll.collector.out_helper.eq(initial_helper_out)
for ref_tag, adpll_matlab in zip(ref_tags, adpll_sim):
# feed collector
yield pll.tag_ref.eq(int(ref_tag))
yield pll.input_stb.eq(1)
yield
yield pll.input_stb.eq(0)
while not (yield pll.collector.out_stb):
yield
tag_diff = yield pll.collector.out_helper
while not (yield pll.loop_filter.output_stb):
yield
adpll_migen = yield pll.adpll
self.assertEqual(adpll_migen, adpll_matlab)
yield
run_simulation(pll, [sim()])

View File

@ -1,55 +0,0 @@
import unittest
from migen import *
from artiq.gateware.drtio.wrpll import thls
a = 0
def simple_test(x):
global a
a = a + (x*4 >> 1)
return a
class TestTHLS(unittest.TestCase):
def test_thls(self):
global a
proc = thls.Processor()
a = 0
cp = thls.compile(proc, simple_test)
print("Program:")
cp.pretty_print()
cp.dimension_processor()
print("Encoded program:", cp.encode())
proc_impl = proc.implement(cp.encode(), cp.data)
def send_values(values):
for value in values:
yield proc_impl.input.eq(value)
yield proc_impl.input_stb.eq(1)
yield
yield proc_impl.input.eq(0)
yield proc_impl.input_stb.eq(0)
yield
while (yield proc_impl.busy):
yield
@passive
def receive_values(callback):
while True:
while not (yield proc_impl.output_stb):
yield
callback((yield proc_impl.output))
yield
send_list = [42, 40, 10, 10]
receive_list = []
run_simulation(proc_impl, [send_values(send_list), receive_values(receive_list.append)])
print("Execution:", send_list, "->", receive_list)
a = 0
expected_list = [simple_test(x) for x in send_list]
self.assertEqual(receive_list, expected_list)

View File

@ -1,49 +0,0 @@
# Copyright (C) 2014, 2015 Robert Jordens <jordens@gmail.com>
import unittest
import numpy as np
from artiq.wavesynth import coefficients, compute_samples
class TestSplineCoef(unittest.TestCase):
def setUp(self):
self.x = np.arange(5.)
self.y = np.sin(2*np.pi*self.x/5) + np.arange(2)[:, None]
self.s = coefficients.SplineSource(self.x, self.y, order=4)
def test_get_segment(self):
return list(self.s.get_segment(start=1.5, stop=3.2, scale=.01))
def test_synth(self):
d = self.test_get_segment()
d[0]["trigger"] = True
return compute_samples.Synthesizer(self.y.shape[0], [d, d + d])
def drive(self, s):
y = []
for f in 0, 1, None, 0:
if f is not None:
s.select(f)
y += s.trigger()[0]
return y
def test_run(self):
return self.drive(self.test_synth())
def test_compare(self):
scale = 100
d = list(self.s.get_segment(start=0, stop=4, scale=1/scale))
d[0]["trigger"] = True
s = compute_samples.Synthesizer(self.y.shape[0], [d])
s.select(0)
y = s.trigger()[0]
np.testing.assert_almost_equal(y[::scale], self.y[0, :-1])
@unittest.skip("manual/visual test")
def test_plot(self):
import matplotlib.pyplot as plt
y = self.test_run()
plt.step(np.arange(len(y)), y)
plt.show()

View File

@ -47,12 +47,6 @@ Digital I/O drivers
.. automodule:: artiq.coredevice.edge_counter
:members:
:mod:`artiq.coredevice.shiftreg` module
+++++++++++++++++++++++++++++++++++++++
.. automodule:: artiq.coredevice.shiftreg
:members:
:mod:`artiq.coredevice.spi2` module
+++++++++++++++++++++++++++++++++++
@ -104,25 +98,6 @@ RF generation drivers
.. automodule:: artiq.coredevice.adf5356
:members:
:mod:`artiq.coredevice.spline` module
+++++++++++++++++++++++++++++++++++++
.. automodule:: artiq.coredevice.spline
:members:
:mod:`artiq.coredevice.sawg` module
+++++++++++++++++++++++++++++++++++
.. automodule:: artiq.coredevice.sawg
:members:
:mod:`artiq.coredevice.basemod_att` module
++++++++++++++++++++++++++++++++++++++++++
.. automodule:: artiq.coredevice.basemod_att
:members:
:mod:`artiq.coredevice.phaser` module
+++++++++++++++++++++++++++++++++++++

View File

@ -137,9 +137,6 @@ Core device RTIO analyzer tool
:ref: artiq.frontend.artiq_coreanalyzer.get_argparser
:prog: artiq_coreanalyzer
.. note::
The RTIO analyzer does not support SAWG.
.. _routing-table-tool:
DRTIO routing table manipulation tool

Binary file not shown.

Before

Width:  |  Height:  |  Size: 57 KiB

View File

@ -183,18 +183,6 @@
propagatedBuildInputs = with pkgs.python3Packages; [ jinja2 numpy migen pyserial asyncserial ];
};
jesd204b = pkgs.python3Packages.buildPythonPackage rec {
pname = "jesd204b";
version = "unstable-2021-05-05";
src = pkgs.fetchFromGitHub {
owner = "m-labs";
repo = "jesd204b";
rev = "bf1cd9014c8b7a9db67609f653634daaf3bcd39b";
sha256 = "sha256-wyYOCRIPANReeCl+KaIpiAStsn2mzfMlK+cSrUzVrAw=";
};
propagatedBuildInputs = with pkgs.python3Packages; [ migen misoc ];
};
microscope = pkgs.python3Packages.buildPythonPackage rec {
pname = "microscope";
version = "unstable-2020-12-28";
@ -405,7 +393,7 @@
devShell.x86_64-linux = pkgs.mkShell {
name = "artiq-dev-shell";
buildInputs = [
(pkgs.python3.withPackages(ps: with packages.x86_64-linux; [ migen misoc jesd204b artiq ps.paramiko ps.jsonschema microscope ]))
(pkgs.python3.withPackages(ps: with packages.x86_64-linux; [ migen misoc artiq ps.paramiko ps.jsonschema microscope ]))
rustPlatform.rust.rustc
rustPlatform.rust.cargo
cargo-xbuild