forked from M-Labs/artiq
Merge branch 'syncrtio'
This commit is contained in:
commit
0a37a1a4c1
File diff suppressed because it is too large
Load Diff
@ -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()
|
@ -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)
|
@ -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)
|
@ -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
|
@ -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.")
|
@ -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),
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
@ -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
|
@ -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
|
@ -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"}
|
||||
}
|
@ -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()
|
@ -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"
|
||||
}
|
||||
}
|
@ -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)
|
@ -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)
|
||||
}
|
@ -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;
|
@ -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(())
|
||||
}
|
@ -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;
|
||||
|
@ -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)?;
|
||||
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
@ -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]); }
|
||||
}
|
||||
|
@ -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 {}
|
||||
|
@ -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();
|
||||
|
@ -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,10 +196,14 @@ 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");
|
||||
@ -258,24 +211,51 @@ pub fn init() {
|
||||
},
|
||||
_ => setup_si5324_as_synthesizer(clock_cfg),
|
||||
}
|
||||
|
||||
// switch sysclk source to si5324
|
||||
#[cfg(not(has_drtio))]
|
||||
{
|
||||
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)]
|
||||
{
|
||||
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);
|
||||
}
|
||||
clock::spin_us(1500); // wait for CPLL/QPLL lock
|
||||
loop {}
|
||||
}
|
||||
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");
|
||||
|
@ -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
|
||||
}
|
@ -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(())
|
||||
}
|
||||
}
|
@ -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,37 +538,7 @@ 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);
|
||||
@ -664,8 +546,6 @@ pub extern fn main() -> i32 {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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()
|
||||
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,22 +309,11 @@ 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)
|
||||
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")
|
||||
else:
|
||||
raise ValueError("invalid action", action)
|
||||
|
@ -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")
|
||||
)
|
||||
)
|
||||
|
@ -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(
|
||||
|
@ -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",
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
]
|
||||
|
||||
|
@ -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 += [
|
||||
|
@ -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",
|
||||
|
@ -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))})
|
||||
]
|
||||
|
@ -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
|
||||
|
@ -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",
|
||||
|
@ -1,704 +0,0 @@
|
||||
from functools import reduce
|
||||
from operator import or_, and_
|
||||
|
||||
from migen import *
|
||||
from migen.genlib.resetsync import AsyncResetSynchronizer
|
||||
|
||||
from misoc.cores.code_8b10b import Encoder, Decoder
|
||||
|
||||
from microscope import *
|
||||
|
||||
from artiq.gateware.drtio.core import TransceiverInterface, ChannelInterface
|
||||
from artiq.gateware.drtio.transceiver.clock_aligner import BruteforceClockAligner
|
||||
from artiq.gateware.drtio.transceiver.gth_ultrascale_init import *
|
||||
|
||||
|
||||
class GTHSingle(Module):
|
||||
def __init__(self, refclk, pads, sys_clk_freq, rtio_clk_freq, rtiox_mul, dw, mode):
|
||||
assert (dw == 20) or (dw == 40)
|
||||
assert mode in ["single", "master", "slave"]
|
||||
self.mode = mode
|
||||
|
||||
# phase alignment
|
||||
self.txsyncallin = Signal()
|
||||
self.txphaligndone = Signal()
|
||||
self.txsyncallin = Signal()
|
||||
self.txsyncin = Signal()
|
||||
self.txsyncout = Signal()
|
||||
self.txdlysreset = Signal()
|
||||
|
||||
# # #
|
||||
|
||||
self.txenable = Signal()
|
||||
nwords = dw//10
|
||||
self.submodules.encoder = encoder = ClockDomainsRenamer("rtio_tx")(
|
||||
Encoder(nwords, True))
|
||||
self.submodules.decoders = decoders = [ClockDomainsRenamer("rtio_rx")(
|
||||
(Decoder(True))) for _ in range(nwords)]
|
||||
self.rx_ready = Signal()
|
||||
|
||||
# transceiver direct clock outputs
|
||||
# for OBUFDS_GTE3
|
||||
self.rxrecclkout = Signal()
|
||||
# useful to specify clock constraints in a way palatable to Vivado
|
||||
self.txoutclk = Signal()
|
||||
self.rxoutclk = Signal()
|
||||
|
||||
# # #
|
||||
|
||||
# TX generates RTIO clock, init must be in system domain
|
||||
self.submodules.tx_init = tx_init = GTHInit(sys_clk_freq, False, mode)
|
||||
# RX receives restart commands from RTIO domain
|
||||
rx_init = ClockDomainsRenamer("rtio_tx")(GTHInit(rtio_clk_freq, True))
|
||||
self.submodules += rx_init
|
||||
|
||||
cpll_reset = Signal()
|
||||
cpll_lock = Signal()
|
||||
self.comb += [
|
||||
cpll_reset.eq(tx_init.pllreset),
|
||||
tx_init.plllock.eq(cpll_lock),
|
||||
rx_init.plllock.eq(cpll_lock)
|
||||
]
|
||||
|
||||
txdata = Signal(dw)
|
||||
rxdata = Signal(dw)
|
||||
rxphaligndone = Signal()
|
||||
gth_params = dict(
|
||||
p_ACJTAG_DEBUG_MODE =0b0,
|
||||
p_ACJTAG_MODE =0b0,
|
||||
p_ACJTAG_RESET =0b0,
|
||||
p_ADAPT_CFG0 =0b1111100000000000,
|
||||
p_ADAPT_CFG1 =0b0000000000000000,
|
||||
p_ALIGN_COMMA_DOUBLE ="FALSE",
|
||||
p_ALIGN_COMMA_ENABLE =0b0000000000,
|
||||
p_ALIGN_COMMA_WORD =1,
|
||||
p_ALIGN_MCOMMA_DET ="FALSE",
|
||||
p_ALIGN_MCOMMA_VALUE =0b1010000011,
|
||||
p_ALIGN_PCOMMA_DET ="FALSE",
|
||||
p_ALIGN_PCOMMA_VALUE =0b0101111100,
|
||||
p_A_RXOSCALRESET =0b0,
|
||||
p_A_RXPROGDIVRESET =0b0,
|
||||
p_A_TXPROGDIVRESET =0b0,
|
||||
p_CBCC_DATA_SOURCE_SEL ="ENCODED",
|
||||
p_CDR_SWAP_MODE_EN =0b0,
|
||||
p_CHAN_BOND_KEEP_ALIGN ="FALSE",
|
||||
p_CHAN_BOND_MAX_SKEW =1,
|
||||
p_CHAN_BOND_SEQ_1_1 =0b0000000000,
|
||||
p_CHAN_BOND_SEQ_1_2 =0b0000000000,
|
||||
p_CHAN_BOND_SEQ_1_3 =0b0000000000,
|
||||
p_CHAN_BOND_SEQ_1_4 =0b0000000000,
|
||||
p_CHAN_BOND_SEQ_1_ENABLE =0b1111,
|
||||
p_CHAN_BOND_SEQ_2_1 =0b0000000000,
|
||||
p_CHAN_BOND_SEQ_2_2 =0b0000000000,
|
||||
p_CHAN_BOND_SEQ_2_3 =0b0000000000,
|
||||
p_CHAN_BOND_SEQ_2_4 =0b0000000000,
|
||||
p_CHAN_BOND_SEQ_2_ENABLE =0b1111,
|
||||
p_CHAN_BOND_SEQ_2_USE ="FALSE",
|
||||
p_CHAN_BOND_SEQ_LEN =1,
|
||||
p_CLK_CORRECT_USE ="FALSE",
|
||||
p_CLK_COR_KEEP_IDLE ="FALSE",
|
||||
p_CLK_COR_MAX_LAT =20,
|
||||
p_CLK_COR_MIN_LAT =18,
|
||||
p_CLK_COR_PRECEDENCE ="TRUE",
|
||||
p_CLK_COR_REPEAT_WAIT =0,
|
||||
p_CLK_COR_SEQ_1_1 =0b0000000000,
|
||||
p_CLK_COR_SEQ_1_2 =0b0000000000,
|
||||
p_CLK_COR_SEQ_1_3 =0b0000000000,
|
||||
p_CLK_COR_SEQ_1_4 =0b0000000000,
|
||||
p_CLK_COR_SEQ_1_ENABLE =0b1111,
|
||||
p_CLK_COR_SEQ_2_1 =0b0000000000,
|
||||
p_CLK_COR_SEQ_2_2 =0b0000000000,
|
||||
p_CLK_COR_SEQ_2_3 =0b0000000000,
|
||||
p_CLK_COR_SEQ_2_4 =0b0000000000,
|
||||
p_CLK_COR_SEQ_2_ENABLE =0b1111,
|
||||
p_CLK_COR_SEQ_2_USE ="FALSE",
|
||||
p_CLK_COR_SEQ_LEN =1,
|
||||
p_CPLL_CFG0 =0b0110011111111000,
|
||||
p_CPLL_CFG1 =0b1010010010101100,
|
||||
p_CPLL_CFG2 =0b0000000000000111,
|
||||
p_CPLL_CFG3 =0b000000,
|
||||
p_CPLL_FBDIV =5,
|
||||
p_CPLL_FBDIV_45 =4,
|
||||
p_CPLL_INIT_CFG0 =0b0000001010110010,
|
||||
p_CPLL_INIT_CFG1 =0b00000000,
|
||||
p_CPLL_LOCK_CFG =0b0000000111101000,
|
||||
p_CPLL_REFCLK_DIV =1,
|
||||
p_DDI_CTRL =0b00,
|
||||
p_DDI_REALIGN_WAIT =15,
|
||||
p_DEC_MCOMMA_DETECT ="FALSE",
|
||||
p_DEC_PCOMMA_DETECT ="FALSE",
|
||||
p_DEC_VALID_COMMA_ONLY ="FALSE",
|
||||
p_DFE_D_X_REL_POS =0b0,
|
||||
p_DFE_VCM_COMP_EN =0b0,
|
||||
p_DMONITOR_CFG0 =0b0000000000,
|
||||
p_DMONITOR_CFG1 =0b00000000,
|
||||
p_ES_CLK_PHASE_SEL =0b0,
|
||||
p_ES_CONTROL =0b000000,
|
||||
p_ES_ERRDET_EN ="FALSE",
|
||||
p_ES_EYE_SCAN_EN ="FALSE",
|
||||
p_ES_HORZ_OFFSET =0b000000000000,
|
||||
p_ES_PMA_CFG =0b0000000000,
|
||||
p_ES_PRESCALE =0b00000,
|
||||
p_ES_QUALIFIER0 =0b0000000000000000,
|
||||
p_ES_QUALIFIER1 =0b0000000000000000,
|
||||
p_ES_QUALIFIER2 =0b0000000000000000,
|
||||
p_ES_QUALIFIER3 =0b0000000000000000,
|
||||
p_ES_QUALIFIER4 =0b0000000000000000,
|
||||
p_ES_QUAL_MASK0 =0b0000000000000000,
|
||||
p_ES_QUAL_MASK1 =0b0000000000000000,
|
||||
p_ES_QUAL_MASK2 =0b0000000000000000,
|
||||
p_ES_QUAL_MASK3 =0b0000000000000000,
|
||||
p_ES_QUAL_MASK4 =0b0000000000000000,
|
||||
p_ES_SDATA_MASK0 =0b0000000000000000,
|
||||
p_ES_SDATA_MASK1 =0b0000000000000000,
|
||||
p_ES_SDATA_MASK2 =0b0000000000000000,
|
||||
p_ES_SDATA_MASK3 =0b0000000000000000,
|
||||
p_ES_SDATA_MASK4 =0b0000000000000000,
|
||||
p_EVODD_PHI_CFG =0b00000000000,
|
||||
p_EYE_SCAN_SWAP_EN =0b0,
|
||||
p_FTS_DESKEW_SEQ_ENABLE =0b1111,
|
||||
p_FTS_LANE_DESKEW_CFG =0b1111,
|
||||
p_FTS_LANE_DESKEW_EN ="FALSE",
|
||||
p_GEARBOX_MODE =0b00000,
|
||||
p_GM_BIAS_SELECT =0b0,
|
||||
p_LOCAL_MASTER =0b1,
|
||||
p_OOBDIVCTL =0b00,
|
||||
p_OOB_PWRUP =0b0,
|
||||
p_PCI3_AUTO_REALIGN ="OVR_1K_BLK",
|
||||
p_PCI3_PIPE_RX_ELECIDLE =0b0,
|
||||
p_PCI3_RX_ASYNC_EBUF_BYPASS =0b00,
|
||||
p_PCI3_RX_ELECIDLE_EI2_ENABLE =0b0,
|
||||
p_PCI3_RX_ELECIDLE_H2L_COUNT =0b000000,
|
||||
p_PCI3_RX_ELECIDLE_H2L_DISABLE =0b000,
|
||||
p_PCI3_RX_ELECIDLE_HI_COUNT =0b000000,
|
||||
p_PCI3_RX_ELECIDLE_LP4_DISABLE =0b0,
|
||||
p_PCI3_RX_FIFO_DISABLE =0b0,
|
||||
p_PCIE_BUFG_DIV_CTRL =0b0001000000000000,
|
||||
p_PCIE_RXPCS_CFG_GEN3 =0b0000001010100100,
|
||||
p_PCIE_RXPMA_CFG =0b0000000000001010,
|
||||
p_PCIE_TXPCS_CFG_GEN3 =0b0010010010100100,
|
||||
p_PCIE_TXPMA_CFG =0b0000000000001010,
|
||||
p_PCS_PCIE_EN ="FALSE",
|
||||
p_PCS_RSVD0 =0b0000000000000000,
|
||||
p_PCS_RSVD1 =0b000,
|
||||
p_PD_TRANS_TIME_FROM_P2 =0b000000111100,
|
||||
p_PD_TRANS_TIME_NONE_P2 =0b00011001,
|
||||
p_PD_TRANS_TIME_TO_P2 =0b01100100,
|
||||
p_PLL_SEL_MODE_GEN12 =0b00,
|
||||
p_PLL_SEL_MODE_GEN3 =0b11,
|
||||
p_PMA_RSV1 =0b1111000000000000,
|
||||
p_PROCESS_PAR =0b010,
|
||||
p_RATE_SW_USE_DRP =0b1,
|
||||
p_RESET_POWERSAVE_DISABLE =0b0,
|
||||
)
|
||||
gth_params.update(
|
||||
p_RXBUFRESET_TIME =0b00011,
|
||||
p_RXBUF_ADDR_MODE ="FAST",
|
||||
p_RXBUF_EIDLE_HI_CNT =0b1000,
|
||||
p_RXBUF_EIDLE_LO_CNT =0b0000,
|
||||
p_RXBUF_EN ="FALSE",
|
||||
p_RXBUF_RESET_ON_CB_CHANGE ="TRUE",
|
||||
p_RXBUF_RESET_ON_COMMAALIGN ="FALSE",
|
||||
p_RXBUF_RESET_ON_EIDLE ="FALSE",
|
||||
p_RXBUF_RESET_ON_RATE_CHANGE ="TRUE",
|
||||
p_RXBUF_THRESH_OVFLW =0,
|
||||
p_RXBUF_THRESH_OVRD ="FALSE",
|
||||
p_RXBUF_THRESH_UNDFLW =0,
|
||||
p_RXCDRFREQRESET_TIME =0b00001,
|
||||
p_RXCDRPHRESET_TIME =0b00001,
|
||||
p_RXCDR_CFG0 =0b0000000000000000,
|
||||
p_RXCDR_CFG0_GEN3 =0b0000000000000000,
|
||||
p_RXCDR_CFG1 =0b0000000000000000,
|
||||
p_RXCDR_CFG1_GEN3 =0b0000000000000000,
|
||||
p_RXCDR_CFG2 =0b0000011111010110,
|
||||
p_RXCDR_CFG2_GEN3 =0b0000011111100110,
|
||||
p_RXCDR_CFG3 =0b0000000000000000,
|
||||
p_RXCDR_CFG3_GEN3 =0b0000000000000000,
|
||||
p_RXCDR_CFG4 =0b0000000000000000,
|
||||
p_RXCDR_CFG4_GEN3 =0b0000000000000000,
|
||||
p_RXCDR_CFG5 =0b0000000000000000,
|
||||
p_RXCDR_CFG5_GEN3 =0b0000000000000000,
|
||||
p_RXCDR_FR_RESET_ON_EIDLE =0b0,
|
||||
p_RXCDR_HOLD_DURING_EIDLE =0b0,
|
||||
p_RXCDR_LOCK_CFG0 =0b0100010010000000,
|
||||
p_RXCDR_LOCK_CFG1 =0b0101111111111111,
|
||||
p_RXCDR_LOCK_CFG2 =0b0111011111000011,
|
||||
p_RXCDR_PH_RESET_ON_EIDLE =0b0,
|
||||
p_RXCFOK_CFG0 =0b0100000000000000,
|
||||
p_RXCFOK_CFG1 =0b0000000001100101,
|
||||
p_RXCFOK_CFG2 =0b0000000000101110,
|
||||
p_RXDFELPMRESET_TIME =0b0001111,
|
||||
p_RXDFELPM_KL_CFG0 =0b0000000000000000,
|
||||
p_RXDFELPM_KL_CFG1 =0b0000000000000010,
|
||||
p_RXDFELPM_KL_CFG2 =0b0000000000000000,
|
||||
p_RXDFE_CFG0 =0b0000101000000000,
|
||||
p_RXDFE_CFG1 =0b0000000000000000,
|
||||
p_RXDFE_GC_CFG0 =0b0000000000000000,
|
||||
p_RXDFE_GC_CFG1 =0b0111100001110000,
|
||||
p_RXDFE_GC_CFG2 =0b0000000000000000,
|
||||
p_RXDFE_H2_CFG0 =0b0000000000000000,
|
||||
p_RXDFE_H2_CFG1 =0b0000000000000000,
|
||||
p_RXDFE_H3_CFG0 =0b0100000000000000,
|
||||
p_RXDFE_H3_CFG1 =0b0000000000000000,
|
||||
p_RXDFE_H4_CFG0 =0b0010000000000000,
|
||||
p_RXDFE_H4_CFG1 =0b0000000000000011,
|
||||
p_RXDFE_H5_CFG0 =0b0010000000000000,
|
||||
p_RXDFE_H5_CFG1 =0b0000000000000011,
|
||||
p_RXDFE_H6_CFG0 =0b0010000000000000,
|
||||
p_RXDFE_H6_CFG1 =0b0000000000000000,
|
||||
p_RXDFE_H7_CFG0 =0b0010000000000000,
|
||||
p_RXDFE_H7_CFG1 =0b0000000000000000,
|
||||
p_RXDFE_H8_CFG0 =0b0010000000000000,
|
||||
p_RXDFE_H8_CFG1 =0b0000000000000000,
|
||||
p_RXDFE_H9_CFG0 =0b0010000000000000,
|
||||
p_RXDFE_H9_CFG1 =0b0000000000000000,
|
||||
p_RXDFE_HA_CFG0 =0b0010000000000000,
|
||||
p_RXDFE_HA_CFG1 =0b0000000000000000,
|
||||
p_RXDFE_HB_CFG0 =0b0010000000000000,
|
||||
p_RXDFE_HB_CFG1 =0b0000000000000000,
|
||||
p_RXDFE_HC_CFG0 =0b0000000000000000,
|
||||
p_RXDFE_HC_CFG1 =0b0000000000000000,
|
||||
p_RXDFE_HD_CFG0 =0b0000000000000000,
|
||||
p_RXDFE_HD_CFG1 =0b0000000000000000,
|
||||
p_RXDFE_HE_CFG0 =0b0000000000000000,
|
||||
p_RXDFE_HE_CFG1 =0b0000000000000000,
|
||||
p_RXDFE_HF_CFG0 =0b0000000000000000,
|
||||
p_RXDFE_HF_CFG1 =0b0000000000000000,
|
||||
p_RXDFE_OS_CFG0 =0b1000000000000000,
|
||||
p_RXDFE_OS_CFG1 =0b0000000000000000,
|
||||
p_RXDFE_UT_CFG0 =0b1000000000000000,
|
||||
p_RXDFE_UT_CFG1 =0b0000000000000011,
|
||||
p_RXDFE_VP_CFG0 =0b1010101000000000,
|
||||
p_RXDFE_VP_CFG1 =0b0000000000110011,
|
||||
p_RXDLY_CFG =0b0000000000011111,
|
||||
p_RXDLY_LCFG =0b0000000000110000,
|
||||
p_RXELECIDLE_CFG ="SIGCFG_4",
|
||||
p_RXGBOX_FIFO_INIT_RD_ADDR =4,
|
||||
p_RXGEARBOX_EN ="FALSE",
|
||||
p_RXISCANRESET_TIME =0b00001,
|
||||
p_RXLPM_CFG =0b0000000000000000,
|
||||
p_RXLPM_GC_CFG =0b0001000000000000,
|
||||
p_RXLPM_KH_CFG0 =0b0000000000000000,
|
||||
p_RXLPM_KH_CFG1 =0b0000000000000010,
|
||||
p_RXLPM_OS_CFG0 =0b1000000000000000,
|
||||
p_RXLPM_OS_CFG1 =0b0000000000000010,
|
||||
p_RXOOB_CFG =0b000000110,
|
||||
p_RXOOB_CLK_CFG ="PMA",
|
||||
p_RXOSCALRESET_TIME =0b00011,
|
||||
p_RXOUT_DIV =2,
|
||||
p_RXPCSRESET_TIME =0b00011,
|
||||
p_RXPHBEACON_CFG =0b0000000000000000,
|
||||
p_RXPHDLY_CFG =0b0010000000100000,
|
||||
p_RXPHSAMP_CFG =0b0010000100000000,
|
||||
p_RXPHSLIP_CFG =0b0110011000100010,
|
||||
p_RXPH_MONITOR_SEL =0b00000,
|
||||
p_RXPI_CFG0 =0b00,
|
||||
p_RXPI_CFG1 =0b00,
|
||||
p_RXPI_CFG2 =0b00,
|
||||
p_RXPI_CFG3 =0b00,
|
||||
p_RXPI_CFG4 =0b1,
|
||||
p_RXPI_CFG5 =0b1,
|
||||
p_RXPI_CFG6 =0b000,
|
||||
p_RXPI_LPM =0b0,
|
||||
p_RXPI_VREFSEL =0b0,
|
||||
p_RXPMACLK_SEL ="DATA",
|
||||
p_RXPMARESET_TIME =0b00011,
|
||||
p_RXPRBS_ERR_LOOPBACK =0b0,
|
||||
p_RXPRBS_LINKACQ_CNT =15,
|
||||
p_RXSLIDE_AUTO_WAIT =7,
|
||||
p_RXSLIDE_MODE ="OFF",
|
||||
p_RXSYNC_MULTILANE =0b0,
|
||||
p_RXSYNC_OVRD =0b0,
|
||||
p_RXSYNC_SKIP_DA =0b0,
|
||||
p_RX_AFE_CM_EN =0b0,
|
||||
p_RX_BIAS_CFG0 =0b0000101010110100,
|
||||
p_RX_BUFFER_CFG =0b000000,
|
||||
p_RX_CAPFF_SARC_ENB =0b0,
|
||||
p_RX_CLK25_DIV =6,
|
||||
p_RX_CLKMUX_EN =0b1,
|
||||
p_RX_CLK_SLIP_OVRD =0b00000,
|
||||
p_RX_CM_BUF_CFG =0b1010,
|
||||
p_RX_CM_BUF_PD =0b0,
|
||||
p_RX_CM_SEL =0b11,
|
||||
p_RX_CM_TRIM =0b1010,
|
||||
p_RX_CTLE3_LPF =0b00000001,
|
||||
p_RX_DATA_WIDTH =dw,
|
||||
p_RX_DDI_SEL =0b000000,
|
||||
p_RX_DEFER_RESET_BUF_EN ="TRUE",
|
||||
p_RX_DFELPM_CFG0 =0b0110,
|
||||
p_RX_DFELPM_CFG1 =0b1,
|
||||
p_RX_DFELPM_KLKH_AGC_STUP_EN =0b1,
|
||||
p_RX_DFE_AGC_CFG0 =0b10,
|
||||
p_RX_DFE_AGC_CFG1 =0b100,
|
||||
p_RX_DFE_KL_LPM_KH_CFG0 =0b01,
|
||||
p_RX_DFE_KL_LPM_KH_CFG1 =0b100,
|
||||
p_RX_DFE_KL_LPM_KL_CFG0 =0b01,
|
||||
p_RX_DFE_KL_LPM_KL_CFG1 =0b100,
|
||||
p_RX_DFE_LPM_HOLD_DURING_EIDLE =0b0,
|
||||
p_RX_DISPERR_SEQ_MATCH ="TRUE",
|
||||
p_RX_DIVRESET_TIME =0b00001,
|
||||
p_RX_EN_HI_LR =0b0,
|
||||
p_RX_EYESCAN_VS_CODE =0b0000000,
|
||||
p_RX_EYESCAN_VS_NEG_DIR =0b0,
|
||||
p_RX_EYESCAN_VS_RANGE =0b00,
|
||||
p_RX_EYESCAN_VS_UT_SIGN =0b0,
|
||||
p_RX_FABINT_USRCLK_FLOP =0b0,
|
||||
p_RX_INT_DATAWIDTH =dw==40,
|
||||
p_RX_PMA_POWER_SAVE =0b0,
|
||||
p_RX_PROGDIV_CFG =0.0,
|
||||
p_RX_SAMPLE_PERIOD =0b111,
|
||||
p_RX_SIG_VALID_DLY =11,
|
||||
p_RX_SUM_DFETAPREP_EN =0b0,
|
||||
p_RX_SUM_IREF_TUNE =0b0000,
|
||||
p_RX_SUM_RES_CTRL =0b00,
|
||||
p_RX_SUM_VCMTUNE =0b0000,
|
||||
p_RX_SUM_VCM_OVWR =0b0,
|
||||
p_RX_SUM_VREF_TUNE =0b000,
|
||||
p_RX_TUNE_AFE_OS =0b10,
|
||||
p_RX_WIDEMODE_CDR =0b0,
|
||||
p_RX_XCLK_SEL ="RXUSR",
|
||||
p_SAS_MAX_COM =64,
|
||||
p_SAS_MIN_COM =36,
|
||||
p_SATA_BURST_SEQ_LEN =0b1110,
|
||||
p_SATA_CPLL_CFG ="VCO_3000MHZ",
|
||||
p_SATA_MAX_BURST =8,
|
||||
p_SATA_MAX_INIT =21,
|
||||
p_SATA_MAX_WAKE =7,
|
||||
p_SATA_MIN_BURST =4,
|
||||
p_SATA_MIN_INIT =12,
|
||||
p_SATA_MIN_WAKE =4,
|
||||
p_SHOW_REALIGN_COMMA ="TRUE",
|
||||
p_SIM_RECEIVER_DETECT_PASS ="TRUE",
|
||||
p_SIM_RESET_SPEEDUP ="TRUE",
|
||||
p_SIM_TX_EIDLE_DRIVE_LEVEL =0b0,
|
||||
p_SIM_VERSION =2,
|
||||
p_TAPDLY_SET_TX =0b00,
|
||||
p_TEMPERATUR_PAR =0b0010,
|
||||
p_TERM_RCAL_CFG =0b100001000010000,
|
||||
p_TERM_RCAL_OVRD =0b000,
|
||||
p_TRANS_TIME_RATE =0b00001110,
|
||||
p_TST_RSV0 =0b00000000,
|
||||
p_TST_RSV1 =0b00000000,
|
||||
)
|
||||
gth_params.update(
|
||||
p_TXBUF_EN ="FALSE",
|
||||
p_TXBUF_RESET_ON_RATE_CHANGE ="TRUE",
|
||||
p_TXDLY_CFG =0b0000000000001001,
|
||||
p_TXDLY_LCFG =0b0000000001010000,
|
||||
p_TXDRVBIAS_N =0b1010,
|
||||
p_TXDRVBIAS_P =0b1010,
|
||||
p_TXFIFO_ADDR_CFG ="LOW",
|
||||
p_TXGBOX_FIFO_INIT_RD_ADDR =4,
|
||||
p_TXGEARBOX_EN ="FALSE",
|
||||
p_TXOUT_DIV =2,
|
||||
p_TXPCSRESET_TIME =0b00011,
|
||||
p_TXPHDLY_CFG0 =0b0010000000100000,
|
||||
p_TXPHDLY_CFG1 =0b0000000001110101,
|
||||
p_TXPH_CFG =0b0000100110000000,
|
||||
p_TXPH_MONITOR_SEL =0b00000,
|
||||
p_TXPI_CFG0 =0b00,
|
||||
p_TXPI_CFG1 =0b00,
|
||||
p_TXPI_CFG2 =0b00,
|
||||
p_TXPI_CFG3 =0b1,
|
||||
p_TXPI_CFG4 =0b1,
|
||||
p_TXPI_CFG5 =0b000,
|
||||
p_TXPI_GRAY_SEL =0b0,
|
||||
p_TXPI_INVSTROBE_SEL =0b0,
|
||||
p_TXPI_LPM =0b0,
|
||||
p_TXPI_PPMCLK_SEL ="TXUSRCLK2",
|
||||
p_TXPI_PPM_CFG =0b00000000,
|
||||
p_TXPI_SYNFREQ_PPM =0b001,
|
||||
p_TXPI_VREFSEL =0b0,
|
||||
p_TXPMARESET_TIME =0b00011,
|
||||
p_TXSYNC_MULTILANE =0 if mode == "single" else 1,
|
||||
p_TXSYNC_OVRD =0b0,
|
||||
p_TXSYNC_SKIP_DA =0b0,
|
||||
p_TX_CLK25_DIV =6,
|
||||
p_TX_CLKMUX_EN =0b1,
|
||||
p_TX_DATA_WIDTH =dw,
|
||||
p_TX_DCD_CFG =0b000010,
|
||||
p_TX_DCD_EN =0b0,
|
||||
p_TX_DEEMPH0 =0b000000,
|
||||
p_TX_DEEMPH1 =0b000000,
|
||||
p_TX_DIVRESET_TIME =0b00001,
|
||||
p_TX_DRIVE_MODE ="DIRECT",
|
||||
p_TX_EIDLE_ASSERT_DELAY =0b100,
|
||||
p_TX_EIDLE_DEASSERT_DELAY =0b011,
|
||||
p_TX_EML_PHI_TUNE =0b0,
|
||||
p_TX_FABINT_USRCLK_FLOP =0b0,
|
||||
p_TX_IDLE_DATA_ZERO =0b0,
|
||||
p_TX_INT_DATAWIDTH =dw==40,
|
||||
p_TX_LOOPBACK_DRIVE_HIZ ="FALSE",
|
||||
p_TX_MAINCURSOR_SEL =0b0,
|
||||
p_TX_MARGIN_FULL_0 =0b1001111,
|
||||
p_TX_MARGIN_FULL_1 =0b1001110,
|
||||
p_TX_MARGIN_FULL_2 =0b1001100,
|
||||
p_TX_MARGIN_FULL_3 =0b1001010,
|
||||
p_TX_MARGIN_FULL_4 =0b1001000,
|
||||
p_TX_MARGIN_LOW_0 =0b1000110,
|
||||
p_TX_MARGIN_LOW_1 =0b1000101,
|
||||
p_TX_MARGIN_LOW_2 =0b1000011,
|
||||
p_TX_MARGIN_LOW_3 =0b1000010,
|
||||
p_TX_MARGIN_LOW_4 =0b1000000,
|
||||
p_TX_MODE_SEL =0b000,
|
||||
p_TX_PMADATA_OPT =0b0,
|
||||
p_TX_PMA_POWER_SAVE =0b0,
|
||||
p_TX_PROGCLK_SEL ="PREPI",
|
||||
p_TX_PROGDIV_CFG =dw/rtiox_mul,
|
||||
p_TX_QPI_STATUS_EN =0b0,
|
||||
p_TX_RXDETECT_CFG =0b00000000110010,
|
||||
p_TX_RXDETECT_REF =0b100,
|
||||
p_TX_SAMPLE_PERIOD =0b111,
|
||||
p_TX_SARC_LPBK_ENB =0b0,
|
||||
p_TX_XCLK_SEL ="TXUSR",
|
||||
p_USE_PCS_CLK_PHASE_SEL =0b0,
|
||||
p_WB_MODE =0b00,
|
||||
)
|
||||
gth_params.update(
|
||||
# Reset modes
|
||||
i_GTRESETSEL=0,
|
||||
i_RESETOVRD=0,
|
||||
|
||||
i_CPLLRESET=0,
|
||||
i_CPLLPD=cpll_reset,
|
||||
o_CPLLLOCK=cpll_lock,
|
||||
i_CPLLLOCKEN=1,
|
||||
i_CPLLREFCLKSEL=0b001,
|
||||
i_TSTIN=2**20-1,
|
||||
i_GTREFCLK0=refclk,
|
||||
|
||||
# TX clock
|
||||
o_TXOUTCLK=self.txoutclk,
|
||||
i_TXSYSCLKSEL=0b00,
|
||||
i_TXPLLCLKSEL=0b00,
|
||||
i_TXOUTCLKSEL=0b101,
|
||||
|
||||
# TX Startup/Reset
|
||||
i_GTTXRESET=tx_init.gtXxreset,
|
||||
o_TXRESETDONE=tx_init.Xxresetdone,
|
||||
i_TXDLYSRESET=tx_init.Xxdlysreset if mode != "slave" else self.txdlysreset,
|
||||
o_TXDLYSRESETDONE=tx_init.Xxdlysresetdone,
|
||||
o_TXPHALIGNDONE=tx_init.Xxphaligndone,
|
||||
i_TXUSERRDY=tx_init.Xxuserrdy,
|
||||
i_TXSYNCMODE=mode != "slave",
|
||||
|
||||
i_TXSYNCALLIN=self.txsyncallin,
|
||||
i_TXSYNCIN=self.txsyncin,
|
||||
o_TXSYNCOUT=self.txsyncout,
|
||||
|
||||
# TX data
|
||||
i_TXINHIBIT=~self.txenable,
|
||||
i_TXCTRL0=Cat(*[txdata[10*i+8] for i in range(nwords)]),
|
||||
i_TXCTRL1=Cat(*[txdata[10*i+9] for i in range(nwords)]),
|
||||
i_TXDATA=Cat(*[txdata[10*i:10*i+8] for i in range(nwords)]),
|
||||
i_TXUSRCLK=ClockSignal("rtio_tx"),
|
||||
i_TXUSRCLK2=ClockSignal("rtio_tx"),
|
||||
|
||||
# TX electrical
|
||||
i_TXPD=0b00,
|
||||
i_TXBUFDIFFCTRL=0b000,
|
||||
i_TXDIFFCTRL=0b1100,
|
||||
|
||||
# RX Startup/Reset
|
||||
i_GTRXRESET=rx_init.gtXxreset,
|
||||
o_RXRESETDONE=rx_init.Xxresetdone,
|
||||
i_RXDLYSRESET=rx_init.Xxdlysreset,
|
||||
o_RXPHALIGNDONE=rxphaligndone,
|
||||
i_RXSYNCALLIN=rxphaligndone,
|
||||
i_RXUSERRDY=rx_init.Xxuserrdy,
|
||||
i_RXSYNCIN=0,
|
||||
i_RXSYNCMODE=1,
|
||||
o_RXSYNCDONE=rx_init.Xxsyncdone,
|
||||
|
||||
# RX AFE
|
||||
i_RXDFEAGCCTRL=1,
|
||||
i_RXDFEXYDEN=1,
|
||||
i_RXLPMEN=1,
|
||||
i_RXOSINTCFG=0xd,
|
||||
i_RXOSINTEN=1,
|
||||
|
||||
# RX clock
|
||||
i_RXRATE=0,
|
||||
i_RXDLYBYPASS=0,
|
||||
i_RXSYSCLKSEL=0b00,
|
||||
i_RXOUTCLKSEL=0b010,
|
||||
i_RXPLLCLKSEL=0b00,
|
||||
o_RXRECCLKOUT=self.rxrecclkout,
|
||||
o_RXOUTCLK=self.rxoutclk,
|
||||
i_RXUSRCLK=ClockSignal("rtio_rx"),
|
||||
i_RXUSRCLK2=ClockSignal("rtio_rx"),
|
||||
|
||||
# RX data
|
||||
o_RXCTRL0=Cat(*[rxdata[10*i+8] for i in range(nwords)]),
|
||||
o_RXCTRL1=Cat(*[rxdata[10*i+9] for i in range(nwords)]),
|
||||
o_RXDATA=Cat(*[rxdata[10*i:10*i+8] for i in range(nwords)]),
|
||||
|
||||
# RX electrical
|
||||
i_RXPD=Replicate(rx_init.restart, 2),
|
||||
i_RXELECIDLEMODE=0b11,
|
||||
|
||||
# Pads
|
||||
i_GTHRXP=pads.rxp,
|
||||
i_GTHRXN=pads.rxn,
|
||||
o_GTHTXP=pads.txp,
|
||||
o_GTHTXN=pads.txn
|
||||
)
|
||||
self.specials += Instance("GTHE3_CHANNEL", **gth_params)
|
||||
self.comb += self.txphaligndone.eq(tx_init.Xxphaligndone)
|
||||
|
||||
self.submodules += [
|
||||
add_probe_async("drtio_gth", "cpll_lock", cpll_lock),
|
||||
add_probe_async("drtio_gth", "txuserrdy", tx_init.Xxuserrdy),
|
||||
add_probe_async("drtio_gth", "tx_init_done", tx_init.done),
|
||||
add_probe_async("drtio_gth", "rxuserrdy", rx_init.Xxuserrdy),
|
||||
add_probe_async("drtio_gth", "rx_init_done", rx_init.done),
|
||||
add_probe_buffer("drtio_gth", "txdata", txdata, clock_domain="rtio_tx"),
|
||||
add_probe_buffer("drtio_gth", "rxdata", rxdata, clock_domain="rtio_rx")
|
||||
]
|
||||
|
||||
# tx clocking
|
||||
tx_reset_deglitched = Signal()
|
||||
tx_reset_deglitched.attr.add("no_retiming")
|
||||
self.sync += tx_reset_deglitched.eq(~tx_init.done)
|
||||
self.clock_domains.cd_rtio_tx = ClockDomain()
|
||||
self.clock_domains.cd_rtiox_tx = ClockDomain()
|
||||
if mode == "master" or mode == "single":
|
||||
self.specials += [
|
||||
Instance("BUFG_GT", i_I=self.txoutclk, o_O=self.cd_rtiox_tx.clk, i_DIV=0),
|
||||
Instance("BUFG_GT", i_I=self.txoutclk, o_O=self.cd_rtio_tx.clk, i_DIV=rtiox_mul-1)
|
||||
]
|
||||
self.specials += AsyncResetSynchronizer(self.cd_rtio_tx, tx_reset_deglitched)
|
||||
|
||||
# rx clocking
|
||||
rx_reset_deglitched = Signal()
|
||||
rx_reset_deglitched.attr.add("no_retiming")
|
||||
self.sync.rtio_tx += rx_reset_deglitched.eq(~rx_init.done)
|
||||
self.clock_domains.cd_rtio_rx = ClockDomain()
|
||||
self.specials += [
|
||||
Instance("BUFG_GT", i_I=self.rxoutclk, o_O=self.cd_rtio_rx.clk),
|
||||
AsyncResetSynchronizer(self.cd_rtio_rx, rx_reset_deglitched)
|
||||
]
|
||||
|
||||
# tx data
|
||||
self.comb += txdata.eq(Cat(*[encoder.output[i] for i in range(nwords)]))
|
||||
|
||||
# rx data
|
||||
for i in range(nwords):
|
||||
self.comb += decoders[i].input.eq(rxdata[10*i:10*(i+1)])
|
||||
|
||||
# clock alignment
|
||||
clock_aligner = BruteforceClockAligner(0b0101111100, rtio_clk_freq)
|
||||
self.submodules += clock_aligner
|
||||
self.comb += [
|
||||
clock_aligner.rxdata.eq(rxdata),
|
||||
rx_init.restart.eq(clock_aligner.restart),
|
||||
self.rx_ready.eq(clock_aligner.ready)
|
||||
]
|
||||
self.submodules += add_probe_async("drtio_gth", "clock_aligner_ready", clock_aligner.ready)
|
||||
|
||||
|
||||
class GTHTXPhaseAlignement(Module):
|
||||
# TX Buffer Bypass in Single-Lane/Multi-Lane Auto Mode (ug576)
|
||||
def __init__(self, gths):
|
||||
txsyncallin = Signal()
|
||||
txsync = Signal()
|
||||
txphaligndone = Signal(len(gths))
|
||||
txdlysreset = Signal()
|
||||
ready_for_align = Signal(len(gths))
|
||||
all_ready_for_align = Signal()
|
||||
|
||||
for i, gth in enumerate(gths):
|
||||
# Common to all transceivers
|
||||
self.comb += [
|
||||
ready_for_align[i].eq(1),
|
||||
gth.txsyncin.eq(txsync),
|
||||
gth.txsyncallin.eq(txsyncallin),
|
||||
txphaligndone[i].eq(gth.txphaligndone)
|
||||
]
|
||||
# Specific to Master or Single transceivers
|
||||
if gth.mode == "master" or gth.mode == "single":
|
||||
self.comb += [
|
||||
gth.tx_init.all_ready_for_align.eq(all_ready_for_align),
|
||||
txsync.eq(gth.txsyncout),
|
||||
txdlysreset.eq(gth.tx_init.Xxdlysreset)
|
||||
]
|
||||
# Specific to Slave transceivers
|
||||
else:
|
||||
self.comb += [
|
||||
ready_for_align[i].eq(gth.tx_init.ready_for_align),
|
||||
gth.txdlysreset.eq(txdlysreset),
|
||||
]
|
||||
|
||||
self.comb += [
|
||||
txsyncallin.eq(reduce(and_, [txphaligndone[i] for i in range(len(gths))])),
|
||||
all_ready_for_align.eq(reduce(and_, [ready_for_align[i] for i in range(len(gths))]))
|
||||
]
|
||||
|
||||
|
||||
class GTH(Module, TransceiverInterface):
|
||||
def __init__(self, clock_pads, data_pads, sys_clk_freq, rtio_clk_freq, rtiox_mul=2, dw=20, master=0, clock_recout_pads=None):
|
||||
self.nchannels = nchannels = len(data_pads)
|
||||
self.gths = []
|
||||
|
||||
# # #
|
||||
|
||||
create_buf = hasattr(clock_pads, "p")
|
||||
if create_buf:
|
||||
refclk = Signal()
|
||||
ibufds_ceb = Signal()
|
||||
self.specials += Instance("IBUFDS_GTE3",
|
||||
i_CEB=ibufds_ceb,
|
||||
i_I=clock_pads.p,
|
||||
i_IB=clock_pads.n,
|
||||
o_O=refclk)
|
||||
else:
|
||||
refclk = clock_pads
|
||||
|
||||
rtio_tx_clk = Signal()
|
||||
channel_interfaces = []
|
||||
for i in range(nchannels):
|
||||
if nchannels == 1:
|
||||
mode = "single"
|
||||
else:
|
||||
mode = "master" if i == master else "slave"
|
||||
gth = GTHSingle(refclk, data_pads[i], sys_clk_freq, rtio_clk_freq, rtiox_mul, dw, mode)
|
||||
if mode == "master":
|
||||
self.comb += rtio_tx_clk.eq(gth.cd_rtio_tx.clk)
|
||||
elif mode == "slave":
|
||||
self.comb += gth.cd_rtio_tx.clk.eq(rtio_tx_clk)
|
||||
self.gths.append(gth)
|
||||
setattr(self.submodules, "gth"+str(i), gth)
|
||||
channel_interface = ChannelInterface(gth.encoder, gth.decoders)
|
||||
self.comb += channel_interface.rx_ready.eq(gth.rx_ready)
|
||||
channel_interfaces.append(channel_interface)
|
||||
|
||||
self.submodules.tx_phase_alignment = GTHTXPhaseAlignement(self.gths)
|
||||
|
||||
TransceiverInterface.__init__(self, channel_interfaces)
|
||||
for n, gth in enumerate(self.gths):
|
||||
self.comb += gth.txenable.eq(self.txenable.storage[n])
|
||||
self.clock_domains.cd_rtiox = ClockDomain(reset_less=True)
|
||||
if create_buf:
|
||||
# GTH PLLs recover on their own from an interrupted clock input,
|
||||
# but be paranoid about HMC7043 noise.
|
||||
self.stable_clkin.storage.attr.add("no_retiming")
|
||||
self.comb += ibufds_ceb.eq(~self.stable_clkin.storage)
|
||||
|
||||
self.comb += [
|
||||
self.cd_rtio.clk.eq(self.gths[master].cd_rtio_tx.clk),
|
||||
self.cd_rtiox.clk.eq(self.gths[master].cd_rtiox_tx.clk),
|
||||
self.cd_rtio.rst.eq(reduce(or_, [gth.cd_rtio_tx.rst for gth in self.gths]))
|
||||
]
|
||||
for i in range(nchannels):
|
||||
self.comb += [
|
||||
getattr(self, "cd_rtio_rx" + str(i)).clk.eq(self.gths[i].cd_rtio_rx.clk),
|
||||
getattr(self, "cd_rtio_rx" + str(i)).rst.eq(self.gths[i].cd_rtio_rx.rst)
|
||||
]
|
||||
|
||||
if clock_recout_pads is not None:
|
||||
self.specials += Instance("OBUFDS_GTE3",
|
||||
p_REFCLK_EN_TX_PATH=0b1,
|
||||
p_REFCLK_ICNTL_TX=0b00111,
|
||||
i_I=self.gths[0].rxrecclkout,
|
||||
i_CEB=0,
|
||||
o_O=clock_recout_pads.p, o_OB=clock_recout_pads.n)
|
@ -1,157 +0,0 @@
|
||||
from math import ceil
|
||||
|
||||
from migen import *
|
||||
from migen.genlib.cdc import MultiReg
|
||||
from migen.genlib.misc import WaitTimer
|
||||
|
||||
|
||||
__all__ = ["GTHInit"]
|
||||
|
||||
|
||||
class GTHInit(Module):
|
||||
def __init__(self, sys_clk_freq, rx, mode="master"):
|
||||
assert not (rx and mode != "master")
|
||||
self.done = Signal()
|
||||
self.restart = Signal()
|
||||
|
||||
# GTH signals
|
||||
self.plllock = Signal()
|
||||
self.pllreset = Signal()
|
||||
self.gtXxreset = Signal()
|
||||
self.Xxresetdone = Signal()
|
||||
self.Xxdlysreset = Signal()
|
||||
self.Xxdlysresetdone = Signal()
|
||||
self.Xxphaligndone = Signal()
|
||||
self.Xxsyncdone = Signal()
|
||||
self.Xxuserrdy = Signal()
|
||||
|
||||
self.all_ready_for_align = Signal(reset=1)
|
||||
self.ready_for_align = Signal()
|
||||
|
||||
# # #
|
||||
|
||||
# Double-latch transceiver asynch outputs
|
||||
plllock = Signal()
|
||||
Xxresetdone = Signal()
|
||||
Xxdlysresetdone = Signal()
|
||||
Xxphaligndone = Signal()
|
||||
Xxsyncdone = Signal()
|
||||
self.specials += [
|
||||
MultiReg(self.plllock, plllock),
|
||||
MultiReg(self.Xxresetdone, Xxresetdone),
|
||||
MultiReg(self.Xxdlysresetdone, Xxdlysresetdone),
|
||||
MultiReg(self.Xxphaligndone, Xxphaligndone),
|
||||
MultiReg(self.Xxsyncdone, Xxsyncdone)
|
||||
]
|
||||
|
||||
# Deglitch FSM outputs driving transceiver asynch inputs
|
||||
gtXxreset = Signal()
|
||||
Xxdlysreset = Signal()
|
||||
Xxuserrdy = Signal()
|
||||
self.sync += [
|
||||
self.gtXxreset.eq(gtXxreset),
|
||||
self.Xxdlysreset.eq(Xxdlysreset),
|
||||
self.Xxuserrdy.eq(Xxuserrdy)
|
||||
]
|
||||
|
||||
# PLL reset must be at least 2us
|
||||
pll_reset_cycles = ceil(2000*sys_clk_freq/1000000000)
|
||||
pll_reset_timer = WaitTimer(pll_reset_cycles)
|
||||
self.submodules += pll_reset_timer
|
||||
|
||||
startup_fsm = ResetInserter()(FSM(reset_state="RESET_ALL"))
|
||||
self.submodules += startup_fsm
|
||||
|
||||
ready_timer = WaitTimer(int(sys_clk_freq/1000))
|
||||
self.submodules += ready_timer
|
||||
self.comb += [
|
||||
ready_timer.wait.eq(~self.done & ~startup_fsm.reset),
|
||||
startup_fsm.reset.eq(self.restart | ready_timer.done)
|
||||
]
|
||||
|
||||
if rx:
|
||||
cdr_stable_timer = WaitTimer(1024)
|
||||
self.submodules += cdr_stable_timer
|
||||
|
||||
Xxphaligndone_r = Signal(reset=1)
|
||||
Xxphaligndone_rising = Signal()
|
||||
self.sync += Xxphaligndone_r.eq(Xxphaligndone)
|
||||
self.comb += Xxphaligndone_rising.eq(Xxphaligndone & ~Xxphaligndone_r)
|
||||
|
||||
startup_fsm.act("RESET_ALL",
|
||||
gtXxreset.eq(1),
|
||||
self.pllreset.eq(1),
|
||||
pll_reset_timer.wait.eq(1),
|
||||
If(pll_reset_timer.done,
|
||||
NextState("RELEASE_PLL_RESET")
|
||||
)
|
||||
)
|
||||
startup_fsm.act("RELEASE_PLL_RESET",
|
||||
gtXxreset.eq(1),
|
||||
If(plllock, NextState("RELEASE_GTH_RESET"))
|
||||
)
|
||||
# Release GTH reset and wait for GTH resetdone
|
||||
# (from UG476, GTH is reset on falling edge
|
||||
# of gtXxreset)
|
||||
if rx:
|
||||
startup_fsm.act("RELEASE_GTH_RESET",
|
||||
Xxuserrdy.eq(1),
|
||||
cdr_stable_timer.wait.eq(1),
|
||||
If(Xxresetdone & cdr_stable_timer.done, NextState("ALIGN"))
|
||||
)
|
||||
else:
|
||||
startup_fsm.act("RELEASE_GTH_RESET",
|
||||
Xxuserrdy.eq(1),
|
||||
If(Xxresetdone,
|
||||
If(mode == "slave",
|
||||
NextState("WAIT_ALIGN")
|
||||
).Else(
|
||||
NextState("ALIGN")
|
||||
)
|
||||
)
|
||||
)
|
||||
# Start delay alignment (pulse)
|
||||
startup_fsm.act("ALIGN",
|
||||
Xxuserrdy.eq(1),
|
||||
If(self.all_ready_for_align,
|
||||
Xxdlysreset.eq(1),
|
||||
NextState("WAIT_ALIGN")
|
||||
)
|
||||
)
|
||||
if rx:
|
||||
# Wait for delay alignment
|
||||
startup_fsm.act("WAIT_ALIGN",
|
||||
Xxuserrdy.eq(1),
|
||||
If(Xxsyncdone,
|
||||
NextState("READY")
|
||||
)
|
||||
)
|
||||
else:
|
||||
# Wait for delay alignment
|
||||
startup_fsm.act("WAIT_ALIGN",
|
||||
Xxuserrdy.eq(1),
|
||||
self.ready_for_align.eq(1),
|
||||
If(Xxdlysresetdone,
|
||||
If(mode == "slave",
|
||||
NextState("WAIT_LAST_ALIGN_DONE")
|
||||
).Else(
|
||||
NextState("WAIT_FIRST_ALIGN_DONE")
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
# Wait 2 rising edges of Xxphaligndone
|
||||
# (from UG576 in TX Buffer Bypass in Single-Lane Auto Mode)
|
||||
startup_fsm.act("WAIT_FIRST_ALIGN_DONE",
|
||||
Xxuserrdy.eq(1),
|
||||
If(Xxphaligndone_rising, NextState("WAIT_LAST_ALIGN_DONE"))
|
||||
)
|
||||
startup_fsm.act("WAIT_LAST_ALIGN_DONE",
|
||||
Xxuserrdy.eq(1),
|
||||
If(Xxphaligndone_rising, NextState("READY"))
|
||||
)
|
||||
startup_fsm.act("READY",
|
||||
Xxuserrdy.eq(1),
|
||||
self.done.eq(1),
|
||||
If(self.restart, NextState("RESET_ALL"))
|
||||
)
|
@ -20,8 +20,7 @@ class GTPSingle(Module):
|
||||
|
||||
self.stable_clkin = Signal()
|
||||
self.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),
|
||||
|
@ -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 += [
|
||||
|
@ -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),
|
||||
|
@ -1,2 +0,0 @@
|
||||
from artiq.gateware.drtio.wrpll.core import WRPLL
|
||||
from artiq.gateware.drtio.wrpll.ddmtd import DDMTDSamplerExtFF, DDMTDSamplerGTP
|
@ -1,156 +0,0 @@
|
||||
from migen import *
|
||||
from migen.genlib.resetsync import AsyncResetSynchronizer
|
||||
from migen.genlib.cdc import MultiReg, PulseSynchronizer
|
||||
from misoc.interconnect.csr import *
|
||||
|
||||
from artiq.gateware.drtio.wrpll.si549 import Si549
|
||||
from artiq.gateware.drtio.wrpll.ddmtd import DDMTD, Collector
|
||||
from artiq.gateware.drtio.wrpll import thls, filters
|
||||
|
||||
|
||||
class FrequencyCounter(Module, AutoCSR):
|
||||
def __init__(self, timer_width=23, counter_width=23, domains=["helper", "rtio", "rtio_rx0"]):
|
||||
for domain in domains:
|
||||
name = "counter_" + domain
|
||||
counter = CSRStatus(counter_width, name=name)
|
||||
setattr(self, name, counter)
|
||||
self.update_en = CSRStorage()
|
||||
|
||||
timer = Signal(timer_width)
|
||||
timer_tick = Signal()
|
||||
self.sync += Cat(timer, timer_tick).eq(timer + 1)
|
||||
|
||||
for domain in domains:
|
||||
sync_domain = getattr(self.sync, domain)
|
||||
divider = Signal(2)
|
||||
sync_domain += divider.eq(divider + 1)
|
||||
|
||||
divided = Signal()
|
||||
divided.attr.add("no_retiming")
|
||||
sync_domain += divided.eq(divider[-1])
|
||||
divided_sys = Signal()
|
||||
self.specials += MultiReg(divided, divided_sys)
|
||||
|
||||
divided_sys_r = Signal()
|
||||
divided_tick = Signal()
|
||||
self.sync += divided_sys_r.eq(divided_sys)
|
||||
self.comb += divided_tick.eq(divided_sys & ~divided_sys_r)
|
||||
|
||||
counter = Signal(counter_width)
|
||||
counter_csr = getattr(self, "counter_" + domain)
|
||||
self.sync += [
|
||||
If(timer_tick,
|
||||
If(self.update_en.storage, counter_csr.status.eq(counter)),
|
||||
counter.eq(0),
|
||||
).Else(
|
||||
If(divided_tick, counter.eq(counter + 1))
|
||||
)
|
||||
]
|
||||
|
||||
|
||||
class WRPLL(Module, AutoCSR):
|
||||
def __init__(self, helper_clk_pads, main_dcxo_i2c, helper_dxco_i2c, ddmtd_inputs, N=15):
|
||||
self.helper_reset = CSRStorage(reset=1)
|
||||
self.collector_reset = CSRStorage(reset=1)
|
||||
self.filter_reset = CSRStorage(reset=1)
|
||||
self.adpll_offset_helper = CSRStorage(24)
|
||||
self.adpll_offset_main = CSRStorage(24)
|
||||
|
||||
self.tag_arm = CSR()
|
||||
self.main_diff_tag = CSRStatus(32)
|
||||
self.helper_diff_tag = CSRStatus(32)
|
||||
self.ref_tag = CSRStatus(N)
|
||||
self.main_tag = CSRStatus(N)
|
||||
|
||||
main_diff_tag_32 = Signal((32, True))
|
||||
helper_diff_tag_32 = Signal((32, True))
|
||||
self.comb += [
|
||||
self.main_diff_tag.status.eq(main_diff_tag_32),
|
||||
self.helper_diff_tag.status.eq(helper_diff_tag_32)
|
||||
]
|
||||
|
||||
self.clock_domains.cd_helper = ClockDomain()
|
||||
self.clock_domains.cd_collector = ClockDomain()
|
||||
self.clock_domains.cd_filter = ClockDomain()
|
||||
self.helper_reset.storage.attr.add("no_retiming")
|
||||
self.filter_reset.storage.attr.add("no_retiming")
|
||||
self.specials += Instance("IBUFGDS",
|
||||
i_I=helper_clk_pads.p, i_IB=helper_clk_pads.n,
|
||||
o_O=self.cd_helper.clk)
|
||||
self.comb += [
|
||||
self.cd_collector.clk.eq(self.cd_collector.clk),
|
||||
self.cd_filter.clk.eq(self.cd_helper.clk),
|
||||
]
|
||||
self.specials += [
|
||||
AsyncResetSynchronizer(self.cd_helper, self.helper_reset.storage),
|
||||
AsyncResetSynchronizer(self.cd_collector, self.collector_reset.storage),
|
||||
AsyncResetSynchronizer(self.cd_filter, self.filter_reset.storage)
|
||||
]
|
||||
|
||||
self.submodules.helper_dcxo = Si549(helper_dxco_i2c)
|
||||
self.submodules.main_dcxo = Si549(main_dcxo_i2c)
|
||||
|
||||
# for diagnostics and PLL initialization
|
||||
self.submodules.frequency_counter = FrequencyCounter()
|
||||
|
||||
ddmtd_counter = Signal(N)
|
||||
self.sync.helper += ddmtd_counter.eq(ddmtd_counter + 1)
|
||||
self.submodules.ddmtd_ref = DDMTD(ddmtd_counter, ddmtd_inputs.rec_clk)
|
||||
self.submodules.ddmtd_main = DDMTD(ddmtd_counter, ddmtd_inputs.main_xo)
|
||||
|
||||
collector_cd = ClockDomainsRenamer("collector")
|
||||
filter_cd = ClockDomainsRenamer("filter")
|
||||
self.submodules.collector = collector_cd(Collector(N))
|
||||
self.submodules.filter_helper = filter_cd(
|
||||
thls.make(filters.helper, data_width=48))
|
||||
self.submodules.filter_main = filter_cd(
|
||||
thls.make(filters.main, data_width=48))
|
||||
|
||||
self.comb += [
|
||||
self.collector.tag_ref.eq(self.ddmtd_ref.h_tag),
|
||||
self.collector.ref_stb.eq(self.ddmtd_ref.h_tag_update),
|
||||
self.collector.tag_main.eq(self.ddmtd_main.h_tag),
|
||||
self.collector.main_stb.eq(self.ddmtd_main.h_tag_update)
|
||||
]
|
||||
|
||||
collector_stb_ps = PulseSynchronizer("helper", "sys")
|
||||
self.submodules += collector_stb_ps
|
||||
self.sync.helper += collector_stb_ps.i.eq(self.collector.out_stb)
|
||||
collector_stb_sys = Signal()
|
||||
self.sync += collector_stb_sys.eq(collector_stb_ps.o)
|
||||
|
||||
main_diff_tag_sys = Signal((N+2, True))
|
||||
helper_diff_tag_sys = Signal((N+2, True))
|
||||
ref_tag_sys = Signal(N)
|
||||
main_tag_sys = Signal(N)
|
||||
self.specials += MultiReg(self.collector.out_main, main_diff_tag_sys)
|
||||
self.specials += MultiReg(self.collector.out_helper, helper_diff_tag_sys)
|
||||
self.specials += MultiReg(self.collector.tag_ref, ref_tag_sys)
|
||||
self.specials += MultiReg(self.collector.tag_main, main_tag_sys)
|
||||
|
||||
self.sync += [
|
||||
If(self.tag_arm.re & self.tag_arm.r, self.tag_arm.w.eq(1)),
|
||||
If(collector_stb_sys,
|
||||
self.tag_arm.w.eq(0),
|
||||
If(self.tag_arm.w,
|
||||
main_diff_tag_32.eq(main_diff_tag_sys),
|
||||
helper_diff_tag_32.eq(helper_diff_tag_sys),
|
||||
self.ref_tag.status.eq(ref_tag_sys),
|
||||
self.main_tag.status.eq(main_tag_sys)
|
||||
)
|
||||
)
|
||||
]
|
||||
|
||||
self.comb += [
|
||||
self.filter_helper.input.eq(self.collector.out_helper << 22),
|
||||
self.filter_helper.input_stb.eq(self.collector.out_stb),
|
||||
self.filter_main.input.eq(self.collector.out_main),
|
||||
self.filter_main.input_stb.eq(self.collector.out_stb)
|
||||
]
|
||||
|
||||
self.sync.helper += [
|
||||
self.helper_dcxo.adpll_stb.eq(self.filter_helper.output_stb),
|
||||
self.helper_dcxo.adpll.eq(self.filter_helper.output + self.adpll_offset_helper.storage),
|
||||
self.main_dcxo.adpll_stb.eq(self.filter_main.output_stb),
|
||||
self.main_dcxo.adpll.eq(self.filter_main.output + self.adpll_offset_main.storage)
|
||||
]
|
@ -1,221 +0,0 @@
|
||||
from migen import *
|
||||
from migen.genlib.cdc import PulseSynchronizer, MultiReg
|
||||
from migen.genlib.fsm import FSM
|
||||
from misoc.interconnect.csr import *
|
||||
|
||||
|
||||
class DDMTDSamplerExtFF(Module):
|
||||
def __init__(self, ddmtd_inputs):
|
||||
self.rec_clk = Signal()
|
||||
self.main_xo = Signal()
|
||||
|
||||
# # #
|
||||
|
||||
# TODO: s/h timing at FPGA pads
|
||||
if hasattr(ddmtd_inputs, "rec_clk"):
|
||||
rec_clk_1 = ddmtd_inputs.rec_clk
|
||||
else:
|
||||
rec_clk_1 = Signal()
|
||||
self.specials += Instance("IBUFDS",
|
||||
i_I=ddmtd_inputs.rec_clk_p, i_IB=ddmtd_inputs.rec_clk_n,
|
||||
o_O=rec_clk_1)
|
||||
if hasattr(ddmtd_inputs, "main_xo"):
|
||||
main_xo_1 = ddmtd_inputs.main_xo
|
||||
else:
|
||||
main_xo_1 = Signal()
|
||||
self.specials += Instance("IBUFDS",
|
||||
i_I=ddmtd_inputs.main_xo_p, i_IB=ddmtd_inputs.main_xo_n,
|
||||
o_O=main_xo_1)
|
||||
self.specials += [
|
||||
Instance("FD", i_C=ClockSignal("helper"),
|
||||
i_D=rec_clk_1, o_Q=self.rec_clk,
|
||||
attr={("IOB", "TRUE")}),
|
||||
Instance("FD", i_C=ClockSignal("helper"),
|
||||
i_D=main_xo_1, o_Q=self.main_xo,
|
||||
attr={("IOB", "TRUE")}),
|
||||
]
|
||||
|
||||
|
||||
class DDMTDSamplerGTP(Module):
|
||||
def __init__(self, gtp, main_xo_pads):
|
||||
self.rec_clk = Signal()
|
||||
self.main_xo = Signal()
|
||||
|
||||
# # #
|
||||
|
||||
# Getting the main XO signal from IBUFDS_GTE2 is problematic because
|
||||
# the transceiver PLL craps out if an improper clock signal is applied,
|
||||
# so we are disabling the buffer until the clock is stable.
|
||||
main_xo_se = Signal()
|
||||
rec_clk_1 = Signal()
|
||||
main_xo_1 = Signal()
|
||||
self.specials += [
|
||||
Instance("IBUFDS",
|
||||
i_I=main_xo_pads.p, i_IB=main_xo_pads.n,
|
||||
o_O=main_xo_se),
|
||||
Instance("FD", i_C=ClockSignal("helper"),
|
||||
i_D=gtp.cd_rtio_rx0.clk, o_Q=rec_clk_1,
|
||||
attr={("DONT_TOUCH", "TRUE")}),
|
||||
Instance("FD", i_C=ClockSignal("helper"),
|
||||
i_D=rec_clk_1, o_Q=self.rec_clk,
|
||||
attr={("DONT_TOUCH", "TRUE")}),
|
||||
Instance("FD", i_C=ClockSignal("helper"),
|
||||
i_D=main_xo_se, o_Q=main_xo_1,
|
||||
attr={("IOB", "TRUE")}),
|
||||
Instance("FD", i_C=ClockSignal("helper"),
|
||||
i_D=main_xo_1, o_Q=self.main_xo,
|
||||
attr={("DONT_TOUCH", "TRUE")}),
|
||||
]
|
||||
|
||||
|
||||
class DDMTDDeglitcherFirstEdge(Module):
|
||||
def __init__(self, input_signal, blind_period=128):
|
||||
self.detect = Signal()
|
||||
self.tag_correction = 0
|
||||
|
||||
rising = Signal()
|
||||
input_signal_r = Signal()
|
||||
self.sync.helper += [
|
||||
input_signal_r.eq(input_signal),
|
||||
rising.eq(input_signal & ~input_signal_r)
|
||||
]
|
||||
|
||||
blind_counter = Signal(max=blind_period)
|
||||
self.sync.helper += [
|
||||
If(blind_counter != 0, blind_counter.eq(blind_counter - 1)),
|
||||
If(input_signal_r, blind_counter.eq(blind_period - 1)),
|
||||
self.detect.eq(rising & (blind_counter == 0))
|
||||
]
|
||||
|
||||
|
||||
class DDMTD(Module):
|
||||
def __init__(self, counter, input_signal):
|
||||
|
||||
# in helper clock domain
|
||||
self.h_tag = Signal(len(counter))
|
||||
self.h_tag_update = Signal()
|
||||
|
||||
# # #
|
||||
|
||||
deglitcher = DDMTDDeglitcherFirstEdge(input_signal)
|
||||
self.submodules += deglitcher
|
||||
|
||||
self.sync.helper += [
|
||||
self.h_tag_update.eq(0),
|
||||
If(deglitcher.detect,
|
||||
self.h_tag_update.eq(1),
|
||||
self.h_tag.eq(counter + deglitcher.tag_correction)
|
||||
)
|
||||
]
|
||||
|
||||
|
||||
class Collector(Module):
|
||||
"""Generates loop filter inputs from DDMTD outputs.
|
||||
|
||||
The input to the main DCXO lock loop filter is the difference between the
|
||||
reference and main tags after unwrapping (see below).
|
||||
|
||||
The input to the helper DCXO lock loop filter is the difference between the
|
||||
current reference tag and the previous reference tag after unwrapping.
|
||||
|
||||
When the WR PLL is locked, the following ideally (no noise/jitter) obtain:
|
||||
- f_main = f_ref
|
||||
- f_helper = f_ref * 2^N/(2^N+1)
|
||||
- f_beat = f_ref - f_helper = f_ref / (2^N + 1) (cycle time is: dt=1/f_beat)
|
||||
- the reference and main DCXO tags are equal to each other at every cycle
|
||||
(the main DCXO lock drives this difference to 0)
|
||||
- the reference and main DCXO tags both have the same value at each cycle
|
||||
(the tag difference for each DDMTD is given by
|
||||
f_helper*dt = f_helper/f_beat = 2^N, which causes the N-bit DDMTD counter
|
||||
to wrap around and come back to its previous value)
|
||||
|
||||
Note that we currently lock the frequency of the helper DCXO to the
|
||||
reference clock, not it's phase. As a result, while the tag differences are
|
||||
controlled, their absolute values are arbitrary. We could consider moving
|
||||
the helper lock to a phase lock at some point in the future...
|
||||
|
||||
Since the DDMTD counter is only N bits, it is possible for tag values to
|
||||
wrap around. This will happen frequently if the locked tags happens to be
|
||||
near the edges of the counter, so that jitter can easily cause a phase wrap.
|
||||
But, it can also easily happen during lock acquisition or other transients.
|
||||
To avoid glitches in the output, we unwrap the tag differences. Currently
|
||||
we do this in hardware, but we should consider extending the processor to
|
||||
allow us to do it inside the filters. Since the processor uses wider
|
||||
signals, this would significantly extend the overall glitch-free
|
||||
range of the PLL and may aid lock acquisition.
|
||||
"""
|
||||
def __init__(self, N):
|
||||
self.ref_stb = Signal()
|
||||
self.main_stb = Signal()
|
||||
self.tag_ref = Signal(N)
|
||||
self.tag_main = Signal(N)
|
||||
|
||||
self.out_stb = Signal()
|
||||
self.out_main = Signal((N+2, True))
|
||||
self.out_helper = Signal((N+2, True))
|
||||
self.out_tag_ref = Signal(N)
|
||||
self.out_tag_main = Signal(N)
|
||||
|
||||
tag_ref_r = Signal(N)
|
||||
tag_main_r = Signal(N)
|
||||
main_tag_diff = Signal((N+2, True))
|
||||
helper_tag_diff = Signal((N+2, True))
|
||||
|
||||
# # #
|
||||
|
||||
fsm = FSM(reset_state="IDLE")
|
||||
self.submodules += fsm
|
||||
|
||||
fsm.act("IDLE",
|
||||
NextValue(self.out_stb, 0),
|
||||
If(self.ref_stb & self.main_stb,
|
||||
NextValue(tag_ref_r, self.tag_ref),
|
||||
NextValue(tag_main_r, self.tag_main),
|
||||
NextState("DIFF")
|
||||
).Elif(self.ref_stb,
|
||||
NextValue(tag_ref_r, self.tag_ref),
|
||||
NextState("WAITMAIN")
|
||||
).Elif(self.main_stb,
|
||||
NextValue(tag_main_r, self.tag_main),
|
||||
NextState("WAITREF")
|
||||
)
|
||||
)
|
||||
fsm.act("WAITREF",
|
||||
If(self.ref_stb,
|
||||
NextValue(tag_ref_r, self.tag_ref),
|
||||
NextState("DIFF")
|
||||
)
|
||||
)
|
||||
fsm.act("WAITMAIN",
|
||||
If(self.main_stb,
|
||||
NextValue(tag_main_r, self.tag_main),
|
||||
NextState("DIFF")
|
||||
)
|
||||
)
|
||||
fsm.act("DIFF",
|
||||
NextValue(main_tag_diff, tag_main_r - tag_ref_r),
|
||||
NextValue(helper_tag_diff, tag_ref_r - self.out_tag_ref),
|
||||
NextState("UNWRAP")
|
||||
)
|
||||
fsm.act("UNWRAP",
|
||||
If(main_tag_diff - self.out_main > 2**(N-1),
|
||||
NextValue(main_tag_diff, main_tag_diff - 2**N)
|
||||
).Elif(self.out_main - main_tag_diff > 2**(N-1),
|
||||
NextValue(main_tag_diff, main_tag_diff + 2**N)
|
||||
),
|
||||
|
||||
If(helper_tag_diff - self.out_helper > 2**(N-1),
|
||||
NextValue(helper_tag_diff, helper_tag_diff - 2**N)
|
||||
).Elif(self.out_helper - helper_tag_diff > 2**(N-1),
|
||||
NextValue(helper_tag_diff, helper_tag_diff + 2**N)
|
||||
),
|
||||
NextState("OUTPUT")
|
||||
)
|
||||
fsm.act("OUTPUT",
|
||||
NextValue(self.out_tag_ref, tag_ref_r),
|
||||
NextValue(self.out_tag_main, tag_main_r),
|
||||
NextValue(self.out_main, main_tag_diff),
|
||||
NextValue(self.out_helper, helper_tag_diff),
|
||||
NextValue(self.out_stb, 1),
|
||||
NextState("IDLE")
|
||||
)
|
@ -1,61 +0,0 @@
|
||||
helper_xn1 = 0
|
||||
helper_xn2 = 0
|
||||
helper_yn0 = 0
|
||||
helper_yn1 = 0
|
||||
helper_yn2 = 0
|
||||
helper_out = 0
|
||||
|
||||
main_xn1 = 0
|
||||
main_xn2 = 0
|
||||
main_yn0 = 0
|
||||
main_yn1 = 0
|
||||
main_yn2 = 0
|
||||
|
||||
|
||||
def helper(tag_diff):
|
||||
global helper_xn1, helper_xn2, helper_yn0, \
|
||||
helper_yn1, helper_yn2, helper_out
|
||||
|
||||
helper_xn0 = 0 - tag_diff # *(2**22)
|
||||
|
||||
helper_yr = 4294967296
|
||||
|
||||
helper_yn2 = helper_yn1
|
||||
helper_yn1 = helper_yn0
|
||||
|
||||
helper_yn0 = (284885690 * (helper_xn0
|
||||
+ (217319150 * helper_xn1 >> 44)
|
||||
- (17591968725108 * helper_xn2 >> 44)
|
||||
) >> 44
|
||||
) + (35184372088832*helper_yn1 >> 44) - helper_yn2
|
||||
|
||||
helper_xn2 = helper_xn1
|
||||
helper_xn1 = helper_xn0
|
||||
|
||||
helper_out = 268435456*helper_yn0 >> 44
|
||||
helper_out = min(helper_out, helper_yr)
|
||||
helper_out = max(helper_out, 0 - helper_yr)
|
||||
|
||||
return helper_out
|
||||
|
||||
|
||||
def main(main_xn0):
|
||||
global main_xn1, main_xn2, main_yn0, main_yn1, main_yn2
|
||||
|
||||
main_yr = 4294967296
|
||||
|
||||
main_yn2 = main_yn1
|
||||
main_yn1 = main_yn0
|
||||
main_yn0 = (
|
||||
((133450380908*(((35184372088832*main_xn0) >> 44) +
|
||||
((17592186044417*main_xn1) >> 44))) >> 44) +
|
||||
((29455872930889*main_yn1) >> 44) -
|
||||
((12673794781453*main_yn2) >> 44))
|
||||
|
||||
main_xn2 = main_xn1
|
||||
main_xn1 = main_xn0
|
||||
|
||||
main_yn0 = min(main_yn0, main_yr)
|
||||
main_yn0 = max(main_yn0, 0 - main_yr)
|
||||
|
||||
return main_yn0
|
@ -1,340 +0,0 @@
|
||||
from migen import *
|
||||
from migen.genlib.fsm import *
|
||||
from migen.genlib.cdc import MultiReg, PulseSynchronizer, BlindTransfer
|
||||
|
||||
from misoc.interconnect.csr import *
|
||||
|
||||
|
||||
class I2CClockGen(Module):
|
||||
def __init__(self, width):
|
||||
self.load = Signal(width)
|
||||
self.clk2x = Signal()
|
||||
|
||||
cnt = Signal.like(self.load)
|
||||
self.comb += [
|
||||
self.clk2x.eq(cnt == 0),
|
||||
]
|
||||
self.sync += [
|
||||
If(self.clk2x,
|
||||
cnt.eq(self.load),
|
||||
).Else(
|
||||
cnt.eq(cnt - 1),
|
||||
)
|
||||
]
|
||||
|
||||
|
||||
class I2CMasterMachine(Module):
|
||||
def __init__(self, clock_width):
|
||||
self.scl = Signal(reset=1)
|
||||
self.sda_o = Signal(reset=1)
|
||||
self.sda_i = Signal()
|
||||
|
||||
self.submodules.cg = CEInserter()(I2CClockGen(clock_width))
|
||||
self.start = Signal()
|
||||
self.stop = Signal()
|
||||
self.write = Signal()
|
||||
self.ack = Signal()
|
||||
self.data = Signal(8)
|
||||
self.ready = Signal()
|
||||
|
||||
###
|
||||
|
||||
bits = Signal(4)
|
||||
data = Signal(8)
|
||||
|
||||
fsm = CEInserter()(FSM("IDLE"))
|
||||
self.submodules += fsm
|
||||
|
||||
fsm.act("IDLE",
|
||||
self.ready.eq(1),
|
||||
If(self.start,
|
||||
NextState("START0"),
|
||||
).Elif(self.stop,
|
||||
NextState("STOP0"),
|
||||
).Elif(self.write,
|
||||
NextValue(bits, 8),
|
||||
NextValue(data, self.data),
|
||||
NextState("WRITE0")
|
||||
)
|
||||
)
|
||||
|
||||
fsm.act("START0",
|
||||
NextValue(self.scl, 1),
|
||||
NextState("START1")
|
||||
)
|
||||
fsm.act("START1",
|
||||
NextValue(self.sda_o, 0),
|
||||
NextState("IDLE")
|
||||
)
|
||||
|
||||
fsm.act("STOP0",
|
||||
NextValue(self.scl, 0),
|
||||
NextState("STOP1")
|
||||
)
|
||||
fsm.act("STOP1",
|
||||
NextValue(self.sda_o, 0),
|
||||
NextState("STOP2")
|
||||
)
|
||||
fsm.act("STOP2",
|
||||
NextValue(self.scl, 1),
|
||||
NextState("STOP3")
|
||||
)
|
||||
fsm.act("STOP3",
|
||||
NextValue(self.sda_o, 1),
|
||||
NextState("IDLE")
|
||||
)
|
||||
|
||||
fsm.act("WRITE0",
|
||||
NextValue(self.scl, 0),
|
||||
NextState("WRITE1")
|
||||
)
|
||||
fsm.act("WRITE1",
|
||||
If(bits == 0,
|
||||
NextValue(self.sda_o, 1),
|
||||
NextState("READACK0"),
|
||||
).Else(
|
||||
NextValue(self.sda_o, data[7]),
|
||||
NextState("WRITE2"),
|
||||
)
|
||||
)
|
||||
fsm.act("WRITE2",
|
||||
NextValue(self.scl, 1),
|
||||
NextValue(data[1:], data[:-1]),
|
||||
NextValue(bits, bits - 1),
|
||||
NextState("WRITE0"),
|
||||
)
|
||||
fsm.act("READACK0",
|
||||
NextValue(self.scl, 1),
|
||||
NextState("READACK1"),
|
||||
)
|
||||
fsm.act("READACK1",
|
||||
NextValue(self.ack, ~self.sda_i),
|
||||
NextState("IDLE")
|
||||
)
|
||||
|
||||
run = Signal()
|
||||
idle = Signal()
|
||||
self.comb += [
|
||||
run.eq((self.start | self.stop | self.write) & self.ready),
|
||||
idle.eq(~run & fsm.ongoing("IDLE")),
|
||||
self.cg.ce.eq(~idle),
|
||||
fsm.ce.eq(run | self.cg.clk2x),
|
||||
]
|
||||
|
||||
|
||||
class ADPLLProgrammer(Module):
|
||||
def __init__(self):
|
||||
self.i2c_divider = Signal(16)
|
||||
self.i2c_address = Signal(7)
|
||||
|
||||
self.adpll = Signal(24)
|
||||
self.stb = Signal()
|
||||
self.busy = Signal()
|
||||
self.nack = Signal()
|
||||
|
||||
self.scl = Signal()
|
||||
self.sda_i = Signal()
|
||||
self.sda_o = Signal()
|
||||
|
||||
self.scl.attr.add("no_retiming")
|
||||
self.sda_o.attr.add("no_retiming")
|
||||
|
||||
# # #
|
||||
|
||||
master = I2CMasterMachine(16)
|
||||
self.submodules += master
|
||||
|
||||
self.comb += [
|
||||
master.cg.load.eq(self.i2c_divider),
|
||||
self.scl.eq(master.scl),
|
||||
master.sda_i.eq(self.sda_i),
|
||||
self.sda_o.eq(master.sda_o)
|
||||
]
|
||||
|
||||
fsm = FSM()
|
||||
self.submodules += fsm
|
||||
|
||||
adpll = Signal.like(self.adpll)
|
||||
|
||||
fsm.act("IDLE",
|
||||
If(self.stb,
|
||||
NextValue(adpll, self.adpll),
|
||||
NextState("START")
|
||||
)
|
||||
)
|
||||
fsm.act("START",
|
||||
master.start.eq(1),
|
||||
If(master.ready, NextState("DEVADDRESS"))
|
||||
)
|
||||
fsm.act("DEVADDRESS",
|
||||
master.data.eq(self.i2c_address << 1),
|
||||
master.write.eq(1),
|
||||
If(master.ready, NextState("REGADRESS"))
|
||||
)
|
||||
fsm.act("REGADRESS",
|
||||
master.data.eq(231),
|
||||
master.write.eq(1),
|
||||
If(master.ready,
|
||||
If(master.ack,
|
||||
NextState("DATA0")
|
||||
).Else(
|
||||
self.nack.eq(1),
|
||||
NextState("STOP")
|
||||
)
|
||||
)
|
||||
)
|
||||
fsm.act("DATA0",
|
||||
master.data.eq(adpll[0:8]),
|
||||
master.write.eq(1),
|
||||
If(master.ready,
|
||||
If(master.ack,
|
||||
NextState("DATA1")
|
||||
).Else(
|
||||
self.nack.eq(1),
|
||||
NextState("STOP")
|
||||
)
|
||||
)
|
||||
)
|
||||
fsm.act("DATA1",
|
||||
master.data.eq(adpll[8:16]),
|
||||
master.write.eq(1),
|
||||
If(master.ready,
|
||||
If(master.ack,
|
||||
NextState("DATA2")
|
||||
).Else(
|
||||
self.nack.eq(1),
|
||||
NextState("STOP")
|
||||
)
|
||||
)
|
||||
)
|
||||
fsm.act("DATA2",
|
||||
master.data.eq(adpll[16:24]),
|
||||
master.write.eq(1),
|
||||
If(master.ready,
|
||||
If(~master.ack, self.nack.eq(1)),
|
||||
NextState("STOP")
|
||||
)
|
||||
)
|
||||
fsm.act("STOP",
|
||||
master.stop.eq(1),
|
||||
If(master.ready,
|
||||
If(~master.ack, self.nack.eq(1)),
|
||||
NextState("IDLE")
|
||||
)
|
||||
)
|
||||
|
||||
self.comb += self.busy.eq(~fsm.ongoing("IDLE"))
|
||||
|
||||
|
||||
def simulate_programmer():
|
||||
from migen.sim.core import run_simulation
|
||||
|
||||
dut = ADPLLProgrammer()
|
||||
|
||||
def generator():
|
||||
yield dut.i2c_divider.eq(4)
|
||||
yield dut.i2c_address.eq(0x55)
|
||||
yield
|
||||
yield dut.adpll.eq(0x123456)
|
||||
yield dut.stb.eq(1)
|
||||
yield
|
||||
yield dut.stb.eq(0)
|
||||
yield
|
||||
while (yield dut.busy):
|
||||
yield
|
||||
for _ in range(20):
|
||||
yield
|
||||
|
||||
run_simulation(dut, generator(), vcd_name="tb.vcd")
|
||||
|
||||
|
||||
class Si549(Module, AutoCSR):
|
||||
def __init__(self, pads):
|
||||
self.gpio_enable = CSRStorage(reset=1)
|
||||
self.gpio_in = CSRStatus(2)
|
||||
self.gpio_out = CSRStorage(2)
|
||||
self.gpio_oe = CSRStorage(2)
|
||||
|
||||
self.i2c_divider = CSRStorage(16, reset=75)
|
||||
self.i2c_address = CSRStorage(7)
|
||||
self.errors = CSR(2)
|
||||
|
||||
# in helper clock domain
|
||||
self.adpll = Signal(24)
|
||||
self.adpll_stb = Signal()
|
||||
|
||||
# # #
|
||||
|
||||
programmer = ClockDomainsRenamer("helper")(ADPLLProgrammer())
|
||||
self.submodules += programmer
|
||||
|
||||
self.i2c_divider.storage.attr.add("no_retiming")
|
||||
self.i2c_address.storage.attr.add("no_retiming")
|
||||
self.specials += [
|
||||
MultiReg(self.i2c_divider.storage, programmer.i2c_divider, "helper"),
|
||||
MultiReg(self.i2c_address.storage, programmer.i2c_address, "helper")
|
||||
]
|
||||
self.comb += [
|
||||
programmer.adpll.eq(self.adpll),
|
||||
programmer.stb.eq(self.adpll_stb)
|
||||
]
|
||||
|
||||
self.gpio_enable.storage.attr.add("no_retiming")
|
||||
self.gpio_out.storage.attr.add("no_retiming")
|
||||
self.gpio_oe.storage.attr.add("no_retiming")
|
||||
|
||||
# SCL GPIO and mux
|
||||
ts_scl = TSTriple(1)
|
||||
self.specials += ts_scl.get_tristate(pads.scl)
|
||||
|
||||
status = Signal()
|
||||
self.comb += self.gpio_in.status[0].eq(status)
|
||||
|
||||
self.specials += MultiReg(ts_scl.i, status)
|
||||
self.comb += [
|
||||
If(self.gpio_enable.storage,
|
||||
ts_scl.o.eq(self.gpio_out.storage[0]),
|
||||
ts_scl.oe.eq(self.gpio_oe.storage[0])
|
||||
).Else(
|
||||
ts_scl.o.eq(0),
|
||||
ts_scl.oe.eq(~programmer.scl)
|
||||
)
|
||||
]
|
||||
|
||||
# SDA GPIO and mux
|
||||
ts_sda = TSTriple(1)
|
||||
self.specials += ts_sda.get_tristate(pads.sda)
|
||||
|
||||
status = Signal()
|
||||
self.comb += self.gpio_in.status[1].eq(status)
|
||||
|
||||
self.specials += MultiReg(ts_sda.i, status)
|
||||
self.comb += [
|
||||
If(self.gpio_enable.storage,
|
||||
ts_sda.o.eq(self.gpio_out.storage[1]),
|
||||
ts_sda.oe.eq(self.gpio_oe.storage[1])
|
||||
).Else(
|
||||
ts_sda.o.eq(0),
|
||||
ts_sda.oe.eq(~programmer.sda_o)
|
||||
)
|
||||
]
|
||||
self.specials += MultiReg(ts_sda.i, programmer.sda_i, "helper")
|
||||
|
||||
# Error reporting
|
||||
collision_cdc = BlindTransfer("helper", "sys")
|
||||
self.submodules += collision_cdc
|
||||
self.comb += collision_cdc.i.eq(programmer.stb & programmer.busy)
|
||||
|
||||
nack_cdc = PulseSynchronizer("helper", "sys")
|
||||
self.submodules += nack_cdc
|
||||
self.comb += nack_cdc.i.eq(programmer.nack)
|
||||
|
||||
for n, trig in enumerate([collision_cdc.o, nack_cdc.o]):
|
||||
self.sync += [
|
||||
If(self.errors.re & self.errors.r[n], self.errors.w[n].eq(0)),
|
||||
If(trig, self.errors.w[n].eq(1))
|
||||
]
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
simulate_programmer()
|
@ -1,618 +0,0 @@
|
||||
import inspect
|
||||
import ast
|
||||
from copy import copy
|
||||
import operator
|
||||
from functools import reduce
|
||||
from collections import OrderedDict
|
||||
|
||||
from migen import *
|
||||
from migen.genlib.fsm import *
|
||||
|
||||
|
||||
class Isn:
|
||||
def __init__(self, immediate=None, inputs=None, outputs=None):
|
||||
if inputs is None:
|
||||
inputs = []
|
||||
if outputs is None:
|
||||
outputs = []
|
||||
self.immediate = immediate
|
||||
self.inputs = inputs
|
||||
self.outputs = outputs
|
||||
|
||||
def __repr__(self):
|
||||
r = "<"
|
||||
r += self.__class__.__name__
|
||||
if self.immediate is not None:
|
||||
r += " (" + str(self.immediate) + ")"
|
||||
for inp in self.inputs:
|
||||
r += " r" + str(inp)
|
||||
if self.outputs:
|
||||
r += " ->"
|
||||
for outp in self.outputs:
|
||||
r += " r" + str(outp)
|
||||
r += ">"
|
||||
return r
|
||||
|
||||
|
||||
class NopIsn(Isn):
|
||||
opcode = 0
|
||||
|
||||
class AddIsn(Isn):
|
||||
opcode = 1
|
||||
|
||||
class SubIsn(Isn):
|
||||
opcode = 2
|
||||
|
||||
class MulShiftIsn(Isn):
|
||||
opcode = 3
|
||||
|
||||
# opcode = 4: MulShift with alternate shift
|
||||
|
||||
class MinIsn(Isn):
|
||||
opcode = 5
|
||||
|
||||
class MaxIsn(Isn):
|
||||
opcode = 6
|
||||
|
||||
class CopyIsn(Isn):
|
||||
opcode = 7
|
||||
|
||||
class InputIsn(Isn):
|
||||
opcode = 8
|
||||
|
||||
class OutputIsn(Isn):
|
||||
opcode = 9
|
||||
|
||||
class EndIsn(Isn):
|
||||
opcode = 10
|
||||
|
||||
|
||||
class ASTCompiler:
|
||||
def __init__(self):
|
||||
self.program = []
|
||||
self.data = []
|
||||
self.next_ssa_reg = -1
|
||||
self.constants = dict()
|
||||
self.names = dict()
|
||||
self.globals = OrderedDict()
|
||||
|
||||
def get_ssa_reg(self):
|
||||
r = self.next_ssa_reg
|
||||
self.next_ssa_reg -= 1
|
||||
return r
|
||||
|
||||
def add_global(self, name):
|
||||
if name not in self.globals:
|
||||
r = len(self.data)
|
||||
self.data.append(0)
|
||||
self.names[name] = r
|
||||
self.globals[name] = r
|
||||
|
||||
def input(self, name):
|
||||
target = self.get_ssa_reg()
|
||||
self.program.append(InputIsn(outputs=[target]))
|
||||
self.names[name] = target
|
||||
|
||||
def emit(self, node):
|
||||
if isinstance(node, ast.BinOp):
|
||||
if isinstance(node.op, ast.RShift):
|
||||
if not isinstance(node.left, ast.BinOp) or not isinstance(node.left.op, ast.Mult):
|
||||
raise NotImplementedError
|
||||
if not isinstance(node.right, ast.Num):
|
||||
raise NotImplementedError
|
||||
left = self.emit(node.left.left)
|
||||
right = self.emit(node.left.right)
|
||||
cons = lambda **kwargs: MulShiftIsn(immediate=node.right.n, **kwargs)
|
||||
else:
|
||||
left = self.emit(node.left)
|
||||
right = self.emit(node.right)
|
||||
if isinstance(node.op, ast.Add):
|
||||
cons = AddIsn
|
||||
elif isinstance(node.op, ast.Sub):
|
||||
cons = SubIsn
|
||||
elif isinstance(node.op, ast.Mult):
|
||||
cons = lambda **kwargs: MulShiftIsn(immediate=0, **kwargs)
|
||||
else:
|
||||
raise NotImplementedError
|
||||
output = self.get_ssa_reg()
|
||||
self.program.append(cons(inputs=[left, right], outputs=[output]))
|
||||
return output
|
||||
elif isinstance(node, ast.Call):
|
||||
if not isinstance(node.func, ast.Name):
|
||||
raise NotImplementedError
|
||||
funcname = node.func.id
|
||||
if node.keywords:
|
||||
raise NotImplementedError
|
||||
inputs = [self.emit(x) for x in node.args]
|
||||
if funcname == "min":
|
||||
cons = MinIsn
|
||||
elif funcname == "max":
|
||||
cons = MaxIsn
|
||||
else:
|
||||
raise NotImplementedError
|
||||
output = self.get_ssa_reg()
|
||||
self.program.append(cons(inputs=inputs, outputs=[output]))
|
||||
return output
|
||||
elif isinstance(node, (ast.Num, ast.UnaryOp)):
|
||||
if isinstance(node, ast.UnaryOp):
|
||||
if not isinstance(node.operand, ast.Num):
|
||||
raise NotImplementedError
|
||||
if isinstance(node.op, ast.UAdd):
|
||||
transform = lambda x: x
|
||||
elif isinstance(node.op, ast.USub):
|
||||
transform = operator.neg
|
||||
elif isinstance(node.op, ast.Invert):
|
||||
transform = operator.invert
|
||||
else:
|
||||
raise NotImplementedError
|
||||
node = node.operand
|
||||
else:
|
||||
transform = lambda x: x
|
||||
n = transform(node.n)
|
||||
if n in self.constants:
|
||||
return self.constants[n]
|
||||
else:
|
||||
r = len(self.data)
|
||||
self.data.append(n)
|
||||
self.constants[n] = r
|
||||
return r
|
||||
elif isinstance(node, ast.Name):
|
||||
return self.names[node.id]
|
||||
elif isinstance(node, ast.Assign):
|
||||
output = self.emit(node.value)
|
||||
for target in node.targets:
|
||||
assert isinstance(target, ast.Name)
|
||||
self.names[target.id] = output
|
||||
elif isinstance(node, ast.Return):
|
||||
value = self.emit(node.value)
|
||||
self.program.append(OutputIsn(inputs=[value]))
|
||||
elif isinstance(node, ast.Global):
|
||||
pass
|
||||
else:
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
class Processor:
|
||||
def __init__(self, data_width=32, multiplier_stages=2):
|
||||
self.data_width = data_width
|
||||
self.multiplier_stages = multiplier_stages
|
||||
self.multiplier_shifts = []
|
||||
self.program_rom_size = None
|
||||
self.data_ram_size = None
|
||||
self.opcode_bits = 4
|
||||
self.reg_bits = None
|
||||
|
||||
def get_instruction_latency(self, isn):
|
||||
return {
|
||||
AddIsn: 2,
|
||||
SubIsn: 2,
|
||||
MulShiftIsn: 1 + self.multiplier_stages,
|
||||
MinIsn: 2,
|
||||
MaxIsn: 2,
|
||||
CopyIsn: 1,
|
||||
InputIsn: 1
|
||||
}[isn.__class__]
|
||||
|
||||
def encode_instruction(self, isn, exit):
|
||||
opcode = isn.opcode
|
||||
if isn.immediate is not None and not isinstance(isn, MulShiftIsn):
|
||||
r0 = isn.immediate
|
||||
if len(isn.inputs) >= 1:
|
||||
r1 = isn.inputs[0]
|
||||
else:
|
||||
r1 = 0
|
||||
else:
|
||||
if len(isn.inputs) >= 1:
|
||||
r0 = isn.inputs[0]
|
||||
else:
|
||||
r0 = 0
|
||||
if len(isn.inputs) >= 2:
|
||||
r1 = isn.inputs[1]
|
||||
else:
|
||||
r1 = 0
|
||||
r = 0
|
||||
for value, bits in ((exit, self.reg_bits), (r1, self.reg_bits), (r0, self.reg_bits), (opcode, self.opcode_bits)):
|
||||
r <<= bits
|
||||
r |= value
|
||||
return r
|
||||
|
||||
def instruction_bits(self):
|
||||
return 3*self.reg_bits + self.opcode_bits
|
||||
|
||||
def implement(self, program, data):
|
||||
return ProcessorImpl(self, program, data)
|
||||
|
||||
|
||||
class Scheduler:
|
||||
def __init__(self, processor, reserved_data, program):
|
||||
self.processor = processor
|
||||
self.reserved_data = reserved_data
|
||||
self.used_registers = set(range(self.reserved_data))
|
||||
self.exits = dict()
|
||||
self.program = program
|
||||
self.remaining = copy(program)
|
||||
self.output = []
|
||||
|
||||
def allocate_register(self):
|
||||
r = min(set(range(max(self.used_registers) + 2)) - self.used_registers)
|
||||
self.used_registers.add(r)
|
||||
return r
|
||||
|
||||
def free_register(self, r):
|
||||
assert r >= self.reserved_data
|
||||
self.used_registers.discard(r)
|
||||
|
||||
def find_inputs(self, cycle, isn):
|
||||
mapped_inputs = []
|
||||
for inp in isn.inputs:
|
||||
if inp >= 0:
|
||||
mapped_inputs.append(inp)
|
||||
else:
|
||||
found = False
|
||||
for i in range(cycle):
|
||||
if i in self.exits:
|
||||
r, rm = self.exits[i]
|
||||
if r == inp:
|
||||
mapped_inputs.append(rm)
|
||||
found = True
|
||||
break
|
||||
if not found:
|
||||
return None
|
||||
return mapped_inputs
|
||||
|
||||
def schedule_one(self, isn):
|
||||
cycle = len(self.output)
|
||||
mapped_inputs = self.find_inputs(cycle, isn)
|
||||
if mapped_inputs is None:
|
||||
return False
|
||||
|
||||
if isn.outputs:
|
||||
# check that exit slot is free
|
||||
latency = self.processor.get_instruction_latency(isn)
|
||||
exit = cycle + latency
|
||||
if exit in self.exits:
|
||||
return False
|
||||
|
||||
# avoid RAW hazard with global writeback
|
||||
for output in isn.outputs:
|
||||
if output >= 0:
|
||||
for risn in self.remaining:
|
||||
for inp in risn.inputs:
|
||||
if inp == output:
|
||||
return False
|
||||
|
||||
# Instruction can be scheduled
|
||||
|
||||
self.remaining.remove(isn)
|
||||
|
||||
for inp, minp in zip(isn.inputs, mapped_inputs):
|
||||
can_free = inp < 0 and all(inp != rinp for risn in self.remaining for rinp in risn.inputs)
|
||||
if can_free:
|
||||
self.free_register(minp)
|
||||
|
||||
if isn.outputs:
|
||||
assert len(isn.outputs) == 1
|
||||
if isn.outputs[0] < 0:
|
||||
output = self.allocate_register()
|
||||
else:
|
||||
output = isn.outputs[0]
|
||||
self.exits[exit] = (isn.outputs[0], output)
|
||||
self.output.append(isn.__class__(immediate=isn.immediate, inputs=mapped_inputs))
|
||||
|
||||
return True
|
||||
|
||||
def schedule(self):
|
||||
while self.remaining:
|
||||
success = False
|
||||
for isn in self.remaining:
|
||||
if self.schedule_one(isn):
|
||||
success = True
|
||||
break
|
||||
if not success:
|
||||
self.output.append(NopIsn())
|
||||
self.output += [NopIsn()]*(max(self.exits.keys()) - len(self.output) + 1)
|
||||
return self.output
|
||||
|
||||
|
||||
class CompiledProgram:
|
||||
def __init__(self, processor, program, exits, data, glbs):
|
||||
self.processor = processor
|
||||
self.program = program
|
||||
self.exits = exits
|
||||
self.data = data
|
||||
self.globals = glbs
|
||||
|
||||
def pretty_print(self):
|
||||
for cycle, isn in enumerate(self.program):
|
||||
l = "{:4d} {:15}".format(cycle, str(isn))
|
||||
if cycle in self.exits:
|
||||
l += " -> r{}".format(self.exits[cycle])
|
||||
print(l)
|
||||
|
||||
def dimension_processor(self):
|
||||
self.processor.program_rom_size = len(self.program)
|
||||
self.processor.data_ram_size = len(self.data)
|
||||
self.processor.reg_bits = (self.processor.data_ram_size - 1).bit_length()
|
||||
for isn in self.program:
|
||||
if isinstance(isn, MulShiftIsn) and isn.immediate not in self.processor.multiplier_shifts:
|
||||
self.processor.multiplier_shifts.append(isn.immediate)
|
||||
|
||||
def encode(self):
|
||||
r = []
|
||||
for i, isn in enumerate(self.program):
|
||||
exit = self.exits.get(i, 0)
|
||||
r.append(self.processor.encode_instruction(isn, exit))
|
||||
return r
|
||||
|
||||
|
||||
def compile(processor, function):
|
||||
node = ast.parse(inspect.getsource(function))
|
||||
assert isinstance(node, ast.Module)
|
||||
assert len(node.body) == 1
|
||||
node = node.body[0]
|
||||
assert isinstance(node, ast.FunctionDef)
|
||||
assert len(node.args.args) == 1
|
||||
arg = node.args.args[0].arg
|
||||
body = node.body
|
||||
|
||||
astcompiler = ASTCompiler()
|
||||
for node in body:
|
||||
if isinstance(node, ast.Global):
|
||||
for name in node.names:
|
||||
astcompiler.add_global(name)
|
||||
arg_r = astcompiler.input(arg)
|
||||
for node in body:
|
||||
astcompiler.emit(node)
|
||||
if isinstance(node, ast.Return):
|
||||
break
|
||||
for glbl, location in astcompiler.globals.items():
|
||||
new_location = astcompiler.names[glbl]
|
||||
if new_location != location:
|
||||
astcompiler.program.append(CopyIsn(inputs=[new_location], outputs=[location]))
|
||||
|
||||
scheduler = Scheduler(processor, len(astcompiler.data), astcompiler.program)
|
||||
scheduler.schedule()
|
||||
|
||||
program = copy(scheduler.output)
|
||||
program.append(EndIsn())
|
||||
|
||||
max_reg = max(max(max(isn.inputs + [0]) for isn in program), max(v[1] for k, v in scheduler.exits.items()))
|
||||
|
||||
return CompiledProgram(
|
||||
processor=processor,
|
||||
program=program,
|
||||
exits={k: v[1] for k, v in scheduler.exits.items()},
|
||||
data=astcompiler.data + [0]*(max_reg - len(astcompiler.data) + 1),
|
||||
glbs=astcompiler.globals)
|
||||
|
||||
|
||||
class BaseUnit(Module):
|
||||
def __init__(self, data_width):
|
||||
self.stb_i = Signal()
|
||||
self.i0 = Signal((data_width, True))
|
||||
self.i1 = Signal((data_width, True))
|
||||
self.stb_o = Signal()
|
||||
self.o = Signal((data_width, True))
|
||||
|
||||
|
||||
class NopUnit(BaseUnit):
|
||||
pass
|
||||
|
||||
|
||||
class OpUnit(BaseUnit):
|
||||
def __init__(self, op, data_width, stages, op_data_width=None):
|
||||
BaseUnit.__init__(self, data_width)
|
||||
# work around Migen's mishandling of Verilog's cretinous operator width rules
|
||||
if op_data_width is None:
|
||||
op_data_width = data_width
|
||||
|
||||
if stages > 1:
|
||||
# Vivado backward retiming for DSP does not work correctly if DSP inputs
|
||||
# are not registered.
|
||||
i0 = Signal.like(self.i0)
|
||||
i1 = Signal.like(self.i1)
|
||||
stb_i = Signal()
|
||||
self.sync += [
|
||||
i0.eq(self.i0),
|
||||
i1.eq(self.i1),
|
||||
stb_i.eq(self.stb_i)
|
||||
]
|
||||
output_stages = stages - 1
|
||||
else:
|
||||
i0, i1, stb_i = self.i0, self.i1, self.stb_i
|
||||
output_stages = stages
|
||||
|
||||
o = Signal((op_data_width, True))
|
||||
self.comb += o.eq(op(i0, i1))
|
||||
stb_o = stb_i
|
||||
for i in range(output_stages):
|
||||
n_o = Signal((data_width, True))
|
||||
if stages > 1:
|
||||
n_o.attr.add(("retiming_backward", 1))
|
||||
n_stb_o = Signal()
|
||||
self.sync += [
|
||||
n_o.eq(o),
|
||||
n_stb_o.eq(stb_o)
|
||||
]
|
||||
o = n_o
|
||||
stb_o = n_stb_o
|
||||
self.comb += [
|
||||
self.o.eq(o),
|
||||
self.stb_o.eq(stb_o)
|
||||
]
|
||||
|
||||
|
||||
class SelectUnit(BaseUnit):
|
||||
def __init__(self, op, data_width):
|
||||
BaseUnit.__init__(self, data_width)
|
||||
|
||||
self.sync += [
|
||||
self.stb_o.eq(self.stb_i),
|
||||
If(op(self.i0, self.i1),
|
||||
self.o.eq(self.i0)
|
||||
).Else(
|
||||
self.o.eq(self.i1)
|
||||
)
|
||||
]
|
||||
|
||||
|
||||
class CopyUnit(BaseUnit):
|
||||
def __init__(self, data_width):
|
||||
BaseUnit.__init__(self, data_width)
|
||||
|
||||
self.comb += [
|
||||
self.stb_o.eq(self.stb_i),
|
||||
self.o.eq(self.i0)
|
||||
]
|
||||
|
||||
|
||||
class InputUnit(BaseUnit):
|
||||
def __init__(self, data_width, input_stb, input):
|
||||
BaseUnit.__init__(self, data_width)
|
||||
self.buffer = Signal(data_width)
|
||||
|
||||
self.comb += [
|
||||
self.stb_o.eq(self.stb_i),
|
||||
self.o.eq(self.buffer)
|
||||
]
|
||||
|
||||
|
||||
class OutputUnit(BaseUnit):
|
||||
def __init__(self, data_width, output_stb, output):
|
||||
BaseUnit.__init__(self, data_width)
|
||||
|
||||
self.sync += [
|
||||
output_stb.eq(self.stb_i),
|
||||
output.eq(self.i0)
|
||||
]
|
||||
|
||||
|
||||
class ProcessorImpl(Module):
|
||||
def __init__(self, pd, program, data):
|
||||
self.input_stb = Signal()
|
||||
self.input = Signal((pd.data_width, True))
|
||||
|
||||
self.output_stb = Signal()
|
||||
self.output = Signal((pd.data_width, True))
|
||||
|
||||
self.busy = Signal()
|
||||
|
||||
# # #
|
||||
|
||||
program_mem = Memory(pd.instruction_bits(), pd.program_rom_size, init=program)
|
||||
data_mem0 = Memory(pd.data_width, pd.data_ram_size, init=data)
|
||||
data_mem1 = Memory(pd.data_width, pd.data_ram_size, init=data)
|
||||
self.specials += program_mem, data_mem0, data_mem1
|
||||
|
||||
pc = Signal(pd.instruction_bits())
|
||||
pc_next = Signal.like(pc)
|
||||
pc_en = Signal()
|
||||
self.sync += pc.eq(pc_next)
|
||||
self.comb += [
|
||||
If(pc_en,
|
||||
pc_next.eq(pc + 1)
|
||||
).Else(
|
||||
pc_next.eq(0)
|
||||
)
|
||||
]
|
||||
program_mem_port = program_mem.get_port()
|
||||
self.specials += program_mem_port
|
||||
self.comb += program_mem_port.adr.eq(pc_next)
|
||||
|
||||
s = 0
|
||||
opcode = Signal(pd.opcode_bits)
|
||||
self.comb += opcode.eq(program_mem_port.dat_r[s:s+pd.opcode_bits])
|
||||
s += pd.opcode_bits
|
||||
r0 = Signal(pd.reg_bits)
|
||||
self.comb += r0.eq(program_mem_port.dat_r[s:s+pd.reg_bits])
|
||||
s += pd.reg_bits
|
||||
r1 = Signal(pd.reg_bits)
|
||||
self.comb += r1.eq(program_mem_port.dat_r[s:s+pd.reg_bits])
|
||||
s += pd.reg_bits
|
||||
exit = Signal(pd.reg_bits)
|
||||
self.comb += exit.eq(program_mem_port.dat_r[s:s+pd.reg_bits])
|
||||
|
||||
data_read_port0 = data_mem0.get_port()
|
||||
data_read_port1 = data_mem1.get_port()
|
||||
self.specials += data_read_port0, data_read_port1
|
||||
self.comb += [
|
||||
data_read_port0.adr.eq(r0),
|
||||
data_read_port1.adr.eq(r1)
|
||||
]
|
||||
|
||||
data_write_port = data_mem0.get_port(write_capable=True)
|
||||
data_write_port_dup = data_mem1.get_port(write_capable=True)
|
||||
self.specials += data_write_port, data_write_port_dup
|
||||
self.comb += [
|
||||
data_write_port_dup.we.eq(data_write_port.we),
|
||||
data_write_port_dup.adr.eq(data_write_port.adr),
|
||||
data_write_port_dup.dat_w.eq(data_write_port.dat_w),
|
||||
data_write_port.adr.eq(exit)
|
||||
]
|
||||
|
||||
nop = NopUnit(pd.data_width)
|
||||
adder = OpUnit(operator.add, pd.data_width, 1)
|
||||
subtractor = OpUnit(operator.sub, pd.data_width, 1)
|
||||
if pd.multiplier_shifts:
|
||||
if len(pd.multiplier_shifts) != 1:
|
||||
raise NotImplementedError
|
||||
multiplier = OpUnit(lambda a, b: a * b >> pd.multiplier_shifts[0],
|
||||
pd.data_width, pd.multiplier_stages, op_data_width=2*pd.data_width)
|
||||
else:
|
||||
multiplier = NopUnit(pd.data_width)
|
||||
minu = SelectUnit(operator.lt, pd.data_width)
|
||||
maxu = SelectUnit(operator.gt, pd.data_width)
|
||||
copier = CopyUnit(pd.data_width)
|
||||
inu = InputUnit(pd.data_width, self.input_stb, self.input)
|
||||
outu = OutputUnit(pd.data_width, self.output_stb, self.output)
|
||||
units = [nop, adder, subtractor, multiplier, minu, maxu, copier, inu, outu]
|
||||
self.submodules += units
|
||||
|
||||
for unit in units:
|
||||
self.sync += unit.stb_i.eq(0)
|
||||
self.comb += [
|
||||
unit.i0.eq(data_read_port0.dat_r),
|
||||
unit.i1.eq(data_read_port1.dat_r),
|
||||
If(unit.stb_o,
|
||||
data_write_port.we.eq(1),
|
||||
data_write_port.dat_w.eq(unit.o)
|
||||
)
|
||||
]
|
||||
|
||||
decode_table = [
|
||||
(NopIsn.opcode, nop),
|
||||
(AddIsn.opcode, adder),
|
||||
(SubIsn.opcode, subtractor),
|
||||
(MulShiftIsn.opcode, multiplier),
|
||||
(MulShiftIsn.opcode + 1, multiplier),
|
||||
(MinIsn.opcode, minu),
|
||||
(MaxIsn.opcode, maxu),
|
||||
(CopyIsn.opcode, copier),
|
||||
(InputIsn.opcode, inu),
|
||||
(OutputIsn.opcode, outu)
|
||||
]
|
||||
for allocated_opcode, unit in decode_table:
|
||||
self.sync += If(pc_en & (opcode == allocated_opcode), unit.stb_i.eq(1))
|
||||
|
||||
fsm = FSM()
|
||||
self.submodules += fsm
|
||||
fsm.act("IDLE",
|
||||
pc_en.eq(0),
|
||||
NextValue(inu.buffer, self.input),
|
||||
If(self.input_stb, NextState("PROCESSING"))
|
||||
)
|
||||
fsm.act("PROCESSING",
|
||||
self.busy.eq(1),
|
||||
pc_en.eq(1),
|
||||
If(opcode == EndIsn.opcode,
|
||||
pc_en.eq(0),
|
||||
NextState("IDLE")
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
def make(function, **kwargs):
|
||||
proc = Processor(**kwargs)
|
||||
cp = compile(proc, function)
|
||||
cp.dimension_processor()
|
||||
return proc.implement(cp.encode(), cp.data)
|
@ -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),
|
||||
),
|
||||
]
|
@ -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
|
@ -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)]
|
@ -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
|
@ -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
|
@ -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()
|
@ -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)
|
@ -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()
|
||||
|
@ -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
|
||||
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()
|
||||
|
@ -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)),
|
||||
|
@ -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(
|
||||
|
@ -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")
|
||||
|
||||
|
@ -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),
|
||||
|
@ -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))
|
@ -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"))
|
||||
|
@ -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))]))
|
||||
|
||||
|
||||
|
@ -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,
|
||||
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_cdr(
|
||||
FIFOs(lane_count, fifo_depth,
|
||||
layouts.fifo_payload(channels), mode, report_buffer_space))
|
||||
self.submodules.gates = gates_cdr(
|
||||
Gates(lane_count, seqn_width,
|
||||
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 = output_driver_cdr(
|
||||
OutputDriver(channels, glbl_fine_ts_width, lane_count, seqn_width))
|
||||
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)
|
||||
|
@ -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:
|
||||
|
@ -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.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)
|
||||
]
|
||||
|
@ -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(
|
||||
|
@ -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,24 +532,7 @@ 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"),
|
||||
@ -601,21 +546,20 @@ class SatelliteBase(BaseSoC):
|
||||
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
|
||||
|
||||
|
@ -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):
|
||||
|
@ -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")
|
||||
|
@ -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()
|
@ -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()
|
@ -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()
|
@ -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):
|
||||
|
@ -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)
|
||||
|
@ -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(
|
||||
|
@ -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)
|
||||
|
@ -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]
|
||||
|
@ -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):
|
||||
|
@ -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)
|
||||
|
@ -1,158 +0,0 @@
|
||||
import unittest
|
||||
|
||||
import numpy as np
|
||||
|
||||
from migen import *
|
||||
|
||||
from artiq.gateware.drtio.wrpll.ddmtd import Collector
|
||||
from artiq.gateware.drtio.wrpll import thls, filters
|
||||
|
||||
|
||||
class HelperChainTB(Module):
|
||||
def __init__(self, N):
|
||||
self.tag_ref = Signal(N)
|
||||
self.input_stb = Signal()
|
||||
self.adpll = Signal((24, True))
|
||||
self.out_stb = Signal()
|
||||
|
||||
###
|
||||
|
||||
self.submodules.collector = Collector(N)
|
||||
self.submodules.loop_filter = thls.make(filters.helper, data_width=48)
|
||||
|
||||
self.comb += [
|
||||
self.collector.tag_ref.eq(self.tag_ref),
|
||||
self.collector.ref_stb.eq(self.input_stb),
|
||||
self.collector.main_stb.eq(self.input_stb),
|
||||
self.loop_filter.input.eq(self.collector.out_helper << 22),
|
||||
self.loop_filter.input_stb.eq(self.collector.out_stb),
|
||||
self.adpll.eq(self.loop_filter.output),
|
||||
self.out_stb.eq(self.loop_filter.output_stb),
|
||||
]
|
||||
|
||||
|
||||
class TestDSP(unittest.TestCase):
|
||||
def test_main_collector(self):
|
||||
N = 2
|
||||
collector = Collector(N=N)
|
||||
# check collector phase unwrapping
|
||||
tags = [(0, 0, 0),
|
||||
(0, 1, 1),
|
||||
(2, 1, -1),
|
||||
(3, 1, -2),
|
||||
(0, 1, -3),
|
||||
(1, 1, -4),
|
||||
(2, 1, -5),
|
||||
(3, 1, -6),
|
||||
(3, 3, -4),
|
||||
(0, 0, -4),
|
||||
(0, 1, -3),
|
||||
(0, 2, -2),
|
||||
(0, 3, -1),
|
||||
(0, 0, 0)]
|
||||
for i in range(10):
|
||||
tags.append((i % (2**N), (i+1) % (2**N), 1))
|
||||
|
||||
def generator():
|
||||
for tag_ref, tag_main, out in tags:
|
||||
yield collector.tag_ref.eq(tag_ref)
|
||||
yield collector.tag_main.eq(tag_main)
|
||||
yield collector.main_stb.eq(1)
|
||||
yield collector.ref_stb.eq(1)
|
||||
|
||||
yield
|
||||
|
||||
yield collector.main_stb.eq(0)
|
||||
yield collector.ref_stb.eq(0)
|
||||
|
||||
while not (yield collector.out_stb):
|
||||
yield
|
||||
|
||||
out_main = yield collector.out_main
|
||||
self.assertEqual(out_main, out)
|
||||
|
||||
run_simulation(collector, generator())
|
||||
|
||||
def test_helper_collector(self):
|
||||
N = 3
|
||||
collector = Collector(N=N)
|
||||
# check collector phase unwrapping
|
||||
tags = [((2**N - 1 - tag) % (2**N), -1) for tag in range(20)]
|
||||
tags += [((tags[-1][0] + 1 + tag) % (2**N), 1) for tag in range(20)]
|
||||
tags += [((tags[-1][0] - 2 - 2*tag) % (2**N), -2) for tag in range(20)]
|
||||
|
||||
def generator():
|
||||
for tag_ref, out in tags:
|
||||
yield collector.tag_ref.eq(tag_ref)
|
||||
yield collector.main_stb.eq(1)
|
||||
yield collector.ref_stb.eq(1)
|
||||
|
||||
yield
|
||||
|
||||
yield collector.main_stb.eq(0)
|
||||
yield collector.ref_stb.eq(0)
|
||||
|
||||
while not (yield collector.out_stb):
|
||||
yield
|
||||
|
||||
out_helper = yield collector.out_helper
|
||||
self.assertEqual(out_helper, out)
|
||||
|
||||
run_simulation(collector, generator())
|
||||
|
||||
# test helper collector + filter against output from MATLAB model
|
||||
def test_helper_chain(self):
|
||||
pll = HelperChainTB(15)
|
||||
|
||||
initial_helper_out = -8000
|
||||
ref_tags = np.array([
|
||||
24778, 16789, 8801, 814, 25596, 17612, 9628, 1646,
|
||||
26433, 18453, 10474, 2496, 27287, 19311, 11337, 3364, 28160,
|
||||
20190, 12221, 4253, 29054, 21088, 13124, 5161, 29966, 22005,
|
||||
14045, 6087, 30897, 22940, 14985, 7031, 31847, 23895, 15944,
|
||||
7995, 47, 24869, 16923, 8978, 1035, 25861, 17920, 9981,
|
||||
2042, 26873, 18937, 11002, 3069, 27904, 19973, 12042, 4113,
|
||||
28953, 21026, 13100, 5175, 30020, 22098, 14177, 6257, 31106,
|
||||
23189, 15273, 7358, 32212, 24300, 16388, 8478, 569, 25429,
|
||||
17522, 9617, 1712, 26577, 18675, 10774, 2875, 27745, 19848,
|
||||
11951, 4056, 28930, 21038, 13147, 5256, 30135, 22247, 14361,
|
||||
6475, 31359, 23476, 15595, 7714, 32603, 24725, 16847, 8971,
|
||||
1096
|
||||
])
|
||||
adpll_sim = np.array([
|
||||
8, 24, 41, 57, 74, 91, 107, 124, 140, 157, 173,
|
||||
190, 206, 223, 239, 256, 273, 289, 306, 322, 339, 355,
|
||||
372, 388, 405, 421, 438, 454, 471, 487, 504, 520, 537,
|
||||
553, 570, 586, 603, 619, 636, 652, 668, 685, 701, 718,
|
||||
734, 751, 767, 784, 800, 817, 833, 850, 866, 882, 899,
|
||||
915, 932, 948, 965, 981, 998, 1014, 1030, 1047, 1063, 1080,
|
||||
1096, 1112, 1129, 1145, 1162, 1178, 1194, 1211, 1227, 1244, 1260,
|
||||
1276, 1293, 1309, 1326, 1342, 1358, 1375, 1391, 1407, 1424, 1440,
|
||||
1457, 1473, 1489, 1506, 1522, 1538, 1555, 1571, 1587, 1604, 1620,
|
||||
1636])
|
||||
|
||||
def sim():
|
||||
yield pll.collector.out_helper.eq(initial_helper_out)
|
||||
for ref_tag, adpll_matlab in zip(ref_tags, adpll_sim):
|
||||
# feed collector
|
||||
yield pll.tag_ref.eq(int(ref_tag))
|
||||
yield pll.input_stb.eq(1)
|
||||
|
||||
yield
|
||||
|
||||
yield pll.input_stb.eq(0)
|
||||
|
||||
while not (yield pll.collector.out_stb):
|
||||
yield
|
||||
|
||||
tag_diff = yield pll.collector.out_helper
|
||||
|
||||
while not (yield pll.loop_filter.output_stb):
|
||||
yield
|
||||
|
||||
adpll_migen = yield pll.adpll
|
||||
self.assertEqual(adpll_migen, adpll_matlab)
|
||||
|
||||
yield
|
||||
|
||||
run_simulation(pll, [sim()])
|
@ -1,55 +0,0 @@
|
||||
import unittest
|
||||
|
||||
from migen import *
|
||||
|
||||
from artiq.gateware.drtio.wrpll import thls
|
||||
|
||||
|
||||
a = 0
|
||||
|
||||
def simple_test(x):
|
||||
global a
|
||||
a = a + (x*4 >> 1)
|
||||
return a
|
||||
|
||||
|
||||
class TestTHLS(unittest.TestCase):
|
||||
def test_thls(self):
|
||||
global a
|
||||
|
||||
proc = thls.Processor()
|
||||
a = 0
|
||||
cp = thls.compile(proc, simple_test)
|
||||
print("Program:")
|
||||
cp.pretty_print()
|
||||
cp.dimension_processor()
|
||||
print("Encoded program:", cp.encode())
|
||||
proc_impl = proc.implement(cp.encode(), cp.data)
|
||||
|
||||
def send_values(values):
|
||||
for value in values:
|
||||
yield proc_impl.input.eq(value)
|
||||
yield proc_impl.input_stb.eq(1)
|
||||
yield
|
||||
yield proc_impl.input.eq(0)
|
||||
yield proc_impl.input_stb.eq(0)
|
||||
yield
|
||||
while (yield proc_impl.busy):
|
||||
yield
|
||||
@passive
|
||||
def receive_values(callback):
|
||||
while True:
|
||||
while not (yield proc_impl.output_stb):
|
||||
yield
|
||||
callback((yield proc_impl.output))
|
||||
yield
|
||||
|
||||
send_list = [42, 40, 10, 10]
|
||||
receive_list = []
|
||||
|
||||
run_simulation(proc_impl, [send_values(send_list), receive_values(receive_list.append)])
|
||||
print("Execution:", send_list, "->", receive_list)
|
||||
|
||||
a = 0
|
||||
expected_list = [simple_test(x) for x in send_list]
|
||||
self.assertEqual(receive_list, expected_list)
|
@ -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()
|
@ -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
|
||||
+++++++++++++++++++++++++++++++++++++
|
||||
|
||||
|
@ -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 |
14
flake.nix
14
flake.nix
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user