remove parts that won't initially be supported by nac3

This commit is contained in:
Sebastien Bourdeauducq 2022-02-25 20:02:36 +08:00
parent 404811cd5c
commit 41c597a707
55 changed files with 13 additions and 9936 deletions

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

@ -134,7 +134,7 @@
"properties": {
"type": {
"type": "string",
"enum": ["dio", "urukul", "novogorny", "sampler", "suservo", "zotino", "grabber", "mirny", "fastino", "phaser", "hvamp"]
"enum": ["dio", "urukul", "sampler", "suservo", "zotino", "grabber", "mirny", "fastino", "phaser", "hvamp"]
},
"board": {
"type": "string"
@ -230,50 +230,6 @@
},
"required": ["ports"]
}
}, {
"title": "Novogorny",
"if": {
"properties": {
"type": {
"const": "novogorny"
}
}
},
"then": {
"properties": {
"ports": {
"type": "array",
"items": {
"type": "integer"
},
"minItems": 1,
"maxItems": 1
}
},
"required": ["ports"]
}
}, {
"title": "Sampler",
"if": {
"properties": {
"type": {
"const": "sampler"
}
}
},
"then": {
"properties": {
"ports": {
"type": "array",
"items": {
"type": "integer"
},
"minItems": 1,
"maxItems": 2
}
},
"required": ["ports"]
}
}, {
"title": "SUServo",
"if": {

View File

@ -1,51 +0,0 @@
# Definitions for using the "FMC DIO 32ch LVDS a" card with the VHDCI-EEM breakout v1.1
eem_fmc_connections = {
0: [0, 8, 2, 3, 4, 5, 6, 7],
1: [1, 9, 10, 11, 12, 13, 14, 15],
2: [17, 16, 24, 19, 20, 21, 22, 23],
3: [18, 25, 26, 27, 28, 29, 30, 31],
}
def shiftreg_bits(eem, out_pins):
"""
Returns the bits that have to be set in the FMC card direction
shift register for the given EEM.
Takes a set of pin numbers (0-7) at the EEM. Return values
of this function for different EEMs should be ORed together.
"""
r = 0
for i in range(8):
if i not in out_pins:
lvds_line = eem_fmc_connections[eem][i]
# lines are swapped in pairs to ease PCB routing
# at the shift register
shift = lvds_line ^ 1
r |= 1 << shift
return r
dio_bank0_out_pins = set(range(4))
dio_bank1_out_pins = set(range(4, 8))
urukul_out_pins = {
0, # clk
1, # mosi
3, 4, 5, # cs_n
6, # io_update
7, # dds_reset
}
urukul_aux_out_pins = {
4, # sw0
5, # sw1
6, # sw2
7, # sw3
}
zotino_out_pins = {
0, # clk
1, # mosi
3, 4, # cs_n
5, # ldac_n
7, # clr_n
}

View File

@ -1,174 +0,0 @@
from artiq.language.core import kernel, delay, portable
from artiq.language.units import ns
from artiq.coredevice import spi2 as spi
SPI_CONFIG = (0*spi.SPI_OFFLINE | 0*spi.SPI_END |
0*spi.SPI_INPUT | 0*spi.SPI_CS_POLARITY |
0*spi.SPI_CLK_POLARITY | 0*spi.SPI_CLK_PHASE |
0*spi.SPI_LSB_FIRST | 0*spi.SPI_HALF_DUPLEX)
SPI_CS_ADC = 1
SPI_CS_SR = 2
@portable
def adc_ctrl(channel=1, softspan=0b111, valid=1):
"""Build a LTC2335-16 control word"""
return (valid << 7) | (channel << 3) | softspan
@portable
def adc_softspan(data):
"""Return the softspan configuration index from a result packet"""
return data & 0x7
@portable
def adc_channel(data):
"""Return the channel index from a result packet"""
return (data >> 3) & 0x7
@portable
def adc_data(data):
"""Return the ADC value from a result packet"""
return (data >> 8) & 0xffff
@portable
def adc_value(data, v_ref=5.):
"""Convert a ADC result packet to SI units (Volt)"""
softspan = adc_softspan(data)
data = adc_data(data)
g = 625
if softspan & 4:
g *= 2
if softspan & 2:
h = 1 << 15
else:
h = 1 << 16
data = -(data & h) + (data & ~h)
if softspan & 1:
h *= 500
else:
h *= 512
v_per_lsb = v_ref*g/h
return data*v_per_lsb
class Novogorny:
"""Novogorny ADC.
Controls the LTC2335-16 8 channel ADC with SPI interface and
the switchable gain instrumentation amplifiers using a shift
register.
:param spi_device: SPI bus device name
:param cnv_device: CNV RTIO TTLOut channel name
:param div: SPI clock divider (default: 8)
:param gains: Initial value for PGIA gains shift register
(default: 0x0000). Knowledge of this state is not transferred
between experiments.
:param core_device: Core device name
"""
kernel_invariants = {"bus", "core", "cnv", "div", "v_ref"}
def __init__(self, dmgr, spi_device, cnv_device, div=8, gains=0x0000,
core_device="core"):
self.bus = dmgr.get(spi_device)
self.core = dmgr.get(core_device)
self.cnv = dmgr.get(cnv_device)
self.div = div
self.gains = gains
self.v_ref = 5. # 5 Volt reference
@kernel
def set_gain_mu(self, channel, gain):
"""Set instrumentation amplifier gain of a channel.
The four gain settings (0, 1, 2, 3) corresponds to gains of
(1, 10, 100, 1000) respectively.
:param channel: Channel index
:param gain: Gain setting
"""
gains = self.gains
gains &= ~(0b11 << (channel*2))
gains |= gain << (channel*2)
self.bus.set_config_mu(SPI_CONFIG | spi.SPI_END,
16, self.div, SPI_CS_SR)
self.bus.write(gains << 16)
self.gains = gains
@kernel
def configure(self, data):
"""Set up the ADC sequencer.
:param data: List of 8 bit control words to write into the sequencer
table.
"""
if len(data) > 1:
self.bus.set_config_mu(SPI_CONFIG,
8, self.div, SPI_CS_ADC)
for i in range(len(data) - 1):
self.bus.write(data[i] << 24)
self.bus.set_config_mu(SPI_CONFIG | spi.SPI_END,
8, self.div, SPI_CS_ADC)
self.bus.write(data[len(data) - 1] << 24)
@kernel
def sample_mu(self, next_ctrl=0):
"""Acquire a sample:
Perform a conversion and transfer the sample.
:param next_ctrl: ADC control word for the next sample
:return: The ADC result packet (machine units)
"""
self.cnv.pulse(40*ns) # t_CNVH
delay(560*ns) # t_CONV max
self.bus.set_config_mu(SPI_CONFIG | spi.SPI_INPUT | spi.SPI_END,
24, self.div, SPI_CS_ADC)
self.bus.write(next_ctrl << 24)
return self.bus.read()
@kernel
def sample(self, next_ctrl=0):
"""Acquire a sample
.. seealso:: :meth:`sample_mu`
:param next_ctrl: ADC control word for the next sample
:return: The ADC result packet (Volt)
"""
return adc_value(self.sample_mu(), self.v_ref)
@kernel
def burst_mu(self, data, dt_mu, ctrl=0):
"""Acquire a burst of samples.
If the burst is too long and the sample rate too high, there will be
RTIO input overflows.
High sample rates lead to gain errors since the impedance between the
instrumentation amplifier and the ADC is high.
:param data: List of data values to write result packets into.
In machine units.
:param dt: Sample interval in machine units.
:param ctrl: ADC control word to write during each result packet
transfer.
"""
self.bus.set_config_mu(SPI_CONFIG | spi.SPI_INPUT | spi.SPI_END,
24, self.div, SPI_CS_ADC)
for i in range(len(data)):
t0 = now_mu()
self.cnv.pulse(40*ns) # t_CNVH
delay(560*ns) # t_CONV max
self.bus.write(ctrl << 24)
at_mu(t0 + dt_mu)
for i in range(len(data)):
data[i] = self.bus.read()

View File

@ -1,372 +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.)
@kernel
def reset(self):
"""Re-establish initial conditions.
This clears all spline interpolators, accumulators and configuration
settings.
This method advances the timeline by the time required to perform all
7 writes to the configuration channel, plus 9 coarse RTIO cycles.
"""
self.config.set_div(0, 0)
self.config.set_clr(1, 1, 1)
self.config.set_iq_en(1, 0)
self.config.set_duc_min(-1.)
self.config.set_duc_max(1.)
self.config.set_out_min(-1.)
self.config.set_out_max(1.)
self.frequency0.set_mu(0)
coarse_cycle = int64(self.core.ref_multiplier)
delay_mu(coarse_cycle)
self.frequency1.set_mu(0)
delay_mu(coarse_cycle)
self.frequency2.set_mu(0)
delay_mu(coarse_cycle)
self.phase0.set_mu(0)
delay_mu(coarse_cycle)
self.phase1.set_mu(0)
delay_mu(coarse_cycle)
self.phase2.set_mu(0)
delay_mu(coarse_cycle)
self.amplitude1.set_mu(0)
delay_mu(coarse_cycle)
self.amplitude2.set_mu(0)
delay_mu(coarse_cycle)
self.offset.set_mu(0)
delay_mu(coarse_cycle)

View File

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

View File

@ -1,228 +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)
@portable(flags={"fast-math"})
def to_mu(self, value: TFloat) -> TInt32:
"""Convert floating point ``value`` from physical units to 32 bit
integer machine units."""
return int32(round(value*self.scale))
@portable(flags={"fast-math"})
def from_mu(self, value: TInt32) -> TFloat:
"""Convert 32 bit integer ``value`` from machine units to floating
point physical units."""
return value/self.scale
@portable(flags={"fast-math"})
def to_mu64(self, value: TFloat) -> TInt64:
"""Convert floating point ``value`` from physical units to 64 bit
integer machine units."""
return int64(round(value*self.scale))
@kernel
def set_mu(self, value: TInt32):
"""Set spline value (machine units).
:param value: Spline value in integer machine units.
"""
rtio_output(self.channel << 8, value)
@kernel(flags={"fast-math"})
def set(self, value: TFloat):
"""Set spline value.
:param value: Spline value relative to full-scale.
"""
if self.width > 32:
l = [int32(0)] * 2
self.pack_coeff_mu([self.to_mu64(value)], l)
rtio_output_wide(self.channel << 8, l)
else:
rtio_output(self.channel << 8, self.to_mu(value))
@kernel
def set_coeff_mu(self, value): # TList(TInt32)
"""Set spline raw values.
:param value: Spline packed raw values.
"""
rtio_output_wide(self.channel << 8, value)
@portable(flags={"fast-math"})
def pack_coeff_mu(self, coeff, packed): # TList(TInt64), TList(TInt32)
"""Pack coefficients into RTIO data
:param coeff: TList(TInt64) list of machine units spline coefficients.
Lowest (zeroth) order first. The coefficient list is zero-extended
by the RTIO gateware.
:param packed: TList(TInt32) list for packed RTIO data. Must be
pre-allocated. Length in bits is
``n*width + (n - 1)*n//2*time_width``
"""
pos = 0
for i in range(len(coeff)):
wi = self.width + i*self.time_width
ci = coeff[i]
while wi != 0:
j = pos//32
used = pos - 32*j
avail = 32 - used
if avail > wi:
avail = wi
cij = int32(ci)
if avail != 32:
cij &= (1 << avail) - 1
packed[j] |= cij << used
ci >>= avail
wi -= avail
pos += avail
@portable(flags={"fast-math"})
def coeff_to_mu(self, coeff, coeff64): # TList(TFloat), TList(TInt64)
"""Convert a floating point list of coefficients into a 64 bit
integer (preallocated).
:param coeff: TList(TFloat) list of coefficients in physical units.
:param coeff64: TList(TInt64) preallocated list of coefficients in
machine units.
"""
for i in range(len(coeff)):
vi = coeff[i] * self.scale
for j in range(i):
vi *= self.time_scale
ci = int64(round(vi))
coeff64[i] = ci
# artiq.wavesynth.coefficients.discrete_compensate:
if i == 2:
coeff64[1] += ci >> self.time_width + 1
elif i == 3:
coeff64[2] += ci >> self.time_width
coeff64[1] += ci // 6 >> 2*self.time_width
def coeff_as_packed_mu(self, coeff64):
"""Pack 64 bit integer machine units coefficients into 32 bit integer
RTIO data list.
This is a host-only method that can be used to generate packed
spline coefficient data to be frozen into kernels at compile time.
"""
n = len(coeff64)
width = n*self.width + (n - 1)*n//2*self.time_width
packed = [int32(0)] * ((width + 31)//32)
self.pack_coeff_mu(coeff64, packed)
return packed
def coeff_as_packed(self, coeff):
"""Convert floating point spline coefficients into 32 bit integer
packed data.
This is a host-only method that can be used to generate packed
spline coefficient data to be frozen into kernels at compile time.
"""
coeff64 = [int64(0)] * len(coeff)
self.coeff_to_mu(coeff, coeff64)
return self.coeff_as_packed_mu(coeff64)
@kernel(flags={"fast-math"})
def set_coeff(self, coeff): # TList(TFloat)
"""Set spline coefficients.
Missing coefficients (high order) are zero-extended byt the RTIO
gateware.
If more coefficients are supplied than the gateware supports the extra
coefficients are ignored.
:param value: List of floating point spline coefficients,
lowest order (constant) coefficient first. Units are the
unit of this spline's value times increasing powers of 1/s.
"""
n = len(coeff)
coeff64 = [int64(0)] * n
self.coeff_to_mu(coeff, coeff64)
width = n*self.width + (n - 1)*n//2*self.time_width
packed = [int32(0)] * ((width + 31)//32)
self.pack_coeff_mu(coeff64, packed)
self.set_coeff_mu(packed)
@kernel(flags={"fast-math"})
def smooth(self, start: TFloat, stop: TFloat, duration: TFloat,
order: TInt32):
"""Initiate an interpolated value change.
For zeroth order (step) interpolation, the step is at
``start + duration/2``.
First order interpolation corresponds to a linear value ramp from
``start`` to ``stop`` over ``duration``.
The third order interpolation is constrained to have zero first
order derivative at both `start` and `stop`.
For first order and third order interpolation (linear and cubic)
the interpolator needs to be stopped explicitly at the stop time
(e.g. by setting spline coefficient data or starting a new
:meth:`smooth` interpolation).
This method advances the timeline by ``duration``.
:param start: Initial value of the change. In physical units.
:param stop: Final value of the change. In physical units.
:param duration: Duration of the interpolation. In physical units.
:param order: Order of the interpolation. Only 0, 1,
and 3 are valid: step, linear, cubic.
"""
if order == 0:
delay(duration/2.)
self.set_coeff([stop])
delay(duration/2.)
elif order == 1:
self.set_coeff([start, (stop - start)/duration])
delay(duration)
elif order == 3:
v2 = 6.*(stop - start)/(duration*duration)
self.set_coeff([start, 0., v2, -2.*v2/duration])
delay(duration)
else:
raise ValueError("Invalid interpolation order. "
"Supported orders are: 0, 1, 3.")

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -175,8 +175,6 @@ 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;

View File

@ -39,10 +39,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]); }
}
@ -54,10 +50,6 @@ pub fn get_adresses() -> NetAddresses {
_ => {
#[cfg(soc_platform = "kasli")]
{ ipv4_addr = IpAddress::v4(192, 168, 1, 70); }
#[cfg(soc_platform = "sayma_amc")]
{ ipv4_addr = IpAddress::v4(192, 168, 1, 60); }
#[cfg(soc_platform = "metlino")]
{ ipv4_addr = IpAddress::v4(192, 168, 1, 65); }
#[cfg(soc_platform = "kc705")]
{ ipv4_addr = IpAddress::v4(192, 168, 1, 50); }
}

View File

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

View File

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

View File

@ -172,7 +172,7 @@ fn setup_si5324_as_synthesizer(cfg: RtioClock) {
crystal_ref: true
}
},
RtioClock::Int_100 => { // 100MHz output, from crystal. Also used as reference for Sayma HMC830.
RtioClock::Int_100 => { // 100MHz output, from crystal
info!("using internal 100MHz RTIO clock");
si5324::FrequencySettings {
n1_hs : 9,
@ -218,8 +218,6 @@ fn setup_si5324_as_synthesizer(cfg: RtioClock) {
let si5324_ref_input = si5324::Input::Ckin1;
#[cfg(all(soc_platform = "kasli", not(hw_rev = "v2.0")))]
let si5324_ref_input = si5324::Input::Ckin2;
#[cfg(soc_platform = "metlino")]
let si5324_ref_input = si5324::Input::Ckin2;
#[cfg(soc_platform = "kc705")]
let si5324_ref_input = si5324::Input::Ckin2;
si5324::setup(&si5324_settings, si5324_ref_input).expect("cannot initialize Si5324");
@ -233,8 +231,6 @@ pub fn init() {
let si5324_ext_input = si5324::Input::Ckin1;
#[cfg(all(soc_platform = "kasli", not(hw_rev = "v2.0")))]
let si5324_ext_input = si5324::Input::Ckin2;
#[cfg(soc_platform = "metlino")]
let si5324_ext_input = si5324::Input::Ckin2;
#[cfg(soc_platform = "kc705")]
let si5324_ext_input = si5324::Input::Ckin2;
match clock_cfg {

View File

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

View File

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

View File

@ -16,15 +16,9 @@ use board_artiq::si5324;
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 {
@ -298,43 +292,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(())
@ -529,17 +486,6 @@ pub extern fn main() -> i32 {
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");
}
}
#[cfg(has_drtio_routing)]
let mut repeaters = [repeater::Repeater::default(); csr::DRTIOREP.len()];
#[cfg(not(has_drtio_routing))]

View File

@ -309,34 +309,6 @@ class PeripheralManager:
return next(channel)
def process_novogorny(self, rtio_offset, peripheral):
self.gen("""
device_db["spi_{name}_adc"] = {{
"type": "local",
"module": "artiq.coredevice.spi2",
"class": "SPIMaster",
"arguments": {{"channel": 0x{adc_channel:06x}}}
}}
device_db["ttl_{name}_cnv"] = {{
"type": "local",
"module": "artiq.coredevice.ttl",
"class": "TTLOut",
"arguments": {{"channel": 0x{cnv_channel:06x}}},
}}
device_db["{name}"] = {{
"type": "local",
"module": "artiq.coredevice.novogorny",
"class": "Novogorny",
"arguments": {{
"spi_adc_device": "spi_{name}_adc",
"cnv_device": "ttl_{name}_cnv"
}}
}}""",
name=self.get_name("novogorny"),
adc_channel=rtio_offset,
cnv_channel=rtio_offset + 1)
return 2
def process_sampler(self, rtio_offset, peripheral):
self.gen("""
device_db["spi_{name}_adc"] = {{

View File

@ -25,12 +25,10 @@ def get_argparser():
Valid actions:
* gateware: write main gateware bitstream to flash
* rtm_gateware: write RTM gateware bitstream to flash
* bootloader: write bootloader to flash
* storage: write storage image to flash
* firmware: write firmware to flash
* load: load main gateware bitstream into device (volatile but fast)
* rtm_load: load RTM gateware bitstream into device
* erase: erase flash memory
* start: trigger the target to (re)load its gateware bitstream from flash
@ -61,7 +59,7 @@ Prerequisites:
help="SSH host to jump through")
parser.add_argument("-t", "--target", default="kasli",
help="target board, default: %(default)s, one of: "
"kasli sayma metlino kc705")
"kasli kc705")
parser.add_argument("-I", "--preinit-command", default=[], action="append",
help="add a pre-initialization OpenOCD command. "
"Useful for selecting a board when several are connected.")
@ -69,8 +67,6 @@ Prerequisites:
parser.add_argument("-d", "--dir", default=None, help="look for board binaries in this directory")
parser.add_argument("--srcbuild", help="board binaries directory is laid out as a source build tree",
default=False, action="store_true")
parser.add_argument("--no-rtm-jtag", help="do not attempt JTAG to the RTM",
default=False, action="store_true")
parser.add_argument("action", metavar="ACTION", nargs="*",
default=[],
help="actions to perform, default: flash everything")
@ -229,76 +225,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)
@ -311,21 +237,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),
@ -336,32 +247,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:
@ -372,7 +272,7 @@ 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, \
@ -380,12 +280,6 @@ def main():
if header:
bin_file.write(b"\x00"*8)
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
@ -394,11 +288,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)
@ -418,22 +307,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)

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -275,53 +275,6 @@ class Sampler(_EEM):
target.specials += DifferentialOutput(1, sdr.p, sdr.n)
class Novogorny(_EEM):
@staticmethod
def io(eem, iostandard):
return [
("novogorny{}_spi_p".format(eem), 0,
Subsignal("clk", Pins(_eem_pin(eem, 0, "p"))),
Subsignal("mosi", Pins(_eem_pin(eem, 1, "p"))),
Subsignal("miso", Pins(_eem_pin(eem, 2, "p"))),
Subsignal("cs_n", Pins(
_eem_pin(eem, 3, "p"), _eem_pin(eem, 4, "p"))),
iostandard(eem),
),
("novogorny{}_spi_n".format(eem), 0,
Subsignal("clk", Pins(_eem_pin(eem, 0, "n"))),
Subsignal("mosi", Pins(_eem_pin(eem, 1, "n"))),
Subsignal("miso", Pins(_eem_pin(eem, 2, "n"))),
Subsignal("cs_n", Pins(
_eem_pin(eem, 3, "n"), _eem_pin(eem, 4, "n"))),
iostandard(eem),
),
] + [
("novogorny{}_{}".format(eem, sig), 0,
Subsignal("p", Pins(_eem_pin(j, i, "p"))),
Subsignal("n", Pins(_eem_pin(j, i, "n"))),
iostandard(j)
) for i, j, sig in [
(5, eem, "cnv"),
(6, eem, "busy"),
(7, eem, "scko"),
]
]
@classmethod
def add_std(cls, target, eem, ttl_out_cls, iostandard=default_iostandard):
cls.add_extension(target, eem, iostandard=iostandard)
phy = spi2.SPIMaster(target.platform.request("novogorny{}_spi_p".format(eem)),
target.platform.request("novogorny{}_spi_n".format(eem)))
target.submodules += phy
target.rtio_channels.append(rtio.Channel.from_phy(phy, ififo_depth=16))
pads = target.platform.request("novogorny{}_cnv".format(eem))
phy = ttl_out_cls(pads.p, pads.n)
target.submodules += phy
target.rtio_channels.append(rtio.Channel.from_phy(phy))
class Zotino(_EEM):
@staticmethod
def io(eem, iostandard):

View File

@ -34,13 +34,6 @@ def peripheral_urukul(module, peripheral, **kwargs):
sync_gen_cls, **kwargs)
def peripheral_novogorny(module, peripheral, **kwargs):
if len(peripheral["ports"]) != 1:
raise ValueError("wrong number of ports")
eem.Novogorny.add_std(module, peripheral["ports"][0],
ttl_serdes_7series.Output_8X, **kwargs)
def peripheral_sampler(module, peripheral, **kwargs):
if len(peripheral["ports"]) == 1:
port, port_aux = peripheral["ports"][0], None
@ -120,7 +113,6 @@ def peripheral_hvamp(module, peripheral, **kwargs):
peripheral_processors = {
"dio": peripheral_dio,
"urukul": peripheral_urukul,
"novogorny": peripheral_novogorny,
"sampler": peripheral_sampler,
"suservo": peripheral_suservo,
"zotino": peripheral_zotino,

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,112 +0,0 @@
import numpy as np
import matplotlib.pyplot as plt
from migen import *
from migen.fhdl import verilog
from artiq.gateware.dsp import fir
class Transfer(Module):
def __init__(self, dut):
self.submodules.dut = dut
def drive(self, x):
for xi in x.reshape(-1, self.dut.parallelism):
yield [ij.eq(int(xj)) for ij, xj in zip(self.dut.i, xi)]
yield
def record(self, y):
for i in range(self.dut.latency):
yield
for yi in y.reshape(-1, self.dut.parallelism):
yield
yi[:] = (yield from [(yield o) for o in self.dut.o])
def run(self, samples, amplitude=1., seed=None):
if seed is not None:
np.random.seed(seed)
w = 2**(self.dut.width - 1) - 1
x = np.round(np.random.uniform(
-amplitude*w, amplitude*w, samples))
y = self.run_data(x)
x /= w
y /= w
return x, y
def run_data(self, x):
y = np.empty_like(x)
run_simulation(self, [self.drive(x), self.record(y)],
vcd_name="fir.vcd")
return y
def analyze(self, x, y):
fig, ax = plt.subplots(3)
ax[0].plot(x, "c-.", label="input")
ax[0].plot(y, "r-", label="output")
ax[0].legend(loc="right")
ax[0].set_xlabel("time (1/fs)")
ax[0].set_ylabel("signal")
n = len(x)
w = np.hanning(n)
x = (x.reshape(-1, n)*w).sum(0)
y = (y.reshape(-1, n)*w).sum(0)
t = (np.fft.rfft(y)/np.fft.rfft(x))
f = np.fft.rfftfreq(n)*2
fmin = f[1]
ax[1].plot(f, 20*np.log10(np.abs(t)), "r-")
ax[1].set_ylim(-70, 3)
ax[1].set_xlim(fmin, 1.)
# ax[1].set_xscale("log")
ax[1].set_xlabel("frequency (fs/2)")
ax[1].set_ylabel("magnitude (dB)")
ax[1].grid(True)
ax[2].plot(f, np.rad2deg(np.angle(t)), "r-")
ax[2].set_xlim(fmin, 1.)
# ax[2].set_xscale("log")
ax[2].set_xlabel("frequency (fs/2)")
ax[2].set_ylabel("phase (deg)")
ax[2].grid(True)
return fig
class UpTransfer(Transfer):
def drive(self, x):
x = x.reshape(-1, len(self.dut.o))
x[:, 1:] = 0
for xi in x:
yield self.dut.i.eq(int(xi[0]))
yield
def record(self, y):
for i in range(self.dut.latency):
yield
for yi in y.reshape(-1, len(self.dut.o)):
yield
yi[:] = (yield from [(yield o) for o in self.dut.o])
def _main():
if True:
coeff = fir.halfgen4_cascade(2, width=.4, order=8)
dut = fir.ParallelHBFUpsampler(coeff, width=16)
# print(verilog.convert(dut, ios=set([dut.i] + dut.o)))
tb = UpTransfer(dut)
else:
coeff = fir.halfgen4(.4/2, 8)
dut = fir.ParallelFIR(coeff, parallelism=4, width=16)
# print(verilog.convert(dut, ios=set(dut.i + dut.o)))
tb = Transfer(dut)
if True:
x, y = tb.run(samples=1 << 10, amplitude=.5, seed=0x1234567)
else:
x = np.zeros(100)
x[:50] = 1 << 8
x[50:] = 1 << 13
y = tb.run_data(x)
tb.analyze(x, y)
plt.show()
if __name__ == "__main__":
_main()

View File

@ -1,46 +0,0 @@
import numpy as np
from migen import *
from migen.fhdl.verilog import convert
from artiq.gateware.dsp.accu import Accu, PhasedAccu
from .tools import xfer
def read(o, n):
p = []
for i in range(n):
p.append((yield from [(yield pi) for pi in o.payload.flatten()]))
yield
return p
def _test_gen_accu(dut, o):
yield dut.o.ack.eq(1)
yield from xfer(dut, i=dict(p=0, f=1, clr=1))
o.extend((yield from read(dut.o, 8)))
yield from xfer(dut, i=dict(p=0, f=2, clr=0))
o.extend((yield from read(dut.o, 8)))
yield from xfer(dut, i=dict(p=0, f=2, clr=1))
o.extend((yield from read(dut.o, 8)))
yield from xfer(dut, i=dict(p=8, f=-1, clr=1))
o.extend((yield from read(dut.o, 8)))
yield from xfer(dut, i=dict(p=0, f=0, clr=1))
yield from xfer(dut, i=dict(p=1, f=0, clr=0))
o.extend((yield from read(dut.o, 8)))
def _test_accu():
dut = PhasedAccu(8, parallelism=8)
if False:
print(convert(dut))
else:
o = []
run_simulation(dut, _test_gen_accu(dut, o), vcd_name="accu.vcd")
o = np.array(o)
print(o)
if __name__ == "__main__":
_test_accu()

View File

@ -1,71 +0,0 @@
import unittest
import migen as mg
from artiq.gateware.dsp.tools import SatAddMixin
class DUT(mg.Module, SatAddMixin):
def __init__(self, width):
self.o = mg.Signal((width, True))
self.i0 = mg.Signal.like(self.o)
self.i1 = mg.Signal.like(self.o)
self.l0 = mg.Signal.like(self.o)
self.l1 = mg.Signal.like(self.o)
self.c = mg.Signal(2)
self.comb += self.o.eq(self.sat_add((self.i0, self.i1),
width=4, limits=(self.l0, self.l1), clipped=self.c))
class SatAddTest(unittest.TestCase):
def setUp(self):
self.dut = DUT(width=4)
# import migen.fhdl.verilog
# print(mg.fhdl.verilog.convert(self.dut))
def _sweep(self):
def gen():
for i0 in range(-8, 8):
yield self.dut.i0.eq(i0)
for i1 in range(-8, 8):
yield self.dut.i1.eq(i1)
yield
def rec():
l0 = yield self.dut.l0
l1 = yield self.dut.l1
for i in range(1 << 8):
i0 = yield self.dut.i0
i1 = yield self.dut.i1
o = yield self.dut.o
c = yield self.dut.c
full = i0 + i1
lim = full
clip = 0
if full < l0:
lim = l0
clip = 1
if full > l1:
lim = l1
clip = 2
with self.subTest(i0=i0, i1=i1):
self.assertEqual(lim, o)
self.assertEqual(clip, c)
yield
mg.run_simulation(self.dut, (gen(), rec()))
def test_inst(self):
pass
def test_run(self):
self._sweep()
def test_limits(self):
for l0 in -8, 0, 1, 7:
for l1 in -8, 0, 1, 7:
self.setUp()
self.dut.l0.reset = l0
self.dut.l1.reset = l1
with self.subTest(l0=l0, l1=l1):
self._sweep()

View File

@ -1,36 +0,0 @@
import numpy as np
from migen import *
from migen.fhdl.verilog import convert
from artiq.gateware.dsp import sawg
from artiq.gateware.test.dsp.tools import xfer
def _test_gen_dds(dut, o):
yield from xfer(dut,
a=dict(a0=10),
p=dict(a0=0),
f=dict(a0=1),
)
for i in range(256//dut.parallelism):
yield
o.append((yield from [(yield _) for _ in dut.xo]))
def _test_channel():
widths = sawg._Widths(t=8, a=4*8, p=8, f=16)
orders = sawg._Orders(a=4, p=1, f=2)
dut = sawg.SplineParallelDDS(widths, orders, parallelism=2)
if False:
print(convert(dut))
else:
o = []
run_simulation(dut, _test_gen_dds(dut, o), vcd_name="dds.vcd")
o = np.array(o)
print(o[:, :])
if __name__ == "__main__":
_test_channel()

View File

@ -1,255 +0,0 @@
import unittest
import migen as mg
from numpy import int32
from artiq.coredevice import sawg, spline
from artiq.language import (at_mu, now_mu, delay,
core as core_language)
from artiq.gateware.rtio.phy.sawg import Channel
from artiq.sim import devices as sim_devices, time as sim_time
class RTIOManager:
def __init__(self):
self.outputs = []
def rtio_output(self, target, data):
channel = target >> 8
addr = target & 0xff
self.outputs.append((now_mu(), channel, addr, data))
def rtio_output_wide(self, *args, **kwargs):
self.rtio_output(*args, **kwargs)
def delay_mu(self, t):
delay(t)
def patch(self, mod):
assert not hasattr(mod, "_saved")
mod._saved = {}
for name in "rtio_output rtio_output_wide delay_mu".split():
mod._saved[name] = getattr(mod, name, None)
setattr(mod, name, getattr(self, name))
def unpatch(self, mod):
mod.__dict__.update(mod._saved)
del mod._saved
class SAWGTest(unittest.TestCase):
def setUp(self):
core_language.set_time_manager(sim_time.Manager())
self.rtio_manager = RTIOManager()
self.rtio_manager.patch(spline)
self.rtio_manager.patch(sawg)
self.core = sim_devices.Core({})
self.core.coarse_ref_period = 20/3
self.core.ref_multiplier = 1
self.t = self.core.coarse_ref_period
self.channel = mg.ClockDomainsRenamer({"rio_phy": "sys"})(
Channel(width=16, parallelism=2))
self.driver = sawg.SAWG({"core": self.core}, channel_base=0,
parallelism=self.channel.parallelism)
def tearDown(self):
self.rtio_manager.unpatch(spline)
self.rtio_manager.unpatch(sawg)
def test_instantiate(self):
pass
def test_make_events(self):
d = self.driver
d.offset.set(.9)
delay(2*self.t)
d.frequency0.set(.1)
d.frequency1.set(.1)
delay(2*self.t)
d.offset.set(0)
v = int(round((1 << 48) * .1 * self.t))
self.assertEqual(
self.rtio_manager.outputs, [
(0., 1, 0, int(round(self.driver.offset.scale*.9))),
(2.*self.t, 8, 0, int(round(
(1 << self.driver.frequency0.width) *
self.t/self.channel.parallelism*.1))),
(2.*self.t, 3, 0, [int32(v), int32(v >> 32)]),
(4.*self.t, 1, 0, 0),
])
def run_channel(self, events):
def gen(dut, events):
c = 0
for time, channel, address, data in events:
time //= self.t
assert c <= time
while c < time:
yield
c += 1
for phy in dut.phys:
yield phy.rtlink.o.stb.eq(0)
rt = dut.phys[channel].rtlink.o
if isinstance(data, list):
data = sum(int(d) << (i*32) for i, d in enumerate(data))
yield rt.data.eq(int(data))
if hasattr(rt, "address"):
yield rt.address.eq(address)
yield rt.stb.eq(1)
assert not (yield rt.busy)
# print("{}: set ch {} to {}".format(time, channel, hex(data)))
def log(dut, data, n):
for i in range(n + dut.latency):
yield
data.append((yield from [(yield _) for _ in dut.o]))
data = []
# print(int(events[-1][0]) + 1)
mg.run_simulation(self.channel, [
gen(self.channel, events),
log(self.channel, data, int(events[-1][0]//self.t) + 1)],
vcd_name="dds.vcd")
return data
def test_run_channel(self):
self.test_make_events()
self.run_channel(self.rtio_manager.outputs)
def test_coeff(self):
import struct
# these get discrete_compensate
# [.1, .01, -.00001], [.1, .01, .00001, -.000000001]
for v in [-.1], [.1, -.01]:
ch = self.driver.offset
p = ch.coeff_as_packed(v)
t = ch.time_width
w = ch.width
p = [_ & 0xffffffff for _ in p]
p0 = [int(round(vi*ch.scale*ch.time_scale**i))
for i, vi in enumerate(v)]
p0 = [struct.pack("<" + "_bhiiqqqq"[(w + i*t)//8], vi
)[:(w + i*t)//8]
for i, vi in enumerate(p0)]
p0 = b"".join(p0)
if len(p0) % 4:
p0 += b"\x00"*(4 - len(p0) % 4)
p0 = list(struct.unpack("<" + "I"*((len(p0) + 3)//4), p0))
with self.subTest(v):
self.assertEqual(p, p0)
def test_linear(self):
d = self.driver
d.offset.set_coeff_mu([100, 10])
delay(10*self.t)
d.offset.set_coeff([0])
delay(1*self.t)
out = self.run_channel(self.rtio_manager.outputs)
out = out[self.channel.latency + self.channel.u.latency:][:11]
for i in range(len(out) - 1):
with self.subTest(i):
v = 100 + i*10
self.assertEqual(out[i], [v, v])
self.assertEqual(out[-1], [0, 0])
def test_pack(self):
ch = self.driver.offset
self.assertEqual(ch.coeff_as_packed_mu([1]), [1])
self.assertEqual(ch.coeff_as_packed_mu([1, 1 << 16]), [1, 1])
self.assertEqual(ch.coeff_as_packed_mu([1, 1 << 32]), [1, 0])
self.assertEqual(ch.coeff_as_packed_mu([0x1234, 0xa5a5a5a5]),
[0xa5a51234, 0xa5a5])
self.assertEqual(ch.coeff_as_packed_mu([1, 2, 3, 4]),
[0x20001, 0x30000, 0, 4, 0])
self.assertEqual(ch.coeff_as_packed_mu([-1, -2, -3, -4]),
[0xfffeffff, 0xfffdffff, -1, -4, -1])
self.assertEqual(ch.coeff_as_packed_mu([0, -1, 0, -1]),
[0xffff0000, 0x0000ffff, 0, -1, -1])
def test_smooth_linear(self):
ch = self.driver.offset
ch.smooth(.1, .2, 13*self.t, 1)
ch.set(.2)
delay(1*self.t)
out = self.run_channel(self.rtio_manager.outputs)
out = out[self.channel.latency + self.channel.u.latency:][:14]
a = int(round(.1*ch.scale))
da = int(round(.1*ch.scale*(1 << ch.width)//13))
for i in range(len(out) - 1):
with self.subTest(i):
v = a + (i*da >> ch.width)
self.assertEqual(out[i], [v, v])
a = int(round(.2*ch.scale))
self.assertEqual(out[-1], [a, a])
def test_smooth_cubic(self):
ch = self.driver.offset
ch.smooth(.1, .2, 13*self.t, 3)
ch.set(.2)
delay(1*self.t)
out = self.run_channel(self.rtio_manager.outputs)
out = out[self.channel.latency + self.channel.u.latency:][:14]
if False:
import matplotlib.pyplot as plt
plt.plot(sum(out, []))
plt.show()
@unittest.skip("needs artiq.sim.time.TimeManager tweak for "
"reverse timeline jumps")
def test_demo_2tone(self):
MHz = 1e-3
ns = 1.
self.sawg0 = self.driver
t_up = t_hold = t_down = 400*ns
a1 = .3
a2 = .4
order = 3
self.sawg0.frequency0.set(10*MHz)
self.sawg0.phase0.set(0.)
self.sawg0.frequency1.set(1*MHz)
self.sawg0.phase1.set(0.)
self.sawg0.frequency2.set(13*MHz)
self.sawg0.phase2.set(0.)
t = now_mu()
self.sawg0.amplitude1.smooth(.0, a1, t_up, order)
at_mu(t)
self.sawg0.amplitude2.smooth(.0, a2, t_up, order)
self.sawg0.amplitude1.set(a1)
self.sawg0.amplitude2.set(a2)
delay(t_hold)
t = now_mu()
self.sawg0.amplitude1.smooth(a1, .0, t_down, order)
at_mu(t)
self.sawg0.amplitude2.smooth(a2, .0, t_down, order)
self.sawg0.amplitude1.set(.0)
self.sawg0.amplitude2.set(.0)
out = self.run_channel(self.rtio_manager.outputs)
out = sum(out, [])
if True:
import matplotlib.pyplot as plt
plt.plot(out)
plt.show()
def test_fir_overflow(self):
MHz = 1e-3
ns = 1.
f1 = self.driver.frequency1
a1 = self.driver.amplitude1
p1 = self.driver.phase1
cfg = self.driver.config
f1.set(1*MHz)
a1.set(.99)
delay(100*ns)
p1.set(.5)
delay(100*ns)
a1.set(0)
out = self.run_channel(self.rtio_manager.outputs)
out = sum(out, [])
if False:
import matplotlib.pyplot as plt
plt.plot(out)
plt.show()

View File

@ -1,70 +0,0 @@
import numpy as np
from operator import or_
from migen import *
from migen.fhdl.verilog import convert
from artiq.gateware.rtio.phy.sawg import Channel
from .tools import rtio_xfer
def pack_tri(port, *v):
r = 0
w = 0
for vi, p in zip(v, port.payload.flatten()):
w += len(p)
r |= int(vi*(1 << w))
return r
def gen_rtio(dut):
yield
yield from rtio_xfer(
dut,
a1=pack_tri(dut.a1.a, .1),
f0=pack_tri(dut.b.f, .01234567),
f1=pack_tri(dut.a1.f, .01234567),
a2=pack_tri(dut.a1.a, .05),
f2=pack_tri(dut.a1.f, .00534567),
)
def gen_log(dut, o, n):
for i in range(3 + dut.latency):
yield
for i in range(n):
yield
o.append((yield from [(yield _) for _ in dut.o]))
#o.append([(yield dut.a1.xo[0])])
def _test_channel():
width = 16
dut = ClockDomainsRenamer({"rio_phy": "sys"})(
Channel(width=width, parallelism=4)
)
if False:
print(convert(dut))
return
o = []
run_simulation(
dut,
[gen_rtio(dut), gen_log(dut, o, 128)],
vcd_name="dds.vcd")
o = np.array(o)/(1 << (width - 1))
o = o.ravel()
np.savez_compressed("dds.npz", o=o)
import matplotlib.pyplot as plt
fig, ax = plt.subplots(2)
ax[0].step(np.arange(o.size), o)
ax[1].psd(o, 1 << 10, Fs=1, noverlap=1 << 9, scale_by_freq=False)
fig.savefig("dds.pdf")
plt.show()
if __name__ == "__main__":
_test_channel()

View File

@ -1,31 +0,0 @@
import numpy as np
from migen import *
from migen.fhdl.verilog import convert
from artiq.gateware.dsp.spline import Spline
from .tools import xfer
def _test_gen_spline(dut, o):
yield dut.o.ack.eq(1)
yield from xfer(dut, i=dict(a0=0, a1=1, a2=2))
for i in range(20):
yield
o.append((yield dut.o.a0))
def _test_spline():
dut = Spline(order=3, width=16, step=1)
if False:
print(convert(dut))
else:
o = []
run_simulation(dut, _test_gen_spline(dut, o), vcd_name="spline.vcd")
o = np.array(o)
print(o)
if __name__ == "__main__":
_test_spline()

View File

@ -1,49 +0,0 @@
def set_dict(e, **k):
for k, v in k.items():
if isinstance(v, dict):
yield from set_dict(getattr(e, k), **v)
else:
yield getattr(e, k).eq(v)
def xfer(dut, **kw):
ep = []
for e, v in kw.items():
e = getattr(dut, e)
yield from set_dict(e, **v)
ep.append(e)
for e in ep:
yield e.stb.eq(1)
while ep:
yield
for e in ep[:]:
if hasattr(e, "busy") and (yield e.busy):
raise ValueError(e, "busy")
if not hasattr(e, "ack") or (yield e.ack):
yield e.stb.eq(0)
ep.remove(e)
def szip(*iters):
active = {it: None for it in iters}
while active:
for it in list(active):
while True:
try:
val = it.send(active[it])
except StopIteration:
del active[it]
break
if val is None:
break
else:
active[it] = (yield val)
val = (yield None)
for it in active:
active[it] = val
def rtio_xfer(dut, **kwargs):
yield from szip(*(
xfer(dut.phys_named[k].rtlink, o={"data": v})
for k, v in kwargs.items()))

View File

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

View File

@ -1,127 +0,0 @@
# Copyright (C) 2014, 2015 Robert Jordens <jordens@gmail.com>
import unittest
from artiq.wavesynth import compute_samples
class TestSynthesizer(unittest.TestCase):
program = [
[
# frame 0
{
# frame 0, segment 0, line 0
"dac_divider": 1,
"duration": 100,
"channel_data": [
{
# channel 0
"dds": {"amplitude": [0.0, 0.0, 0.01],
"phase": [0.0, 0.0, 0.0005],
"clear": False}
}
],
"trigger": True
},
{
# frame 0, segment 0, line 1
"dac_divider": 1,
"duration": 100,
"channel_data": [
{
# channel 0
"dds": {"amplitude": [49.5, 1.0, -0.01],
"phase": [0.0, 0.05, 0.0005],
"clear": False}
}
],
"trigger": False
},
],
[
# frame 1
{
# frame 1, segment 0, line 0
"dac_divider": 1,
"duration": 100,
"channel_data": [
{
# channel 0
"dds": {"amplitude": [100.0, 0.0, -0.01],
"phase": [0.0, 0.1, -0.0005],
"clear": False}
}
],
"trigger": True
},
{
# frame 1, segment 0, line 1
"dac_divider": 1,
"duration": 100,
"channel_data": [
{
# channel 0
"dds": {"amplitude": [50.5, -1.0, 0.01],
"phase": [0.0, 0.05, -0.0005],
"clear": False}
}
],
"trigger": False
}
],
[
# frame 2
{
# frame 2, segment 0, line 0
"dac_divider": 1,
"duration": 84,
"channel_data": [
{
# channel 0
"dds": {"amplitude": [100.0],
"phase": [0.0, 0.05],
"clear": False}
}
],
"trigger": True
},
{
# frame 2, segment 1, line 0
"dac_divider": 1,
"duration": 116,
"channel_data": [
{
# channel 0
"dds": {"amplitude": [100.0],
"phase": [0.0, 0.05],
"clear": True}
}
],
"trigger": True
}
]
]
def setUp(self):
self.dev = compute_samples.Synthesizer(1, self.program)
self.t = list(range(600))
def drive(self):
s = self.dev
y = []
for f in 0, 2, None, 1:
if f is not None:
s.select(f)
y += s.trigger()[0]
x = list(range(600))
return x, y
def test_run(self):
x, y = self.drive()
@unittest.skip("manual/visual test")
def test_plot(self):
from matplotlib import pyplot as plt
x, y = self.drive()
plt.plot(x, y)
plt.show()

View File

@ -1,234 +0,0 @@
# Copyright (C) 2014, 2015 Robert Jordens <jordens@gmail.com>
import numpy as np
from scipy.interpolate import splrep, splev, spalde
class UnivariateMultiSpline:
"""Multidimensional wrapper around `scipy.interpolate.sp*` functions.
`scipy.inteprolate.splprep` is limited to 12 dimensions.
"""
def __init__(self, x, y, *, x0=None, order=4, **kwargs):
self.order = order
self.x = x
self.s = []
for i, yi in enumerate(y):
if x0 is not None:
yi = self.upsample_knots(x0[i], yi, x)
self.s.append(splrep(x, yi, k=order - 1, **kwargs))
def upsample_knots(self, x0, y0, x):
return splev(x, splrep(x0, y0, k=self.order - 1))
def lev(self, x, *a, **k):
return np.array([splev(x, si) for si in self.s])
def alde(self, x):
u = np.array([spalde(x, si) for si in self.s])
if len(x) == 1:
u = u[:, None, :]
return u
def __call__(self, x, use_alde=True):
if use_alde:
u = self.alde(x)[:, :, :self.order]
s = (len(self.s), len(x), self.order)
assert u.shape == s, (u.shape, s)
return u.transpose(2, 0, 1)
else:
return np.array([self.lev(x, der=i) for i in range(self.order)])
def pad_const(x, n, axis=0):
"""Prefix and postfix the array `x` by `n` repetitions of the first and
last value along `axis`.
"""
a = np.repeat(x.take([0], axis), n, axis)
b = np.repeat(x.take([-1], axis), n, axis)
xp = np.concatenate([a, x, b], axis)
s = list(x.shape)
s[axis] += 2*n
assert xp.shape == tuple(s), (x.shape, s, xp.shape)
return xp
def build_segment(durations, coefficients, target="bias",
variable="amplitude", compress=True):
"""Build a wavesynth-style segment from homogeneous duration and
coefficient data.
:param durations: 1D sequence of line durations.
:param coefficients: 3D array with shape `(n, m, len(durations))`,
with `n` being the interpolation order + 1 and `m` the number of
channels.
:param target: The target component of the channel to affect.
:param variable: The variable within the target component.
:param compress: If `True`, skip zero high order coefficients.
"""
for dxi, yi in zip(durations, coefficients.transpose()):
cd = []
for yij in yi:
cdj = []
for yijk in reversed(yij):
if cdj or abs(yijk) or not compress:
cdj.append(float(yijk))
cdj.reverse()
if not cdj:
cdj.append(float(yij[0]))
cd.append({target: {variable: cdj}})
yield {"duration": int(dxi), "channel_data": cd}
class CoefficientSource:
def crop_x(self, start, stop, num=2):
"""Return an array of valid sample positions.
This method needs to be overloaded if this `CoefficientSource`
does not support sampling at arbitrary positions or at arbitrary
density.
:param start: First sample position.
:param stop: Last sample position.
:param num: Number of samples between `start` and `stop`.
:return: Array of sample positions. `start` and `stop` should be
returned as the first and last value in the array respectively.
"""
return np.linspace(start, stop, num)
def scale_x(self, x, scale):
# TODO: This could be moved to the the Driver/Mediator code as it is
# device-specific.
"""Scale and round sample positions.
The sample times may need to be changed and/or decimated if
incompatible with hardware requirements.
:param x: Input sample positions in data space.
:param scale: Data space position to cycles conversion scale,
in units of x-units per clock cycle.
:return: `x_sample`, the rounded sample positions and `durations`, the
integer durations of the individual samples in cycles.
"""
t = np.rint(x/scale)
x_sample = t*scale
durations = np.diff(t).astype(int)
return x_sample, durations
def __call__(self, x, **kwargs):
"""Perform sampling and return coefficients.
:param x: Sample positions.
:return: `y` the array of coefficients. `y.shape == (order, n, len(x))`
with `n` being the number of channels."""
raise NotImplementedError
def get_segment(self, start, stop, scale, *, cutoff=1e-12,
target="bias", variable="amplitude"):
"""Build wavesynth segment.
:param start: see `crop_x()`.
:param stop: see `crop_x()`.
:param scale: see `scale_x()`.
:param cutoff: coefficient cutoff towards zero to compress data.
"""
x = self.crop_x(start, stop)
x_sample, durations = self.scale_x(x, scale)
coefficients = self(x_sample)
if len(x_sample) == 1 and start == stop:
coefficients = coefficients[:1]
# rescale coefficients accordingly
coefficients *= (scale*np.sign(durations))**np.arange(
coefficients.shape[0])[:, None, None]
if cutoff:
coefficients[np.fabs(coefficients) < cutoff] = 0
return build_segment(np.fabs(durations), coefficients, target=target,
variable=variable)
def extend_segment(self, segment, *args, **kwargs):
"""Extend a wavesynth segment.
See `get_segment()` for arguments.
"""
for line in self.get_segment(*args, **kwargs):
segment.add_line(**line)
class SplineSource(CoefficientSource):
def __init__(self, x, y, order=4, pad_dx=1.):
"""
:param x: 1D sample positions.
:param y: 2D sample values.
"""
self.x = np.asanyarray(x)
assert self.x.ndim == 1
self.y = np.asanyarray(y)
assert self.y.ndim == 2
if pad_dx is not None:
a = np.arange(-order, 0)*pad_dx + self.x[0]
b = self.x[-1] + np.arange(1, order + 1)*pad_dx
self.x = np.r_[a, self.x, b]
self.y = pad_const(self.y, order, axis=1)
assert self.y.shape[1] == self.x.shape[0]
self.spline = UnivariateMultiSpline(self.x, self.y, order=order)
def crop_x(self, start, stop):
ia, ib = np.searchsorted(self.x, (start, stop))
if start > stop:
x = self.x[ia - 1:ib - 1:-1]
else:
x = self.x[ia:ib]
return np.r_[start, x, stop]
def scale_x(self, x, scale, min_duration=1, min_length=20):
"""Enforce, round, and scale x to device-dependent values.
Due to minimum duration and/or minimum segment length constraints
this method may drop samples from `x_sample` to comply.
:param min_duration: Minimum duration of a line.
:param min_length: Minimum segment length to space triggers.
"""
# We want to only sample a spline at t_knot + epsilon
# where the highest order derivative has just jumped
# and is valid at least up to the next knot after t_knot.
#
# To ensure that we are on the correct side of a knot:
# * only ever increase t when rounding (for increasing t)
# * or only ever decrease it (for decreasing t)
t = x/scale
inc = np.diff(t) >= 0
inc = np.r_[inc, inc[-1]]
t = np.where(inc, np.ceil(t), np.floor(t))
dt = np.diff(t.astype(int))
valid = np.absolute(dt) >= min_duration
if not np.any(valid):
valid[0] = True
dt[0] = max(dt[0], min_length)
dt = dt[valid]
x_sample = t[:-1][valid]*scale
return x_sample, dt
def __call__(self, x):
return self.spline(x)
def discrete_compensate(c):
"""Compensate spline coefficients for discrete accumulators
Given continuous-time b-spline coefficients, this function
compensates for the effect of discrete time steps in the
target devices.
The compensation is performed in-place.
"""
l = len(c)
if l > 2:
c[1] += c[2]/2.
if l > 3:
c[1] += c[3]/6.
c[2] += c[3]
if l > 4:
raise ValueError("only third-order splines supported")

View File

@ -1,133 +0,0 @@
# Copyright (C) 2014, 2015 M-Labs Limited
# Copyright (C) 2014, 2015 Robert Jordens <jordens@gmail.com>
from copy import copy
from math import cos, pi
from artiq.wavesynth.coefficients import discrete_compensate
class Spline:
def __init__(self):
self.c = [0.0]
def set_coefficients(self, c):
if not c:
c = [0.]
self.c = copy(c)
discrete_compensate(self.c)
def next(self):
r = self.c[0]
for i in range(len(self.c) - 1):
self.c[i] += self.c[i + 1]
return r
class SplinePhase:
def __init__(self):
self.c = [0.0]
self.c0 = 0.0
def set_coefficients(self, c):
if not c:
c = [0.]
self.c0 = c[0]
c1p = c[1:]
discrete_compensate(c1p)
self.c[1:] = c1p
def clear(self):
self.c[0] = 0.0
def next(self):
r = self.c[0]
for i in range(len(self.c) - 1):
self.c[i] += self.c[i + 1]
self.c[i] %= 1.0
return r + self.c0
class DDS:
def __init__(self):
self.amplitude = Spline()
self.phase = SplinePhase()
def next(self):
return self.amplitude.next()*cos(2*pi*self.phase.next())
class Channel:
def __init__(self):
self.bias = Spline()
self.dds = DDS()
self.v = 0.
self.silence = False
def next(self):
v = self.bias.next() + self.dds.next()
if not self.silence:
self.v = v
return self.v
def set_silence(self, s):
self.silence = s
class TriggerError(Exception):
pass
class Synthesizer:
def __init__(self, nchannels, program):
self.channels = [Channel() for _ in range(nchannels)]
self.program = program
# line_iter is None: "wait for segment selection" state
# otherwise: iterator on the current position in the frame
self.line_iter = None
def select(self, selection):
if self.line_iter is not None:
raise TriggerError("a frame is already selected")
self.line_iter = iter(self.program[selection])
self.line = next(self.line_iter)
def trigger(self):
if self.line_iter is None:
raise TriggerError("no frame selected")
line = self.line
if not line.get("trigger", False):
raise TriggerError("segment is not triggered")
r = [[] for _ in self.channels]
while True:
for channel, channel_data in zip(self.channels,
line["channel_data"]):
channel.set_silence(channel_data.get("silence", False))
if "bias" in channel_data:
channel.bias.set_coefficients(
channel_data["bias"]["amplitude"])
if "dds" in channel_data:
channel.dds.amplitude.set_coefficients(
channel_data["dds"]["amplitude"])
if "phase" in channel_data["dds"]:
channel.dds.phase.set_coefficients(
channel_data["dds"]["phase"])
if channel_data["dds"].get("clear", False):
channel.dds.phase.clear()
if line.get("dac_divider", 1) != 1:
raise NotImplementedError
for channel, rc in zip(self.channels, r):
for i in range(line["duration"]):
rc.append(channel.next())
try:
self.line = line = next(self.line_iter)
if line.get("trigger", False):
return r
except StopIteration:
self.line_iter = None
return r

View File

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

View File

@ -120,9 +120,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

View File

@ -135,18 +135,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";
@ -356,7 +344,7 @@
devShell.x86_64-linux = pkgs.mkShell {
name = "artiq-dev-shell";
buildInputs = [
(packages.x86_64-linux.python3-mimalloc.withPackages(ps: with packages.x86_64-linux; [ migen misoc jesd204b artiq ps.paramiko ps.jsonschema microscope ]))
(packages.x86_64-linux.python3-mimalloc.withPackages(ps: with packages.x86_64-linux; [ migen misoc artiq ps.paramiko ps.jsonschema microscope ]))
rustPlatform.rust.rustc
rustPlatform.rust.cargo
cargo-xbuild