mirror of https://github.com/m-labs/artiq.git
remove parts that won't initially be supported by nac3
This commit is contained in:
parent
404811cd5c
commit
41c597a707
File diff suppressed because it is too large
Load Diff
|
@ -1,23 +0,0 @@
|
||||||
from artiq.language.core import kernel
|
|
||||||
|
|
||||||
|
|
||||||
class AD9154:
|
|
||||||
"""Kernel interface to AD9154 registers, using non-realtime SPI."""
|
|
||||||
|
|
||||||
def __init__(self, dmgr, spi_device, chip_select):
|
|
||||||
self.core = dmgr.get("core")
|
|
||||||
self.bus = dmgr.get(spi_device)
|
|
||||||
self.chip_select = chip_select
|
|
||||||
|
|
||||||
@kernel
|
|
||||||
def setup_bus(self, div=16):
|
|
||||||
self.bus.set_config_mu(0, 24, div, self.chip_select)
|
|
||||||
|
|
||||||
@kernel
|
|
||||||
def write(self, addr, data):
|
|
||||||
self.bus.write((addr << 16) | (data<< 8))
|
|
||||||
|
|
||||||
@kernel
|
|
||||||
def read(self, addr):
|
|
||||||
self.write((1 << 15) | addr, 0)
|
|
||||||
return self.bus.read()
|
|
|
@ -1,79 +0,0 @@
|
||||||
from artiq.language.core import kernel, portable, delay
|
|
||||||
from artiq.language.units import us, ms
|
|
||||||
from artiq.coredevice.shiftreg import ShiftReg
|
|
||||||
|
|
||||||
|
|
||||||
@portable
|
|
||||||
def to_mu(att):
|
|
||||||
return round(att*2.0) ^ 0x3f
|
|
||||||
|
|
||||||
@portable
|
|
||||||
def from_mu(att_mu):
|
|
||||||
return 0.5*(att_mu ^ 0x3f)
|
|
||||||
|
|
||||||
|
|
||||||
class BaseModAtt:
|
|
||||||
def __init__(self, dmgr, rst_n, clk, le, mosi, miso):
|
|
||||||
self.rst_n = dmgr.get(rst_n)
|
|
||||||
self.shift_reg = ShiftReg(dmgr,
|
|
||||||
clk=clk, ser=mosi, latch=le, ser_in=miso, n=8*4)
|
|
||||||
|
|
||||||
@kernel
|
|
||||||
def reset(self):
|
|
||||||
# HMC's incompetence in digital design and interfaces means that
|
|
||||||
# the HMC542 needs a level low on RST_N and then a rising edge
|
|
||||||
# on Latch Enable. Their "latch" isn't a latch but a DFF.
|
|
||||||
# Of course, it also powers up with a random attenuation, and
|
|
||||||
# that cannot be fixed with simple pull-ups/pull-downs.
|
|
||||||
self.rst_n.off()
|
|
||||||
self.shift_reg.latch.off()
|
|
||||||
delay(1*us)
|
|
||||||
self.shift_reg.latch.on()
|
|
||||||
delay(1*us)
|
|
||||||
self.shift_reg.latch.off()
|
|
||||||
self.rst_n.on()
|
|
||||||
delay(1*us)
|
|
||||||
|
|
||||||
@kernel
|
|
||||||
def set_mu(self, att0, att1, att2, att3):
|
|
||||||
"""
|
|
||||||
Sets the four attenuators on BaseMod.
|
|
||||||
The values are in half decibels, between 0 (no attenuation)
|
|
||||||
and 63 (31.5dB attenuation).
|
|
||||||
"""
|
|
||||||
word = (
|
|
||||||
(att0 << 2) |
|
|
||||||
(att1 << 10) |
|
|
||||||
(att2 << 18) |
|
|
||||||
(att3 << 26)
|
|
||||||
)
|
|
||||||
self.shift_reg.set(word)
|
|
||||||
|
|
||||||
@kernel
|
|
||||||
def get_mu(self):
|
|
||||||
"""
|
|
||||||
Retrieves the current settings of the four attenuators on BaseMod.
|
|
||||||
"""
|
|
||||||
word = self.shift_reg.get()
|
|
||||||
att0 = (word >> 2) & 0x3f
|
|
||||||
att1 = (word >> 10) & 0x3f
|
|
||||||
att2 = (word >> 18) & 0x3f
|
|
||||||
att3 = (word >> 26) & 0x3f
|
|
||||||
return att0, att1, att2, att3
|
|
||||||
|
|
||||||
@kernel
|
|
||||||
def set(self, att0, att1, att2, att3):
|
|
||||||
"""
|
|
||||||
Sets the four attenuators on BaseMod.
|
|
||||||
The values are in decibels.
|
|
||||||
"""
|
|
||||||
self.set_mu(to_mu(att0), to_mu(att1), to_mu(att2), to_mu(att3))
|
|
||||||
|
|
||||||
@kernel
|
|
||||||
def get(self):
|
|
||||||
"""
|
|
||||||
Retrieves the current settings of the four attenuators on BaseMod.
|
|
||||||
The values are in decibels.
|
|
||||||
"""
|
|
||||||
att0, att1, att2, att3 = self.get_mu()
|
|
||||||
return from_mu(att0), from_mu(att1), from_mu(att2), from_mu(att3)
|
|
|
@ -134,7 +134,7 @@
|
||||||
"properties": {
|
"properties": {
|
||||||
"type": {
|
"type": {
|
||||||
"type": "string",
|
"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": {
|
"board": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
|
@ -230,50 +230,6 @@
|
||||||
},
|
},
|
||||||
"required": ["ports"]
|
"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",
|
"title": "SUServo",
|
||||||
"if": {
|
"if": {
|
||||||
|
|
|
@ -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
|
|
||||||
}
|
|
|
@ -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()
|
|
|
@ -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)
|
|
|
@ -1,54 +0,0 @@
|
||||||
from artiq.language.core import kernel, delay
|
|
||||||
from artiq.language.units import us
|
|
||||||
|
|
||||||
|
|
||||||
class ShiftReg:
|
|
||||||
"""Driver for shift registers/latch combos connected to TTLs"""
|
|
||||||
kernel_invariants = {"dt", "n"}
|
|
||||||
|
|
||||||
def __init__(self, dmgr, clk, ser, latch, n=32, dt=10*us, ser_in=None):
|
|
||||||
self.core = dmgr.get("core")
|
|
||||||
self.clk = dmgr.get(clk)
|
|
||||||
self.ser = dmgr.get(ser)
|
|
||||||
self.latch = dmgr.get(latch)
|
|
||||||
self.n = n
|
|
||||||
self.dt = dt
|
|
||||||
if ser_in is not None:
|
|
||||||
self.ser_in = dmgr.get(ser_in)
|
|
||||||
|
|
||||||
@kernel
|
|
||||||
def set(self, data):
|
|
||||||
"""Sets the values of the latch outputs. This does not
|
|
||||||
advance the timeline and the waveform is generated before
|
|
||||||
`now`."""
|
|
||||||
delay(-2*(self.n + 1)*self.dt)
|
|
||||||
for i in range(self.n):
|
|
||||||
if (data >> (self.n-i-1)) & 1 == 0:
|
|
||||||
self.ser.off()
|
|
||||||
else:
|
|
||||||
self.ser.on()
|
|
||||||
self.clk.off()
|
|
||||||
delay(self.dt)
|
|
||||||
self.clk.on()
|
|
||||||
delay(self.dt)
|
|
||||||
self.clk.off()
|
|
||||||
self.latch.on()
|
|
||||||
delay(self.dt)
|
|
||||||
self.latch.off()
|
|
||||||
delay(self.dt)
|
|
||||||
|
|
||||||
@kernel
|
|
||||||
def get(self):
|
|
||||||
delay(-2*(self.n + 1)*self.dt)
|
|
||||||
data = 0
|
|
||||||
for i in range(self.n):
|
|
||||||
data <<= 1
|
|
||||||
self.ser_in.sample_input()
|
|
||||||
if self.ser_in.sample_get():
|
|
||||||
data |= 1
|
|
||||||
delay(self.dt)
|
|
||||||
self.clk.on()
|
|
||||||
delay(self.dt)
|
|
||||||
self.clk.off()
|
|
||||||
delay(self.dt)
|
|
||||||
return data
|
|
|
@ -1,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.")
|
|
|
@ -1,549 +0,0 @@
|
||||||
use board_misoc::{csr, clock};
|
|
||||||
use ad9154_reg;
|
|
||||||
|
|
||||||
fn spi_setup(dacno: u8) {
|
|
||||||
unsafe {
|
|
||||||
while csr::converter_spi::idle_read() == 0 {}
|
|
||||||
csr::converter_spi::offline_write(0);
|
|
||||||
csr::converter_spi::end_write(1);
|
|
||||||
csr::converter_spi::cs_polarity_write(0b0001);
|
|
||||||
csr::converter_spi::clk_polarity_write(0);
|
|
||||||
csr::converter_spi::clk_phase_write(0);
|
|
||||||
csr::converter_spi::lsb_first_write(0);
|
|
||||||
csr::converter_spi::half_duplex_write(0);
|
|
||||||
csr::converter_spi::length_write(24 - 1);
|
|
||||||
csr::converter_spi::div_write(16 - 2);
|
|
||||||
csr::converter_spi::cs_write(1 << (csr::CONFIG_CONVERTER_SPI_FIRST_AD9154_CS + dacno as u32));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn write(addr: u16, data: u8) {
|
|
||||||
unsafe {
|
|
||||||
while csr::converter_spi::writable_read() == 0 {}
|
|
||||||
csr::converter_spi::data_write(
|
|
||||||
((addr as u32) << 16) | ((data as u32) << 8));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn read(addr: u16) -> u8 {
|
|
||||||
unsafe {
|
|
||||||
write((1 << 15) | addr, 0);
|
|
||||||
while csr::converter_spi::writable_read() == 0 {}
|
|
||||||
csr::converter_spi::data_read() as u8
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ad9154 mode 1
|
|
||||||
// linerate 5Gbps or 6Gbps
|
|
||||||
// deviceclock_fpga 125MHz or 150MHz
|
|
||||||
// deviceclock_dac 500MHz or 600MHz
|
|
||||||
|
|
||||||
struct JESDSettings {
|
|
||||||
did: u8,
|
|
||||||
bid: u8,
|
|
||||||
|
|
||||||
l: u8, // lanes
|
|
||||||
m: u8, // converters
|
|
||||||
n: u8, // bits/converter
|
|
||||||
np: u8, // bits/sample
|
|
||||||
|
|
||||||
f: u8, // octets/(lane and frame)
|
|
||||||
s: u8, // samples/(converter and frame)
|
|
||||||
k: u8, // frames/multiframe
|
|
||||||
cs: u8, // control bits/sample
|
|
||||||
|
|
||||||
subclassv: u8,
|
|
||||||
jesdv: u8
|
|
||||||
}
|
|
||||||
|
|
||||||
fn jesd_checksum(settings: &JESDSettings) -> u8 {
|
|
||||||
let mut r: u8 = 0;
|
|
||||||
for field in [
|
|
||||||
settings.did,
|
|
||||||
settings.bid,
|
|
||||||
settings.l - 1,
|
|
||||||
settings.f - 1,
|
|
||||||
settings.k - 1,
|
|
||||||
settings.m - 1,
|
|
||||||
settings.n - 1,
|
|
||||||
settings.cs,
|
|
||||||
settings.np - 1,
|
|
||||||
settings.subclassv,
|
|
||||||
settings.s - 1,
|
|
||||||
settings.jesdv,
|
|
||||||
].iter() {
|
|
||||||
r = r.overflowing_add(*field).0;
|
|
||||||
}
|
|
||||||
r
|
|
||||||
}
|
|
||||||
|
|
||||||
const JESD_SETTINGS: JESDSettings = JESDSettings {
|
|
||||||
did: 0x5a,
|
|
||||||
bid: 0x5,
|
|
||||||
|
|
||||||
l: 8,
|
|
||||||
m: 4,
|
|
||||||
n: 16,
|
|
||||||
np: 16,
|
|
||||||
f: 2,
|
|
||||||
s: 2,
|
|
||||||
k: 16,
|
|
||||||
cs: 0,
|
|
||||||
|
|
||||||
subclassv: 1,
|
|
||||||
jesdv: 1
|
|
||||||
};
|
|
||||||
|
|
||||||
pub fn reset_and_detect(dacno: u8) -> Result<(), &'static str> {
|
|
||||||
spi_setup(dacno);
|
|
||||||
// reset
|
|
||||||
write(ad9154_reg::SPI_INTFCONFA,
|
|
||||||
1*ad9154_reg::SOFTRESET_M | 1*ad9154_reg::SOFTRESET |
|
|
||||||
0*ad9154_reg::LSBFIRST_M | 0*ad9154_reg::LSBFIRST |
|
|
||||||
0*ad9154_reg::ADDRINC_M | 0*ad9154_reg::ADDRINC |
|
|
||||||
1*ad9154_reg::SDOACTIVE_M | 1*ad9154_reg::SDOACTIVE);
|
|
||||||
clock::spin_us(100);
|
|
||||||
write(ad9154_reg::SPI_INTFCONFA,
|
|
||||||
0*ad9154_reg::SOFTRESET_M | 0*ad9154_reg::SOFTRESET |
|
|
||||||
0*ad9154_reg::LSBFIRST_M | 0*ad9154_reg::LSBFIRST |
|
|
||||||
0*ad9154_reg::ADDRINC_M | 0*ad9154_reg::ADDRINC |
|
|
||||||
1*ad9154_reg::SDOACTIVE_M | 1*ad9154_reg::SDOACTIVE);
|
|
||||||
clock::spin_us(100);
|
|
||||||
if (read(ad9154_reg::PRODIDH) as u16) << 8 | (read(ad9154_reg::PRODIDL) as u16) != 0x9154 {
|
|
||||||
return Err("invalid AD9154 identification");
|
|
||||||
} else {
|
|
||||||
info!("AD9154-{} found", dacno);
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn setup(dacno: u8, linerate: u64) -> Result<(), &'static str> {
|
|
||||||
spi_setup(dacno);
|
|
||||||
info!("AD9154-{} initializing...", dacno);
|
|
||||||
write(ad9154_reg::PWRCNTRL0,
|
|
||||||
0*ad9154_reg::PD_DAC0 | 0*ad9154_reg::PD_DAC1 |
|
|
||||||
0*ad9154_reg::PD_DAC2 | 0*ad9154_reg::PD_DAC3 |
|
|
||||||
0*ad9154_reg::PD_BG);
|
|
||||||
clock::spin_us(100);
|
|
||||||
write(ad9154_reg::TXENMASK1, 0*ad9154_reg::DACA_MASK |
|
|
||||||
0*ad9154_reg::DACB_MASK); // DAC PD not controlled by TXEN pins
|
|
||||||
write(ad9154_reg::PWRCNTRL3, 1*ad9154_reg::ENA_SPI_TXEN |
|
|
||||||
1*ad9154_reg::SPI_TXEN);
|
|
||||||
write(ad9154_reg::CLKCFG0,
|
|
||||||
0*ad9154_reg::REF_CLKDIV_EN | 1*ad9154_reg::RF_SYNC_EN |
|
|
||||||
1*ad9154_reg::DUTY_EN | 0*ad9154_reg::PD_CLK_REC |
|
|
||||||
0*ad9154_reg::PD_SERDES_PCLK | 0*ad9154_reg::PD_CLK_DIG |
|
|
||||||
0*ad9154_reg::PD_CLK23 | 0*ad9154_reg::PD_CLK01);
|
|
||||||
write(ad9154_reg::DACPLLCNTRL,
|
|
||||||
0*ad9154_reg::ENABLE_DACPLL | 0*ad9154_reg::RECAL_DACPLL);
|
|
||||||
write(ad9154_reg::SYSREF_ACTRL0, // jesd204b subclass 1
|
|
||||||
0*ad9154_reg::HYS_CNTRL1 | 0*ad9154_reg::SYSREF_RISE |
|
|
||||||
0*ad9154_reg::HYS_ON | 0*ad9154_reg::PD_SYSREF_BUFFER);
|
|
||||||
|
|
||||||
write(ad9154_reg::DEVICE_CONFIG_REG_0, 0x8b); // magic
|
|
||||||
write(ad9154_reg::DEVICE_CONFIG_REG_1, 0x01); // magic
|
|
||||||
write(ad9154_reg::DEVICE_CONFIG_REG_2, 0x01); // magic
|
|
||||||
|
|
||||||
write(ad9154_reg::SPI_PAGEINDX, 0x3); // A and B dual
|
|
||||||
|
|
||||||
write(ad9154_reg::INTERP_MODE, 0x03); // 4x
|
|
||||||
write(ad9154_reg::MIX_MODE, 0);
|
|
||||||
write(ad9154_reg::DATA_FORMAT, 0*ad9154_reg::BINARY_FORMAT); // s16
|
|
||||||
write(ad9154_reg::DATAPATH_CTRL,
|
|
||||||
0*ad9154_reg::I_TO_Q | 0*ad9154_reg::SEL_SIDEBAND |
|
|
||||||
0*ad9154_reg::MODULATION_TYPE | 0*ad9154_reg::PHASE_ADJ_ENABLE |
|
|
||||||
1*ad9154_reg::DIG_GAIN_ENABLE | 0*ad9154_reg::INVSINC_ENABLE);
|
|
||||||
write(ad9154_reg::IDAC_DIG_GAIN0, 0x00);
|
|
||||||
write(ad9154_reg::IDAC_DIG_GAIN1, 0x8);
|
|
||||||
write(ad9154_reg::QDAC_DIG_GAIN0, 0x00);
|
|
||||||
write(ad9154_reg::QDAC_DIG_GAIN1, 0x8);
|
|
||||||
write(ad9154_reg::DC_OFFSET_CTRL, 0);
|
|
||||||
write(ad9154_reg::IPATH_DC_OFFSET_1PART0, 0x00);
|
|
||||||
write(ad9154_reg::IPATH_DC_OFFSET_1PART1, 0x00);
|
|
||||||
write(ad9154_reg::IPATH_DC_OFFSET_2PART, 0x00);
|
|
||||||
write(ad9154_reg::QPATH_DC_OFFSET_1PART0, 0x00);
|
|
||||||
write(ad9154_reg::QPATH_DC_OFFSET_1PART1, 0x00);
|
|
||||||
write(ad9154_reg::QPATH_DC_OFFSET_2PART, 0x00);
|
|
||||||
write(ad9154_reg::PHASE_ADJ0, 0);
|
|
||||||
write(ad9154_reg::PHASE_ADJ1, 0);
|
|
||||||
write(ad9154_reg::GROUP_DLY, 0x8*ad9154_reg::COARSE_GROUP_DELAY |
|
|
||||||
0x8*ad9154_reg::GROUP_DELAY_RESERVED);
|
|
||||||
write(ad9154_reg::GROUPDELAY_COMP_BYP,
|
|
||||||
1*ad9154_reg::GROUPCOMP_BYPQ |
|
|
||||||
1*ad9154_reg::GROUPCOMP_BYPI);
|
|
||||||
write(ad9154_reg::GROUPDELAY_COMP_I, 0);
|
|
||||||
write(ad9154_reg::GROUPDELAY_COMP_Q, 0);
|
|
||||||
write(ad9154_reg::PDP_AVG_TIME, 0*ad9154_reg::PDP_ENABLE);
|
|
||||||
|
|
||||||
write(ad9154_reg::MASTER_PD, 0);
|
|
||||||
write(ad9154_reg::PHY_PD, 0x00); // lanes 0-7 enabled
|
|
||||||
write(ad9154_reg::GENERIC_PD,
|
|
||||||
0*ad9154_reg::PD_SYNCOUT0B |
|
|
||||||
1*ad9154_reg::PD_SYNCOUT1B);
|
|
||||||
write(ad9154_reg::GENERAL_JRX_CTRL_0,
|
|
||||||
0x0*ad9154_reg::LINK_EN | 0*ad9154_reg::LINK_PAGE |
|
|
||||||
0*ad9154_reg::LINK_MODE | 0*ad9154_reg::CHECKSUM_MODE);
|
|
||||||
write(ad9154_reg::ILS_DID, JESD_SETTINGS.did);
|
|
||||||
write(ad9154_reg::ILS_BID, JESD_SETTINGS.bid);
|
|
||||||
write(ad9154_reg::ILS_LID0, 0x00); // lane id
|
|
||||||
write(ad9154_reg::ILS_SCR_L,
|
|
||||||
(JESD_SETTINGS.l - 1)*ad9154_reg::L_1 |
|
|
||||||
1*ad9154_reg::SCR);
|
|
||||||
write(ad9154_reg::ILS_F, JESD_SETTINGS.f - 1);
|
|
||||||
write(ad9154_reg::ILS_K, JESD_SETTINGS.k - 1);
|
|
||||||
write(ad9154_reg::ILS_M, JESD_SETTINGS.m - 1);
|
|
||||||
write(ad9154_reg::ILS_CS_N,
|
|
||||||
(JESD_SETTINGS.n - 1)*ad9154_reg::N_1 |
|
|
||||||
0*ad9154_reg::CS);
|
|
||||||
write(ad9154_reg::ILS_NP,
|
|
||||||
(JESD_SETTINGS.np - 1)*ad9154_reg::NP_1 |
|
|
||||||
JESD_SETTINGS.subclassv*ad9154_reg::SUBCLASSV);
|
|
||||||
write(ad9154_reg::ILS_S,
|
|
||||||
(JESD_SETTINGS.s - 1)*ad9154_reg::S_1 |
|
|
||||||
JESD_SETTINGS.jesdv*ad9154_reg::JESDV);
|
|
||||||
write(ad9154_reg::ILS_HD_CF,
|
|
||||||
0*ad9154_reg::HD | 0*ad9154_reg::CF);
|
|
||||||
write(ad9154_reg::ILS_CHECKSUM, jesd_checksum(&JESD_SETTINGS));
|
|
||||||
write(ad9154_reg::LANEDESKEW, 0xff);
|
|
||||||
for i in 0..8 {
|
|
||||||
write(ad9154_reg::BADDISPARITY, 0*ad9154_reg::RST_IRQ_DIS |
|
|
||||||
0*ad9154_reg::DISABLE_ERR_CNTR_DIS |
|
|
||||||
1*ad9154_reg::RST_ERR_CNTR_DIS | i*ad9154_reg::LANE_ADDR_DIS);
|
|
||||||
write(ad9154_reg::BADDISPARITY, 0*ad9154_reg::RST_IRQ_DIS |
|
|
||||||
0*ad9154_reg::DISABLE_ERR_CNTR_DIS |
|
|
||||||
0*ad9154_reg::RST_ERR_CNTR_DIS | i*ad9154_reg::LANE_ADDR_DIS);
|
|
||||||
write(ad9154_reg::NIT_W, 0*ad9154_reg::RST_IRQ_NIT |
|
|
||||||
0*ad9154_reg::DISABLE_ERR_CNTR_NIT |
|
|
||||||
1*ad9154_reg::RST_ERR_CNTR_NIT | i*ad9154_reg::LANE_ADDR_NIT);
|
|
||||||
write(ad9154_reg::NIT_W, 0*ad9154_reg::RST_IRQ_NIT |
|
|
||||||
0*ad9154_reg::DISABLE_ERR_CNTR_NIT |
|
|
||||||
0*ad9154_reg::RST_ERR_CNTR_NIT | i*ad9154_reg::LANE_ADDR_NIT);
|
|
||||||
write(ad9154_reg::UNEXPECTEDCONTROL_W, 0*ad9154_reg::RST_IRQ_UCC |
|
|
||||||
0*ad9154_reg::DISABLE_ERR_CNTR_UCC |
|
|
||||||
1*ad9154_reg::RST_ERR_CNTR_UCC | i*ad9154_reg::LANE_ADDR_UCC);
|
|
||||||
write(ad9154_reg::BADDISPARITY, 0*ad9154_reg::RST_IRQ_UCC |
|
|
||||||
0*ad9154_reg::DISABLE_ERR_CNTR_UCC |
|
|
||||||
0*ad9154_reg::RST_ERR_CNTR_UCC | i*ad9154_reg::LANE_ADDR_UCC);
|
|
||||||
}
|
|
||||||
write(ad9154_reg::CTRLREG1, JESD_SETTINGS.f);
|
|
||||||
write(ad9154_reg::CTRLREG2, 0*ad9154_reg::ILAS_MODE |
|
|
||||||
0*ad9154_reg::THRESHOLD_MASK_EN);
|
|
||||||
write(ad9154_reg::KVAL, 1); // *4*K multiframes during ILAS
|
|
||||||
write(ad9154_reg::LANEENABLE, 0xff); // CGS _after_ this
|
|
||||||
|
|
||||||
write(ad9154_reg::TERM_BLK1_CTRLREG0, 1);
|
|
||||||
write(ad9154_reg::TERM_BLK2_CTRLREG0, 1);
|
|
||||||
write(ad9154_reg::SERDES_SPI_REG, 1);
|
|
||||||
if linerate > 5_650_000_000 {
|
|
||||||
write(ad9154_reg::CDR_OPERATING_MODE_REG_0,
|
|
||||||
0*ad9154_reg::CDR_OVERSAMP | 0x2*ad9154_reg::CDR_RESERVED |
|
|
||||||
1*ad9154_reg::ENHALFRATE);
|
|
||||||
} else {
|
|
||||||
write(ad9154_reg::CDR_OPERATING_MODE_REG_0,
|
|
||||||
0*ad9154_reg::CDR_OVERSAMP | 0x2*ad9154_reg::CDR_RESERVED |
|
|
||||||
0*ad9154_reg::ENHALFRATE);
|
|
||||||
}
|
|
||||||
write(ad9154_reg::CDR_RESET, 0);
|
|
||||||
write(ad9154_reg::CDR_RESET, 1);
|
|
||||||
if linerate > 5_650_000_000 {
|
|
||||||
write(ad9154_reg::REF_CLK_DIVIDER_LDO,
|
|
||||||
0*ad9154_reg::SPI_CDR_OVERSAMP |
|
|
||||||
1*ad9154_reg::SPI_LDO_BYPASS_FILT |
|
|
||||||
0*ad9154_reg::SPI_LDO_REF_SEL);
|
|
||||||
} else {
|
|
||||||
write(ad9154_reg::REF_CLK_DIVIDER_LDO,
|
|
||||||
1*ad9154_reg::SPI_CDR_OVERSAMP |
|
|
||||||
1*ad9154_reg::SPI_LDO_BYPASS_FILT |
|
|
||||||
0*ad9154_reg::SPI_LDO_REF_SEL);
|
|
||||||
}
|
|
||||||
write(ad9154_reg::LDO_FILTER_1, 0x62); // magic
|
|
||||||
write(ad9154_reg::LDO_FILTER_2, 0xc9); // magic
|
|
||||||
write(ad9154_reg::LDO_FILTER_3, 0x0e); // magic
|
|
||||||
write(ad9154_reg::CP_CURRENT_SPI,
|
|
||||||
0x12*ad9154_reg::SPI_CP_CURRENT |
|
|
||||||
0*ad9154_reg::SPI_SERDES_LOGEN_POWER_MODE);
|
|
||||||
write(ad9154_reg::VCO_LDO, 0x7b); // magic
|
|
||||||
write(ad9154_reg::PLL_RD_REG,
|
|
||||||
0*ad9154_reg::SPI_SERDES_LOGEN_PD_CORE |
|
|
||||||
0*ad9154_reg::SPI_SERDES_LDO_PD | 0*ad9154_reg::SPI_SYN_PD |
|
|
||||||
0*ad9154_reg::SPI_VCO_PD_ALC | 0*ad9154_reg::SPI_VCO_PD_PTAT |
|
|
||||||
0*ad9154_reg::SPI_VCO_PD);
|
|
||||||
write(ad9154_reg::ALC_VARACTOR,
|
|
||||||
0x9*ad9154_reg::SPI_VCO_VARACTOR |
|
|
||||||
0x8*ad9154_reg::SPI_INIT_ALC_VALUE);
|
|
||||||
write(ad9154_reg::VCO_OUTPUT,
|
|
||||||
0xc*ad9154_reg::SPI_VCO_OUTPUT_LEVEL |
|
|
||||||
0x4*ad9154_reg::SPI_VCO_OUTPUT_RESERVED);
|
|
||||||
write(ad9154_reg::CP_CONFIG,
|
|
||||||
0*ad9154_reg::SPI_CP_TEST |
|
|
||||||
1*ad9154_reg::SPI_CP_CAL_EN |
|
|
||||||
0*ad9154_reg::SPI_CP_FORCE_CALBITS |
|
|
||||||
0*ad9154_reg::SPI_CP_OFFSET_OFF |
|
|
||||||
1*ad9154_reg::SPI_CP_ENABLE_MACHINE |
|
|
||||||
0*ad9154_reg::SPI_CP_DITHER_MODE |
|
|
||||||
0*ad9154_reg::SPI_CP_HALF_VCO_CAL_CLK);
|
|
||||||
write(ad9154_reg::VCO_BIAS_1,
|
|
||||||
0x3*ad9154_reg::SPI_VCO_BIAS_REF |
|
|
||||||
0x3*ad9154_reg::SPI_VCO_BIAS_TCF);
|
|
||||||
write(ad9154_reg::VCO_BIAS_2,
|
|
||||||
0x1*ad9154_reg::SPI_PRESCALE_BIAS |
|
|
||||||
1*ad9154_reg::SPI_LAST_ALC_EN |
|
|
||||||
0x1*ad9154_reg::SPI_PRESCALE_BYPASS_R |
|
|
||||||
0*ad9154_reg::SPI_VCO_COMP_BYPASS_BIASR |
|
|
||||||
0*ad9154_reg::SPI_VCO_BYPASS_DAC_R);
|
|
||||||
write(ad9154_reg::VCO_PD_OVERRIDES,
|
|
||||||
0*ad9154_reg::SPI_VCO_PD_OVERRIDE_VCO_BUF |
|
|
||||||
1*ad9154_reg::SPI_VCO_PD_OVERRIDE_CAL_TCF |
|
|
||||||
0*ad9154_reg::SPI_VCO_PD_OVERRIDE_VAR_REF_TCF |
|
|
||||||
0*ad9154_reg::SPI_VCO_PD_OVERRIDE_VAR_REF);
|
|
||||||
write(ad9154_reg::VCO_CAL,
|
|
||||||
0x2*ad9154_reg::SPI_FB_CLOCK_ADV |
|
|
||||||
0x3*ad9154_reg::SPI_VCO_CAL_COUNT |
|
|
||||||
0*ad9154_reg::SPI_VCO_CAL_ALC_WAIT |
|
|
||||||
1*ad9154_reg::SPI_VCO_CAL_EN);
|
|
||||||
write(ad9154_reg::CP_LEVEL_DETECT,
|
|
||||||
0x2*ad9154_reg::SPI_CP_LEVEL_THRESHOLD_HIGH |
|
|
||||||
0x5*ad9154_reg::SPI_CP_LEVEL_THRESHOLD_LOW |
|
|
||||||
0*ad9154_reg::SPI_CP_LEVEL_DET_PD);
|
|
||||||
write(ad9154_reg::VCO_VARACTOR_CTRL_0,
|
|
||||||
0xe*ad9154_reg::SPI_VCO_VARACTOR_OFFSET |
|
|
||||||
0x7*ad9154_reg::SPI_VCO_VARACTOR_REF_TCF);
|
|
||||||
write(ad9154_reg::VCO_VARACTOR_CTRL_1,
|
|
||||||
0x6*ad9154_reg::SPI_VCO_VARACTOR_REF);
|
|
||||||
// ensure link is txing
|
|
||||||
//write(ad9154_reg::SERDESPLL_ENABLE_CNTRL,
|
|
||||||
// 1*ad9154_reg::ENABLE_SERDESPLL | 1*ad9154_reg::RECAL_SERDESPLL)
|
|
||||||
write(ad9154_reg::SERDESPLL_ENABLE_CNTRL,
|
|
||||||
1*ad9154_reg::ENABLE_SERDESPLL | 0*ad9154_reg::RECAL_SERDESPLL);
|
|
||||||
let t = clock::get_ms();
|
|
||||||
while read(ad9154_reg::PLL_STATUS) & ad9154_reg::SERDES_PLL_LOCK_RB == 0 {
|
|
||||||
if clock::get_ms() > t + 200 {
|
|
||||||
return Err("SERDES PLL lock timeout");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
write(ad9154_reg::EQ_BIAS_REG, 0x22*ad9154_reg::EQ_BIAS_RESERVED |
|
|
||||||
1*ad9154_reg::EQ_POWER_MODE);
|
|
||||||
|
|
||||||
write(ad9154_reg::GENERAL_JRX_CTRL_1, 1); // subclass 1
|
|
||||||
write(ad9154_reg::LMFC_DELAY_0, 0);
|
|
||||||
write(ad9154_reg::LMFC_DELAY_1, 0);
|
|
||||||
write(ad9154_reg::LMFC_VAR_0, 0x0a); // receive buffer delay
|
|
||||||
write(ad9154_reg::LMFC_VAR_1, 0x0a);
|
|
||||||
write(ad9154_reg::SYNC_ERRWINDOW, 0); // +- 1/2 DAC clock
|
|
||||||
// datasheet seems to say ENABLE and ARM should be separate steps,
|
|
||||||
// so enable now so it can be armed in sync().
|
|
||||||
write(ad9154_reg::SYNC_CONTROL,
|
|
||||||
0x1*ad9154_reg::SYNCMODE | 1*ad9154_reg::SYNCENABLE |
|
|
||||||
0*ad9154_reg::SYNCARM | 0*ad9154_reg::SYNCCLRSTKY);
|
|
||||||
|
|
||||||
write(ad9154_reg::XBAR_LN_0_1,
|
|
||||||
0*ad9154_reg::LOGICAL_LANE0_SRC | 1*ad9154_reg::LOGICAL_LANE1_SRC);
|
|
||||||
write(ad9154_reg::XBAR_LN_2_3,
|
|
||||||
2*ad9154_reg::LOGICAL_LANE2_SRC | 3*ad9154_reg::LOGICAL_LANE3_SRC);
|
|
||||||
write(ad9154_reg::XBAR_LN_4_5,
|
|
||||||
4*ad9154_reg::LOGICAL_LANE4_SRC | 5*ad9154_reg::LOGICAL_LANE5_SRC);
|
|
||||||
write(ad9154_reg::XBAR_LN_6_7,
|
|
||||||
6*ad9154_reg::LOGICAL_LANE6_SRC | 7*ad9154_reg::LOGICAL_LANE7_SRC);
|
|
||||||
write(ad9154_reg::JESD_BIT_INVERSE_CTRL, 0x00);
|
|
||||||
write(ad9154_reg::GENERAL_JRX_CTRL_0,
|
|
||||||
0x1*ad9154_reg::LINK_EN | 0*ad9154_reg::LINK_PAGE |
|
|
||||||
0*ad9154_reg::LINK_MODE | 0*ad9154_reg::CHECKSUM_MODE);
|
|
||||||
info!(" ...done");
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn status(dacno: u8) {
|
|
||||||
spi_setup(dacno);
|
|
||||||
info!("Printing status of AD9154-{}", dacno);
|
|
||||||
info!("PRODID: 0x{:04x}", (read(ad9154_reg::PRODIDH) as u16) << 8 | (read(ad9154_reg::PRODIDL) as u16));
|
|
||||||
info!("SERDES_PLL_LOCK: {}",
|
|
||||||
(read(ad9154_reg::PLL_STATUS) & ad9154_reg::SERDES_PLL_LOCK_RB));
|
|
||||||
info!("");
|
|
||||||
info!("CODEGRPSYNC: 0x{:02x}", read(ad9154_reg::CODEGRPSYNCFLG));
|
|
||||||
info!("FRAMESYNC: 0x{:02x}", read(ad9154_reg::FRAMESYNCFLG));
|
|
||||||
info!("GOODCHECKSUM: 0x{:02x}", read(ad9154_reg::GOODCHKSUMFLG));
|
|
||||||
info!("INITLANESYNC: 0x{:02x}", read(ad9154_reg::INITLANESYNCFLG));
|
|
||||||
info!("");
|
|
||||||
info!("DID_REG: 0x{:02x}", read(ad9154_reg::DID_REG));
|
|
||||||
info!("BID_REG: 0x{:02x}", read(ad9154_reg::BID_REG));
|
|
||||||
info!("SCR_L_REG: 0x{:02x}", read(ad9154_reg::SCR_L_REG));
|
|
||||||
info!("F_REG: 0x{:02x}", read(ad9154_reg::F_REG));
|
|
||||||
info!("K_REG: 0x{:02x}", read(ad9154_reg::K_REG));
|
|
||||||
info!("M_REG: 0x{:02x}", read(ad9154_reg::M_REG));
|
|
||||||
info!("CS_N_REG: 0x{:02x}", read(ad9154_reg::CS_N_REG));
|
|
||||||
info!("NP_REG: 0x{:02x}", read(ad9154_reg::NP_REG));
|
|
||||||
info!("S_REG: 0x{:02x}", read(ad9154_reg::S_REG));
|
|
||||||
info!("HD_CF_REG: 0x{:02x}", read(ad9154_reg::HD_CF_REG));
|
|
||||||
info!("RES1_REG: 0x{:02x}", read(ad9154_reg::RES1_REG));
|
|
||||||
info!("RES2_REG: 0x{:02x}", read(ad9154_reg::RES2_REG));
|
|
||||||
info!("LIDx_REG: 0x{:02x} 0x{:02x} 0x{:02x} 0x{:02x} 0x{:02x} 0x{:02x} 0x{:02x} 0x{:02x}",
|
|
||||||
read(ad9154_reg::LID0_REG),
|
|
||||||
read(ad9154_reg::LID1_REG),
|
|
||||||
read(ad9154_reg::LID2_REG),
|
|
||||||
read(ad9154_reg::LID3_REG),
|
|
||||||
read(ad9154_reg::LID4_REG),
|
|
||||||
read(ad9154_reg::LID5_REG),
|
|
||||||
read(ad9154_reg::LID6_REG),
|
|
||||||
read(ad9154_reg::LID7_REG));
|
|
||||||
info!("CHECKSUMx_REG: 0x{:02x} 0x{:02x} 0x{:02x} 0x{:02x} 0x{:02x} 0x{:02x} 0x{:02x} 0x{:02x}",
|
|
||||||
read(ad9154_reg::CHECKSUM0_REG),
|
|
||||||
read(ad9154_reg::CHECKSUM1_REG),
|
|
||||||
read(ad9154_reg::CHECKSUM2_REG),
|
|
||||||
read(ad9154_reg::CHECKSUM3_REG),
|
|
||||||
read(ad9154_reg::CHECKSUM4_REG),
|
|
||||||
read(ad9154_reg::CHECKSUM5_REG),
|
|
||||||
read(ad9154_reg::CHECKSUM6_REG),
|
|
||||||
read(ad9154_reg::CHECKSUM7_REG));
|
|
||||||
info!("COMPSUMx_REG: 0x{:02x} 0x{:02x} 0x{:02x} 0x{:02x} 0x{:02x} 0x{:02x} 0x{:02x} 0x{:02x}",
|
|
||||||
read(ad9154_reg::COMPSUM0_REG),
|
|
||||||
read(ad9154_reg::COMPSUM1_REG),
|
|
||||||
read(ad9154_reg::COMPSUM2_REG),
|
|
||||||
read(ad9154_reg::COMPSUM3_REG),
|
|
||||||
read(ad9154_reg::COMPSUM4_REG),
|
|
||||||
read(ad9154_reg::COMPSUM5_REG),
|
|
||||||
read(ad9154_reg::COMPSUM6_REG),
|
|
||||||
read(ad9154_reg::COMPSUM7_REG));
|
|
||||||
info!("BADDISPARITY: 0x{:02x}", read(ad9154_reg::BADDISPARITY));
|
|
||||||
info!("NITDISPARITY: 0x{:02x}", read(ad9154_reg::NIT_W));
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn prbs(dacno: u8) -> Result<(), &'static str> {
|
|
||||||
let mut prbs_errors: u32 = 0;
|
|
||||||
spi_setup(dacno);
|
|
||||||
|
|
||||||
/* follow phy prbs testing (p58 of ad9154 datasheet) */
|
|
||||||
info!("AD9154-{} running PRBS test...", dacno);
|
|
||||||
|
|
||||||
/* step 2: select prbs mode */
|
|
||||||
write(ad9154_reg::PHY_PRBS_TEST_CTRL,
|
|
||||||
0b00*ad9154_reg::PHY_PRBS_PAT_SEL);
|
|
||||||
|
|
||||||
/* step 3: enable test for all lanes */
|
|
||||||
write(ad9154_reg::PHY_PRBS_TEST_EN, 0xff);
|
|
||||||
|
|
||||||
/* step 4: reset */
|
|
||||||
write(ad9154_reg::PHY_PRBS_TEST_CTRL,
|
|
||||||
0b00*ad9154_reg::PHY_PRBS_PAT_SEL |
|
|
||||||
1*ad9154_reg::PHY_TEST_RESET);
|
|
||||||
write(ad9154_reg::PHY_PRBS_TEST_CTRL,
|
|
||||||
0b00*ad9154_reg::PHY_PRBS_PAT_SEL);
|
|
||||||
|
|
||||||
/* step 5: prbs threshold */
|
|
||||||
write(ad9154_reg::PHY_PRBS_TEST_THRESHOLD_LOBITS, 0);
|
|
||||||
write(ad9154_reg::PHY_PRBS_TEST_THRESHOLD_MIDBITS, 0);
|
|
||||||
write(ad9154_reg::PHY_PRBS_TEST_THRESHOLD_HIBITS, 0);
|
|
||||||
|
|
||||||
/* step 6: start */
|
|
||||||
write(ad9154_reg::PHY_PRBS_TEST_CTRL,
|
|
||||||
0b00*ad9154_reg::PHY_PRBS_PAT_SEL);
|
|
||||||
write(ad9154_reg::PHY_PRBS_TEST_CTRL,
|
|
||||||
0b00*ad9154_reg::PHY_PRBS_PAT_SEL |
|
|
||||||
1*ad9154_reg::PHY_TEST_START);
|
|
||||||
|
|
||||||
/* step 7: wait 500 ms */
|
|
||||||
clock::spin_us(500000);
|
|
||||||
|
|
||||||
/* step 8 : stop */
|
|
||||||
write(ad9154_reg::PHY_PRBS_TEST_CTRL,
|
|
||||||
0b00*ad9154_reg::PHY_PRBS_PAT_SEL);
|
|
||||||
|
|
||||||
for i in 0..8 {
|
|
||||||
/* step 9.a: select src err */
|
|
||||||
write(ad9154_reg::PHY_PRBS_TEST_CTRL,
|
|
||||||
i*ad9154_reg::PHY_SRC_ERR_CNT);
|
|
||||||
/* step 9.b: retrieve number of errors */
|
|
||||||
let lane_errors = (read(ad9154_reg::PHY_PRBS_TEST_ERRCNT_LOBITS) as u32) |
|
|
||||||
((read(ad9154_reg::PHY_PRBS_TEST_ERRCNT_MIDBITS) as u32) << 8) |
|
|
||||||
((read(ad9154_reg::PHY_PRBS_TEST_ERRCNT_HIBITS) as u32) << 16);
|
|
||||||
if lane_errors > 0 {
|
|
||||||
warn!(" PRBS errors on lane{}: {:06x}", i, lane_errors);
|
|
||||||
}
|
|
||||||
prbs_errors += lane_errors
|
|
||||||
}
|
|
||||||
|
|
||||||
if prbs_errors > 0 {
|
|
||||||
return Err("PRBS failed")
|
|
||||||
}
|
|
||||||
info!(" ...passed");
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn stpl(dacno: u8, m: u8, s: u8) -> Result<(), &'static str> {
|
|
||||||
spi_setup(dacno);
|
|
||||||
|
|
||||||
info!("AD9154-{} running STPL test...", dacno);
|
|
||||||
|
|
||||||
fn prng(seed: u32) -> u32 {
|
|
||||||
return ((seed + 1)*0x31415979 + 1) & 0xffff;
|
|
||||||
}
|
|
||||||
|
|
||||||
for i in 0..m {
|
|
||||||
let mut data: u32;
|
|
||||||
let mut errors: u8 = 0;
|
|
||||||
for j in 0..s {
|
|
||||||
/* select converter */
|
|
||||||
write(ad9154_reg::SHORT_TPL_TEST_0,
|
|
||||||
0b0*ad9154_reg::SHORT_TPL_TEST_EN |
|
|
||||||
0b0*ad9154_reg::SHORT_TPL_TEST_RESET |
|
|
||||||
i*ad9154_reg::SHORT_TPL_DAC_SEL |
|
|
||||||
j*ad9154_reg::SHORT_TPL_SP_SEL);
|
|
||||||
|
|
||||||
/* set expected value */
|
|
||||||
data = prng(((i as u32) << 8) | (j as u32));
|
|
||||||
write(ad9154_reg::SHORT_TPL_TEST_1, (data & 0x00ff) as u8);
|
|
||||||
write(ad9154_reg::SHORT_TPL_TEST_2, ((data & 0xff00) >> 8) as u8);
|
|
||||||
|
|
||||||
/* enable stpl */
|
|
||||||
write(ad9154_reg::SHORT_TPL_TEST_0,
|
|
||||||
0b1*ad9154_reg::SHORT_TPL_TEST_EN |
|
|
||||||
0b0*ad9154_reg::SHORT_TPL_TEST_RESET |
|
|
||||||
i*ad9154_reg::SHORT_TPL_DAC_SEL |
|
|
||||||
j*ad9154_reg::SHORT_TPL_SP_SEL);
|
|
||||||
|
|
||||||
/* reset stpl */
|
|
||||||
write(ad9154_reg::SHORT_TPL_TEST_0,
|
|
||||||
0b1*ad9154_reg::SHORT_TPL_TEST_EN |
|
|
||||||
0b1*ad9154_reg::SHORT_TPL_TEST_RESET |
|
|
||||||
i*ad9154_reg::SHORT_TPL_DAC_SEL |
|
|
||||||
j*ad9154_reg::SHORT_TPL_SP_SEL);
|
|
||||||
|
|
||||||
/* release reset stpl */
|
|
||||||
write(ad9154_reg::SHORT_TPL_TEST_0,
|
|
||||||
0b1*ad9154_reg::SHORT_TPL_TEST_EN |
|
|
||||||
0b0*ad9154_reg::SHORT_TPL_TEST_RESET |
|
|
||||||
i*ad9154_reg::SHORT_TPL_DAC_SEL |
|
|
||||||
j*ad9154_reg::SHORT_TPL_SP_SEL);
|
|
||||||
errors += read(ad9154_reg::SHORT_TPL_TEST_3);
|
|
||||||
}
|
|
||||||
info!(" c{} errors: {}", i, errors);
|
|
||||||
if errors > 0 {
|
|
||||||
return Err("STPL failed")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
info!(" ...passed");
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn sync(dacno: u8) -> Result<bool, &'static str> {
|
|
||||||
spi_setup(dacno);
|
|
||||||
|
|
||||||
write(ad9154_reg::SYNC_CONTROL,
|
|
||||||
0x1*ad9154_reg::SYNCMODE | 1*ad9154_reg::SYNCENABLE |
|
|
||||||
1*ad9154_reg::SYNCARM | 1*ad9154_reg::SYNCCLRSTKY);
|
|
||||||
clock::spin_us(1000); // ensure at least one sysref edge
|
|
||||||
let sync_status = read(ad9154_reg::SYNC_STATUS);
|
|
||||||
|
|
||||||
if sync_status & ad9154_reg::SYNC_BUSY != 0 {
|
|
||||||
return Err("sync logic busy");
|
|
||||||
}
|
|
||||||
if sync_status & ad9154_reg::SYNC_LOCK == 0 {
|
|
||||||
return Err("no sync lock");
|
|
||||||
}
|
|
||||||
if sync_status & ad9154_reg::SYNC_TRIP == 0 {
|
|
||||||
return Err("no sysref edge");
|
|
||||||
}
|
|
||||||
let realign_occured = sync_status & ad9154_reg::SYNC_ROTATE != 0;
|
|
||||||
Ok(realign_occured)
|
|
||||||
}
|
|
|
@ -1,826 +0,0 @@
|
||||||
#![allow(dead_code)]
|
|
||||||
|
|
||||||
pub const SPI_INTFCONFA : u16 = 0x000;
|
|
||||||
pub const SOFTRESET : u8 = 1 << 0;
|
|
||||||
pub const LSBFIRST : u8 = 1 << 1;
|
|
||||||
pub const ADDRINC : u8 = 1 << 2;
|
|
||||||
pub const SDOACTIVE : u8 = 1 << 3;
|
|
||||||
pub const SDOACTIVE_M : u8 = 1 << 4;
|
|
||||||
pub const ADDRINC_M : u8 = 1 << 5;
|
|
||||||
pub const LSBFIRST_M : u8 = 1 << 6;
|
|
||||||
pub const SOFTRESET_M : u8 = 1 << 7;
|
|
||||||
|
|
||||||
pub const CHIPTYPE : u16 = 0x003;
|
|
||||||
|
|
||||||
pub const PRODIDL : u16 = 0x004;
|
|
||||||
|
|
||||||
pub const PRODIDH : u16 = 0x005;
|
|
||||||
|
|
||||||
pub const CHIPGRADE : u16 = 0x006;
|
|
||||||
pub const DEV_REVISION : u8 = 1 << 0;
|
|
||||||
pub const PROD_GRADE : u8 = 1 << 4;
|
|
||||||
|
|
||||||
pub const SPI_PAGEINDX : u16 = 0x008;
|
|
||||||
|
|
||||||
pub const PWRCNTRL0 : u16 = 0x011;
|
|
||||||
pub const PD_DAC3 : u8 = 1 << 3;
|
|
||||||
pub const PD_DAC2 : u8 = 1 << 4;
|
|
||||||
pub const PD_DAC1 : u8 = 1 << 5;
|
|
||||||
pub const PD_DAC0 : u8 = 1 << 6;
|
|
||||||
pub const PD_BG : u8 = 1 << 7;
|
|
||||||
|
|
||||||
pub const TXENMASK1 : u16 = 0x012;
|
|
||||||
pub const DACA_MASK : u8 = 1 << 6;
|
|
||||||
pub const DACB_MASK : u8 = 1 << 7;
|
|
||||||
|
|
||||||
pub const PWRCNTRL3 : u16 = 0x013;
|
|
||||||
pub const SPI_TXEN : u8 = 1 << 0;
|
|
||||||
pub const ENA_SPI_TXEN : u8 = 1 << 1;
|
|
||||||
pub const SPI_PA_CTRL : u8 = 1 << 2;
|
|
||||||
pub const ENA_PA_CTRL_FROM_SPI : u8 = 1 << 3;
|
|
||||||
pub const ENA_PA_CTRL_FROM_BLSM : u8 = 1 << 4;
|
|
||||||
pub const ENA_PA_CTRL_FROM_TXENSM : u8 = 1 << 5;
|
|
||||||
pub const ENA_PA_CTRL_FROM_PARROT_ERR : u8 = 1 << 6;
|
|
||||||
|
|
||||||
pub const GROUP_DLY : u16 = 0x014;
|
|
||||||
pub const COARSE_GROUP_DELAY : u8 = 1 << 0;
|
|
||||||
pub const GROUP_DELAY_RESERVED : u8 = 1 << 4;
|
|
||||||
|
|
||||||
pub const IRQEN_STATUSMODE0 : u16 = 0x01f;
|
|
||||||
pub const IRQEN_SMODE_LANEFIFOERR : u8 = 1 << 1;
|
|
||||||
pub const IRQEN_SMODE_SERPLLLOCK : u8 = 1 << 2;
|
|
||||||
pub const IRQEN_SMODE_SERPLLLOST : u8 = 1 << 3;
|
|
||||||
pub const IRQEN_SMODE_DACPLLLOCK : u8 = 1 << 4;
|
|
||||||
pub const IRQEN_SMODE_DACPLLLOST : u8 = 1 << 5;
|
|
||||||
|
|
||||||
pub const IRQEN_STATUSMODE1 : u16 = 0x020;
|
|
||||||
pub const IRQEN_SMODE_PRBS0 : u8 = 1 << 0;
|
|
||||||
pub const IRQEN_SMODE_PRBS1 : u8 = 1 << 1;
|
|
||||||
pub const IRQEN_SMODE_PRBS2 : u8 = 1 << 2;
|
|
||||||
pub const IRQEN_SMODE_PRBS3 : u8 = 1 << 3;
|
|
||||||
|
|
||||||
pub const IRQEN_STATUSMODE2 : u16 = 0x021;
|
|
||||||
pub const IRQEN_SMODE_SYNC_TRIP0 : u8 = 1 << 0;
|
|
||||||
pub const IRQEN_SMODE_SYNC_WLIM0 : u8 = 1 << 1;
|
|
||||||
pub const IRQEN_SMODE_SYNC_ROTATE0 : u8 = 1 << 2;
|
|
||||||
pub const IRQEN_SMODE_SYNC_LOCK0 : u8 = 1 << 3;
|
|
||||||
pub const IRQEN_SMODE_NCO_ALIGN0 : u8 = 1 << 4;
|
|
||||||
pub const IRQEN_SMODE_BLNKDONE0 : u8 = 1 << 5;
|
|
||||||
pub const IRQEN_SMODE_PDPERR0 : u8 = 1 << 7;
|
|
||||||
|
|
||||||
pub const IRQEN_STATUSMODE3 : u16 = 0x022;
|
|
||||||
pub const IRQEN_SMODE_SYNC_TRIP1 : u8 = 1 << 0;
|
|
||||||
pub const IRQEN_SMODE_SYNC_WLIM1 : u8 = 1 << 1;
|
|
||||||
pub const IRQEN_SMODE_SYNC_ROTATE1 : u8 = 1 << 2;
|
|
||||||
pub const IRQEN_SMODE_SYNC_LOCK1 : u8 = 1 << 3;
|
|
||||||
pub const IRQEN_SMODE_NCO_ALIGN1 : u8 = 1 << 4;
|
|
||||||
pub const IRQEN_SMODE_BLNKDONE1 : u8 = 1 << 5;
|
|
||||||
pub const IRQEN_SMODE_PDPERR1 : u8 = 1 << 7;
|
|
||||||
|
|
||||||
pub const IRQ_STATUS0 : u16 = 0x023;
|
|
||||||
pub const LANEFIFOERR : u8 = 1 << 1;
|
|
||||||
pub const SERPLLLOCK : u8 = 1 << 2;
|
|
||||||
pub const SERPLLLOST : u8 = 1 << 3;
|
|
||||||
pub const DACPLLLOCK : u8 = 1 << 4;
|
|
||||||
pub const DACPLLLOST : u8 = 1 << 5;
|
|
||||||
|
|
||||||
pub const IRQ_STATUS1 : u16 = 0x024;
|
|
||||||
pub const PRBS0 : u8 = 1 << 0;
|
|
||||||
pub const PRBS1 : u8 = 1 << 1;
|
|
||||||
pub const PRBS2 : u8 = 1 << 2;
|
|
||||||
pub const PRBS3 : u8 = 1 << 3;
|
|
||||||
|
|
||||||
pub const IRQ_STATUS2 : u16 = 0x025;
|
|
||||||
pub const SYNC_TRIP0 : u8 = 1 << 0;
|
|
||||||
pub const SYNC_WLIM0 : u8 = 1 << 1;
|
|
||||||
pub const SYNC_ROTATE0 : u8 = 1 << 2;
|
|
||||||
pub const SYNC_LOCK0 : u8 = 1 << 3;
|
|
||||||
pub const NCO_ALIGN0 : u8 = 1 << 4;
|
|
||||||
pub const BLNKDONE0 : u8 = 1 << 5;
|
|
||||||
pub const PDPERR0 : u8 = 1 << 7;
|
|
||||||
|
|
||||||
pub const IRQ_STATUS3 : u16 = 0x026;
|
|
||||||
pub const SYNC_TRIP1 : u8 = 1 << 0;
|
|
||||||
pub const SYNC_WLIM1 : u8 = 1 << 1;
|
|
||||||
pub const SYNC_ROTATE1 : u8 = 1 << 2;
|
|
||||||
pub const SYNC_LOCK1 : u8 = 1 << 3;
|
|
||||||
pub const NCO_ALIGN1 : u8 = 1 << 4;
|
|
||||||
pub const BLNKDONE1 : u8 = 1 << 5;
|
|
||||||
pub const PDPERR1 : u8 = 1 << 7;
|
|
||||||
|
|
||||||
pub const JESD_CHECKS : u16 = 0x030;
|
|
||||||
pub const ERR_INTSUPP : u8 = 1 << 0;
|
|
||||||
pub const ERR_SUBCLASS : u8 = 1 << 1;
|
|
||||||
pub const ERR_KUNSUPP : u8 = 1 << 2;
|
|
||||||
pub const ERR_JESDBAD : u8 = 1 << 3;
|
|
||||||
pub const ERR_WINLIMIT : u8 = 1 << 4;
|
|
||||||
pub const ERR_DLYOVER : u8 = 1 << 5;
|
|
||||||
|
|
||||||
pub const SYNC_ERRWINDOW : u16 = 0x034;
|
|
||||||
|
|
||||||
pub const SYNC_LASTERR_L : u16 = 0x038;
|
|
||||||
|
|
||||||
pub const SYNC_LASTERR_H : u16 = 0x039;
|
|
||||||
pub const LASTERROR_H : u8 = 1 << 0;
|
|
||||||
pub const LASTOVER : u8 = 1 << 6;
|
|
||||||
pub const LASTUNDER : u8 = 1 << 7;
|
|
||||||
|
|
||||||
pub const SYNC_CONTROL : u16 = 0x03a;
|
|
||||||
pub const SYNCMODE : u8 = 1 << 0;
|
|
||||||
pub const SYNCCLRLAST : u8 = 1 << 4;
|
|
||||||
pub const SYNCCLRSTKY : u8 = 1 << 5;
|
|
||||||
pub const SYNCARM : u8 = 1 << 6;
|
|
||||||
pub const SYNCENABLE : u8 = 1 << 7;
|
|
||||||
|
|
||||||
pub const SYNC_STATUS : u16 = 0x03b;
|
|
||||||
pub const SYNC_TRIP : u8 = 1 << 0;
|
|
||||||
pub const SYNC_WLIM : u8 = 1 << 1;
|
|
||||||
pub const SYNC_ROTATE : u8 = 1 << 2;
|
|
||||||
pub const SYNC_LOCK : u8 = 1 << 3;
|
|
||||||
pub const SYNC_BUSY : u8 = 1 << 7;
|
|
||||||
|
|
||||||
pub const SYNC_CURRERR_L : u16 = 0x03c;
|
|
||||||
|
|
||||||
pub const SYNC_CURRERR_H : u16 = 0x03d;
|
|
||||||
pub const CURRERROR_H : u8 = 1 << 0;
|
|
||||||
pub const CURROVER : u8 = 1 << 6;
|
|
||||||
pub const CURRUNDER : u8 = 1 << 7;
|
|
||||||
|
|
||||||
pub const DACGAIN0_I : u16 = 0x040;
|
|
||||||
|
|
||||||
pub const DACGAIN1_I : u16 = 0x041;
|
|
||||||
|
|
||||||
pub const DACGAIN0_Q : u16 = 0x042;
|
|
||||||
|
|
||||||
pub const DACGAIN1_Q : u16 = 0x043;
|
|
||||||
|
|
||||||
pub const GROUPDELAY_COMP_I : u16 = 0x044;
|
|
||||||
|
|
||||||
pub const GROUPDELAY_COMP_Q : u16 = 0x045;
|
|
||||||
|
|
||||||
pub const GROUPDELAY_COMP_BYP : u16 = 0x046;
|
|
||||||
pub const GROUPCOMP_BYPQ : u8 = 1 << 0;
|
|
||||||
pub const GROUPCOMP_BYPI : u8 = 1 << 1;
|
|
||||||
|
|
||||||
pub const MIX_MODE : u16 = 0x04a;
|
|
||||||
|
|
||||||
pub const NCOALIGN_MODE : u16 = 0x050;
|
|
||||||
pub const NCO_ALIGN_MODE : u8 = 1 << 0;
|
|
||||||
pub const NCO_ALIGN_FAIL : u8 = 1 << 3;
|
|
||||||
pub const NCO_ALIGN_PASS : u8 = 1 << 4;
|
|
||||||
pub const NCO_ALIGN_MTCH : u8 = 1 << 5;
|
|
||||||
pub const NCO_ALIGN_ARM : u8 = 1 << 7;
|
|
||||||
|
|
||||||
pub const NCOKEY_ILSB : u16 = 0x051;
|
|
||||||
|
|
||||||
pub const NCOKEY_IMSB : u16 = 0x052;
|
|
||||||
|
|
||||||
pub const NCOKEY_QLSB : u16 = 0x053;
|
|
||||||
|
|
||||||
pub const NCOKEY_QMSB : u16 = 0x054;
|
|
||||||
|
|
||||||
pub const PDP_THRES0 : u16 = 0x060;
|
|
||||||
|
|
||||||
pub const PDP_THRES1 : u16 = 0x061;
|
|
||||||
|
|
||||||
pub const PDP_AVG_TIME : u16 = 0x062;
|
|
||||||
pub const PDP_AVG_TIME_ : u8 = 1 << 0;
|
|
||||||
pub const PA_BUS_SWAP : u8 = 1 << 6;
|
|
||||||
pub const PDP_ENABLE : u8 = 1 << 7;
|
|
||||||
|
|
||||||
pub const PDP_POWER0 : u16 = 0x063;
|
|
||||||
|
|
||||||
pub const PDP_POWER1 : u16 = 0x064;
|
|
||||||
|
|
||||||
pub const CLKCFG0 : u16 = 0x080;
|
|
||||||
pub const REF_CLKDIV_EN : u8 = 1 << 0;
|
|
||||||
pub const RF_SYNC_EN : u8 = 1 << 1;
|
|
||||||
pub const DUTY_EN : u8 = 1 << 2;
|
|
||||||
pub const PD_CLK_REC : u8 = 1 << 3;
|
|
||||||
pub const PD_SERDES_PCLK : u8 = 1 << 4;
|
|
||||||
pub const PD_CLK_DIG : u8 = 1 << 5;
|
|
||||||
pub const PD_CLK23 : u8 = 1 << 6;
|
|
||||||
pub const PD_CLK01 : u8 = 1 << 7;
|
|
||||||
|
|
||||||
pub const SYSREF_ACTRL0 : u16 = 0x081;
|
|
||||||
pub const HYS_CNTRL1 : u8 = 1 << 0;
|
|
||||||
pub const SYSREF_RISE : u8 = 1 << 2;
|
|
||||||
pub const HYS_ON : u8 = 1 << 3;
|
|
||||||
pub const PD_SYSREF_BUFFER : u8 = 1 << 4;
|
|
||||||
|
|
||||||
pub const SYSREF_ACTRL1 : u16 = 0x082;
|
|
||||||
|
|
||||||
pub const DACPLLCNTRL : u16 = 0x083;
|
|
||||||
pub const ENABLE_DACPLL : u8 = 1 << 4;
|
|
||||||
pub const RECAL_DACPLL : u8 = 1 << 7;
|
|
||||||
|
|
||||||
pub const DACPLLSTATUS : u16 = 0x084;
|
|
||||||
pub const DACPLL_LOCK : u8 = 1 << 1;
|
|
||||||
pub const VCO_CAL_PROGRESS : u8 = 1 << 3;
|
|
||||||
pub const CP_CAL_VALID : u8 = 1 << 4;
|
|
||||||
pub const CP_OVERRANGE_L : u8 = 1 << 5;
|
|
||||||
pub const CP_OVERRANGE_H : u8 = 1 << 6;
|
|
||||||
|
|
||||||
pub const DACINTEGERWORD0 : u16 = 0x085;
|
|
||||||
|
|
||||||
pub const DACLOOPFILT1 : u16 = 0x087;
|
|
||||||
pub const LF_C1_WORD : u8 = 1 << 0;
|
|
||||||
pub const LF_C2_WORD : u8 = 1 << 4;
|
|
||||||
|
|
||||||
pub const DACLOOPFILT2 : u16 = 0x088;
|
|
||||||
pub const LF_C3_WORD : u8 = 1 << 0;
|
|
||||||
pub const LF_R1_WORD : u8 = 1 << 4;
|
|
||||||
|
|
||||||
pub const DACLOOPFILT3 : u16 = 0x089;
|
|
||||||
pub const LF_R3_WORD : u8 = 1 << 0;
|
|
||||||
pub const LF_BYPASS_C1 : u8 = 1 << 4;
|
|
||||||
pub const LF_BYPASS_C2 : u8 = 1 << 5;
|
|
||||||
pub const LF_BYPASS_R1 : u8 = 1 << 6;
|
|
||||||
pub const LF_BYPASS_R3 : u8 = 1 << 7;
|
|
||||||
|
|
||||||
pub const DACCPCNTRL : u16 = 0x08a;
|
|
||||||
pub const CP_CURRENT : u8 = 1 << 0;
|
|
||||||
pub const VT_FORCE : u8 = 1 << 6;
|
|
||||||
|
|
||||||
pub const DACLOGENCNTRL : u16 = 0x08b;
|
|
||||||
pub const LODIVMODE : u8 = 1 << 0;
|
|
||||||
pub const LO_POWER_MODE : u8 = 1 << 4;
|
|
||||||
|
|
||||||
pub const DACLDOCNTRL1 : u16 = 0x08c;
|
|
||||||
pub const REFDIVMODE : u8 = 1 << 0;
|
|
||||||
pub const LDO_BYPASS_FLT : u8 = 1 << 6;
|
|
||||||
pub const LDO_REF_SEL : u8 = 1 << 7;
|
|
||||||
|
|
||||||
pub const DACLDOCNTRL2 : u16 = 0x08d;
|
|
||||||
pub const LDO_VDROP : u8 = 1 << 0;
|
|
||||||
pub const LDO_SEL : u8 = 1 << 2;
|
|
||||||
pub const LDO_INRUSH : u8 = 1 << 5;
|
|
||||||
pub const LDO_BYPASS : u8 = 1 << 7;
|
|
||||||
|
|
||||||
pub const DATA_FORMAT : u16 = 0x110;
|
|
||||||
pub const BINARY_FORMAT : u8 = 1 << 7;
|
|
||||||
|
|
||||||
pub const DATAPATH_CTRL : u16 = 0x111;
|
|
||||||
pub const I_TO_Q : u8 = 1 << 0;
|
|
||||||
pub const SEL_SIDEBAND : u8 = 1 << 1;
|
|
||||||
pub const MODULATION_TYPE : u8 = 1 << 2;
|
|
||||||
pub const PHASE_ADJ_ENABLE : u8 = 1 << 4;
|
|
||||||
pub const DIG_GAIN_ENABLE : u8 = 1 << 5;
|
|
||||||
pub const INVSINC_ENABLE : u8 = 1 << 7;
|
|
||||||
|
|
||||||
pub const INTERP_MODE : u16 = 0x112;
|
|
||||||
|
|
||||||
pub const NCO_FTW_UPDATE : u16 = 0x113;
|
|
||||||
pub const FTW_UPDATE_REQ : u8 = 1 << 0;
|
|
||||||
pub const FTW_UPDATE_ACK : u8 = 1 << 1;
|
|
||||||
|
|
||||||
pub const FTW0 : u16 = 0x114;
|
|
||||||
|
|
||||||
pub const FTW1 : u16 = 0x115;
|
|
||||||
|
|
||||||
pub const FTW2 : u16 = 0x116;
|
|
||||||
|
|
||||||
pub const FTW3 : u16 = 0x117;
|
|
||||||
|
|
||||||
pub const FTW4 : u16 = 0x118;
|
|
||||||
|
|
||||||
pub const FTW5 : u16 = 0x119;
|
|
||||||
|
|
||||||
pub const NCO_PHASE_OFFSET0 : u16 = 0x11a;
|
|
||||||
|
|
||||||
pub const NCO_PHASE_OFFSET1 : u16 = 0x11b;
|
|
||||||
|
|
||||||
pub const PHASE_ADJ0 : u16 = 0x11c;
|
|
||||||
|
|
||||||
pub const PHASE_ADJ1 : u16 = 0x11d;
|
|
||||||
|
|
||||||
pub const TXEN_SM_0 : u16 = 0x11f;
|
|
||||||
pub const TXEN_SM_EN : u8 = 1 << 0;
|
|
||||||
pub const GP_PA_CTRL : u8 = 1 << 1;
|
|
||||||
pub const GP_PA_ON_INVERT : u8 = 1 << 2;
|
|
||||||
pub const RISE_COUNTERS : u8 = 1 << 4;
|
|
||||||
pub const FALL_COUNTERS : u8 = 1 << 6;
|
|
||||||
|
|
||||||
pub const TXEN_RISE_COUNT_0 : u16 = 0x121;
|
|
||||||
|
|
||||||
pub const TXEN_RISE_COUNT_1 : u16 = 0x122;
|
|
||||||
|
|
||||||
pub const TXEN_FALL_COUNT_0 : u16 = 0x123;
|
|
||||||
|
|
||||||
pub const TXEN_FALL_COUNT_1 : u16 = 0x124;
|
|
||||||
|
|
||||||
pub const DEVICE_CONFIG_REG_0 : u16 = 0x12d;
|
|
||||||
|
|
||||||
pub const DIE_TEMP_CTRL0 : u16 = 0x12f;
|
|
||||||
pub const AUXADC_ENABLE : u8 = 1 << 0;
|
|
||||||
pub const AUXADC_RESERVED : u8 = 1 << 1;
|
|
||||||
|
|
||||||
pub const DIE_TEMP0 : u16 = 0x132;
|
|
||||||
|
|
||||||
pub const DIE_TEMP1 : u16 = 0x133;
|
|
||||||
|
|
||||||
pub const DIE_TEMP_UPDATE : u16 = 0x134;
|
|
||||||
|
|
||||||
pub const DC_OFFSET_CTRL : u16 = 0x135;
|
|
||||||
|
|
||||||
pub const IPATH_DC_OFFSET_1PART0 : u16 = 0x136;
|
|
||||||
|
|
||||||
pub const IPATH_DC_OFFSET_1PART1 : u16 = 0x137;
|
|
||||||
|
|
||||||
pub const QPATH_DC_OFFSET_1PART0 : u16 = 0x138;
|
|
||||||
|
|
||||||
pub const QPATH_DC_OFFSET_1PART1 : u16 = 0x139;
|
|
||||||
|
|
||||||
pub const IPATH_DC_OFFSET_2PART : u16 = 0x13a;
|
|
||||||
|
|
||||||
pub const QPATH_DC_OFFSET_2PART : u16 = 0x13b;
|
|
||||||
|
|
||||||
pub const IDAC_DIG_GAIN0 : u16 = 0x13c;
|
|
||||||
|
|
||||||
pub const IDAC_DIG_GAIN1 : u16 = 0x13d;
|
|
||||||
|
|
||||||
pub const QDAC_DIG_GAIN0 : u16 = 0x13e;
|
|
||||||
|
|
||||||
pub const QDAC_DIG_GAIN1 : u16 = 0x13f;
|
|
||||||
|
|
||||||
pub const GAIN_RAMP_UP_STEP0 : u16 = 0x140;
|
|
||||||
|
|
||||||
pub const GAIN_RAMP_UP_STEP1 : u16 = 0x141;
|
|
||||||
|
|
||||||
pub const GAIN_RAMP_DOWN_STEP0 : u16 = 0x142;
|
|
||||||
|
|
||||||
pub const GAIN_RAMP_DOWN_STEP1 : u16 = 0x143;
|
|
||||||
|
|
||||||
pub const DEVICE_CONFIG_REG_1 : u16 = 0x146;
|
|
||||||
|
|
||||||
pub const BSM_STAT : u16 = 0x147;
|
|
||||||
pub const SOFTBLANKRB : u8 = 1 << 6;
|
|
||||||
|
|
||||||
pub const PRBS : u16 = 0x14b;
|
|
||||||
pub const PRBS_EN : u8 = 1 << 0;
|
|
||||||
pub const PRBS_RESET : u8 = 1 << 1;
|
|
||||||
pub const PRBS_MODE : u8 = 1 << 2;
|
|
||||||
pub const PRBS_GOOD_I : u8 = 1 << 6;
|
|
||||||
pub const PRBS_GOOD_Q : u8 = 1 << 7;
|
|
||||||
|
|
||||||
pub const PRBS_ERROR_I : u16 = 0x14c;
|
|
||||||
|
|
||||||
pub const PRBS_ERROR_Q : u16 = 0x14d;
|
|
||||||
|
|
||||||
pub const DACPLLT0 : u16 = 0x1b0;
|
|
||||||
pub const LOGEN_PD : u8 = 1 << 1;
|
|
||||||
pub const LDO_PD : u8 = 1 << 3;
|
|
||||||
pub const SYNTH_PD : u8 = 1 << 4;
|
|
||||||
pub const VCO_PD_ALC : u8 = 1 << 5;
|
|
||||||
pub const VCO_PD_PTAT : u8 = 1 << 6;
|
|
||||||
pub const VCO_PD_IN : u8 = 1 << 7;
|
|
||||||
|
|
||||||
pub const DACPLLT1 : u16 = 0x1b1;
|
|
||||||
pub const PFD_EDGE : u8 = 1 << 1;
|
|
||||||
pub const PFD_DELAY : u8 = 1 << 2;
|
|
||||||
|
|
||||||
pub const DACPLLT2 : u16 = 0x1b2;
|
|
||||||
pub const EXT_ALC_WORD : u8 = 1 << 0;
|
|
||||||
pub const EXT_ALC_WORD_EN : u8 = 1 << 7;
|
|
||||||
|
|
||||||
pub const DACPLLT3 : u16 = 0x1b3;
|
|
||||||
pub const EXT_BAND1 : u8 = 1 << 0;
|
|
||||||
|
|
||||||
pub const DACPLLT4 : u16 = 0x1b4;
|
|
||||||
pub const EXT_BAND2 : u8 = 1 << 0;
|
|
||||||
pub const EXT_BAND_EN : u8 = 1 << 1;
|
|
||||||
pub const VCO_CAL_OFFSET : u8 = 1 << 3;
|
|
||||||
pub const BYP_LOAD_DELAY : u8 = 1 << 7;
|
|
||||||
|
|
||||||
pub const DACPLLT5 : u16 = 0x1b5;
|
|
||||||
|
|
||||||
pub const DACPLLT6 : u16 = 0x1b6;
|
|
||||||
|
|
||||||
pub const DACPLLT7 : u16 = 0x1b7;
|
|
||||||
|
|
||||||
pub const DACPLLT8 : u16 = 0x1b8;
|
|
||||||
|
|
||||||
pub const DACPLLT9 : u16 = 0x1b9;
|
|
||||||
|
|
||||||
pub const DACPLLTA : u16 = 0x1ba;
|
|
||||||
|
|
||||||
pub const DACPLLTB : u16 = 0x1bb;
|
|
||||||
pub const VCO_BIAS_REF : u8 = 1 << 0;
|
|
||||||
pub const VCO_BIAS_TCF : u8 = 1 << 3;
|
|
||||||
|
|
||||||
pub const DACPLLTC : u16 = 0x1bc;
|
|
||||||
|
|
||||||
pub const DACPLLTD : u16 = 0x1bd;
|
|
||||||
|
|
||||||
pub const DACPLLTE : u16 = 0x1be;
|
|
||||||
|
|
||||||
pub const DACPLLTF : u16 = 0x1bf;
|
|
||||||
|
|
||||||
pub const DACPLLT10 : u16 = 0x1c0;
|
|
||||||
|
|
||||||
pub const DACPLLT11 : u16 = 0x1c1;
|
|
||||||
|
|
||||||
pub const DACPLLT15 : u16 = 0x1c2;
|
|
||||||
|
|
||||||
pub const DACPLLT16 : u16 = 0x1c3;
|
|
||||||
|
|
||||||
pub const DACPLLT17 : u16 = 0x1c4;
|
|
||||||
|
|
||||||
pub const DACPLLT18 : u16 = 0x1c5;
|
|
||||||
|
|
||||||
pub const MASTER_PD : u16 = 0x200;
|
|
||||||
|
|
||||||
pub const PHY_PD : u16 = 0x201;
|
|
||||||
|
|
||||||
pub const GENERIC_PD : u16 = 0x203;
|
|
||||||
pub const PD_SYNCOUT1B : u8 = 1 << 0;
|
|
||||||
pub const PD_SYNCOUT0B : u8 = 1 << 1;
|
|
||||||
|
|
||||||
pub const CDR_RESET : u16 = 0x206;
|
|
||||||
|
|
||||||
pub const CDR_OPERATING_MODE_REG_0 : u16 = 0x230;
|
|
||||||
pub const CDR_OVERSAMP : u8 = 1 << 1;
|
|
||||||
pub const CDR_RESERVED : u8 = 1 << 2;
|
|
||||||
pub const ENHALFRATE : u8 = 1 << 5;
|
|
||||||
|
|
||||||
pub const EQ_BIAS_REG : u16 = 0x268;
|
|
||||||
pub const EQ_BIAS_RESERVED : u8 = 1 << 0;
|
|
||||||
pub const EQ_POWER_MODE : u8 = 1 << 6;
|
|
||||||
|
|
||||||
pub const SERDESPLL_ENABLE_CNTRL : u16 = 0x280;
|
|
||||||
pub const ENABLE_SERDESPLL : u8 = 1 << 0;
|
|
||||||
pub const RECAL_SERDESPLL : u8 = 1 << 2;
|
|
||||||
|
|
||||||
pub const PLL_STATUS : u16 = 0x281;
|
|
||||||
pub const SERDES_PLL_LOCK_RB : u8 = 1 << 0;
|
|
||||||
pub const SERDES_CURRENTS_READY_RB : u8 = 1 << 1;
|
|
||||||
pub const SERDES_VCO_CAL_IN_PROGRESS_RB : u8 = 1 << 2;
|
|
||||||
pub const SERDES_PLL_CAL_VALID_RB : u8 = 1 << 3;
|
|
||||||
pub const SERDES_PLL_OVERRANGE_L_RB : u8 = 1 << 4;
|
|
||||||
pub const SERDES_PLL_OVERRANGE_H_RB : u8 = 1 << 5;
|
|
||||||
|
|
||||||
pub const LDO_FILTER_1 : u16 = 0x284;
|
|
||||||
|
|
||||||
pub const LDO_FILTER_2 : u16 = 0x285;
|
|
||||||
|
|
||||||
pub const LDO_FILTER_3 : u16 = 0x286;
|
|
||||||
|
|
||||||
pub const CP_CURRENT_SPI : u16 = 0x287;
|
|
||||||
pub const SPI_CP_CURRENT : u8 = 1 << 0;
|
|
||||||
pub const SPI_SERDES_LOGEN_POWER_MODE : u8 = 1 << 6;
|
|
||||||
|
|
||||||
pub const REF_CLK_DIVIDER_LDO : u16 = 0x289;
|
|
||||||
pub const SPI_CDR_OVERSAMP : u8 = 1 << 0;
|
|
||||||
pub const SPI_LDO_BYPASS_FILT : u8 = 1 << 2;
|
|
||||||
pub const SPI_LDO_REF_SEL : u8 = 1 << 3;
|
|
||||||
|
|
||||||
pub const VCO_LDO : u16 = 0x28a;
|
|
||||||
|
|
||||||
pub const PLL_RD_REG : u16 = 0x28b;
|
|
||||||
pub const SPI_SERDES_LOGEN_PD_CORE : u8 = 1 << 0;
|
|
||||||
pub const SPI_SERDES_LDO_PD : u8 = 1 << 2;
|
|
||||||
pub const SPI_SYN_PD : u8 = 1 << 3;
|
|
||||||
pub const SPI_VCO_PD_ALC : u8 = 1 << 4;
|
|
||||||
pub const SPI_VCO_PD_PTAT : u8 = 1 << 5;
|
|
||||||
pub const SPI_VCO_PD : u8 = 1 << 6;
|
|
||||||
|
|
||||||
pub const ALC_VARACTOR : u16 = 0x290;
|
|
||||||
pub const SPI_VCO_VARACTOR : u8 = 1 << 0;
|
|
||||||
pub const SPI_INIT_ALC_VALUE : u8 = 1 << 4;
|
|
||||||
|
|
||||||
pub const VCO_OUTPUT : u16 = 0x291;
|
|
||||||
pub const SPI_VCO_OUTPUT_LEVEL : u8 = 1 << 0;
|
|
||||||
pub const SPI_VCO_OUTPUT_RESERVED : u8 = 1 << 4;
|
|
||||||
|
|
||||||
pub const CP_CONFIG : u16 = 0x294;
|
|
||||||
pub const SPI_CP_TEST : u8 = 1 << 0;
|
|
||||||
pub const SPI_CP_CAL_EN : u8 = 1 << 2;
|
|
||||||
pub const SPI_CP_FORCE_CALBITS : u8 = 1 << 3;
|
|
||||||
pub const SPI_CP_OFFSET_OFF : u8 = 1 << 4;
|
|
||||||
pub const SPI_CP_ENABLE_MACHINE : u8 = 1 << 5;
|
|
||||||
pub const SPI_CP_DITHER_MODE : u8 = 1 << 6;
|
|
||||||
pub const SPI_CP_HALF_VCO_CAL_CLK : u8 = 1 << 7;
|
|
||||||
|
|
||||||
pub const VCO_BIAS_1 : u16 = 0x296;
|
|
||||||
pub const SPI_VCO_BIAS_REF : u8 = 1 << 0;
|
|
||||||
pub const SPI_VCO_BIAS_TCF : u8 = 1 << 3;
|
|
||||||
|
|
||||||
pub const VCO_BIAS_2 : u16 = 0x297;
|
|
||||||
pub const SPI_PRESCALE_BIAS : u8 = 1 << 0;
|
|
||||||
pub const SPI_LAST_ALC_EN : u8 = 1 << 2;
|
|
||||||
pub const SPI_PRESCALE_BYPASS_R : u8 = 1 << 3;
|
|
||||||
pub const SPI_VCO_COMP_BYPASS_BIASR : u8 = 1 << 4;
|
|
||||||
pub const SPI_VCO_BYPASS_DAC_R : u8 = 1 << 5;
|
|
||||||
|
|
||||||
pub const VCO_PD_OVERRIDES : u16 = 0x299;
|
|
||||||
pub const SPI_VCO_PD_OVERRIDE_VCO_BUF : u8 = 1 << 0;
|
|
||||||
pub const SPI_VCO_PD_OVERRIDE_CAL_TCF : u8 = 1 << 1;
|
|
||||||
pub const SPI_VCO_PD_OVERRIDE_VAR_REF_TCF : u8 = 1 << 2;
|
|
||||||
pub const SPI_VCO_PD_OVERRIDE_VAR_REF : u8 = 1 << 3;
|
|
||||||
|
|
||||||
pub const VCO_CAL : u16 = 0x29a;
|
|
||||||
pub const SPI_FB_CLOCK_ADV : u8 = 1 << 0;
|
|
||||||
pub const SPI_VCO_CAL_COUNT : u8 = 1 << 2;
|
|
||||||
pub const SPI_VCO_CAL_ALC_WAIT : u8 = 1 << 4;
|
|
||||||
pub const SPI_VCO_CAL_EN : u8 = 1 << 7;
|
|
||||||
|
|
||||||
pub const CP_LEVEL_DETECT : u16 = 0x29c;
|
|
||||||
pub const SPI_CP_LEVEL_THRESHOLD_HIGH : u8 = 1 << 0;
|
|
||||||
pub const SPI_CP_LEVEL_THRESHOLD_LOW : u8 = 1 << 3;
|
|
||||||
pub const SPI_CP_LEVEL_DET_PD : u8 = 1 << 6;
|
|
||||||
|
|
||||||
pub const VCO_VARACTOR_CTRL_0 : u16 = 0x29f;
|
|
||||||
pub const SPI_VCO_VARACTOR_OFFSET : u8 = 1 << 0;
|
|
||||||
pub const SPI_VCO_VARACTOR_REF_TCF : u8 = 1 << 4;
|
|
||||||
|
|
||||||
pub const VCO_VARACTOR_CTRL_1 : u16 = 0x2a0;
|
|
||||||
pub const SPI_VCO_VARACTOR_REF : u8 = 1 << 0;
|
|
||||||
|
|
||||||
pub const TERM_BLK1_CTRLREG0 : u16 = 0x2a7;
|
|
||||||
|
|
||||||
pub const TERM_BLK2_CTRLREG0 : u16 = 0x2ae;
|
|
||||||
|
|
||||||
pub const GENERAL_JRX_CTRL_0 : u16 = 0x300;
|
|
||||||
pub const LINK_EN : u8 = 1 << 0;
|
|
||||||
pub const LINK_PAGE : u8 = 1 << 2;
|
|
||||||
pub const LINK_MODE : u8 = 1 << 3;
|
|
||||||
pub const CHECKSUM_MODE : u8 = 1 << 6;
|
|
||||||
|
|
||||||
pub const GENERAL_JRX_CTRL_1 : u16 = 0x301;
|
|
||||||
|
|
||||||
pub const DYN_LINK_LATENCY_0 : u16 = 0x302;
|
|
||||||
|
|
||||||
pub const DYN_LINK_LATENCY_1 : u16 = 0x303;
|
|
||||||
|
|
||||||
pub const LMFC_DELAY_0 : u16 = 0x304;
|
|
||||||
|
|
||||||
pub const LMFC_DELAY_1 : u16 = 0x305;
|
|
||||||
|
|
||||||
pub const LMFC_VAR_0 : u16 = 0x306;
|
|
||||||
|
|
||||||
pub const LMFC_VAR_1 : u16 = 0x307;
|
|
||||||
|
|
||||||
pub const XBAR_LN_0_1 : u16 = 0x308;
|
|
||||||
pub const LOGICAL_LANE0_SRC : u8 = 1 << 0;
|
|
||||||
pub const LOGICAL_LANE1_SRC : u8 = 1 << 3;
|
|
||||||
|
|
||||||
pub const XBAR_LN_2_3 : u16 = 0x309;
|
|
||||||
pub const LOGICAL_LANE2_SRC : u8 = 1 << 0;
|
|
||||||
pub const LOGICAL_LANE3_SRC : u8 = 1 << 3;
|
|
||||||
|
|
||||||
pub const XBAR_LN_4_5 : u16 = 0x30a;
|
|
||||||
pub const LOGICAL_LANE4_SRC : u8 = 1 << 0;
|
|
||||||
pub const LOGICAL_LANE5_SRC : u8 = 1 << 3;
|
|
||||||
|
|
||||||
pub const XBAR_LN_6_7 : u16 = 0x30b;
|
|
||||||
pub const LOGICAL_LANE6_SRC : u8 = 1 << 0;
|
|
||||||
pub const LOGICAL_LANE7_SRC : u8 = 1 << 3;
|
|
||||||
|
|
||||||
pub const FIFO_STATUS_REG_0 : u16 = 0x30c;
|
|
||||||
|
|
||||||
pub const FIFO_STATUS_REG_1 : u16 = 0x30d;
|
|
||||||
|
|
||||||
pub const SYNCB_GEN_1 : u16 = 0x312;
|
|
||||||
pub const SYNCB_ERR_DUR : u8 = 1 << 4;
|
|
||||||
|
|
||||||
pub const SERDES_SPI_REG : u16 = 0x314;
|
|
||||||
|
|
||||||
pub const PHY_PRBS_TEST_EN : u16 = 0x315;
|
|
||||||
|
|
||||||
pub const PHY_PRBS_TEST_CTRL : u16 = 0x316;
|
|
||||||
pub const PHY_TEST_RESET : u8 = 1 << 0;
|
|
||||||
pub const PHY_TEST_START : u8 = 1 << 1;
|
|
||||||
pub const PHY_PRBS_PAT_SEL : u8 = 1 << 2;
|
|
||||||
pub const PHY_SRC_ERR_CNT : u8 = 1 << 4;
|
|
||||||
|
|
||||||
pub const PHY_PRBS_TEST_THRESHOLD_LOBITS : u16 = 0x317;
|
|
||||||
|
|
||||||
pub const PHY_PRBS_TEST_THRESHOLD_MIDBITS : u16 = 0x318;
|
|
||||||
|
|
||||||
pub const PHY_PRBS_TEST_THRESHOLD_HIBITS : u16 = 0x319;
|
|
||||||
|
|
||||||
pub const PHY_PRBS_TEST_ERRCNT_LOBITS : u16 = 0x31a;
|
|
||||||
|
|
||||||
pub const PHY_PRBS_TEST_ERRCNT_MIDBITS : u16 = 0x31b;
|
|
||||||
|
|
||||||
pub const PHY_PRBS_TEST_ERRCNT_HIBITS : u16 = 0x31c;
|
|
||||||
|
|
||||||
pub const PHY_PRBS_TEST_STATUS : u16 = 0x31d;
|
|
||||||
|
|
||||||
pub const SHORT_TPL_TEST_0 : u16 = 0x32c;
|
|
||||||
pub const SHORT_TPL_TEST_EN : u8 = 1 << 0;
|
|
||||||
pub const SHORT_TPL_TEST_RESET : u8 = 1 << 1;
|
|
||||||
pub const SHORT_TPL_DAC_SEL : u8 = 1 << 2;
|
|
||||||
pub const SHORT_TPL_SP_SEL : u8 = 1 << 4;
|
|
||||||
|
|
||||||
pub const SHORT_TPL_TEST_1 : u16 = 0x32d;
|
|
||||||
|
|
||||||
pub const SHORT_TPL_TEST_2 : u16 = 0x32e;
|
|
||||||
|
|
||||||
pub const SHORT_TPL_TEST_3 : u16 = 0x32f;
|
|
||||||
|
|
||||||
pub const DEVICE_CONFIG_REG_2 : u16 = 0x333;
|
|
||||||
|
|
||||||
pub const JESD_BIT_INVERSE_CTRL : u16 = 0x334;
|
|
||||||
|
|
||||||
pub const DID_REG : u16 = 0x400;
|
|
||||||
|
|
||||||
pub const BID_REG : u16 = 0x401;
|
|
||||||
pub const BID_RD : u8 = 1 << 0;
|
|
||||||
pub const ADJCNT_RD : u8 = 1 << 4;
|
|
||||||
|
|
||||||
pub const LID0_REG : u16 = 0x402;
|
|
||||||
pub const LID0_RD : u8 = 1 << 0;
|
|
||||||
pub const PHADJ_RD : u8 = 1 << 5;
|
|
||||||
pub const ADJDIR_RD : u8 = 1 << 6;
|
|
||||||
|
|
||||||
pub const SCR_L_REG : u16 = 0x403;
|
|
||||||
pub const L_1_RD : u8 = 1 << 0;
|
|
||||||
pub const SCR_RD : u8 = 1 << 7;
|
|
||||||
|
|
||||||
pub const F_REG : u16 = 0x404;
|
|
||||||
|
|
||||||
pub const K_REG : u16 = 0x405;
|
|
||||||
|
|
||||||
pub const M_REG : u16 = 0x406;
|
|
||||||
|
|
||||||
pub const CS_N_REG : u16 = 0x407;
|
|
||||||
pub const N_1_RD : u8 = 1 << 0;
|
|
||||||
pub const CS_RD : u8 = 1 << 6;
|
|
||||||
|
|
||||||
pub const NP_REG : u16 = 0x408;
|
|
||||||
pub const NP_1_RD : u8 = 1 << 0;
|
|
||||||
pub const SUBCLASSV_RD : u8 = 1 << 5;
|
|
||||||
|
|
||||||
pub const S_REG : u16 = 0x409;
|
|
||||||
pub const S_1_RD : u8 = 1 << 0;
|
|
||||||
pub const JESDV_RD : u8 = 1 << 5;
|
|
||||||
|
|
||||||
pub const HD_CF_REG : u16 = 0x40a;
|
|
||||||
pub const CF_RD : u8 = 1 << 0;
|
|
||||||
pub const HD_RD : u8 = 1 << 7;
|
|
||||||
|
|
||||||
pub const RES1_REG : u16 = 0x40b;
|
|
||||||
|
|
||||||
pub const RES2_REG : u16 = 0x40c;
|
|
||||||
|
|
||||||
pub const CHECKSUM0_REG : u16 = 0x40d;
|
|
||||||
|
|
||||||
pub const COMPSUM0_REG : u16 = 0x40e;
|
|
||||||
|
|
||||||
pub const LID1_REG : u16 = 0x412;
|
|
||||||
|
|
||||||
pub const CHECKSUM1_REG : u16 = 0x415;
|
|
||||||
|
|
||||||
pub const COMPSUM1_REG : u16 = 0x416;
|
|
||||||
|
|
||||||
pub const LID2_REG : u16 = 0x41a;
|
|
||||||
|
|
||||||
pub const CHECKSUM2_REG : u16 = 0x41d;
|
|
||||||
|
|
||||||
pub const COMPSUM2_REG : u16 = 0x41e;
|
|
||||||
|
|
||||||
pub const LID3_REG : u16 = 0x422;
|
|
||||||
|
|
||||||
pub const CHECKSUM3_REG : u16 = 0x425;
|
|
||||||
|
|
||||||
pub const COMPSUM3_REG : u16 = 0x426;
|
|
||||||
|
|
||||||
pub const LID4_REG : u16 = 0x42a;
|
|
||||||
|
|
||||||
pub const CHECKSUM4_REG : u16 = 0x42d;
|
|
||||||
|
|
||||||
pub const COMPSUM4_REG : u16 = 0x42e;
|
|
||||||
|
|
||||||
pub const LID5_REG : u16 = 0x432;
|
|
||||||
|
|
||||||
pub const CHECKSUM5_REG : u16 = 0x435;
|
|
||||||
|
|
||||||
pub const COMPSUM5_REG : u16 = 0x436;
|
|
||||||
|
|
||||||
pub const LID6_REG : u16 = 0x43a;
|
|
||||||
|
|
||||||
pub const CHECKSUM6_REG : u16 = 0x43d;
|
|
||||||
|
|
||||||
pub const COMPSUM6_REG : u16 = 0x43e;
|
|
||||||
|
|
||||||
pub const LID7_REG : u16 = 0x442;
|
|
||||||
|
|
||||||
pub const CHECKSUM7_REG : u16 = 0x445;
|
|
||||||
|
|
||||||
pub const COMPSUM7_REG : u16 = 0x446;
|
|
||||||
|
|
||||||
pub const ILS_DID : u16 = 0x450;
|
|
||||||
|
|
||||||
pub const ILS_BID : u16 = 0x451;
|
|
||||||
pub const BID : u8 = 1 << 0;
|
|
||||||
pub const ADJCNT : u8 = 1 << 4;
|
|
||||||
|
|
||||||
pub const ILS_LID0 : u16 = 0x452;
|
|
||||||
pub const LID0 : u8 = 1 << 0;
|
|
||||||
pub const PHADJ : u8 = 1 << 5;
|
|
||||||
pub const ADJDIR : u8 = 1 << 6;
|
|
||||||
|
|
||||||
pub const ILS_SCR_L : u16 = 0x453;
|
|
||||||
pub const L_1 : u8 = 1 << 0;
|
|
||||||
pub const SCR : u8 = 1 << 7;
|
|
||||||
|
|
||||||
pub const ILS_F : u16 = 0x454;
|
|
||||||
|
|
||||||
pub const ILS_K : u16 = 0x455;
|
|
||||||
|
|
||||||
pub const ILS_M : u16 = 0x456;
|
|
||||||
|
|
||||||
pub const ILS_CS_N : u16 = 0x457;
|
|
||||||
pub const N_1 : u8 = 1 << 0;
|
|
||||||
pub const CS : u8 = 1 << 6;
|
|
||||||
|
|
||||||
pub const ILS_NP : u16 = 0x458;
|
|
||||||
pub const NP_1 : u8 = 1 << 0;
|
|
||||||
pub const SUBCLASSV : u8 = 1 << 5;
|
|
||||||
|
|
||||||
pub const ILS_S : u16 = 0x459;
|
|
||||||
pub const S_1 : u8 = 1 << 0;
|
|
||||||
pub const JESDV : u8 = 1 << 5;
|
|
||||||
|
|
||||||
pub const ILS_HD_CF : u16 = 0x45a;
|
|
||||||
pub const CF : u8 = 1 << 0;
|
|
||||||
pub const HD : u8 = 1 << 7;
|
|
||||||
|
|
||||||
pub const ILS_RES1 : u16 = 0x45b;
|
|
||||||
|
|
||||||
pub const ILS_RES2 : u16 = 0x45c;
|
|
||||||
|
|
||||||
pub const ILS_CHECKSUM : u16 = 0x45d;
|
|
||||||
|
|
||||||
pub const ERRCNTRMON : u16 = 0x46b;
|
|
||||||
pub const CNTRSEL : u8 = 1 << 0;
|
|
||||||
pub const LANESEL : u8 = 1 << 4;
|
|
||||||
|
|
||||||
pub const LANEDESKEW : u16 = 0x46c;
|
|
||||||
|
|
||||||
pub const BADDISPARITY : u16 = 0x46d;
|
|
||||||
pub const LANE_ADDR_DIS : u8 = 1 << 0;
|
|
||||||
pub const RST_ERR_CNTR_DIS : u8 = 1 << 5;
|
|
||||||
pub const DISABLE_ERR_CNTR_DIS : u8 = 1 << 6;
|
|
||||||
pub const RST_IRQ_DIS : u8 = 1 << 7;
|
|
||||||
|
|
||||||
pub const NIT_W : u16 = 0x46e;
|
|
||||||
pub const LANE_ADDR_NIT : u8 = 1 << 0;
|
|
||||||
pub const RST_ERR_CNTR_NIT : u8 = 1 << 5;
|
|
||||||
pub const DISABLE_ERR_CNTR_NIT : u8 = 1 << 6;
|
|
||||||
pub const RST_IRQ_NIT : u8 = 1 << 7;
|
|
||||||
|
|
||||||
pub const UNEXPECTEDCONTROL_W : u16 = 0x46f;
|
|
||||||
pub const LANE_ADDR_UCC : u8 = 1 << 0;
|
|
||||||
pub const RST_ERR_CNTR_UCC : u8 = 1 << 5;
|
|
||||||
pub const DISABLE_ERR_CNTR_UCC : u8 = 1 << 6;
|
|
||||||
pub const RST_IRQ_UCC : u8 = 1 << 7;
|
|
||||||
|
|
||||||
pub const CODEGRPSYNCFLG : u16 = 0x470;
|
|
||||||
|
|
||||||
pub const FRAMESYNCFLG : u16 = 0x471;
|
|
||||||
|
|
||||||
pub const GOODCHKSUMFLG : u16 = 0x472;
|
|
||||||
|
|
||||||
pub const INITLANESYNCFLG : u16 = 0x473;
|
|
||||||
|
|
||||||
pub const CTRLREG1 : u16 = 0x476;
|
|
||||||
|
|
||||||
pub const CTRLREG2 : u16 = 0x477;
|
|
||||||
pub const THRESHOLD_MASK_EN : u8 = 1 << 3;
|
|
||||||
pub const ILAS_MODE : u8 = 1 << 7;
|
|
||||||
|
|
||||||
pub const KVAL : u16 = 0x478;
|
|
||||||
|
|
||||||
pub const IRQVECTOR_MASK : u16 = 0x47a;
|
|
||||||
pub const CODEGRPSYNC_MASK : u8 = 1 << 0;
|
|
||||||
pub const BADCHECKSUM_MASK : u8 = 1 << 2;
|
|
||||||
pub const INITIALLANESYNC_MASK : u8 = 1 << 3;
|
|
||||||
pub const UCC_MASK : u8 = 1 << 5;
|
|
||||||
pub const NIT_MASK : u8 = 1 << 6;
|
|
||||||
pub const BADDIS_MASK : u8 = 1 << 7;
|
|
||||||
|
|
||||||
pub const SYNCASSERTIONMASK : u16 = 0x47b;
|
|
||||||
pub const CMM_ENABLE : u8 = 1 << 3;
|
|
||||||
pub const CMM : u8 = 1 << 4;
|
|
||||||
pub const UCC_S : u8 = 1 << 5;
|
|
||||||
pub const NIT_S : u8 = 1 << 6;
|
|
||||||
pub const BADDIS_S : u8 = 1 << 7;
|
|
||||||
|
|
||||||
pub const ERRORTHRES : u16 = 0x47c;
|
|
||||||
|
|
||||||
pub const LANEENABLE : u16 = 0x47d;
|
|
||||||
|
|
||||||
pub const RAMP_ENA : u16 = 0x47e;
|
|
||||||
|
|
||||||
pub const DIG_TEST0 : u16 = 0x520;
|
|
||||||
pub const DC_TEST_MODE : u8 = 1 << 1;
|
|
||||||
|
|
||||||
pub const DC_TEST_VALUEI0 : u16 = 0x521;
|
|
||||||
|
|
||||||
pub const DC_TEST_VALUEI1 : u16 = 0x522;
|
|
||||||
|
|
||||||
pub const DC_TEST_VALUEQ0 : u16 = 0x523;
|
|
||||||
|
|
||||||
pub const DC_TEST_VALUEQ1 : u16 = 0x524;
|
|
|
@ -1,419 +0,0 @@
|
||||||
mod hmc830 {
|
|
||||||
use board_misoc::{csr, clock};
|
|
||||||
|
|
||||||
fn spi_setup() {
|
|
||||||
unsafe {
|
|
||||||
while csr::converter_spi::idle_read() == 0 {}
|
|
||||||
csr::converter_spi::offline_write(0);
|
|
||||||
csr::converter_spi::end_write(1);
|
|
||||||
csr::converter_spi::cs_polarity_write(0b0001);
|
|
||||||
csr::converter_spi::clk_polarity_write(0);
|
|
||||||
csr::converter_spi::clk_phase_write(0);
|
|
||||||
csr::converter_spi::lsb_first_write(0);
|
|
||||||
csr::converter_spi::half_duplex_write(0);
|
|
||||||
csr::converter_spi::length_write(32 - 1);
|
|
||||||
csr::converter_spi::div_write(16 - 2);
|
|
||||||
csr::converter_spi::cs_write(1 << csr::CONFIG_CONVERTER_SPI_HMC830_CS);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn select_spi_mode() {
|
|
||||||
spi_setup();
|
|
||||||
unsafe {
|
|
||||||
// rising egde on CS since cs_polarity still 0
|
|
||||||
// selects "HMC Mode"
|
|
||||||
// do a dummy cycle with cs still high to clear CS
|
|
||||||
csr::converter_spi::length_write(0);
|
|
||||||
csr::converter_spi::data_write(0);
|
|
||||||
while csr::converter_spi::writable_read() == 0 {}
|
|
||||||
csr::converter_spi::length_write(32 - 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn write(addr: u8, data: u32) {
|
|
||||||
let val = ((addr as u32) << 24) | data;
|
|
||||||
unsafe {
|
|
||||||
while csr::converter_spi::writable_read() == 0 {}
|
|
||||||
csr::converter_spi::data_write(val << 1); // last clk cycle loads data
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn read(addr: u8) -> u32 {
|
|
||||||
// SDO (miso/read bits) is technically CPHA=1, while SDI is CPHA=0
|
|
||||||
// trust that the 8.2ns+0.2ns/pF provide enough hold time on top of
|
|
||||||
// the SPI round trip delay and stick with CPHA=0
|
|
||||||
write((1 << 6) | addr, 0);
|
|
||||||
unsafe {
|
|
||||||
while csr::converter_spi::writable_read() == 0 {}
|
|
||||||
csr::converter_spi::data_read() & 0xffffff
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn detect() -> Result<(), &'static str> {
|
|
||||||
spi_setup();
|
|
||||||
let id = read(0x00);
|
|
||||||
if id != 0xa7975 {
|
|
||||||
error!("invalid HMC830 ID: 0x{:08x}", id);
|
|
||||||
return Err("invalid HMC830 identification");
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn init() {
|
|
||||||
// Configure HMC830 for integer-N operation
|
|
||||||
// See "PLLs with integrated VCO- RF Applications Product & Operating
|
|
||||||
// Guide"
|
|
||||||
spi_setup();
|
|
||||||
info!("loading HMC830 configuration...");
|
|
||||||
|
|
||||||
write(0x0, 0x20); // software reset
|
|
||||||
write(0x0, 0x00); // normal operation
|
|
||||||
write(0x6, 0x307ca); // integer-N mode (NB data sheet table 5.8 not self-consistent)
|
|
||||||
write(0x7, 0x4d); // digital lock detect, 1/2 cycle window (6.5ns window)
|
|
||||||
write(0x9, 0x2850); // charge pump: 1.6mA, no offset
|
|
||||||
write(0xa, 0x2045); // for wideband devices like the HMC830
|
|
||||||
write(0xb, 0x7c061); // for HMC830
|
|
||||||
|
|
||||||
// VCO subsystem registers
|
|
||||||
// NB software reset does not seem to reset these registers, so always
|
|
||||||
// program them all!
|
|
||||||
write(0x5, 0xf88); // 1: defaults
|
|
||||||
write(0x5, 0x6010); // 2: mute output until output divider set
|
|
||||||
write(0x5, 0x2818); // 3: wideband PLL defaults
|
|
||||||
write(0x5, 0x60a0); // 4: HMC830 magic value
|
|
||||||
write(0x5, 0x1628); // 5: HMC830 magic value
|
|
||||||
write(0x5, 0x7fb0); // 6: HMC830 magic value
|
|
||||||
write(0x5, 0x0); // ready for VCO auto-cal
|
|
||||||
|
|
||||||
info!(" ...done");
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_dividers(r_div: u32, n_div: u32, m_div: u32, out_div: u32) {
|
|
||||||
// VCO frequency: f_vco = (f_ref/r_div)*(n_int + n_frac/2**24)
|
|
||||||
// VCO frequency range [1.5GHz, 3GHz]
|
|
||||||
// Output frequency: f_out = f_vco/out_div
|
|
||||||
// Max PFD frequency: 125MHz for integer-N, 100MHz for fractional
|
|
||||||
// (mode B)
|
|
||||||
// Max reference frequency: 350MHz, however f_ref >= 200MHz requires
|
|
||||||
// setting 0x08[21]=1
|
|
||||||
//
|
|
||||||
// Warning: Output divider is not synchronized! Set to 1 for deterministic
|
|
||||||
// phase at the output.
|
|
||||||
//
|
|
||||||
// :param r_div: reference divider [1, 16383]
|
|
||||||
// :param n_div: VCO divider, integer part. Integer-N mode: [16, 2**19-1]
|
|
||||||
// fractional mode: [20, 2**19-4]
|
|
||||||
// :param m_div: VCO divider, fractional part [0, 2**24-1]
|
|
||||||
// :param out_div: output divider [1, 62] (0 mutes output)
|
|
||||||
info!("setting HMC830 dividers...");
|
|
||||||
write(0x5, 0x6010 + (out_div << 7) + (((out_div <= 2) as u32) << 15));
|
|
||||||
write(0x5, 0x0); // ready for VCO auto-cal
|
|
||||||
write(0x2, r_div);
|
|
||||||
write(0x4, m_div);
|
|
||||||
write(0x3, n_div);
|
|
||||||
|
|
||||||
info!(" ...done");
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn check_locked() -> Result<(), &'static str> {
|
|
||||||
info!("waiting for HMC830 lock...");
|
|
||||||
let t = clock::get_ms();
|
|
||||||
while read(0x12) & 0x02 == 0 {
|
|
||||||
if clock::get_ms() > t + 2000 {
|
|
||||||
error!("lock timeout. Register dump:");
|
|
||||||
for addr in 0x00..0x14 {
|
|
||||||
// These registers don't exist (in the data sheet at least)
|
|
||||||
if addr == 0x0d || addr == 0x0e { continue; }
|
|
||||||
error!(" [0x{:02x}] = 0x{:04x}", addr, read(addr));
|
|
||||||
}
|
|
||||||
return Err("lock timeout");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
info!(" ...locked");
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub mod hmc7043 {
|
|
||||||
use board_misoc::{csr, clock};
|
|
||||||
|
|
||||||
// Warning: dividers are not synchronized with HMC830 clock input!
|
|
||||||
// Set DAC_CLK_DIV to 1 or 0 for deterministic phase.
|
|
||||||
// (0 bypasses the divider and reduces noise)
|
|
||||||
const DAC_CLK_DIV: u16 = 0;
|
|
||||||
const FPGA_CLK_DIV: u16 = 16; // Keep in sync with jdcg.rs
|
|
||||||
const SYSREF_DIV: u16 = 256; // Keep in sync with jdcg.rs
|
|
||||||
const HMC_SYSREF_DIV: u16 = SYSREF_DIV*8; // must be <= 4MHz
|
|
||||||
|
|
||||||
// enabled, divider, output config, is sysref
|
|
||||||
const OUTPUT_CONFIG: [(bool, u16, u8, bool); 14] = [
|
|
||||||
(true, DAC_CLK_DIV, 0x08, false), // 0: DAC1_CLK
|
|
||||||
(true, SYSREF_DIV, 0x01, true), // 1: DAC1_SYSREF
|
|
||||||
(true, DAC_CLK_DIV, 0x08, false), // 2: DAC0_CLK
|
|
||||||
(true, SYSREF_DIV, 0x01, true), // 3: DAC0_SYSREF
|
|
||||||
(true, SYSREF_DIV, 0x10, true), // 4: AMC_FPGA_SYSREF0
|
|
||||||
(false, FPGA_CLK_DIV, 0x10, true), // 5: AMC_FPGA_SYSREF1
|
|
||||||
(false, 0, 0x10, false), // 6: unused
|
|
||||||
(true, FPGA_CLK_DIV, 0x10, true), // 7: RTM_FPGA_SYSREF0
|
|
||||||
(true, FPGA_CLK_DIV, 0x08, false), // 8: GTP_CLK0_IN
|
|
||||||
(false, 0, 0x10, false), // 9: unused
|
|
||||||
(false, 0, 0x10, false), // 10: unused
|
|
||||||
(false, 0, 0x08, false), // 11: unused / uFL
|
|
||||||
(false, 0, 0x10, false), // 12: unused
|
|
||||||
(false, FPGA_CLK_DIV, 0x10, true), // 13: RTM_FPGA_SYSREF1
|
|
||||||
];
|
|
||||||
|
|
||||||
fn spi_setup() {
|
|
||||||
unsafe {
|
|
||||||
while csr::converter_spi::idle_read() == 0 {}
|
|
||||||
csr::converter_spi::offline_write(0);
|
|
||||||
csr::converter_spi::end_write(1);
|
|
||||||
csr::converter_spi::cs_polarity_write(0b0001);
|
|
||||||
csr::converter_spi::clk_polarity_write(0);
|
|
||||||
csr::converter_spi::clk_phase_write(0);
|
|
||||||
csr::converter_spi::lsb_first_write(0);
|
|
||||||
csr::converter_spi::half_duplex_write(0); // change mid-transaction for reads
|
|
||||||
csr::converter_spi::length_write(24 - 1);
|
|
||||||
csr::converter_spi::div_write(16 - 2);
|
|
||||||
csr::converter_spi::cs_write(1 << csr::CONFIG_CONVERTER_SPI_HMC7043_CS);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn spi_wait_idle() {
|
|
||||||
unsafe {
|
|
||||||
while csr::converter_spi::idle_read() == 0 {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn write(addr: u16, data: u8) {
|
|
||||||
let cmd = (0 << 15) | addr;
|
|
||||||
let val = ((cmd as u32) << 8) | data as u32;
|
|
||||||
unsafe {
|
|
||||||
while csr::converter_spi::writable_read() == 0 {}
|
|
||||||
csr::converter_spi::data_write(val << 8);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn read(addr: u16) -> u8 {
|
|
||||||
let cmd = (1 << 15) | addr;
|
|
||||||
let val = cmd as u32;
|
|
||||||
unsafe {
|
|
||||||
while csr::converter_spi::writable_read() == 0 {}
|
|
||||||
csr::converter_spi::end_write(0);
|
|
||||||
csr::converter_spi::length_write(16 - 1);
|
|
||||||
csr::converter_spi::data_write(val << 16);
|
|
||||||
while csr::converter_spi::writable_read() == 0 {}
|
|
||||||
csr::converter_spi::end_write(1);
|
|
||||||
csr::converter_spi::half_duplex_write(1);
|
|
||||||
csr::converter_spi::length_write(8 - 1);
|
|
||||||
csr::converter_spi::data_write(0);
|
|
||||||
while csr::converter_spi::writable_read() == 0 {}
|
|
||||||
csr::converter_spi::half_duplex_write(0);
|
|
||||||
csr::converter_spi::length_write(24 - 1);
|
|
||||||
csr::converter_spi::data_read() as u8
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub const CHIP_ID: u32 = 0xf17904;
|
|
||||||
|
|
||||||
pub fn get_id() -> u32 {
|
|
||||||
spi_setup();
|
|
||||||
(read(0x78) as u32) << 16 | (read(0x79) as u32) << 8 | read(0x7a) as u32
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn detect() -> Result<(), &'static str> {
|
|
||||||
let id = get_id();
|
|
||||||
if id != CHIP_ID {
|
|
||||||
error!("invalid HMC7043 ID: 0x{:08x}", id);
|
|
||||||
return Err("invalid HMC7043 identification");
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn enable() {
|
|
||||||
info!("enabling HMC7043");
|
|
||||||
|
|
||||||
unsafe {
|
|
||||||
csr::hmc7043_reset::out_write(0);
|
|
||||||
}
|
|
||||||
clock::spin_us(10_000);
|
|
||||||
|
|
||||||
spi_setup();
|
|
||||||
write(0x0, 0x1); // Software reset
|
|
||||||
write(0x0, 0x0); // Normal operation
|
|
||||||
write(0x1, 0x48); // mute all outputs
|
|
||||||
}
|
|
||||||
|
|
||||||
const GPO_MUX_CLK_OUT_PHASE: u8 = 3;
|
|
||||||
const GPO_MUX_FORCE1: u8 = 10;
|
|
||||||
const GPO_MUX_FORCE0: u8 = 11;
|
|
||||||
|
|
||||||
/* Read an HMC7043 internal status bit through the GPO interface.
|
|
||||||
* This method is required to work around bugs in the register interface.
|
|
||||||
*/
|
|
||||||
fn gpo_indirect_read(mux_setting: u8) -> bool {
|
|
||||||
write(0x50, (mux_setting << 2) | 0x3);
|
|
||||||
spi_wait_idle();
|
|
||||||
unsafe {
|
|
||||||
csr::hmc7043_gpo::in_read() == 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn init() {
|
|
||||||
spi_setup();
|
|
||||||
info!("loading configuration...");
|
|
||||||
|
|
||||||
write(0x3, 0x14); // Disable the RFSYNCIN reseeder
|
|
||||||
write(0xA, 0x06); // Disable the RFSYNCIN input buffer
|
|
||||||
write(0xB, 0x07); // Enable the CLKIN input as LVPECL
|
|
||||||
write(0x9F, 0x4d); // Unexplained high-performance mode
|
|
||||||
write(0xA0, 0xdf); // Unexplained high-performance mode
|
|
||||||
|
|
||||||
// Enable required output groups
|
|
||||||
let mut output_group_en = 0;
|
|
||||||
for channel in 0..OUTPUT_CONFIG.len() {
|
|
||||||
let enabled = OUTPUT_CONFIG[channel].0;
|
|
||||||
if enabled {
|
|
||||||
let group = channel/2;
|
|
||||||
output_group_en |= 1 << group;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
write(0x4, output_group_en);
|
|
||||||
|
|
||||||
// Set SYSREF timer divider.
|
|
||||||
// We don't need this "feature", but the HMC7043 won't work without.
|
|
||||||
write(0x5c, (HMC_SYSREF_DIV & 0xff) as u8);
|
|
||||||
write(0x5d, ((HMC_SYSREF_DIV & 0xf00) >> 8) as u8);
|
|
||||||
|
|
||||||
for channel in 0..OUTPUT_CONFIG.len() {
|
|
||||||
let channel_base = 0xc8 + 0x0a*(channel as u16);
|
|
||||||
let (enabled, divider, outcfg, is_sysref) = OUTPUT_CONFIG[channel];
|
|
||||||
|
|
||||||
if enabled {
|
|
||||||
if !is_sysref {
|
|
||||||
// DCLK channel: enable high-performance mode
|
|
||||||
write(channel_base, 0xd1);
|
|
||||||
} else {
|
|
||||||
// SYSREF channel: disable hi-perf mode, enable slip
|
|
||||||
write(channel_base, 0x71);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
write(channel_base, 0x10);
|
|
||||||
}
|
|
||||||
write(channel_base + 0x1, (divider & 0xff) as u8);
|
|
||||||
write(channel_base + 0x2, ((divider & 0xf00) >> 8) as u8);
|
|
||||||
|
|
||||||
// bypass analog phase shift on DCLK channels to reduce noise
|
|
||||||
if !is_sysref {
|
|
||||||
if divider != 0 {
|
|
||||||
write(channel_base + 0x7, 0x00); // enable divider
|
|
||||||
} else {
|
|
||||||
write(channel_base + 0x7, 0x03); // bypass divider for lowest noise
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
write(channel_base + 0x7, 0x01);
|
|
||||||
}
|
|
||||||
|
|
||||||
write(channel_base + 0x8, outcfg)
|
|
||||||
}
|
|
||||||
|
|
||||||
write(0x1, 0x4a); // Reset dividers and FSMs
|
|
||||||
write(0x1, 0x48);
|
|
||||||
write(0x1, 0xc8); // Synchronize dividers
|
|
||||||
write(0x1, 0x40); // Unmute, high-performance/low-noise mode
|
|
||||||
|
|
||||||
clock::spin_us(10_000);
|
|
||||||
|
|
||||||
info!(" ...done");
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn test_gpo() -> Result<(), &'static str> {
|
|
||||||
info!("testing GPO...");
|
|
||||||
for trial in 0..10 {
|
|
||||||
if !gpo_indirect_read(GPO_MUX_FORCE1) {
|
|
||||||
info!(" ...failed. GPO I/O did not go high (#{})", trial + 1);
|
|
||||||
return Err("GPO is not functioning");
|
|
||||||
}
|
|
||||||
if gpo_indirect_read(GPO_MUX_FORCE0) {
|
|
||||||
info!(" ...failed. GPO I/O did not return low (#{})", trial + 1);
|
|
||||||
return Err("GPO is not functioning");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
info!(" ...passed");
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn check_phased() -> Result<(), &'static str> {
|
|
||||||
if !gpo_indirect_read(GPO_MUX_CLK_OUT_PHASE) {
|
|
||||||
return Err("GPO reported phases did not align");
|
|
||||||
}
|
|
||||||
// Should be the same as the GPO read
|
|
||||||
let sysref_fsm_status = read(0x91);
|
|
||||||
if sysref_fsm_status != 0x2 {
|
|
||||||
error!("Bad SYSREF FSM status: {:02x}", sysref_fsm_status);
|
|
||||||
return Err("Bad SYSREF FSM status");
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn unmute() {
|
|
||||||
/*
|
|
||||||
* Never missing an opportunity to be awful, the HMC7043 produces broadband noise
|
|
||||||
* prior to intialization, which can upset the AMC FPGA.
|
|
||||||
* External circuitry mutes it.
|
|
||||||
*/
|
|
||||||
unsafe {
|
|
||||||
csr::hmc7043_out_en::out_write(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn sysref_delay_dac(dacno: u8, phase_offset: u8) {
|
|
||||||
spi_setup();
|
|
||||||
if dacno == 0 {
|
|
||||||
write(0x00e9, phase_offset);
|
|
||||||
} else if dacno == 1 {
|
|
||||||
write(0x00d5, phase_offset);
|
|
||||||
} else {
|
|
||||||
unimplemented!();
|
|
||||||
}
|
|
||||||
clock::spin_us(100);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn sysref_slip() {
|
|
||||||
spi_setup();
|
|
||||||
write(0x0002, 0x02);
|
|
||||||
write(0x0002, 0x00);
|
|
||||||
clock::spin_us(100);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn init() -> Result<(), &'static str> {
|
|
||||||
#[cfg(all(hmc830_ref = "125", rtio_frequency = "125.0"))]
|
|
||||||
const DIV: (u32, u32, u32, u32) = (2, 32, 0, 1); // 125MHz -> 2.0GHz
|
|
||||||
#[cfg(all(hmc830_ref = "150", rtio_frequency = "150.0"))]
|
|
||||||
const DIV: (u32, u32, u32, u32) = (2, 32, 0, 1); // 150MHz -> 2.4GHz
|
|
||||||
|
|
||||||
/* do not use other SPI devices before HMC830 SPI mode selection */
|
|
||||||
hmc830::select_spi_mode();
|
|
||||||
hmc830::detect()?;
|
|
||||||
hmc830::init();
|
|
||||||
|
|
||||||
hmc830::set_dividers(DIV.0, DIV.1, DIV.2, DIV.3);
|
|
||||||
|
|
||||||
hmc830::check_locked()?;
|
|
||||||
|
|
||||||
if hmc7043::get_id() == hmc7043::CHIP_ID {
|
|
||||||
error!("HMC7043 detected while in reset (board rework missing?)");
|
|
||||||
}
|
|
||||||
hmc7043::enable();
|
|
||||||
hmc7043::detect()?;
|
|
||||||
hmc7043::init();
|
|
||||||
hmc7043::test_gpo()?;
|
|
||||||
hmc7043::check_phased()?;
|
|
||||||
hmc7043::unmute();
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
|
@ -25,13 +25,6 @@ pub mod si5324;
|
||||||
#[cfg(has_wrpll)]
|
#[cfg(has_wrpll)]
|
||||||
pub mod 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)]
|
#[cfg(has_grabber)]
|
||||||
pub mod grabber;
|
pub mod grabber;
|
||||||
|
|
||||||
|
|
|
@ -182,12 +182,6 @@ fn init() -> Result<()> {
|
||||||
i2c::pca9548_select(BUSNO, 0x70, 0)?;
|
i2c::pca9548_select(BUSNO, 0x70, 0)?;
|
||||||
i2c::pca9548_select(BUSNO, 0x71, 1 << 3)?;
|
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")]
|
#[cfg(soc_platform = "kc705")]
|
||||||
i2c::pca9548_select(BUSNO, 0x74, 1 << 7)?;
|
i2c::pca9548_select(BUSNO, 0x74, 1 << 7)?;
|
||||||
|
|
||||||
|
|
|
@ -175,8 +175,6 @@ mod si549 {
|
||||||
use board_misoc::clock;
|
use board_misoc::clock;
|
||||||
use super::i2c;
|
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")]
|
#[cfg(soc_platform = "kasli")]
|
||||||
pub const ADDRESS: u8 = 0x67;
|
pub const ADDRESS: u8 = 0x67;
|
||||||
|
|
||||||
|
|
|
@ -39,10 +39,6 @@ pub fn get_adresses() -> NetAddresses {
|
||||||
.map(|addr_buf| EthernetAddress(addr_buf))
|
.map(|addr_buf| EthernetAddress(addr_buf))
|
||||||
.unwrap_or_else(|_e| EthernetAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x21]));
|
.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")]
|
#[cfg(soc_platform = "kc705")]
|
||||||
{ hardware_addr = EthernetAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x01]); }
|
{ hardware_addr = EthernetAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x01]); }
|
||||||
}
|
}
|
||||||
|
@ -54,10 +50,6 @@ pub fn get_adresses() -> NetAddresses {
|
||||||
_ => {
|
_ => {
|
||||||
#[cfg(soc_platform = "kasli")]
|
#[cfg(soc_platform = "kasli")]
|
||||||
{ ipv4_addr = IpAddress::v4(192, 168, 1, 70); }
|
{ 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")]
|
#[cfg(soc_platform = "kc705")]
|
||||||
{ ipv4_addr = IpAddress::v4(192, 168, 1, 50); }
|
{ ipv4_addr = IpAddress::v4(192, 168, 1, 50); }
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 () -> ! {
|
pub unsafe fn reload () -> ! {
|
||||||
csr::icap::iprog_write(1);
|
csr::icap::iprog_write(1);
|
||||||
loop {}
|
loop {}
|
||||||
|
|
|
@ -364,7 +364,7 @@ pub fn panic_impl(info: &core::panic::PanicInfo) -> ! {
|
||||||
});
|
});
|
||||||
|
|
||||||
if config::read_str("panic_reset", |r| r == Ok("1")) &&
|
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...");
|
println!("restarting...");
|
||||||
unsafe {
|
unsafe {
|
||||||
kernel::stop();
|
kernel::stop();
|
||||||
|
|
|
@ -172,7 +172,7 @@ fn setup_si5324_as_synthesizer(cfg: RtioClock) {
|
||||||
crystal_ref: true
|
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");
|
info!("using internal 100MHz RTIO clock");
|
||||||
si5324::FrequencySettings {
|
si5324::FrequencySettings {
|
||||||
n1_hs : 9,
|
n1_hs : 9,
|
||||||
|
@ -218,8 +218,6 @@ fn setup_si5324_as_synthesizer(cfg: RtioClock) {
|
||||||
let si5324_ref_input = si5324::Input::Ckin1;
|
let si5324_ref_input = si5324::Input::Ckin1;
|
||||||
#[cfg(all(soc_platform = "kasli", not(hw_rev = "v2.0")))]
|
#[cfg(all(soc_platform = "kasli", not(hw_rev = "v2.0")))]
|
||||||
let si5324_ref_input = si5324::Input::Ckin2;
|
let si5324_ref_input = si5324::Input::Ckin2;
|
||||||
#[cfg(soc_platform = "metlino")]
|
|
||||||
let si5324_ref_input = si5324::Input::Ckin2;
|
|
||||||
#[cfg(soc_platform = "kc705")]
|
#[cfg(soc_platform = "kc705")]
|
||||||
let si5324_ref_input = si5324::Input::Ckin2;
|
let si5324_ref_input = si5324::Input::Ckin2;
|
||||||
si5324::setup(&si5324_settings, si5324_ref_input).expect("cannot initialize Si5324");
|
si5324::setup(&si5324_settings, si5324_ref_input).expect("cannot initialize Si5324");
|
||||||
|
@ -233,8 +231,6 @@ pub fn init() {
|
||||||
let si5324_ext_input = si5324::Input::Ckin1;
|
let si5324_ext_input = si5324::Input::Ckin1;
|
||||||
#[cfg(all(soc_platform = "kasli", not(hw_rev = "v2.0")))]
|
#[cfg(all(soc_platform = "kasli", not(hw_rev = "v2.0")))]
|
||||||
let si5324_ext_input = si5324::Input::Ckin2;
|
let si5324_ext_input = si5324::Input::Ckin2;
|
||||||
#[cfg(soc_platform = "metlino")]
|
|
||||||
let si5324_ext_input = si5324::Input::Ckin2;
|
|
||||||
#[cfg(soc_platform = "kc705")]
|
#[cfg(soc_platform = "kc705")]
|
||||||
let si5324_ext_input = si5324::Input::Ckin2;
|
let si5324_ext_input = si5324::Input::Ckin2;
|
||||||
match clock_cfg {
|
match clock_cfg {
|
||||||
|
|
|
@ -1,74 +0,0 @@
|
||||||
pub const INIT: u8 = 0x00;
|
|
||||||
pub const PRINT_STATUS: u8 = 0x01;
|
|
||||||
pub const PRBS: u8 = 0x02;
|
|
||||||
pub const STPL: u8 = 0x03;
|
|
||||||
|
|
||||||
pub const SYSREF_DELAY_DAC: u8 = 0x10;
|
|
||||||
pub const SYSREF_SLIP: u8 = 0x11;
|
|
||||||
pub const SYNC: u8 = 0x12;
|
|
||||||
|
|
||||||
pub const DDMTD_SYSREF_RAW: u8 = 0x20;
|
|
||||||
pub const DDMTD_SYSREF: u8 = 0x21;
|
|
||||||
|
|
||||||
|
|
||||||
fn average_2phases(a: i32, b: i32, modulo: i32) -> i32 {
|
|
||||||
let diff = ((a - b + modulo/2 + modulo) % modulo) - modulo/2;
|
|
||||||
return (modulo + b + diff/2) % modulo;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn average_phases(phases: &[i32], modulo: i32) -> i32 {
|
|
||||||
if phases.len() == 1 {
|
|
||||||
panic!("input array length must be a power of 2");
|
|
||||||
} else if phases.len() == 2 {
|
|
||||||
average_2phases(phases[0], phases[1], modulo)
|
|
||||||
} else {
|
|
||||||
let cut = phases.len()/2;
|
|
||||||
average_2phases(
|
|
||||||
average_phases(&phases[..cut], modulo),
|
|
||||||
average_phases(&phases[cut..], modulo),
|
|
||||||
modulo)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub const RAW_DDMTD_N_SHIFT: i32 = 6;
|
|
||||||
pub const RAW_DDMTD_N: i32 = 1 << RAW_DDMTD_N_SHIFT;
|
|
||||||
pub const DDMTD_DITHER_BITS: i32 = 1;
|
|
||||||
pub const DDMTD_N_SHIFT: i32 = RAW_DDMTD_N_SHIFT + DDMTD_DITHER_BITS;
|
|
||||||
pub const DDMTD_N: i32 = 1 << DDMTD_N_SHIFT;
|
|
||||||
|
|
||||||
#[cfg(has_ad9154)]
|
|
||||||
use board_misoc::{clock, csr};
|
|
||||||
|
|
||||||
#[cfg(has_ad9154)]
|
|
||||||
pub fn init_ddmtd() -> Result<(), &'static str> {
|
|
||||||
unsafe {
|
|
||||||
csr::sysref_ddmtd::reset_write(1);
|
|
||||||
clock::spin_us(1);
|
|
||||||
csr::sysref_ddmtd::reset_write(0);
|
|
||||||
clock::spin_us(100);
|
|
||||||
if csr::sysref_ddmtd::locked_read() != 0 {
|
|
||||||
Ok(())
|
|
||||||
} else {
|
|
||||||
Err("DDMTD helper PLL failed to lock")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(has_ad9154)]
|
|
||||||
pub fn measure_ddmdt_phase_raw() -> i32 {
|
|
||||||
unsafe { csr::sysref_ddmtd::dt_read() as i32 }
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(has_ad9154)]
|
|
||||||
pub fn measure_ddmdt_phase() -> i32 {
|
|
||||||
const AVG_PRECISION_SHIFT: i32 = 6;
|
|
||||||
const AVG_PRECISION: i32 = 1 << AVG_PRECISION_SHIFT;
|
|
||||||
const AVG_MOD: i32 = 1 << (RAW_DDMTD_N_SHIFT + AVG_PRECISION_SHIFT + DDMTD_DITHER_BITS);
|
|
||||||
|
|
||||||
let mut measurements = [0; AVG_PRECISION as usize];
|
|
||||||
for i in 0..AVG_PRECISION {
|
|
||||||
measurements[i as usize] = measure_ddmdt_phase_raw() << (AVG_PRECISION_SHIFT + DDMTD_DITHER_BITS);
|
|
||||||
clock::spin_us(10);
|
|
||||||
}
|
|
||||||
average_phases(&measurements, AVG_MOD) >> AVG_PRECISION_SHIFT
|
|
||||||
}
|
|
|
@ -1,589 +0,0 @@
|
||||||
pub mod jesd {
|
|
||||||
use board_misoc::{csr, clock};
|
|
||||||
|
|
||||||
pub fn reset(reset: bool) {
|
|
||||||
unsafe {
|
|
||||||
csr::jesd_crg::jreset_write(if reset {1} else {0});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn enable(dacno: u8, en: bool) {
|
|
||||||
unsafe {
|
|
||||||
(csr::JDCG[dacno as usize].jesd_control_enable_write)(if en {1} else {0})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn phy_done(dacno: u8) -> bool {
|
|
||||||
unsafe {
|
|
||||||
(csr::JDCG[dacno as usize].jesd_control_phy_done_read)() != 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn ready(dacno: u8) -> bool {
|
|
||||||
unsafe {
|
|
||||||
(csr::JDCG[dacno as usize].jesd_control_ready_read)() != 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn prbs(dacno: u8, en: bool) {
|
|
||||||
unsafe {
|
|
||||||
(csr::JDCG[dacno as usize].jesd_control_prbs_config_write)(if en {0b01} else {0b00})
|
|
||||||
}
|
|
||||||
clock::spin_us(5000);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn stpl(dacno: u8, en: bool) {
|
|
||||||
unsafe {
|
|
||||||
(csr::JDCG[dacno as usize].jesd_control_stpl_enable_write)(if en {1} else {0})
|
|
||||||
}
|
|
||||||
clock::spin_us(5000);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn jsync(dacno: u8) -> bool {
|
|
||||||
unsafe {
|
|
||||||
(csr::JDCG[dacno as usize].jesd_control_jsync_read)() != 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub mod jdac {
|
|
||||||
use board_misoc::{csr, clock};
|
|
||||||
use board_artiq::drtioaux;
|
|
||||||
|
|
||||||
use super::jesd;
|
|
||||||
use super::super::jdac_common;
|
|
||||||
|
|
||||||
pub fn basic_request(dacno: u8, reqno: u8, param: u8) -> Result<u8, &'static str> {
|
|
||||||
if let Err(e) = drtioaux::send(1, &drtioaux::Packet::JdacBasicRequest {
|
|
||||||
destination: 0,
|
|
||||||
dacno: dacno,
|
|
||||||
reqno: reqno,
|
|
||||||
param: param
|
|
||||||
}) {
|
|
||||||
error!("aux packet error ({})", e);
|
|
||||||
return Err("aux packet error while sending for JESD DAC basic request");
|
|
||||||
}
|
|
||||||
match drtioaux::recv_timeout(1, Some(1000)) {
|
|
||||||
Ok(drtioaux::Packet::JdacBasicReply { succeeded, retval }) => {
|
|
||||||
if succeeded {
|
|
||||||
Ok(retval)
|
|
||||||
} else {
|
|
||||||
error!("JESD DAC basic request failed (dacno={}, reqno={})", dacno, reqno);
|
|
||||||
Err("remote error status to JESD DAC basic request")
|
|
||||||
}
|
|
||||||
},
|
|
||||||
Ok(packet) => {
|
|
||||||
error!("received unexpected aux packet: {:?}", packet);
|
|
||||||
Err("unexpected aux packet in reply to JESD DAC basic request")
|
|
||||||
},
|
|
||||||
Err(e) => {
|
|
||||||
error!("aux packet error ({})", e);
|
|
||||||
Err("aux packet error while waiting for JESD DAC basic reply")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn init() -> Result<(), &'static str> {
|
|
||||||
for dacno in 0..csr::JDCG.len() {
|
|
||||||
let dacno = dacno as u8;
|
|
||||||
info!("DAC-{} initializing...", dacno);
|
|
||||||
|
|
||||||
jesd::enable(dacno, true);
|
|
||||||
clock::spin_us(10_000);
|
|
||||||
if !jesd::phy_done(dacno) {
|
|
||||||
error!("JESD core PHY not done");
|
|
||||||
return Err("JESD core PHY not done");
|
|
||||||
}
|
|
||||||
|
|
||||||
basic_request(dacno, jdac_common::INIT, 0)?;
|
|
||||||
|
|
||||||
// JESD ready depends on JSYNC being valid, so DAC init needs to happen first
|
|
||||||
if !jesd::ready(dacno) {
|
|
||||||
error!("JESD core reported not ready, sending DAC status print request");
|
|
||||||
basic_request(dacno, jdac_common::PRINT_STATUS, 0)?;
|
|
||||||
return Err("JESD core reported not ready");
|
|
||||||
}
|
|
||||||
|
|
||||||
jesd::prbs(dacno, true);
|
|
||||||
basic_request(dacno, jdac_common::PRBS, 0)?;
|
|
||||||
jesd::prbs(dacno, false);
|
|
||||||
|
|
||||||
basic_request(dacno, jdac_common::INIT, 0)?;
|
|
||||||
clock::spin_us(5000);
|
|
||||||
|
|
||||||
if !jesd::jsync(dacno) {
|
|
||||||
error!("JESD core reported bad SYNC");
|
|
||||||
return Err("JESD core reported bad SYNC");
|
|
||||||
}
|
|
||||||
|
|
||||||
info!(" ...done initializing");
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn stpl() -> Result<(), &'static str> {
|
|
||||||
for dacno in 0..csr::JDCG.len() {
|
|
||||||
let dacno = dacno as u8;
|
|
||||||
|
|
||||||
info!("Running STPL test on DAC-{}...", dacno);
|
|
||||||
|
|
||||||
jesd::stpl(dacno, true);
|
|
||||||
basic_request(dacno, jdac_common::STPL, 0)?;
|
|
||||||
jesd::stpl(dacno, false);
|
|
||||||
|
|
||||||
info!(" ...done STPL test");
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub mod jesd204sync {
|
|
||||||
use board_misoc::{csr, clock, config};
|
|
||||||
|
|
||||||
use super::jdac;
|
|
||||||
use super::super::jdac_common;
|
|
||||||
|
|
||||||
const HMC7043_ANALOG_DELAY_RANGE: u8 = 24;
|
|
||||||
|
|
||||||
const FPGA_CLK_DIV: u16 = 16; // Keep in sync with hmc830_7043.rs
|
|
||||||
const SYSREF_DIV: u16 = 256; // Keep in sync with hmc830_7043.rs
|
|
||||||
|
|
||||||
fn hmc7043_sysref_delay_dac(dacno: u8, phase_offset: u8) -> Result<(), &'static str> {
|
|
||||||
match jdac::basic_request(dacno, jdac_common::SYSREF_DELAY_DAC, phase_offset) {
|
|
||||||
Ok(_) => Ok(()),
|
|
||||||
Err(e) => Err(e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
fn hmc7043_sysref_slip() -> Result<(), &'static str> {
|
|
||||||
match jdac::basic_request(0, jdac_common::SYSREF_SLIP, 0) {
|
|
||||||
Ok(_) => Ok(()),
|
|
||||||
Err(e) => Err(e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn ad9154_sync(dacno: u8) -> Result<bool, &'static str> {
|
|
||||||
match jdac::basic_request(dacno, jdac_common::SYNC, 0) {
|
|
||||||
Ok(0) => Ok(false),
|
|
||||||
Ok(_) => Ok(true),
|
|
||||||
Err(e) => Err(e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn measure_ddmdt_phase_raw() -> Result<i32, &'static str> {
|
|
||||||
Ok(jdac::basic_request(0, jdac_common::DDMTD_SYSREF_RAW, 0)? as i32)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn measure_ddmdt_phase() -> Result<i32, &'static str> {
|
|
||||||
Ok(jdac::basic_request(0, jdac_common::DDMTD_SYSREF, 0)? as i32)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn test_ddmtd_stability(raw: bool, tolerance: i32) -> Result<(), &'static str> {
|
|
||||||
info!("testing DDMTD stability (raw={}, tolerance={})...", raw, tolerance);
|
|
||||||
|
|
||||||
let modulo = if raw { jdac_common::RAW_DDMTD_N } else { jdac_common::DDMTD_N };
|
|
||||||
let measurement = if raw { measure_ddmdt_phase_raw } else { measure_ddmdt_phase };
|
|
||||||
let ntests = if raw { 150 } else { 15 };
|
|
||||||
|
|
||||||
let mut max_pkpk = 0;
|
|
||||||
for _ in 0..32 {
|
|
||||||
// If we are near the edges, wraparound can throw off the simple min/max computation.
|
|
||||||
// In this case, add an offset to get near the center.
|
|
||||||
let quadrant = measure_ddmdt_phase()?;
|
|
||||||
let center_offset =
|
|
||||||
if quadrant < jdac_common::DDMTD_N/4 || quadrant > 3*jdac_common::DDMTD_N/4 {
|
|
||||||
modulo/2
|
|
||||||
} else {
|
|
||||||
0
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut min = modulo;
|
|
||||||
let mut max = 0;
|
|
||||||
for _ in 0..ntests {
|
|
||||||
let m = (measurement()? + center_offset) % modulo;
|
|
||||||
if m < min {
|
|
||||||
min = m;
|
|
||||||
}
|
|
||||||
if m > max {
|
|
||||||
max = m;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let pkpk = max - min;
|
|
||||||
if pkpk > max_pkpk {
|
|
||||||
max_pkpk = pkpk;
|
|
||||||
}
|
|
||||||
if pkpk > tolerance {
|
|
||||||
error!(" ...excessive peak-peak jitter: {} (min={} max={} center_offset={})", pkpk,
|
|
||||||
min, max, center_offset);
|
|
||||||
return Err("excessive DDMTD peak-peak jitter");
|
|
||||||
}
|
|
||||||
hmc7043_sysref_slip();
|
|
||||||
}
|
|
||||||
|
|
||||||
info!(" ...passed, peak-peak jitter: {}", max_pkpk);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn test_slip_ddmtd() -> Result<(), &'static str> {
|
|
||||||
// expected_step = (RTIO clock frequency)*(DDMTD N)/(HMC7043 CLKIN frequency)
|
|
||||||
let expected_step = 8;
|
|
||||||
let tolerance = 1;
|
|
||||||
|
|
||||||
info!("testing HMC7043 SYSREF slip against DDMTD...");
|
|
||||||
let mut old_phase = measure_ddmdt_phase()?;
|
|
||||||
for _ in 0..1024 {
|
|
||||||
hmc7043_sysref_slip();
|
|
||||||
let phase = measure_ddmdt_phase()?;
|
|
||||||
let step = (jdac_common::DDMTD_N + old_phase - phase) % jdac_common::DDMTD_N;
|
|
||||||
if (step - expected_step).abs() > tolerance {
|
|
||||||
error!(" ...got unexpected step: {} ({} -> {})", step, old_phase, phase);
|
|
||||||
return Err("HMC7043 SYSREF slip produced unexpected DDMTD step");
|
|
||||||
}
|
|
||||||
old_phase = phase;
|
|
||||||
}
|
|
||||||
info!(" ...passed");
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn sysref_sh_error() -> bool {
|
|
||||||
unsafe {
|
|
||||||
csr::sysref_sampler::sh_error_reset_write(1);
|
|
||||||
clock::spin_us(1);
|
|
||||||
csr::sysref_sampler::sh_error_reset_write(0);
|
|
||||||
clock::spin_us(10);
|
|
||||||
csr::sysref_sampler::sh_error_read() != 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const SYSREF_SH_PRECISION_SHIFT: i32 = 5;
|
|
||||||
const SYSREF_SH_PRECISION: i32 = 1 << SYSREF_SH_PRECISION_SHIFT;
|
|
||||||
const SYSREF_SH_MOD: i32 = 1 << (jdac_common::DDMTD_N_SHIFT + SYSREF_SH_PRECISION_SHIFT);
|
|
||||||
|
|
||||||
#[derive(Default)]
|
|
||||||
struct SysrefShLimits {
|
|
||||||
rising_phases: [i32; SYSREF_SH_PRECISION as usize],
|
|
||||||
falling_phases: [i32; SYSREF_SH_PRECISION as usize],
|
|
||||||
}
|
|
||||||
|
|
||||||
fn measure_sysref_sh_limits() -> Result<SysrefShLimits, &'static str> {
|
|
||||||
let mut ret = SysrefShLimits::default();
|
|
||||||
let mut nslips = 0;
|
|
||||||
let mut rising_n = 0;
|
|
||||||
let mut falling_n = 0;
|
|
||||||
|
|
||||||
let mut previous = sysref_sh_error();
|
|
||||||
while rising_n < SYSREF_SH_PRECISION || falling_n < SYSREF_SH_PRECISION {
|
|
||||||
hmc7043_sysref_slip();
|
|
||||||
nslips += 1;
|
|
||||||
if nslips > 1024 {
|
|
||||||
return Err("too many slips and not enough SYSREF S/H error transitions");
|
|
||||||
}
|
|
||||||
|
|
||||||
let current = sysref_sh_error();
|
|
||||||
let phase = measure_ddmdt_phase()?;
|
|
||||||
if current && !previous && rising_n < SYSREF_SH_PRECISION {
|
|
||||||
ret.rising_phases[rising_n as usize] = phase << SYSREF_SH_PRECISION_SHIFT;
|
|
||||||
rising_n += 1;
|
|
||||||
}
|
|
||||||
if !current && previous && falling_n < SYSREF_SH_PRECISION {
|
|
||||||
ret.falling_phases[falling_n as usize] = phase << SYSREF_SH_PRECISION_SHIFT;
|
|
||||||
falling_n += 1;
|
|
||||||
}
|
|
||||||
previous = current;
|
|
||||||
}
|
|
||||||
Ok(ret)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn max_phase_deviation(average: i32, phases: &[i32]) -> i32 {
|
|
||||||
let mut ret = 0;
|
|
||||||
for phase in phases.iter() {
|
|
||||||
let deviation = (phase - average + jdac_common::DDMTD_N) % jdac_common::DDMTD_N;
|
|
||||||
if deviation > ret {
|
|
||||||
ret = deviation;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn reach_sysref_ddmtd_target(target: i32, tolerance: i32) -> Result<i32, &'static str> {
|
|
||||||
for _ in 0..1024 {
|
|
||||||
let delta = (measure_ddmdt_phase()? - target + jdac_common::DDMTD_N) % jdac_common::DDMTD_N;
|
|
||||||
if delta <= tolerance {
|
|
||||||
return Ok(delta)
|
|
||||||
}
|
|
||||||
hmc7043_sysref_slip();
|
|
||||||
}
|
|
||||||
Err("failed to reach SYSREF DDMTD phase target")
|
|
||||||
}
|
|
||||||
|
|
||||||
fn calibrate_sysref_target(rising_average: i32, falling_average: i32) -> Result<i32, &'static str> {
|
|
||||||
info!("calibrating SYSREF DDMTD target phase...");
|
|
||||||
let coarse_target =
|
|
||||||
if rising_average < falling_average {
|
|
||||||
(rising_average + falling_average)/2
|
|
||||||
} else {
|
|
||||||
((falling_average - (jdac_common::DDMTD_N - rising_average))/2 + jdac_common::DDMTD_N) % jdac_common::DDMTD_N
|
|
||||||
};
|
|
||||||
info!(" SYSREF calibration coarse target: {}", coarse_target);
|
|
||||||
reach_sysref_ddmtd_target(coarse_target, 8)?;
|
|
||||||
let target = measure_ddmdt_phase()?;
|
|
||||||
info!(" ...done, target={}", target);
|
|
||||||
Ok(target)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn sysref_get_tsc_phase_raw() -> Result<u8, &'static str> {
|
|
||||||
if sysref_sh_error() {
|
|
||||||
return Err("SYSREF failed S/H timing");
|
|
||||||
}
|
|
||||||
let ret = unsafe { csr::sysref_sampler::sysref_phase_read() };
|
|
||||||
Ok(ret)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Note: the code below assumes RTIO/SYSREF frequency ratio is a power of 2
|
|
||||||
|
|
||||||
fn sysref_get_tsc_phase() -> Result<i32, &'static str> {
|
|
||||||
let mask = (SYSREF_DIV/FPGA_CLK_DIV - 1) as u8;
|
|
||||||
Ok((sysref_get_tsc_phase_raw()? & mask) as i32)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn test_sysref_frequency() -> Result<(), &'static str> {
|
|
||||||
info!("testing SYSREF frequency against raw TSC phase bit toggles...");
|
|
||||||
|
|
||||||
let mut all_toggles = 0;
|
|
||||||
let initial_phase = sysref_get_tsc_phase_raw()?;
|
|
||||||
for _ in 0..20000 {
|
|
||||||
clock::spin_us(1);
|
|
||||||
all_toggles |= sysref_get_tsc_phase_raw()? ^ initial_phase;
|
|
||||||
}
|
|
||||||
|
|
||||||
let ratio = (SYSREF_DIV/FPGA_CLK_DIV) as u8;
|
|
||||||
let expected_toggles = 0xff ^ (ratio - 1);
|
|
||||||
if all_toggles == expected_toggles {
|
|
||||||
info!(" ...done (0x{:02x})", all_toggles);
|
|
||||||
Ok(())
|
|
||||||
} else {
|
|
||||||
error!(" ...unexpected toggles: got 0x{:02x}, expected 0x{:02x}",
|
|
||||||
all_toggles, expected_toggles);
|
|
||||||
Err("unexpected toggles")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn sysref_slip_rtio_cycle() {
|
|
||||||
for _ in 0..FPGA_CLK_DIV {
|
|
||||||
hmc7043_sysref_slip();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn test_slip_tsc() -> Result<(), &'static str> {
|
|
||||||
info!("testing HMC7043 SYSREF slip against TSC phase...");
|
|
||||||
let initial_phase = sysref_get_tsc_phase()?;
|
|
||||||
let modulo = (SYSREF_DIV/FPGA_CLK_DIV) as i32;
|
|
||||||
for i in 0..128 {
|
|
||||||
sysref_slip_rtio_cycle();
|
|
||||||
let expected_phase = (initial_phase + i + 1) % modulo;
|
|
||||||
let phase = sysref_get_tsc_phase()?;
|
|
||||||
if phase != expected_phase {
|
|
||||||
error!(" ...unexpected TSC phase: got {}, expected {} ", phase, expected_phase);
|
|
||||||
return Err("HMC7043 SYSREF slip produced unexpected TSC phase");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
info!(" ...done");
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn sysref_rtio_align() -> Result<(), &'static str> {
|
|
||||||
info!("aligning SYSREF with RTIO TSC...");
|
|
||||||
let mut nslips = 0;
|
|
||||||
loop {
|
|
||||||
sysref_slip_rtio_cycle();
|
|
||||||
if sysref_get_tsc_phase()? == 0 {
|
|
||||||
info!(" ...done");
|
|
||||||
return Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
nslips += 1;
|
|
||||||
if nslips > SYSREF_DIV/FPGA_CLK_DIV {
|
|
||||||
return Err("failed to find SYSREF transition aligned with RTIO TSC");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn sysref_auto_rtio_align() -> Result<(), &'static str> {
|
|
||||||
test_ddmtd_stability(true, 4)?;
|
|
||||||
test_ddmtd_stability(false, 1)?;
|
|
||||||
test_slip_ddmtd()?;
|
|
||||||
|
|
||||||
info!("determining SYSREF S/H limits...");
|
|
||||||
let sysref_sh_limits = measure_sysref_sh_limits()?;
|
|
||||||
let rising_average = jdac_common::average_phases(&sysref_sh_limits.rising_phases, SYSREF_SH_MOD);
|
|
||||||
let falling_average = jdac_common::average_phases(&sysref_sh_limits.falling_phases, SYSREF_SH_MOD);
|
|
||||||
let rising_max_deviation = max_phase_deviation(rising_average, &sysref_sh_limits.rising_phases);
|
|
||||||
let falling_max_deviation = max_phase_deviation(falling_average, &sysref_sh_limits.falling_phases);
|
|
||||||
|
|
||||||
let rising_average = rising_average >> SYSREF_SH_PRECISION_SHIFT;
|
|
||||||
let falling_average = falling_average >> SYSREF_SH_PRECISION_SHIFT;
|
|
||||||
let rising_max_deviation = rising_max_deviation >> SYSREF_SH_PRECISION_SHIFT;
|
|
||||||
let falling_max_deviation = falling_max_deviation >> SYSREF_SH_PRECISION_SHIFT;
|
|
||||||
|
|
||||||
info!(" SYSREF S/H average limits (DDMTD phases): {} {}", rising_average, falling_average);
|
|
||||||
info!(" SYSREF S/H maximum limit deviation: {} {}", rising_max_deviation, falling_max_deviation);
|
|
||||||
if rising_max_deviation > 8 || falling_max_deviation > 8 {
|
|
||||||
return Err("excessive SYSREF S/H limit deviation");
|
|
||||||
}
|
|
||||||
info!(" ...done");
|
|
||||||
|
|
||||||
let entry = config::read_str("sysref_ddmtd_phase_fpga", |r| r.map(|s| s.parse()));
|
|
||||||
let target_phase = match entry {
|
|
||||||
Ok(Ok(phase)) => {
|
|
||||||
info!("using FPGA SYSREF DDMTD phase target from config: {}", phase);
|
|
||||||
phase
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
let phase = calibrate_sysref_target(rising_average, falling_average)?;
|
|
||||||
if let Err(e) = config::write_int("sysref_ddmtd_phase_fpga", phase as u32) {
|
|
||||||
error!("failed to update FPGA SYSREF DDMTD phase target in config: {}", e);
|
|
||||||
}
|
|
||||||
phase
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
info!("aligning SYSREF with RTIO clock...");
|
|
||||||
let delta = reach_sysref_ddmtd_target(target_phase, 3)?;
|
|
||||||
if sysref_sh_error() {
|
|
||||||
return Err("SYSREF does not meet S/H timing at DDMTD phase target");
|
|
||||||
}
|
|
||||||
info!(" ...done, delta={}", delta);
|
|
||||||
|
|
||||||
test_sysref_frequency()?;
|
|
||||||
test_slip_tsc()?;
|
|
||||||
sysref_rtio_align()?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn sysref_cal_dac(dacno: u8) -> Result<u8, &'static str> {
|
|
||||||
info!("calibrating SYSREF delay at DAC-{}...", dacno);
|
|
||||||
|
|
||||||
// Allocate for more than expected as jitter may create spurious entries.
|
|
||||||
let mut limits_buf = [0; 8];
|
|
||||||
let mut n_limits = 0;
|
|
||||||
|
|
||||||
limits_buf[n_limits] = -1;
|
|
||||||
n_limits += 1;
|
|
||||||
|
|
||||||
// avoid spurious rotation at delay=0
|
|
||||||
hmc7043_sysref_delay_dac(dacno, 0);
|
|
||||||
ad9154_sync(dacno)?;
|
|
||||||
|
|
||||||
for scan_delay in 0..HMC7043_ANALOG_DELAY_RANGE {
|
|
||||||
hmc7043_sysref_delay_dac(dacno, scan_delay);
|
|
||||||
if ad9154_sync(dacno)? {
|
|
||||||
limits_buf[n_limits] = scan_delay as i16;
|
|
||||||
n_limits += 1;
|
|
||||||
if n_limits >= limits_buf.len() - 1 {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
limits_buf[n_limits] = HMC7043_ANALOG_DELAY_RANGE as i16;
|
|
||||||
n_limits += 1;
|
|
||||||
|
|
||||||
info!(" using limits: {:?}", &limits_buf[..n_limits]);
|
|
||||||
|
|
||||||
let mut delay = 0;
|
|
||||||
let mut best_margin = 0;
|
|
||||||
|
|
||||||
for i in 0..(n_limits-1) {
|
|
||||||
let margin = limits_buf[i+1] - limits_buf[i];
|
|
||||||
if margin > best_margin {
|
|
||||||
best_margin = margin;
|
|
||||||
delay = ((limits_buf[i+1] + limits_buf[i])/2) as u8;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
info!(" ...done, delay={}", delay);
|
|
||||||
Ok(delay)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn sysref_dac_align(dacno: u8, delay: u8) -> Result<(), &'static str> {
|
|
||||||
let tolerance = 5;
|
|
||||||
|
|
||||||
info!("verifying SYSREF margins at DAC-{}...", dacno);
|
|
||||||
|
|
||||||
// avoid spurious rotation at delay=0
|
|
||||||
hmc7043_sysref_delay_dac(dacno, 0);
|
|
||||||
ad9154_sync(dacno)?;
|
|
||||||
|
|
||||||
let mut rotation_seen = false;
|
|
||||||
for scan_delay in 0..HMC7043_ANALOG_DELAY_RANGE {
|
|
||||||
hmc7043_sysref_delay_dac(dacno, scan_delay);
|
|
||||||
if ad9154_sync(dacno)? {
|
|
||||||
rotation_seen = true;
|
|
||||||
let distance = (scan_delay as i16 - delay as i16).abs();
|
|
||||||
if distance < tolerance {
|
|
||||||
error!(" rotation at delay={} is {} delay steps from target (FAIL)", scan_delay, distance);
|
|
||||||
return Err("insufficient SYSREF margin at DAC");
|
|
||||||
} else {
|
|
||||||
info!(" rotation at delay={} is {} delay steps from target (PASS)", scan_delay, distance);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !rotation_seen {
|
|
||||||
return Err("no rotation seen when scanning DAC SYSREF delay");
|
|
||||||
}
|
|
||||||
|
|
||||||
info!(" ...done");
|
|
||||||
|
|
||||||
// We tested that the value is correct - now use it
|
|
||||||
info!("synchronizing DAC-{}", dacno);
|
|
||||||
hmc7043_sysref_delay_dac(dacno, delay);
|
|
||||||
ad9154_sync(dacno)?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn sysref_auto_dac_align() -> Result<(), &'static str> {
|
|
||||||
// We assume that DAC SYSREF traces are length-matched so only one delay
|
|
||||||
// value is needed, and we use DAC-0 as calibration reference.
|
|
||||||
|
|
||||||
let entry = config::read_str("sysref_7043_delay_dac", |r| r.map(|s| s.parse()));
|
|
||||||
let delay = match entry {
|
|
||||||
Ok(Ok(delay)) => {
|
|
||||||
info!("using DAC SYSREF delay from config: {}", delay);
|
|
||||||
delay
|
|
||||||
},
|
|
||||||
_ => {
|
|
||||||
let delay = sysref_cal_dac(0)?;
|
|
||||||
if let Err(e) = config::write_int("sysref_7043_delay_dac", delay as u32) {
|
|
||||||
error!("failed to update DAC SYSREF delay in config: {}", e);
|
|
||||||
}
|
|
||||||
delay
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
for dacno in 0..csr::JDCG.len() {
|
|
||||||
sysref_dac_align(dacno as u8, delay)?;
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn sysref_auto_align() {
|
|
||||||
if let Err(e) = sysref_auto_rtio_align() {
|
|
||||||
error!("failed to align SYSREF at FPGA: {}", e);
|
|
||||||
}
|
|
||||||
if let Err(e) = sysref_auto_dac_align() {
|
|
||||||
error!("failed to align SYSREF at DAC: {}", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn resync_dacs() -> Result<(), &'static str> {
|
|
||||||
for dacno in 0..csr::JDCG.len() {
|
|
||||||
info!("resynchronizing DAC-{}", dacno);
|
|
||||||
ad9154_sync(dacno as u8)?;
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -16,15 +16,9 @@ use board_artiq::si5324;
|
||||||
use board_artiq::wrpll;
|
use board_artiq::wrpll;
|
||||||
use board_artiq::{spi, drtioaux};
|
use board_artiq::{spi, drtioaux};
|
||||||
use board_artiq::drtio_routing;
|
use board_artiq::drtio_routing;
|
||||||
#[cfg(has_hmc830_7043)]
|
|
||||||
use board_artiq::hmc830_7043;
|
|
||||||
use riscv::register::{mcause, mepc, mtval};
|
use riscv::register::{mcause, mepc, mtval};
|
||||||
|
|
||||||
mod repeater;
|
mod repeater;
|
||||||
#[cfg(has_jdcg)]
|
|
||||||
mod jdcg;
|
|
||||||
#[cfg(any(has_ad9154, has_jdcg))]
|
|
||||||
pub mod jdac_common;
|
|
||||||
|
|
||||||
fn drtiosat_reset(reset: bool) {
|
fn drtiosat_reset(reset: bool) {
|
||||||
unsafe {
|
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");
|
warn!("received unexpected aux packet");
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -529,17 +486,6 @@ pub extern fn main() -> i32 {
|
||||||
wrpll::diagnostics();
|
wrpll::diagnostics();
|
||||||
init_rtio_crg();
|
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)]
|
#[cfg(has_drtio_routing)]
|
||||||
let mut repeaters = [repeater::Repeater::default(); csr::DRTIOREP.len()];
|
let mut repeaters = [repeater::Repeater::default(); csr::DRTIOREP.len()];
|
||||||
#[cfg(not(has_drtio_routing))]
|
#[cfg(not(has_drtio_routing))]
|
||||||
|
|
|
@ -309,34 +309,6 @@ class PeripheralManager:
|
||||||
|
|
||||||
return next(channel)
|
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):
|
def process_sampler(self, rtio_offset, peripheral):
|
||||||
self.gen("""
|
self.gen("""
|
||||||
device_db["spi_{name}_adc"] = {{
|
device_db["spi_{name}_adc"] = {{
|
||||||
|
|
|
@ -25,12 +25,10 @@ def get_argparser():
|
||||||
Valid actions:
|
Valid actions:
|
||||||
|
|
||||||
* gateware: write main gateware bitstream to flash
|
* gateware: write main gateware bitstream to flash
|
||||||
* rtm_gateware: write RTM gateware bitstream to flash
|
|
||||||
* bootloader: write bootloader to flash
|
* bootloader: write bootloader to flash
|
||||||
* storage: write storage image to flash
|
* storage: write storage image to flash
|
||||||
* firmware: write firmware to flash
|
* firmware: write firmware to flash
|
||||||
* load: load main gateware bitstream into device (volatile but fast)
|
* load: load main gateware bitstream into device (volatile but fast)
|
||||||
* rtm_load: load RTM gateware bitstream into device
|
|
||||||
* erase: erase flash memory
|
* erase: erase flash memory
|
||||||
* start: trigger the target to (re)load its gateware bitstream from flash
|
* start: trigger the target to (re)load its gateware bitstream from flash
|
||||||
|
|
||||||
|
@ -61,7 +59,7 @@ Prerequisites:
|
||||||
help="SSH host to jump through")
|
help="SSH host to jump through")
|
||||||
parser.add_argument("-t", "--target", default="kasli",
|
parser.add_argument("-t", "--target", default="kasli",
|
||||||
help="target board, default: %(default)s, one of: "
|
help="target board, default: %(default)s, one of: "
|
||||||
"kasli sayma metlino kc705")
|
"kasli kc705")
|
||||||
parser.add_argument("-I", "--preinit-command", default=[], action="append",
|
parser.add_argument("-I", "--preinit-command", default=[], action="append",
|
||||||
help="add a pre-initialization OpenOCD command. "
|
help="add a pre-initialization OpenOCD command. "
|
||||||
"Useful for selecting a board when several are connected.")
|
"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("-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",
|
parser.add_argument("--srcbuild", help="board binaries directory is laid out as a source build tree",
|
||||||
default=False, action="store_true")
|
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="*",
|
parser.add_argument("action", metavar="ACTION", nargs="*",
|
||||||
default=[],
|
default=[],
|
||||||
help="actions to perform, default: flash everything")
|
help="actions to perform, default: flash everything")
|
||||||
|
@ -229,76 +225,6 @@ class ProgrammerXC7(Programmer):
|
||||||
"xc7_program xc7.tap")
|
"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():
|
def main():
|
||||||
args = get_argparser().parse_args()
|
args = get_argparser().parse_args()
|
||||||
common_args.init_logger_from_args(args)
|
common_args.init_logger_from_args(args)
|
||||||
|
@ -311,21 +237,6 @@ def main():
|
||||||
"storage": ("spi0", 0x440000),
|
"storage": ("spi0", 0x440000),
|
||||||
"firmware": ("spi0", 0x450000),
|
"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": {
|
"kc705": {
|
||||||
"programmer": partial(ProgrammerXC7, board="kc705", proxy="bscan_spi_xc7k325t.bit"),
|
"programmer": partial(ProgrammerXC7, board="kc705", proxy="bscan_spi_xc7k325t.bit"),
|
||||||
"gateware": ("spi0", 0x000000),
|
"gateware": ("spi0", 0x000000),
|
||||||
|
@ -336,32 +247,21 @@ def main():
|
||||||
}[args.target]
|
}[args.target]
|
||||||
|
|
||||||
if not args.action:
|
if not args.action:
|
||||||
if args.target == "sayma":
|
|
||||||
args.action = "gateware rtm_gateware bootloader firmware start".split()
|
|
||||||
else:
|
|
||||||
args.action = "gateware bootloader firmware start".split()
|
args.action = "gateware bootloader firmware start".split()
|
||||||
needs_artifacts = any(
|
needs_artifacts = any(
|
||||||
action in args.action
|
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:
|
if needs_artifacts and args.dir is None:
|
||||||
raise ValueError("the directory containing the binaries need to be specified using -d.")
|
raise ValueError("the directory containing the binaries need to be specified using -d.")
|
||||||
|
|
||||||
binary_dir = args.dir
|
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:
|
if args.host is None:
|
||||||
client = LocalClient()
|
client = LocalClient()
|
||||||
else:
|
else:
|
||||||
client = SSHClient(args.host, args.jump)
|
client = SSHClient(args.host, args.jump)
|
||||||
|
|
||||||
if args.target == "sayma" and args.no_rtm_jtag:
|
programmer = config["programmer"](client, preinit_script=args.preinit_command)
|
||||||
programmer_cls = ProgrammerAMC
|
|
||||||
else:
|
|
||||||
programmer_cls = config["programmer"]
|
|
||||||
programmer = programmer_cls(client, preinit_script=args.preinit_command)
|
|
||||||
|
|
||||||
def artifact_path(this_binary_dir, *path_filename):
|
def artifact_path(this_binary_dir, *path_filename):
|
||||||
if args.srcbuild:
|
if args.srcbuild:
|
||||||
|
@ -372,7 +272,7 @@ def main():
|
||||||
*_, filename = path_filename
|
*_, filename = path_filename
|
||||||
return os.path.join(this_binary_dir, 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(
|
bin_handle, bin_filename = tempfile.mkstemp(
|
||||||
prefix="artiq_", suffix="_" + os.path.basename(bit_filename))
|
prefix="artiq_", suffix="_" + os.path.basename(bit_filename))
|
||||||
with open(bit_filename, "rb") as bit_file, \
|
with open(bit_filename, "rb") as bit_file, \
|
||||||
|
@ -380,12 +280,6 @@ def main():
|
||||||
if header:
|
if header:
|
||||||
bin_file.write(b"\x00"*8)
|
bin_file.write(b"\x00"*8)
|
||||||
bit2bin(bit_file, bin_file)
|
bit2bin(bit_file, bin_file)
|
||||||
if header:
|
|
||||||
magic = 0x5352544d # "SRTM", see sayma_rtm target
|
|
||||||
length = bin_file.tell() - 8
|
|
||||||
bin_file.seek(0)
|
|
||||||
bin_file.write(magic.to_bytes(4, byteorder="little"))
|
|
||||||
bin_file.write(length.to_bytes(4, byteorder="little"))
|
|
||||||
atexit.register(lambda: os.unlink(bin_filename))
|
atexit.register(lambda: os.unlink(bin_filename))
|
||||||
return bin_filename
|
return bin_filename
|
||||||
|
|
||||||
|
@ -394,11 +288,6 @@ def main():
|
||||||
gateware_bin = convert_gateware(
|
gateware_bin = convert_gateware(
|
||||||
artifact_path(binary_dir, "gateware", "top.bit"))
|
artifact_path(binary_dir, "gateware", "top.bit"))
|
||||||
programmer.write_binary(*config["gateware"], gateware_bin)
|
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":
|
elif action == "bootloader":
|
||||||
bootloader_bin = artifact_path(binary_dir, "software", "bootloader", "bootloader.bin")
|
bootloader_bin = artifact_path(binary_dir, "software", "bootloader", "bootloader.bin")
|
||||||
programmer.write_binary(*config["bootloader"], bootloader_bin)
|
programmer.write_binary(*config["bootloader"], bootloader_bin)
|
||||||
|
@ -418,22 +307,11 @@ def main():
|
||||||
"Found firmware files: {}".format(" ".join(firmware_fbis)))
|
"Found firmware files: {}".format(" ".join(firmware_fbis)))
|
||||||
programmer.write_binary(*config["firmware"], firmware_fbis[0])
|
programmer.write_binary(*config["firmware"], firmware_fbis[0])
|
||||||
elif action == "load":
|
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")
|
gateware_bit = artifact_path(binary_dir, "gateware", "top.bit")
|
||||||
programmer.load(gateware_bit, 0)
|
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":
|
elif action == "start":
|
||||||
programmer.start()
|
programmer.start()
|
||||||
elif action == "erase":
|
elif action == "erase":
|
||||||
if args.target == "sayma" or args.target == "metlino":
|
|
||||||
programmer.erase_flash("spi0")
|
|
||||||
programmer.erase_flash("spi1")
|
|
||||||
else:
|
|
||||||
programmer.erase_flash("spi0")
|
programmer.erase_flash("spi0")
|
||||||
else:
|
else:
|
||||||
raise ValueError("invalid action", action)
|
raise ValueError("invalid action", action)
|
||||||
|
|
|
@ -1,112 +0,0 @@
|
||||||
from migen import *
|
|
||||||
from misoc.interconnect.stream import Endpoint
|
|
||||||
|
|
||||||
|
|
||||||
class Accu(Module):
|
|
||||||
def __init__(self, width, meta=[]):
|
|
||||||
self.i = Endpoint([("p", width), ("f", width), ("clr", 1)])
|
|
||||||
self.o = Endpoint([("z", width)])
|
|
||||||
self.latency = 1
|
|
||||||
|
|
||||||
###
|
|
||||||
|
|
||||||
f = Signal.like(self.i.f)
|
|
||||||
p = Signal.like(self.i.p)
|
|
||||||
self.comb += self.i.ack.eq(~self.o.stb | self.o.ack)
|
|
||||||
self.sync += [
|
|
||||||
If(self.o.ack,
|
|
||||||
self.o.stb.eq(0),
|
|
||||||
),
|
|
||||||
If(self.i.ack,
|
|
||||||
self.o.stb.eq(1),
|
|
||||||
If(self.i.stb,
|
|
||||||
self.o.z.eq(self.i.p + Mux(self.i.clr, 0, self.o.z + p)),
|
|
||||||
f.eq(self.i.f),
|
|
||||||
p.eq(self.i.f - self.i.p),
|
|
||||||
).Else(
|
|
||||||
self.o.z.eq(self.o.z + f),
|
|
||||||
)
|
|
||||||
)
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class MCM(Module):
|
|
||||||
def __init__(self, width, constants):
|
|
||||||
n = len(constants)
|
|
||||||
self.i = i = Signal(width)
|
|
||||||
self.o = o = [Signal.like(self.i) for i in range(n)]
|
|
||||||
|
|
||||||
###
|
|
||||||
|
|
||||||
# TODO: improve MCM
|
|
||||||
assert range(n) == constants
|
|
||||||
assert n <= 9
|
|
||||||
|
|
||||||
if n > 0:
|
|
||||||
self.comb += o[0].eq(0)
|
|
||||||
if n > 1:
|
|
||||||
self.comb += o[1].eq(i)
|
|
||||||
if n > 2:
|
|
||||||
self.comb += o[2].eq(i << 1)
|
|
||||||
if n > 3:
|
|
||||||
self.comb += o[3].eq(i + (i << 1))
|
|
||||||
if n > 4:
|
|
||||||
self.comb += o[4].eq(i << 2)
|
|
||||||
if n > 5:
|
|
||||||
self.comb += o[5].eq(i + (i << 2))
|
|
||||||
if n > 6:
|
|
||||||
self.comb += o[6].eq(o[3] << 1)
|
|
||||||
if n > 7:
|
|
||||||
self.comb += o[7].eq((i << 3) - i)
|
|
||||||
if n > 8:
|
|
||||||
self.comb += o[8].eq(i << 3)
|
|
||||||
|
|
||||||
|
|
||||||
class PhasedAccu(Module):
|
|
||||||
def __init__(self, width, parallelism=8):
|
|
||||||
self.i = Endpoint([("p", width), ("f", width), ("clr", 1)])
|
|
||||||
self.o = Endpoint([("z{}".format(i), width) for i in
|
|
||||||
range(parallelism)])
|
|
||||||
self.parallelism = parallelism
|
|
||||||
self.latency = 2
|
|
||||||
|
|
||||||
###
|
|
||||||
|
|
||||||
a = MCM(width, range(parallelism + 1))
|
|
||||||
self.submodules += a
|
|
||||||
z = [Signal(width, reset_less=True) for i in range(parallelism)]
|
|
||||||
o = self.o.payload.flatten()
|
|
||||||
load = Signal(reset_less=True)
|
|
||||||
clr = Signal(reset_less=True)
|
|
||||||
p = Signal.like(self.i.p)
|
|
||||||
f = Signal.like(self.i.f, reset_less=True)
|
|
||||||
fp = Signal.like(self.i.f)
|
|
||||||
self.comb += [
|
|
||||||
self.i.ack.eq(self.o.ack),
|
|
||||||
a.i.eq(self.i.f),
|
|
||||||
]
|
|
||||||
|
|
||||||
self.sync += [
|
|
||||||
If(self.o.ack,
|
|
||||||
self.o.stb.eq(0),
|
|
||||||
),
|
|
||||||
If(~self.o.stb | self.o.ack,
|
|
||||||
self.o.stb.eq(1),
|
|
||||||
If(load,
|
|
||||||
load.eq(0),
|
|
||||||
[oi.eq(Mux(clr, 0, o[0] + fp) + zi)
|
|
||||||
for oi, zi in zip(o, z)],
|
|
||||||
fp.eq(f),
|
|
||||||
).Else(
|
|
||||||
[oi.eq(oi + fp) for oi in o],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
If(self.i.stb & self.i.ack,
|
|
||||||
[zi.eq(self.i.p - Mux(self.i.clr, 0, p) + aoi)
|
|
||||||
for zi, aoi in zip(z, a.o)],
|
|
||||||
clr.eq(self.i.clr),
|
|
||||||
p.eq(self.i.p),
|
|
||||||
f.eq(a.o[parallelism]),
|
|
||||||
load.eq(1),
|
|
||||||
),
|
|
||||||
]
|
|
|
@ -1,172 +0,0 @@
|
||||||
from math import floor
|
|
||||||
from operator import add
|
|
||||||
from functools import reduce
|
|
||||||
from collections import namedtuple
|
|
||||||
|
|
||||||
import numpy as np
|
|
||||||
|
|
||||||
from migen import *
|
|
||||||
|
|
||||||
|
|
||||||
def halfgen4(width, n, df=1e-3):
|
|
||||||
"""
|
|
||||||
http://recycle.lbl.gov/~ldoolitt/halfband
|
|
||||||
|
|
||||||
params:
|
|
||||||
* `up` is the passband/stopband width, as a fraction of
|
|
||||||
input sampling rate
|
|
||||||
* `n is the order of half-band filter to generate
|
|
||||||
returns:
|
|
||||||
* `a` is the full set of FIR coefficients, `4*n-1` long.
|
|
||||||
implement wisely.
|
|
||||||
"""
|
|
||||||
|
|
||||||
npt = n*40
|
|
||||||
wmax = 2*np.pi*width
|
|
||||||
wfit = (1 - np.linspace(0, 1, npt)[:, None]**2)*wmax
|
|
||||||
|
|
||||||
target = .5*np.ones_like(wfit)
|
|
||||||
basis = np.cos(wfit*np.arange(1, 2*n, 2))
|
|
||||||
weight = np.ones_like(wfit)
|
|
||||||
|
|
||||||
f0 = None
|
|
||||||
|
|
||||||
for i in range(40):
|
|
||||||
l = np.linalg.pinv(basis*weight)@(target*weight)
|
|
||||||
err = np.fabs(basis@l - .5)
|
|
||||||
f = np.max(err)/np.mean(err)
|
|
||||||
if f0 and (f0 - f)/(f0 + f) < df/2:
|
|
||||||
break
|
|
||||||
f0 = f
|
|
||||||
weight[err > (1 - df)*np.max(err)] *= 1 + 1.5/(i + 11)
|
|
||||||
a = np.c_[l, np.zeros_like(l)].ravel()[:-1]
|
|
||||||
a = np.r_[a[::-1], 1, a]/2
|
|
||||||
return a
|
|
||||||
|
|
||||||
|
|
||||||
_Widths = namedtuple("_Widths", "A B P")
|
|
||||||
|
|
||||||
_widths = {
|
|
||||||
"DSP48E1": _Widths(25, 18, 48),
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class ParallelFIR(Module):
|
|
||||||
"""Full-rate parallelized finite impulse response filter.
|
|
||||||
|
|
||||||
Tries to use transposed form as much as possible.
|
|
||||||
|
|
||||||
:param coefficients: tap coefficients (normalized to 1.),
|
|
||||||
increasing delay.
|
|
||||||
:param parallelism: number of samples per cycle.
|
|
||||||
:param width: bit width of input and output.
|
|
||||||
:param arch: architecture (default: "DSP48E1").
|
|
||||||
"""
|
|
||||||
def __init__(self, coefficients, parallelism, width=16,
|
|
||||||
arch="DSP48E1", cull_delays=()):
|
|
||||||
self.width = width
|
|
||||||
self.parallelism = p = parallelism
|
|
||||||
n = len(coefficients)
|
|
||||||
# input and output: old to new, decreasing delay
|
|
||||||
self.i = [Signal((width, True)) for i in range(p)]
|
|
||||||
self.o = [Signal((width, True), reset_less=True) for i in range(p)]
|
|
||||||
self.latency = (n + 1)//2//p + 2
|
|
||||||
w = _widths[arch]
|
|
||||||
|
|
||||||
c_max = max(abs(c) for c in coefficients)
|
|
||||||
c_shift = bits_for(floor((1 << w.B - 2) / c_max))
|
|
||||||
self.coefficients = cs = [int(round(c*(1 << c_shift)))
|
|
||||||
for c in coefficients]
|
|
||||||
assert max(bits_for(c) for c in cs) <= w.B
|
|
||||||
max_out = sum(abs(c)*(1 << w.A - 1) for c in cs)
|
|
||||||
assert max_out <= (1 << w.P - 1) - 1, (bits_for(max_out), w)
|
|
||||||
|
|
||||||
###
|
|
||||||
|
|
||||||
# Delay line: increasing delay
|
|
||||||
x = [Signal((w.A, True), reset_less=True) for _ in range(n + p - 1)]
|
|
||||||
|
|
||||||
for xi, xj in zip(x, self.i[::-1]):
|
|
||||||
self.comb += xi.eq(xj)
|
|
||||||
for xi, xj in zip(x[len(self.i):], x):
|
|
||||||
self.sync += xi.eq(xj)
|
|
||||||
|
|
||||||
for delay in range(p):
|
|
||||||
o = Signal((w.P, True), reset_less=True)
|
|
||||||
self.sync += self.o[delay].eq(o >> c_shift)
|
|
||||||
# Make products
|
|
||||||
tap = delay
|
|
||||||
for i, c in enumerate(cs):
|
|
||||||
# simplify for halfband and symmetric filters
|
|
||||||
if not c or c in cs[:i]:
|
|
||||||
continue
|
|
||||||
js = [j + p - 1 for j, cj in enumerate(cs) if cj == c]
|
|
||||||
m = Signal.like(o)
|
|
||||||
o0, o = o, Signal.like(o)
|
|
||||||
q = Signal.like(x[0])
|
|
||||||
if tap + p <= js[0]:
|
|
||||||
self.sync += o0.eq(o + m)
|
|
||||||
tap += p
|
|
||||||
else:
|
|
||||||
self.comb += o0.eq(o + m)
|
|
||||||
assert min(js) - tap >= 0
|
|
||||||
js = [j for j in js
|
|
||||||
if (p - 1 - j - tap) % p not in cull_delays]
|
|
||||||
if not js:
|
|
||||||
continue
|
|
||||||
self.comb += q.eq(reduce(add, [x[j - tap] for j in js]))
|
|
||||||
self.sync += m.eq(c*q)
|
|
||||||
# symmetric rounding
|
|
||||||
if c_shift > 1 and delay not in cull_delays:
|
|
||||||
self.comb += o.eq((1 << c_shift - 1) - 1)
|
|
||||||
|
|
||||||
|
|
||||||
class FIR(ParallelFIR):
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
super().__init__(self, *args, parallelism=1, **kwargs)
|
|
||||||
self.i = self.i[0]
|
|
||||||
self.o = self.o[0]
|
|
||||||
|
|
||||||
|
|
||||||
def halfgen4_cascade(rate, width, order=None):
|
|
||||||
"""Generate coefficients for cascaded half-band filters.
|
|
||||||
|
|
||||||
Coefficients are normalized to a gain of two per stage to compensate for
|
|
||||||
the zero stuffing.
|
|
||||||
|
|
||||||
:param rate: upsampling rate. power of two
|
|
||||||
:param width: passband/stopband width in units of input sampling rate.
|
|
||||||
:param order: highest order, defaults to :param:`rate`"""
|
|
||||||
if order is None:
|
|
||||||
order = rate
|
|
||||||
coeff = []
|
|
||||||
p = 1
|
|
||||||
while p < rate:
|
|
||||||
p *= 2
|
|
||||||
coeff.append(2*halfgen4(width*p/rate/2, order*p//rate))
|
|
||||||
return coeff
|
|
||||||
|
|
||||||
|
|
||||||
class ParallelHBFUpsampler(Module):
|
|
||||||
"""Parallel, power-of-two, half-band, cascading upsampler.
|
|
||||||
|
|
||||||
Coefficients should be normalized to overall gain of 2
|
|
||||||
(highest/center coefficient being 1)."""
|
|
||||||
def __init__(self, coefficients, width=16, **kwargs):
|
|
||||||
self.parallelism = 1 # accumulate
|
|
||||||
self.latency = 0 # accumulate
|
|
||||||
self.width = width
|
|
||||||
self.i = Signal((width, True))
|
|
||||||
|
|
||||||
###
|
|
||||||
|
|
||||||
i = [self.i]
|
|
||||||
for coeff in coefficients:
|
|
||||||
self.parallelism *= 2
|
|
||||||
hbf = ParallelFIR(coeff, self.parallelism, width=width,
|
|
||||||
cull_delays={0}, **kwargs)
|
|
||||||
self.submodules += hbf
|
|
||||||
self.comb += [a.eq(b) for a, b in zip(hbf.i[1::2], i)]
|
|
||||||
i = hbf.o
|
|
||||||
self.latency += hbf.latency
|
|
||||||
self.o = i
|
|
|
@ -1,220 +0,0 @@
|
||||||
from collections import namedtuple
|
|
||||||
|
|
||||||
from migen import *
|
|
||||||
from misoc.interconnect.stream import Endpoint
|
|
||||||
from misoc.cores.cordic import Cordic
|
|
||||||
|
|
||||||
from .accu import PhasedAccu
|
|
||||||
from .tools import eqh, SatAddMixin
|
|
||||||
from .spline import Spline
|
|
||||||
from .fir import ParallelHBFUpsampler, halfgen4_cascade
|
|
||||||
|
|
||||||
|
|
||||||
_Widths = namedtuple("_Widths", "t a p f")
|
|
||||||
_Orders = namedtuple("_Orders", "a p f")
|
|
||||||
|
|
||||||
|
|
||||||
class SplineParallelDUC(Module):
|
|
||||||
def __init__(self, widths, orders, parallelism=1, **kwargs):
|
|
||||||
self.parallelism = parallelism
|
|
||||||
self.widths = widths
|
|
||||||
p = Spline(order=orders.p, width=widths.p)
|
|
||||||
f = Spline(order=orders.f, width=widths.f)
|
|
||||||
self.f = f.tri(widths.t)
|
|
||||||
self.p = p.tri(widths.t)
|
|
||||||
self.submodules += p, f
|
|
||||||
self.ce = Signal(reset=1)
|
|
||||||
self.clr = Signal()
|
|
||||||
|
|
||||||
###
|
|
||||||
accu = PhasedAccu(len(self.f.a0), parallelism)
|
|
||||||
cordic = [Cordic(width=widths.a, widthz=len(self.p.a0), guard=None,
|
|
||||||
eval_mode="pipelined") for i in range(parallelism)]
|
|
||||||
self.submodules += accu, cordic
|
|
||||||
|
|
||||||
self.xi = [c.xi for c in cordic]
|
|
||||||
self.yi = [c.yi for c in cordic]
|
|
||||||
self.xo = [c.xo for c in cordic]
|
|
||||||
self.yo = [c.yo for c in cordic]
|
|
||||||
self.latency = cordic[0].latency
|
|
||||||
self.gain = cordic[0].gain
|
|
||||||
self.f.latency += accu.latency + self.latency
|
|
||||||
self.p.latency += accu.latency + self.latency
|
|
||||||
|
|
||||||
###
|
|
||||||
|
|
||||||
assert p.latency == f.latency
|
|
||||||
self.comb += [
|
|
||||||
p.o.ack.eq(self.ce),
|
|
||||||
f.o.ack.eq(self.ce),
|
|
||||||
eqh(accu.i.f, f.o.a0),
|
|
||||||
eqh(accu.i.p, p.o.a0),
|
|
||||||
accu.i.stb.eq(p.o.stb | f.o.stb),
|
|
||||||
accu.o.ack.eq(1),
|
|
||||||
[eqh(c.zi, zi) for c, zi in
|
|
||||||
zip(cordic, accu.o.payload.flatten())]
|
|
||||||
]
|
|
||||||
|
|
||||||
assert p.latency == 1
|
|
||||||
accu.i.clr.reset_less = True
|
|
||||||
self.sync += [
|
|
||||||
accu.i.clr.eq(0),
|
|
||||||
If(p.i.stb,
|
|
||||||
accu.i.clr.eq(self.clr),
|
|
||||||
),
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class SplineParallelDDS(SplineParallelDUC):
|
|
||||||
def __init__(self, widths, orders, **kwargs):
|
|
||||||
a = Spline(order=orders.a, width=widths.a)
|
|
||||||
self.a = a.tri(widths.t)
|
|
||||||
self.submodules += a
|
|
||||||
super().__init__(widths._replace(a=len(self.a.a0)), orders, **kwargs)
|
|
||||||
|
|
||||||
self.a.latency += self.latency
|
|
||||||
|
|
||||||
###
|
|
||||||
|
|
||||||
self.comb += [
|
|
||||||
a.o.ack.eq(self.ce),
|
|
||||||
[eqh(x, a.o.a0) for x in self.xi],
|
|
||||||
[y.eq(0) for y in self.yi],
|
|
||||||
]
|
|
||||||
del self.xi
|
|
||||||
del self.yi
|
|
||||||
|
|
||||||
|
|
||||||
class Config(Module):
|
|
||||||
def __init__(self, width, cordic_gain):
|
|
||||||
self.clr = Signal(3, reset=0b111)
|
|
||||||
self.iq_en = Signal(2, reset=0b01)
|
|
||||||
self.limits = [[Signal((width, True)), Signal((width, True))]
|
|
||||||
for i in range(2)]
|
|
||||||
limit = (1 << width - 1) - 1
|
|
||||||
limit_cordic = int(limit/cordic_gain)
|
|
||||||
self.limits[0][0].reset = Constant(-limit, (width, True))
|
|
||||||
self.limits[0][1].reset = Constant(limit, (width, True))
|
|
||||||
self.limits[1][0].reset = Constant(-limit_cordic, (width, True))
|
|
||||||
self.limits[1][1].reset = Constant(limit_cordic, (width, True))
|
|
||||||
# TODO make persistent, add read-out/notification/clear
|
|
||||||
self.clipped = [Signal(2) for i in range(2)]
|
|
||||||
self.i = Endpoint([("addr", bits_for(4 + 2*len(self.limits) - 1)),
|
|
||||||
("data", width)])
|
|
||||||
assert len(self.i.addr) == 3
|
|
||||||
self.ce = Signal()
|
|
||||||
|
|
||||||
###
|
|
||||||
|
|
||||||
div = Signal(16, reset=0)
|
|
||||||
n = Signal.like(div)
|
|
||||||
|
|
||||||
self.comb += self.ce.eq(n == 0)
|
|
||||||
self.sync += [
|
|
||||||
n.eq(n - 1),
|
|
||||||
If(self.ce,
|
|
||||||
n.eq(div),
|
|
||||||
)
|
|
||||||
]
|
|
||||||
|
|
||||||
pad = Signal()
|
|
||||||
|
|
||||||
reg = Array(sum(self.limits,
|
|
||||||
[Cat(div, n), self.clr, self.iq_en, pad]))
|
|
||||||
|
|
||||||
self.comb += self.i.ack.eq(1)
|
|
||||||
self.sync += [
|
|
||||||
If(self.i.stb,
|
|
||||||
reg[self.i.addr].eq(self.i.data),
|
|
||||||
),
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class Channel(Module, SatAddMixin):
|
|
||||||
def __init__(self, width=16, parallelism=4, widths=None, orders=None):
|
|
||||||
if orders is None:
|
|
||||||
orders = _Orders(a=4, f=2, p=1)
|
|
||||||
if widths is None:
|
|
||||||
widths = _Widths(t=width, a=orders.a*width, p=orders.p*width,
|
|
||||||
f=(orders.f + 2)*width)
|
|
||||||
|
|
||||||
self.submodules.a1 = a1 = SplineParallelDDS(widths, orders)
|
|
||||||
self.submodules.a2 = a2 = SplineParallelDDS(widths, orders)
|
|
||||||
coeff = halfgen4_cascade(parallelism, width=.4, order=8)
|
|
||||||
hbf = [ParallelHBFUpsampler(coeff, width=width + 1) for i in range(2)]
|
|
||||||
self.submodules.b = b = SplineParallelDUC(
|
|
||||||
widths._replace(a=len(hbf[0].o[0]), f=widths.f - width), orders,
|
|
||||||
parallelism=parallelism)
|
|
||||||
cfg = Config(width, b.gain)
|
|
||||||
u = Spline(width=widths.a, order=orders.a)
|
|
||||||
self.submodules += cfg, u, hbf
|
|
||||||
self.u = u.tri(widths.t)
|
|
||||||
self.i = [cfg.i, self.u, a1.a, a1.f, a1.p, a2.a, a2.f, a2.p, b.f, b.p]
|
|
||||||
self.i_names = "cfg u a1 f1 p1 a2 f2 p2 f0 p0".split()
|
|
||||||
self.i_named = dict(zip(self.i_names, self.i))
|
|
||||||
self.y_in = [Signal.like(b.yo[0]) for i in range(parallelism)]
|
|
||||||
self.o = [Signal((width, True), reset_less=True)
|
|
||||||
for i in range(parallelism)]
|
|
||||||
self.widths = widths
|
|
||||||
self.orders = orders
|
|
||||||
self.parallelism = parallelism
|
|
||||||
self.cordic_gain = a2.gain*b.gain
|
|
||||||
|
|
||||||
self.u.latency += 1 # self.o
|
|
||||||
b.p.latency += 1 # self.o
|
|
||||||
b.f.latency += 1 # self.o
|
|
||||||
a_latency_delta = hbf[0].latency + b.latency + 2 # hbf.i, self.o
|
|
||||||
for a in a1, a2:
|
|
||||||
a.a.latency += a_latency_delta
|
|
||||||
a.p.latency += a_latency_delta
|
|
||||||
a.f.latency += a_latency_delta
|
|
||||||
|
|
||||||
self.latency = max(_.latency for _ in self.i[1:])
|
|
||||||
for i in self.i[1:]:
|
|
||||||
i.latency -= self.latency
|
|
||||||
assert i.latency <= 0
|
|
||||||
cfg.i.latency = 0
|
|
||||||
|
|
||||||
###
|
|
||||||
|
|
||||||
self.comb += [
|
|
||||||
a1.ce.eq(cfg.ce),
|
|
||||||
a2.ce.eq(cfg.ce),
|
|
||||||
b.ce.eq(cfg.ce),
|
|
||||||
u.o.ack.eq(cfg.ce),
|
|
||||||
Cat(b.clr, a1.clr, a2.clr).eq(cfg.clr),
|
|
||||||
[i.eq(j) for i, j in zip(b.xi, hbf[0].o)],
|
|
||||||
[i.eq(j) for i, j in zip(b.yi, hbf[1].o)],
|
|
||||||
]
|
|
||||||
hbf[0].i.reset_less = True
|
|
||||||
hbf[1].i.reset_less = True
|
|
||||||
self.sync += [
|
|
||||||
hbf[0].i.eq(self.sat_add((a1.xo[0], a2.xo[0]),
|
|
||||||
width=len(hbf[0].i),
|
|
||||||
limits=cfg.limits[1], clipped=cfg.clipped[1])),
|
|
||||||
hbf[1].i.eq(self.sat_add((a1.yo[0], a2.yo[0]),
|
|
||||||
width=len(hbf[1].i),
|
|
||||||
limits=cfg.limits[1])),
|
|
||||||
]
|
|
||||||
# wire up outputs and q_{i,o} exchange
|
|
||||||
for o, x, y in zip(self.o, b.xo, self.y_in):
|
|
||||||
o_offset = Signal.like(o)
|
|
||||||
o_x = Signal.like(x)
|
|
||||||
o_y = Signal.like(y)
|
|
||||||
self.comb += [
|
|
||||||
o_offset.eq(u.o.a0[-len(o):]),
|
|
||||||
If(cfg.iq_en[0],
|
|
||||||
o_x.eq(x)
|
|
||||||
),
|
|
||||||
If(cfg.iq_en[1],
|
|
||||||
o_y.eq(y)
|
|
||||||
),
|
|
||||||
]
|
|
||||||
self.sync += [
|
|
||||||
o.eq(self.sat_add((o_offset, o_x, o_y),
|
|
||||||
width=len(o),
|
|
||||||
limits=cfg.limits[0], clipped=cfg.clipped[0])),
|
|
||||||
]
|
|
||||||
|
|
||||||
def connect_y(self, buddy):
|
|
||||||
self.comb += [i.eq(j) for i, j in zip(buddy.y_in, self.b.yo)]
|
|
|
@ -1,47 +0,0 @@
|
||||||
from migen import *
|
|
||||||
from misoc.interconnect.stream import Endpoint
|
|
||||||
|
|
||||||
|
|
||||||
class Spline(Module):
|
|
||||||
def __init__(self, order, width, step=1, time_width=None):
|
|
||||||
if not (step == 1 or order <= 2):
|
|
||||||
raise ValueError("For non-linear splines, "
|
|
||||||
"`step` needs to be one.")
|
|
||||||
layout = [("a{}".format(i), (width, True)) for i in range(order)]
|
|
||||||
self.i = Endpoint(layout)
|
|
||||||
self.o = Endpoint(layout)
|
|
||||||
self.latency = 1
|
|
||||||
|
|
||||||
###
|
|
||||||
|
|
||||||
o = self.o.payload.flatten()
|
|
||||||
|
|
||||||
self.comb += self.i.ack.eq(~self.o.stb | self.o.ack)
|
|
||||||
self.sync += [
|
|
||||||
If(self.o.ack,
|
|
||||||
self.o.stb.eq(0),
|
|
||||||
),
|
|
||||||
If(self.i.ack,
|
|
||||||
self.o.stb.eq(1),
|
|
||||||
[o[i].eq(o[i] + (o[i + 1] << log2_int(step)))
|
|
||||||
for i in range(order - 1)],
|
|
||||||
If(self.i.stb,
|
|
||||||
self.o.payload.eq(self.i.payload),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
]
|
|
||||||
|
|
||||||
def tri(self, time_width):
|
|
||||||
layout = [(name, (length - i*time_width, signed))
|
|
||||||
for i, (name, (length, signed), dir) in
|
|
||||||
enumerate(self.i.payload.layout[::-1])]
|
|
||||||
layout.reverse()
|
|
||||||
i = Endpoint(layout)
|
|
||||||
i.latency = self.latency
|
|
||||||
self.comb += [
|
|
||||||
self.i.stb.eq(i.stb),
|
|
||||||
i.ack.eq(self.i.ack),
|
|
||||||
[i0[-len(i1):].eq(i1) for i0, i1 in
|
|
||||||
zip(self.i.payload.flatten(), i.payload.flatten())]
|
|
||||||
]
|
|
||||||
return i
|
|
|
@ -1,69 +0,0 @@
|
||||||
from operator import add
|
|
||||||
from functools import reduce
|
|
||||||
|
|
||||||
from migen import *
|
|
||||||
|
|
||||||
|
|
||||||
class Delay(Module):
|
|
||||||
def __init__(self, i, delay, o=None):
|
|
||||||
if isinstance(i, (int, tuple)):
|
|
||||||
z = [Signal(i) for j in range(delay + 1)]
|
|
||||||
elif isinstance(i, list):
|
|
||||||
z = [Record(i) for j in range(delay + 1)]
|
|
||||||
elif isinstance(i, Record):
|
|
||||||
z = [Record(i.layout) for j in range(delay + 1)]
|
|
||||||
else:
|
|
||||||
z = [Signal.like(i) for j in range(delay + 1)]
|
|
||||||
self.i = z[0]
|
|
||||||
self.o = z[-1]
|
|
||||||
if not isinstance(i, (int, list, tuple)):
|
|
||||||
self.comb += self.i.eq(i)
|
|
||||||
if o is not None:
|
|
||||||
self.comb += o.eq(self.o)
|
|
||||||
self.latency = delay
|
|
||||||
self.sync += [z[j + 1].eq(z[j]) for j in range(delay)]
|
|
||||||
|
|
||||||
|
|
||||||
def eqh(a, b):
|
|
||||||
return a[-len(b):].eq(b[-len(a):])
|
|
||||||
|
|
||||||
|
|
||||||
class SatAddMixin:
|
|
||||||
"""Signed saturating addition mixin"""
|
|
||||||
def sat_add(self, a, width, limits=None, clipped=None):
|
|
||||||
a = list(a)
|
|
||||||
# assert all(value_bits_sign(ai)[1] for ai in a)
|
|
||||||
max_width = max(value_bits_sign(ai)[0] for ai in a)
|
|
||||||
carry = log2_int(len(a), need_pow2=False)
|
|
||||||
full = Signal((max_width + carry, True))
|
|
||||||
limited = Signal((width, True))
|
|
||||||
carry = len(full) - width
|
|
||||||
assert carry >= 0
|
|
||||||
clip = Signal(2)
|
|
||||||
sign = Signal()
|
|
||||||
if clipped is not None:
|
|
||||||
self.comb += clipped.eq(clip)
|
|
||||||
self.comb += [
|
|
||||||
full.eq(reduce(add, a)),
|
|
||||||
sign.eq(full[-1]),
|
|
||||||
limited.eq(full)
|
|
||||||
]
|
|
||||||
if limits is None:
|
|
||||||
self.comb += [
|
|
||||||
If(full[-1-carry:] != Replicate(sign, carry + 1),
|
|
||||||
clip.eq(Cat(sign, ~sign)),
|
|
||||||
limited.eq(Cat(Replicate(~sign, width - 1), sign)),
|
|
||||||
)
|
|
||||||
]
|
|
||||||
else:
|
|
||||||
self.comb += [
|
|
||||||
If(full < limits[0],
|
|
||||||
clip.eq(0b01),
|
|
||||||
limited.eq(limits[0])
|
|
||||||
),
|
|
||||||
If(full > limits[1],
|
|
||||||
clip.eq(0b10),
|
|
||||||
limited.eq(limits[1]),
|
|
||||||
)
|
|
||||||
]
|
|
||||||
return limited
|
|
|
@ -275,53 +275,6 @@ class Sampler(_EEM):
|
||||||
target.specials += DifferentialOutput(1, sdr.p, sdr.n)
|
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):
|
class Zotino(_EEM):
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def io(eem, iostandard):
|
def io(eem, iostandard):
|
||||||
|
|
|
@ -34,13 +34,6 @@ def peripheral_urukul(module, peripheral, **kwargs):
|
||||||
sync_gen_cls, **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):
|
def peripheral_sampler(module, peripheral, **kwargs):
|
||||||
if len(peripheral["ports"]) == 1:
|
if len(peripheral["ports"]) == 1:
|
||||||
port, port_aux = peripheral["ports"][0], None
|
port, port_aux = peripheral["ports"][0], None
|
||||||
|
@ -120,7 +113,6 @@ def peripheral_hvamp(module, peripheral, **kwargs):
|
||||||
peripheral_processors = {
|
peripheral_processors = {
|
||||||
"dio": peripheral_dio,
|
"dio": peripheral_dio,
|
||||||
"urukul": peripheral_urukul,
|
"urukul": peripheral_urukul,
|
||||||
"novogorny": peripheral_novogorny,
|
|
||||||
"sampler": peripheral_sampler,
|
"sampler": peripheral_sampler,
|
||||||
"suservo": peripheral_suservo,
|
"suservo": peripheral_suservo,
|
||||||
"zotino": peripheral_zotino,
|
"zotino": peripheral_zotino,
|
||||||
|
|
|
@ -1,29 +0,0 @@
|
||||||
from migen.build.generic_platform import *
|
|
||||||
|
|
||||||
from artiq.coredevice.fmcdio_vhdci_eem import *
|
|
||||||
|
|
||||||
|
|
||||||
io = [
|
|
||||||
("fmcdio_dirctl", 0,
|
|
||||||
Subsignal("clk", Pins("LPC:LA32_N")),
|
|
||||||
Subsignal("ser", Pins("LPC:LA33_P")),
|
|
||||||
Subsignal("latch", Pins("LPC:LA32_P")),
|
|
||||||
IOStandard("LVCMOS18")
|
|
||||||
),
|
|
||||||
]
|
|
||||||
|
|
||||||
def _get_connectors():
|
|
||||||
connectors = []
|
|
||||||
for i in range(4):
|
|
||||||
connections = dict()
|
|
||||||
for j, pair in enumerate(eem_fmc_connections[i]):
|
|
||||||
for pn in "n", "p":
|
|
||||||
cc = "cc_" if j == 0 else ""
|
|
||||||
lpc_cc = "CC_" if eem_fmc_connections[i][j] in (0, 1, 17, 18) else ""
|
|
||||||
connections["d{}_{}{}".format(j, cc, pn)] = \
|
|
||||||
"LPC:LA{:02d}_{}{}".format(pair, lpc_cc, pn.upper())
|
|
||||||
connectors.append(("eem{}".format(i), connections))
|
|
||||||
return connectors
|
|
||||||
|
|
||||||
|
|
||||||
connectors = _get_connectors()
|
|
|
@ -1,290 +0,0 @@
|
||||||
from collections import namedtuple
|
|
||||||
|
|
||||||
from migen import *
|
|
||||||
from migen.genlib.cdc import MultiReg, BusSynchronizer
|
|
||||||
from migen.genlib.resetsync import AsyncResetSynchronizer
|
|
||||||
from misoc.interconnect.csr import *
|
|
||||||
|
|
||||||
from jesd204b.common import (JESD204BTransportSettings,
|
|
||||||
JESD204BPhysicalSettings,
|
|
||||||
JESD204BSettings)
|
|
||||||
from jesd204b.phy.gth import (GTHChannelPLL as JESD204BGTHChannelPLL,
|
|
||||||
GTHQuadPLL as JESD204BGTHQuadPLL,
|
|
||||||
GTHTransmitter as JESD204BGTHTransmitter,
|
|
||||||
GTHInit as JESD204BGTHInit,
|
|
||||||
GTHTransmitterInterconnect as JESD204BGTHTransmitterInterconnect)
|
|
||||||
from jesd204b.phy import JESD204BPhyTX
|
|
||||||
from jesd204b.core import JESD204BCoreTX
|
|
||||||
from jesd204b.core import JESD204BCoreTXControl
|
|
||||||
|
|
||||||
|
|
||||||
class UltrascaleCRG(Module, AutoCSR):
|
|
||||||
linerate = int(6e9) # linerate = 20*data_rate*4/8 = data_rate*10
|
|
||||||
# data_rate = dac_rate/interp_factor
|
|
||||||
refclk_freq = int(150e6)
|
|
||||||
fabric_freq = int(125e6)
|
|
||||||
|
|
||||||
def __init__(self, platform, use_rtio_clock=False):
|
|
||||||
self.jreset = CSRStorage(reset=1)
|
|
||||||
self.refclk = Signal()
|
|
||||||
self.clock_domains.cd_jesd = ClockDomain()
|
|
||||||
|
|
||||||
refclk2 = Signal()
|
|
||||||
refclk_pads = platform.request("dac_refclk", 0)
|
|
||||||
platform.add_period_constraint(refclk_pads.p, 1e9/self.refclk_freq)
|
|
||||||
self.specials += [
|
|
||||||
Instance("IBUFDS_GTE3", i_CEB=0, p_REFCLK_HROW_CK_SEL=0b00,
|
|
||||||
i_I=refclk_pads.p, i_IB=refclk_pads.n,
|
|
||||||
o_O=self.refclk, o_ODIV2=refclk2),
|
|
||||||
AsyncResetSynchronizer(self.cd_jesd, self.jreset.storage),
|
|
||||||
]
|
|
||||||
|
|
||||||
if use_rtio_clock:
|
|
||||||
self.cd_jesd.clk.attr.add("keep")
|
|
||||||
self.comb += self.cd_jesd.clk.eq(ClockSignal("rtio"))
|
|
||||||
else:
|
|
||||||
self.specials += Instance("BUFG_GT", i_I=refclk2, o_O=self.cd_jesd.clk)
|
|
||||||
|
|
||||||
|
|
||||||
PhyPads = namedtuple("PhyPads", "txp txn")
|
|
||||||
|
|
||||||
|
|
||||||
class UltrascaleTX(Module, AutoCSR):
|
|
||||||
def __init__(self, platform, sys_crg, jesd_crg, dac, pll_type="cpll", tx_half=False):
|
|
||||||
# Note: In general, the choice between channel and quad PLLs can be made based on the "nominal operating ranges", which are (see UG576, Ch.2):
|
|
||||||
# CPLL: 2.0 - 6.25 GHz
|
|
||||||
# QPLL0: 9.8 - 16.375 GHz
|
|
||||||
# QPLL1: 8.0 - 13.0 GHz
|
|
||||||
# However, the exact frequency and/or linerate range should be checked according to the model and speed grade from their corresponding datasheets.
|
|
||||||
pll_cls = {
|
|
||||||
"cpll": JESD204BGTHChannelPLL,
|
|
||||||
"qpll": JESD204BGTHQuadPLL
|
|
||||||
}[pll_type]
|
|
||||||
ps = JESD204BPhysicalSettings(l=8, m=4, n=16, np=16)
|
|
||||||
ts = JESD204BTransportSettings(f=2, s=2, k=16, cs=0)
|
|
||||||
settings = JESD204BSettings(ps, ts, did=0x5a, bid=0x5)
|
|
||||||
|
|
||||||
jesd_pads = platform.request("dac_jesd", dac)
|
|
||||||
plls = []
|
|
||||||
phys = []
|
|
||||||
for i in range(len(jesd_pads.txp)):
|
|
||||||
pll = pll_cls(
|
|
||||||
jesd_crg.refclk, jesd_crg.refclk_freq, jesd_crg.linerate)
|
|
||||||
self.submodules += pll
|
|
||||||
plls.append(pll)
|
|
||||||
# QPLL quads
|
|
||||||
if pll_type == "qpll":
|
|
||||||
gthe3_common_cfgs = []
|
|
||||||
for i in range(0, len(plls), 4):
|
|
||||||
# GTHE3_COMMON common signals
|
|
||||||
qpll_clk = Signal()
|
|
||||||
qpll_refclk = Signal()
|
|
||||||
qpll_reset = Signal()
|
|
||||||
qpll_lock = Signal()
|
|
||||||
# GTHE3_COMMON
|
|
||||||
self.specials += pll_cls.get_gthe3_common(
|
|
||||||
jesd_crg.refclk, jesd_crg.refclk_freq, jesd_crg.linerate,
|
|
||||||
qpll_clk, qpll_refclk, qpll_reset, qpll_lock)
|
|
||||||
gthe3_common_cfgs.append({
|
|
||||||
"clk": qpll_clk,
|
|
||||||
"refclk": qpll_refclk,
|
|
||||||
"reset": qpll_reset,
|
|
||||||
"lock": qpll_lock
|
|
||||||
})
|
|
||||||
# Per-channel PLL phys
|
|
||||||
for i, pll in enumerate(plls):
|
|
||||||
# PhyTX
|
|
||||||
phy = JESD204BPhyTX(
|
|
||||||
pll, jesd_crg.refclk, PhyPads(jesd_pads.txp[i], jesd_pads.txn[i]),
|
|
||||||
jesd_crg.fabric_freq, transceiver="gth", tx_half=tx_half)
|
|
||||||
phys.append(phy)
|
|
||||||
if tx_half:
|
|
||||||
platform.add_period_constraint(phy.transmitter.cd_tx_half.clk,
|
|
||||||
80*1e9/jesd_crg.linerate)
|
|
||||||
platform.add_false_path_constraints(
|
|
||||||
sys_crg.cd_sys.clk,
|
|
||||||
jesd_crg.cd_jesd.clk,
|
|
||||||
phy.transmitter.cd_tx_half.clk)
|
|
||||||
else:
|
|
||||||
platform.add_period_constraint(phy.transmitter.cd_tx.clk,
|
|
||||||
40*1e9/jesd_crg.linerate)
|
|
||||||
platform.add_false_path_constraints(
|
|
||||||
sys_crg.cd_sys.clk,
|
|
||||||
jesd_crg.cd_jesd.clk,
|
|
||||||
phy.transmitter.cd_tx.clk)
|
|
||||||
# CHANNEL & init interconnects
|
|
||||||
for i, (pll, phy) in enumerate(zip(plls, phys)):
|
|
||||||
# CPLLs: 1 init per channel
|
|
||||||
if pll_type == "cpll":
|
|
||||||
phy_channel_cfg = {}
|
|
||||||
# Connect reset/lock to init
|
|
||||||
pll_reset = pll.reset
|
|
||||||
pll_lock = pll.lock
|
|
||||||
self.submodules += JESD204BGTHTransmitterInterconnect(
|
|
||||||
pll_reset, pll_lock, phy.transmitter, phy.transmitter.init)
|
|
||||||
# QPLL: 4 inits and 4 channels per quad
|
|
||||||
elif pll_type == "qpll":
|
|
||||||
# Connect clk/refclk to CHANNEL
|
|
||||||
phy_cfg = gthe3_common_cfgs[int(i//4)]
|
|
||||||
phy_channel_cfg = {
|
|
||||||
"qpll_clk": phy_cfg["clk"],
|
|
||||||
"qpll_refclk": phy_cfg["refclk"]
|
|
||||||
}
|
|
||||||
# Connect reset/lock to init
|
|
||||||
pll_reset = phy_cfg["reset"]
|
|
||||||
pll_lock = phy_cfg["lock"]
|
|
||||||
if i % 4 == 0:
|
|
||||||
self.submodules += JESD204BGTHTransmitterInterconnect(
|
|
||||||
pll_reset, pll_lock, phy.transmitter,
|
|
||||||
[phys[j].transmitter.init for j in range(i, min(len(phys), i+4))])
|
|
||||||
# GTHE3_CHANNEL
|
|
||||||
self.specials += JESD204BGTHTransmitter.get_gthe3_channel(
|
|
||||||
pll, phy.transmitter, **phy_channel_cfg)
|
|
||||||
|
|
||||||
self.submodules.core = JESD204BCoreTX(
|
|
||||||
phys, settings, converter_data_width=64)
|
|
||||||
self.submodules.control = JESD204BCoreTXControl(self.core)
|
|
||||||
self.core.register_jsync(platform.request("dac_sync", dac))
|
|
||||||
|
|
||||||
|
|
||||||
class DDMTDEdgeDetector(Module):
|
|
||||||
def __init__(self, i):
|
|
||||||
self.rising = Signal()
|
|
||||||
|
|
||||||
history = Signal(4)
|
|
||||||
deglitched = Signal()
|
|
||||||
self.sync.helper += history.eq(Cat(history[1:], i))
|
|
||||||
self.comb += deglitched.eq(i | history[0] | history[1] | history[2] | history[3])
|
|
||||||
|
|
||||||
deglitched_r = Signal()
|
|
||||||
self.sync.helper += [
|
|
||||||
deglitched_r.eq(deglitched),
|
|
||||||
self.rising.eq(deglitched & ~deglitched_r)
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
# See "Digital femtosecond time difference circuit for CERN's timing system"
|
|
||||||
# by P. Moreira and I. Darwazeh
|
|
||||||
class DDMTD(Module, AutoCSR):
|
|
||||||
def __init__(self, input_pads, rtio_clk_freq=150e6):
|
|
||||||
N = 64
|
|
||||||
self.reset = CSRStorage(reset=1)
|
|
||||||
self.locked = CSRStatus()
|
|
||||||
self.dt = CSRStatus(N.bit_length())
|
|
||||||
|
|
||||||
# # #
|
|
||||||
|
|
||||||
self.clock_domains.cd_helper = ClockDomain(reset_less=True)
|
|
||||||
helper_locked = Signal()
|
|
||||||
helper_fb = Signal()
|
|
||||||
helper_output = Signal()
|
|
||||||
|
|
||||||
input_se = Signal()
|
|
||||||
beat1 = Signal()
|
|
||||||
beat2 = Signal()
|
|
||||||
self.specials += [
|
|
||||||
Instance("MMCME2_BASE",
|
|
||||||
p_CLKIN1_PERIOD=1e9/rtio_clk_freq,
|
|
||||||
i_CLKIN1=ClockSignal("rtio"),
|
|
||||||
i_RST=self.reset.storage,
|
|
||||||
o_LOCKED=helper_locked,
|
|
||||||
|
|
||||||
# VCO at 1200MHz with 150MHz RTIO frequency
|
|
||||||
p_CLKFBOUT_MULT_F=8.0,
|
|
||||||
p_DIVCLK_DIVIDE=1,
|
|
||||||
|
|
||||||
o_CLKFBOUT=helper_fb, i_CLKFBIN=helper_fb,
|
|
||||||
|
|
||||||
# helper PLL ratio: 64/65 (N=64)
|
|
||||||
p_CLKOUT0_DIVIDE_F=8.125,
|
|
||||||
o_CLKOUT0=helper_output,
|
|
||||||
),
|
|
||||||
MultiReg(helper_locked, self.locked.status),
|
|
||||||
Instance("BUFG", i_I=helper_output, o_O=self.cd_helper.clk),
|
|
||||||
Instance("IBUFDS", i_I=input_pads.p, i_IB=input_pads.n, o_O=input_se),
|
|
||||||
Instance("FD", i_C=self.cd_helper.clk, i_D=input_se, o_Q=beat1, attr={("IOB", "TRUE")}),
|
|
||||||
Instance("FD", i_C=self.cd_helper.clk, i_D=ClockSignal("rtio"), o_Q=beat2),
|
|
||||||
]
|
|
||||||
|
|
||||||
ed1 = DDMTDEdgeDetector(beat1)
|
|
||||||
ed2 = DDMTDEdgeDetector(beat2)
|
|
||||||
self.submodules += ed1, ed2
|
|
||||||
|
|
||||||
counting = Signal()
|
|
||||||
counter = Signal(N.bit_length())
|
|
||||||
result = Signal.like(counter)
|
|
||||||
self.sync.helper += [
|
|
||||||
If(counting,
|
|
||||||
counter.eq(counter + 1)
|
|
||||||
).Else(
|
|
||||||
result.eq(counter)
|
|
||||||
),
|
|
||||||
|
|
||||||
If(ed1.rising, counting.eq(1), counter.eq(0)),
|
|
||||||
If(ed2.rising, counting.eq(0))
|
|
||||||
]
|
|
||||||
|
|
||||||
bsync = BusSynchronizer(len(result), "helper", "sys")
|
|
||||||
self.submodules += bsync
|
|
||||||
self.comb += [
|
|
||||||
bsync.i.eq(result),
|
|
||||||
self.dt.status.eq(bsync.o)
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
# This assumes:
|
|
||||||
# * fine RTIO frequency (rtiox) = 2*RTIO frequency
|
|
||||||
# * JESD and coarse RTIO clocks are the same
|
|
||||||
# (only reset may differ).
|
|
||||||
class SysrefSampler(Module, AutoCSR):
|
|
||||||
def __init__(self, sysref_pads, coarse_ts, sysref_phase_bits=8):
|
|
||||||
self.sh_error = CSRStatus()
|
|
||||||
self.sh_error_reset = CSRStorage()
|
|
||||||
# Note: only the lower log2(RTIO frequency / SYSREF frequency) bits are stable
|
|
||||||
self.sysref_phase = CSRStatus(8)
|
|
||||||
|
|
||||||
self.jref = Signal()
|
|
||||||
|
|
||||||
# # #
|
|
||||||
|
|
||||||
sysref_se = Signal()
|
|
||||||
sysref_oversample = Signal(4)
|
|
||||||
self.specials += [
|
|
||||||
Instance("IBUFDS", i_I=sysref_pads.p, i_IB=sysref_pads.n, o_O=sysref_se),
|
|
||||||
Instance("ISERDESE3",
|
|
||||||
p_IS_CLK_INVERTED=0,
|
|
||||||
p_IS_CLK_B_INVERTED=1,
|
|
||||||
p_DATA_WIDTH=4,
|
|
||||||
|
|
||||||
i_D=sysref_se,
|
|
||||||
i_RST=ResetSignal("rtio"),
|
|
||||||
i_FIFO_RD_EN=0,
|
|
||||||
i_CLK=ClockSignal("rtiox"),
|
|
||||||
i_CLK_B=ClockSignal("rtiox"), # locally inverted
|
|
||||||
i_CLKDIV=ClockSignal("rtio"),
|
|
||||||
o_Q=sysref_oversample)
|
|
||||||
]
|
|
||||||
|
|
||||||
self.comb += self.jref.eq(sysref_oversample[1])
|
|
||||||
sh_error = Signal()
|
|
||||||
sh_error_reset = Signal()
|
|
||||||
self.sync.rtio += [
|
|
||||||
If(~( (sysref_oversample[0] == sysref_oversample[1])
|
|
||||||
& (sysref_oversample[1] == sysref_oversample[2])),
|
|
||||||
sh_error.eq(1)
|
|
||||||
),
|
|
||||||
If(sh_error_reset, sh_error.eq(0))
|
|
||||||
]
|
|
||||||
self.specials += [
|
|
||||||
MultiReg(self.sh_error_reset.storage, sh_error_reset, "rtio"),
|
|
||||||
MultiReg(sh_error, self.sh_error.status)
|
|
||||||
]
|
|
||||||
|
|
||||||
jref_r = Signal()
|
|
||||||
sysref_phase_rtio = Signal(sysref_phase_bits)
|
|
||||||
self.sync.rtio += [
|
|
||||||
jref_r.eq(self.jref),
|
|
||||||
If(self.jref & ~jref_r, sysref_phase_rtio.eq(coarse_ts))
|
|
||||||
]
|
|
||||||
sysref_phase_rtio.attr.add("no_retiming")
|
|
||||||
self.specials += MultiReg(sysref_phase_rtio, self.sysref_phase.status)
|
|
|
@ -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))
|
|
|
@ -1,180 +0,0 @@
|
||||||
#!/usr/bin/env python3
|
|
||||||
|
|
||||||
import argparse
|
|
||||||
from functools import partial
|
|
||||||
|
|
||||||
from migen import *
|
|
||||||
from migen.build.generic_platform import IOStandard
|
|
||||||
|
|
||||||
from misoc.cores import gpio
|
|
||||||
from misoc.integration.builder import builder_args, builder_argdict
|
|
||||||
from misoc.interconnect.csr import *
|
|
||||||
from misoc.targets.metlino import *
|
|
||||||
|
|
||||||
from artiq.gateware.amp import AMPSoC
|
|
||||||
from artiq.gateware import eem
|
|
||||||
from artiq.gateware import rtio
|
|
||||||
from artiq.gateware.rtio.phy import ttl_simple, ttl_serdes_ultrascale
|
|
||||||
from artiq.gateware.drtio.transceiver import gth_ultrascale
|
|
||||||
from artiq.gateware.drtio import *
|
|
||||||
from artiq.build_soc import *
|
|
||||||
|
|
||||||
|
|
||||||
def workaround_us_lvds_tristate(platform):
|
|
||||||
# Those shoddy Kintex Ultrascale FPGAs take almost a microsecond to change the direction of a
|
|
||||||
# LVDS I/O buffer. The application has to cope with it and this cannot be handled at static
|
|
||||||
# timing analysis. Disable the latter for IOBUFDS.
|
|
||||||
# See:
|
|
||||||
# https://forums.xilinx.com/t5/Timing-Analysis/Delay-890-ns-in-OBUFTDS-in-Kintex-UltraScale/td-p/868364
|
|
||||||
platform.add_platform_command(
|
|
||||||
"set_false_path -through [get_pins -filter {{REF_PIN_NAME == T}} -of [get_cells -filter {{REF_NAME == IOBUFDS}}]]")
|
|
||||||
|
|
||||||
|
|
||||||
class Master(MiniSoC, AMPSoC):
|
|
||||||
mem_map = {
|
|
||||||
"cri_con": 0x10000000,
|
|
||||||
"rtio": 0x11000000,
|
|
||||||
"rtio_dma": 0x12000000,
|
|
||||||
"drtioaux": 0x14000000,
|
|
||||||
"mailbox": 0x70000000
|
|
||||||
}
|
|
||||||
mem_map.update(MiniSoC.mem_map)
|
|
||||||
|
|
||||||
def __init__(self, gateware_identifier_str=None, **kwargs):
|
|
||||||
MiniSoC.__init__(self,
|
|
||||||
cpu_type="vexriscv",
|
|
||||||
cpu_bus_width=64,
|
|
||||||
sdram_controller_type="minicon",
|
|
||||||
l2_size=128*1024,
|
|
||||||
integrated_sram_size=8192,
|
|
||||||
ethmac_nrxslots=4,
|
|
||||||
ethmac_ntxslots=4,
|
|
||||||
csr_address_width=15,
|
|
||||||
**kwargs)
|
|
||||||
AMPSoC.__init__(self)
|
|
||||||
add_identifier(self, gateware_identifier_str=gateware_identifier_str)
|
|
||||||
|
|
||||||
platform = self.platform
|
|
||||||
rtio_clk_freq = 150e6
|
|
||||||
|
|
||||||
self.comb += platform.request("input_clk_sel").eq(1)
|
|
||||||
self.comb += platform.request("filtered_clk_sel").eq(1)
|
|
||||||
self.submodules.si5324_rst_n = gpio.GPIOOut(platform.request("si5324").rst_n)
|
|
||||||
self.csr_devices.append("si5324_rst_n")
|
|
||||||
i2c = self.platform.request("i2c")
|
|
||||||
self.submodules.i2c = gpio.GPIOTristate([i2c.scl, i2c.sda])
|
|
||||||
self.csr_devices.append("i2c")
|
|
||||||
self.config["I2C_BUS_COUNT"] = 1
|
|
||||||
self.config["HAS_SI5324"] = None
|
|
||||||
self.config["SI5324_AS_SYNTHESIZER"] = None
|
|
||||||
self.config["RTIO_FREQUENCY"] = str(rtio_clk_freq/1e6)
|
|
||||||
|
|
||||||
self.submodules.drtio_transceiver = gth_ultrascale.GTH(
|
|
||||||
clock_pads=platform.request("cdr_clk_clean", 0),
|
|
||||||
data_pads=[platform.request("mch_fabric_d", i) for i in range(11)],
|
|
||||||
sys_clk_freq=self.clk_freq,
|
|
||||||
rtio_clk_freq=rtio_clk_freq)
|
|
||||||
self.csr_devices.append("drtio_transceiver")
|
|
||||||
|
|
||||||
self.submodules.rtio_tsc = rtio.TSC("async", glbl_fine_ts_width=3)
|
|
||||||
|
|
||||||
drtio_csr_group = []
|
|
||||||
drtioaux_csr_group = []
|
|
||||||
drtioaux_memory_group = []
|
|
||||||
drtio_cri = []
|
|
||||||
for i in range(len(self.drtio_transceiver.channels)):
|
|
||||||
core_name = "drtio" + str(i)
|
|
||||||
coreaux_name = "drtioaux" + str(i)
|
|
||||||
memory_name = "drtioaux" + str(i) + "_mem"
|
|
||||||
drtio_csr_group.append(core_name)
|
|
||||||
drtioaux_csr_group.append(coreaux_name)
|
|
||||||
drtioaux_memory_group.append(memory_name)
|
|
||||||
|
|
||||||
cdr = ClockDomainsRenamer({"rtio_rx": "rtio_rx" + str(i)})
|
|
||||||
|
|
||||||
core = cdr(DRTIOMaster(self.rtio_tsc, self.drtio_transceiver.channels[i]))
|
|
||||||
setattr(self.submodules, core_name, core)
|
|
||||||
drtio_cri.append(core.cri)
|
|
||||||
self.csr_devices.append(core_name)
|
|
||||||
|
|
||||||
coreaux = cdr(DRTIOAuxController(core.link_layer, self.cpu_dw))
|
|
||||||
setattr(self.submodules, coreaux_name, coreaux)
|
|
||||||
self.csr_devices.append(coreaux_name)
|
|
||||||
|
|
||||||
memory_address = self.mem_map["drtioaux"] + 0x800*i
|
|
||||||
self.add_wb_slave(memory_address, 0x800,
|
|
||||||
coreaux.bus)
|
|
||||||
self.add_memory_region(memory_name, memory_address | self.shadow_base, 0x800)
|
|
||||||
self.config["HAS_DRTIO"] = None
|
|
||||||
self.config["HAS_DRTIO_ROUTING"] = None
|
|
||||||
self.add_csr_group("drtio", drtio_csr_group)
|
|
||||||
self.add_csr_group("drtioaux", drtioaux_csr_group)
|
|
||||||
self.add_memory_group("drtioaux_mem", drtioaux_memory_group)
|
|
||||||
|
|
||||||
rtio_clk_period = 1e9/rtio_clk_freq
|
|
||||||
gth0 = self.drtio_transceiver.gths[0]
|
|
||||||
platform.add_period_constraint(gth0.txoutclk, rtio_clk_period/2)
|
|
||||||
platform.add_period_constraint(gth0.rxoutclk, rtio_clk_period)
|
|
||||||
platform.add_false_path_constraints(
|
|
||||||
self.crg.cd_sys.clk,
|
|
||||||
gth0.txoutclk, gth0.rxoutclk)
|
|
||||||
for gth in self.drtio_transceiver.gths[1:]:
|
|
||||||
platform.add_period_constraint(gth.rxoutclk, rtio_clk_period)
|
|
||||||
platform.add_false_path_constraints(
|
|
||||||
self.crg.cd_sys.clk, gth0.txoutclk, gth.rxoutclk)
|
|
||||||
|
|
||||||
self.rtio_channels = rtio_channels = []
|
|
||||||
for i in range(4):
|
|
||||||
phy = ttl_simple.Output(platform.request("user_led", i))
|
|
||||||
self.submodules += phy
|
|
||||||
rtio_channels.append(rtio.Channel.from_phy(phy))
|
|
||||||
|
|
||||||
output_4x = partial(ttl_serdes_ultrascale.Output, 4)
|
|
||||||
eem.DIO.add_std(self, 2, output_4x, output_4x,
|
|
||||||
iostandard=lambda eem: IOStandard("LVDS"))
|
|
||||||
eem.Urukul.add_std(self, 0, 1, output_4x,
|
|
||||||
iostandard=lambda eem: IOStandard("LVDS"))
|
|
||||||
eem.Zotino.add_std(self, 3, output_4x,
|
|
||||||
iostandard=lambda eem: IOStandard("LVDS"))
|
|
||||||
workaround_us_lvds_tristate(platform)
|
|
||||||
|
|
||||||
self.config["HAS_RTIO_LOG"] = None
|
|
||||||
self.config["RTIO_LOG_CHANNEL"] = len(rtio_channels)
|
|
||||||
rtio_channels.append(rtio.LogChannel())
|
|
||||||
|
|
||||||
self.submodules.rtio_moninj = rtio.MonInj(rtio_channels)
|
|
||||||
self.csr_devices.append("rtio_moninj")
|
|
||||||
|
|
||||||
self.submodules.rtio_core = rtio.Core(self.rtio_tsc, rtio_channels)
|
|
||||||
self.csr_devices.append("rtio_core")
|
|
||||||
|
|
||||||
self.submodules.rtio = rtio.KernelInitiator(self.rtio_tsc)
|
|
||||||
self.submodules.rtio_dma = ClockDomainsRenamer("sys_kernel")(
|
|
||||||
rtio.DMA(self.get_native_sdram_if(), self.cpu_dw))
|
|
||||||
self.register_kernel_cpu_csrdevice("rtio")
|
|
||||||
self.register_kernel_cpu_csrdevice("rtio_dma")
|
|
||||||
self.submodules.cri_con = rtio.CRIInterconnectShared(
|
|
||||||
[self.rtio.cri, self.rtio_dma.cri],
|
|
||||||
[self.rtio_core.cri] + drtio_cri,
|
|
||||||
enable_routing=True)
|
|
||||||
self.register_kernel_cpu_csrdevice("cri_con")
|
|
||||||
self.submodules.routing_table = rtio.RoutingTableAccess(self.cri_con)
|
|
||||||
self.csr_devices.append("routing_table")
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
|
||||||
parser = argparse.ArgumentParser(
|
|
||||||
description="Metlino gateware and firmware builder")
|
|
||||||
builder_args(parser)
|
|
||||||
soc_sdram_args(parser)
|
|
||||||
parser.set_defaults(output_dir="artiq_metlino")
|
|
||||||
parser.add_argument("--gateware-identifier-str", default=None,
|
|
||||||
help="Override ROM identifier")
|
|
||||||
args = parser.parse_args()
|
|
||||||
args.variant = "master"
|
|
||||||
soc = Master(gateware_identifier_str=args.gateware_identifier_str, **soc_sdram_argdict(args))
|
|
||||||
build_artiq_soc(soc, builder_argdict(args))
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
main()
|
|
|
@ -1,462 +0,0 @@
|
||||||
#!/usr/bin/env python3
|
|
||||||
|
|
||||||
import argparse
|
|
||||||
import os
|
|
||||||
import warnings
|
|
||||||
from functools import partial
|
|
||||||
|
|
||||||
from migen import *
|
|
||||||
from migen.build.generic_platform import IOStandard
|
|
||||||
|
|
||||||
from misoc.cores import gpio
|
|
||||||
from misoc.integration.builder import builder_args, builder_argdict
|
|
||||||
from misoc.interconnect.csr import *
|
|
||||||
from misoc.targets.sayma_amc import *
|
|
||||||
|
|
||||||
from artiq.gateware.amp import AMPSoC
|
|
||||||
from artiq.gateware import eem
|
|
||||||
from artiq.gateware import rtio
|
|
||||||
from artiq.gateware import jesd204_tools
|
|
||||||
from artiq.gateware import fmcdio_vhdci_eem
|
|
||||||
from artiq.gateware.rtio.phy import ttl_simple, ttl_serdes_ultrascale, sawg
|
|
||||||
from artiq.gateware.drtio.transceiver import gth_ultrascale
|
|
||||||
from artiq.gateware.drtio.siphaser import SiPhaser7Series
|
|
||||||
from artiq.gateware.drtio.wrpll import WRPLL, DDMTDSamplerExtFF
|
|
||||||
from artiq.gateware.drtio.rx_synchronizer import XilinxRXSynchronizer
|
|
||||||
from artiq.gateware.drtio import *
|
|
||||||
from artiq.build_soc import *
|
|
||||||
|
|
||||||
|
|
||||||
def workaround_us_lvds_tristate(platform):
|
|
||||||
# Those shoddy Kintex Ultrascale FPGAs take almost a microsecond to change the direction of a
|
|
||||||
# LVDS I/O buffer. The application has to cope with it and this cannot be handled at static
|
|
||||||
# timing analysis. Disable the latter for IOBUFDS.
|
|
||||||
# See:
|
|
||||||
# https://forums.xilinx.com/t5/Timing-Analysis/Delay-890-ns-in-OBUFTDS-in-Kintex-UltraScale/td-p/868364
|
|
||||||
platform.add_platform_command(
|
|
||||||
"set_false_path -through [get_pins -filter {{REF_PIN_NAME == T}} -of [get_cells -filter {{REF_NAME == IOBUFDS}}]]")
|
|
||||||
|
|
||||||
|
|
||||||
class RTMUARTForward(Module):
|
|
||||||
def __init__(self, platform):
|
|
||||||
# forward RTM UART to second FTDI UART channel
|
|
||||||
serial_1 = platform.request("serial", 1)
|
|
||||||
serial_rtm = platform.request("serial_rtm")
|
|
||||||
self.comb += [
|
|
||||||
serial_1.tx.eq(serial_rtm.rx),
|
|
||||||
serial_rtm.tx.eq(serial_1.rx)
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class SatelliteBase(MiniSoC):
|
|
||||||
mem_map = {
|
|
||||||
"drtioaux": 0x14000000,
|
|
||||||
}
|
|
||||||
mem_map.update(MiniSoC.mem_map)
|
|
||||||
|
|
||||||
def __init__(self, rtio_clk_freq=125e6, identifier_suffix="", gateware_identifier_str=None, with_sfp=False, *, with_wrpll, **kwargs):
|
|
||||||
MiniSoC.__init__(self,
|
|
||||||
cpu_type="vexriscv",
|
|
||||||
cpu_bus_width=64,
|
|
||||||
sdram_controller_type="minicon",
|
|
||||||
l2_size=128*1024,
|
|
||||||
integrated_sram_size=8192,
|
|
||||||
ethmac_nrxslots=4,
|
|
||||||
ethmac_ntxslots=4,
|
|
||||||
**kwargs)
|
|
||||||
add_identifier(self, suffix=identifier_suffix, gateware_identifier_str=gateware_identifier_str)
|
|
||||||
self.rtio_clk_freq = rtio_clk_freq
|
|
||||||
|
|
||||||
platform = self.platform
|
|
||||||
|
|
||||||
if with_wrpll:
|
|
||||||
clock_recout_pads = platform.request("ddmtd_rec_clk")
|
|
||||||
else:
|
|
||||||
clock_recout_pads = None
|
|
||||||
if with_sfp:
|
|
||||||
# Use SFP0 to connect to master (Kasli)
|
|
||||||
self.comb += platform.request("sfp_tx_disable", 0).eq(0)
|
|
||||||
drtio_uplink = platform.request("sfp", 0)
|
|
||||||
else:
|
|
||||||
drtio_uplink = platform.request("fat_pipe", 0)
|
|
||||||
self.submodules.drtio_transceiver = gth_ultrascale.GTH(
|
|
||||||
clock_pads=platform.request("cdr_clk_clean"),
|
|
||||||
data_pads=[drtio_uplink, platform.request("rtm_amc_link")],
|
|
||||||
sys_clk_freq=self.clk_freq,
|
|
||||||
rtio_clk_freq=rtio_clk_freq,
|
|
||||||
clock_recout_pads=clock_recout_pads)
|
|
||||||
self.csr_devices.append("drtio_transceiver")
|
|
||||||
|
|
||||||
self.submodules.rtio_tsc = rtio.TSC("sync", glbl_fine_ts_width=3)
|
|
||||||
|
|
||||||
drtioaux_csr_group = []
|
|
||||||
drtioaux_memory_group = []
|
|
||||||
drtiorep_csr_group = []
|
|
||||||
self.drtio_cri = []
|
|
||||||
for i in range(len(self.drtio_transceiver.channels)):
|
|
||||||
coreaux_name = "drtioaux" + str(i)
|
|
||||||
memory_name = "drtioaux" + str(i) + "_mem"
|
|
||||||
drtioaux_csr_group.append(coreaux_name)
|
|
||||||
drtioaux_memory_group.append(memory_name)
|
|
||||||
|
|
||||||
cdr = ClockDomainsRenamer({"rtio_rx": "rtio_rx" + str(i)})
|
|
||||||
|
|
||||||
if i == 0:
|
|
||||||
self.submodules.rx_synchronizer = cdr(XilinxRXSynchronizer())
|
|
||||||
core = cdr(DRTIOSatellite(
|
|
||||||
self.rtio_tsc, self.drtio_transceiver.channels[i],
|
|
||||||
self.rx_synchronizer))
|
|
||||||
self.submodules.drtiosat = core
|
|
||||||
self.csr_devices.append("drtiosat")
|
|
||||||
else:
|
|
||||||
corerep_name = "drtiorep" + str(i-1)
|
|
||||||
drtiorep_csr_group.append(corerep_name)
|
|
||||||
|
|
||||||
core = cdr(DRTIORepeater(
|
|
||||||
self.rtio_tsc, self.drtio_transceiver.channels[i]))
|
|
||||||
setattr(self.submodules, corerep_name, core)
|
|
||||||
self.drtio_cri.append(core.cri)
|
|
||||||
self.csr_devices.append(corerep_name)
|
|
||||||
|
|
||||||
coreaux = cdr(DRTIOAuxController(core.link_layer, self.cpu_dw))
|
|
||||||
setattr(self.submodules, coreaux_name, coreaux)
|
|
||||||
self.csr_devices.append(coreaux_name)
|
|
||||||
|
|
||||||
memory_address = self.mem_map["drtioaux"] + 0x800*i
|
|
||||||
self.add_wb_slave(memory_address, 0x800,
|
|
||||||
coreaux.bus)
|
|
||||||
self.add_memory_region(memory_name, memory_address | self.shadow_base, 0x800)
|
|
||||||
self.config["HAS_DRTIO"] = None
|
|
||||||
self.config["HAS_DRTIO_ROUTING"] = None
|
|
||||||
self.add_csr_group("drtioaux", drtioaux_csr_group)
|
|
||||||
self.add_memory_group("drtioaux_mem", drtioaux_memory_group)
|
|
||||||
self.add_csr_group("drtiorep", drtiorep_csr_group)
|
|
||||||
|
|
||||||
rtio_clk_period = 1e9/rtio_clk_freq
|
|
||||||
self.config["RTIO_FREQUENCY"] = str(rtio_clk_freq/1e6)
|
|
||||||
if with_wrpll:
|
|
||||||
self.comb += [
|
|
||||||
platform.request("filtered_clk_sel").eq(0),
|
|
||||||
platform.request("ddmtd_main_dcxo_oe").eq(1),
|
|
||||||
platform.request("ddmtd_helper_dcxo_oe").eq(1)
|
|
||||||
]
|
|
||||||
self.submodules.wrpll_sampler = DDMTDSamplerExtFF(
|
|
||||||
platform.request("ddmtd_inputs"))
|
|
||||||
self.submodules.wrpll = WRPLL(
|
|
||||||
helper_clk_pads=platform.request("ddmtd_helper_clk"),
|
|
||||||
main_dcxo_i2c=platform.request("ddmtd_main_dcxo_i2c"),
|
|
||||||
helper_dxco_i2c=platform.request("ddmtd_helper_dcxo_i2c"),
|
|
||||||
ddmtd_inputs=self.wrpll_sampler)
|
|
||||||
self.csr_devices.append("wrpll")
|
|
||||||
platform.add_period_constraint(self.wrpll.cd_helper.clk, rtio_clk_period*0.99)
|
|
||||||
platform.add_false_path_constraints(self.crg.cd_sys.clk, self.wrpll.cd_helper.clk)
|
|
||||||
else:
|
|
||||||
self.comb += platform.request("filtered_clk_sel").eq(1)
|
|
||||||
self.submodules.siphaser = SiPhaser7Series(
|
|
||||||
si5324_clkin=platform.request("si5324_clkin"),
|
|
||||||
rx_synchronizer=self.rx_synchronizer,
|
|
||||||
ultrascale=True,
|
|
||||||
rtio_clk_freq=rtio_clk_freq)
|
|
||||||
platform.add_false_path_constraints(
|
|
||||||
self.crg.cd_sys.clk, self.siphaser.mmcm_freerun_output)
|
|
||||||
self.csr_devices.append("siphaser")
|
|
||||||
self.submodules.si5324_rst_n = gpio.GPIOOut(platform.request("si5324").rst_n)
|
|
||||||
self.csr_devices.append("si5324_rst_n")
|
|
||||||
i2c = self.platform.request("i2c")
|
|
||||||
self.submodules.i2c = gpio.GPIOTristate([i2c.scl, i2c.sda])
|
|
||||||
self.csr_devices.append("i2c")
|
|
||||||
self.config["I2C_BUS_COUNT"] = 1
|
|
||||||
self.config["HAS_SI5324"] = None
|
|
||||||
|
|
||||||
gth = self.drtio_transceiver.gths[0]
|
|
||||||
platform.add_period_constraint(gth.txoutclk, rtio_clk_period/2)
|
|
||||||
platform.add_period_constraint(gth.rxoutclk, rtio_clk_period)
|
|
||||||
platform.add_false_path_constraints(
|
|
||||||
self.crg.cd_sys.clk,
|
|
||||||
gth.txoutclk, gth.rxoutclk)
|
|
||||||
|
|
||||||
def add_rtio(self, rtio_channels):
|
|
||||||
# Only add MonInj core if there is anything to monitor
|
|
||||||
if any([len(c.probes) for c in rtio_channels]):
|
|
||||||
self.submodules.rtio_moninj = rtio.MonInj(rtio_channels)
|
|
||||||
self.csr_devices.append("rtio_moninj")
|
|
||||||
|
|
||||||
self.submodules.local_io = SyncRTIO(self.rtio_tsc, rtio_channels)
|
|
||||||
self.comb += self.drtiosat.async_errors.eq(self.local_io.async_errors)
|
|
||||||
self.submodules.cri_con = rtio.CRIInterconnectShared(
|
|
||||||
[self.drtiosat.cri],
|
|
||||||
[self.local_io.cri] + self.drtio_cri,
|
|
||||||
mode="sync", enable_routing=True)
|
|
||||||
self.csr_devices.append("cri_con")
|
|
||||||
self.submodules.routing_table = rtio.RoutingTableAccess(self.cri_con)
|
|
||||||
self.csr_devices.append("routing_table")
|
|
||||||
|
|
||||||
|
|
||||||
# JESD204 DAC Channel Group
|
|
||||||
class JDCGSAWG(Module, AutoCSR):
|
|
||||||
def __init__(self, platform, sys_crg, jesd_crg, dac):
|
|
||||||
# Kintex Ultrascale GTH, speed grade -1C:
|
|
||||||
# CPLL linerate (D=1): 4.0 - 8.5 Gb/s
|
|
||||||
self.submodules.jesd = jesd204_tools.UltrascaleTX(
|
|
||||||
platform, sys_crg, jesd_crg, dac)
|
|
||||||
|
|
||||||
self.submodules.sawgs = [sawg.Channel(width=16, parallelism=4) for i in range(4)]
|
|
||||||
|
|
||||||
for conv, ch in zip(self.jesd.core.sink.flatten(), self.sawgs):
|
|
||||||
assert len(Cat(ch.o)) == len(conv)
|
|
||||||
self.sync.jesd += conv.eq(Cat(ch.o))
|
|
||||||
|
|
||||||
|
|
||||||
class JDCGPattern(Module, AutoCSR):
|
|
||||||
def __init__(self, platform, sys_crg, jesd_crg, dac):
|
|
||||||
self.submodules.jesd = jesd204_tools.UltrascaleTX(
|
|
||||||
platform, sys_crg, jesd_crg, dac)
|
|
||||||
|
|
||||||
self.sawgs = []
|
|
||||||
|
|
||||||
ramp = Signal(4)
|
|
||||||
self.sync.rtio += ramp.eq(ramp + 1)
|
|
||||||
|
|
||||||
samples = [[Signal(16) for i in range(4)] for j in range(4)]
|
|
||||||
self.comb += [
|
|
||||||
a.eq(Cat(b)) for a, b in zip(
|
|
||||||
self.jesd.core.sink.flatten(), samples)
|
|
||||||
]
|
|
||||||
# ch0: 16-step ramp with big carry toggles
|
|
||||||
for i in range(4):
|
|
||||||
self.comb += [
|
|
||||||
samples[0][i][-4:].eq(ramp),
|
|
||||||
samples[0][i][:-4].eq(0x7ff if i % 2 else 0x800)
|
|
||||||
]
|
|
||||||
# ch1: 50 MHz
|
|
||||||
from math import pi, cos
|
|
||||||
data = [int(round(cos(i/12*2*pi)*((1 << 15) - 1)))
|
|
||||||
for i in range(12)]
|
|
||||||
k = Signal(2)
|
|
||||||
self.sync.rtio += If(k == 2, k.eq(0)).Else(k.eq(k + 1))
|
|
||||||
self.comb += [
|
|
||||||
Case(k, {
|
|
||||||
i: [samples[1][j].eq(data[i*4 + j]) for j in range(4)]
|
|
||||||
for i in range(3)
|
|
||||||
})
|
|
||||||
]
|
|
||||||
# ch2: ch0, ch3: ch1
|
|
||||||
self.comb += [
|
|
||||||
Cat(samples[2]).eq(Cat(samples[0])),
|
|
||||||
Cat(samples[3]).eq(Cat(samples[1]))
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class JDCGSyncDDS(Module, AutoCSR):
|
|
||||||
def __init__(self, platform, sys_crg, jesd_crg, dac):
|
|
||||||
self.submodules.jesd = jesd204_tools.UltrascaleTX(
|
|
||||||
platform, sys_crg, jesd_crg, dac)
|
|
||||||
self.coarse_ts = Signal(32)
|
|
||||||
|
|
||||||
self.sawgs = []
|
|
||||||
|
|
||||||
ftw = round(2**len(self.coarse_ts)*9e6/600e6)
|
|
||||||
parallelism = 4
|
|
||||||
|
|
||||||
mul_1 = Signal.like(self.coarse_ts)
|
|
||||||
mul_2 = Signal.like(self.coarse_ts)
|
|
||||||
mul_3 = Signal.like(self.coarse_ts)
|
|
||||||
self.sync.rtio += [
|
|
||||||
mul_1.eq(self.coarse_ts*ftw*parallelism),
|
|
||||||
mul_2.eq(mul_1),
|
|
||||||
mul_3.eq(mul_2)
|
|
||||||
]
|
|
||||||
|
|
||||||
phases = [Signal.like(self.coarse_ts) for i in range(parallelism)]
|
|
||||||
self.sync.rtio += [phases[i].eq(mul_3 + i*ftw) for i in range(parallelism)]
|
|
||||||
|
|
||||||
resolution = 10
|
|
||||||
steps = 2**resolution
|
|
||||||
from math import pi, cos
|
|
||||||
data = [(2**16 + round(cos(i/steps*2*pi)*((1 << 15) - 1))) & 0xffff
|
|
||||||
for i in range(steps)]
|
|
||||||
samples = [Signal(16) for i in range(4)]
|
|
||||||
for phase, sample in zip(phases, samples):
|
|
||||||
table = Memory(16, steps, init=data)
|
|
||||||
table_port = table.get_port(clock_domain="rtio")
|
|
||||||
self.specials += table, table_port
|
|
||||||
self.comb += [
|
|
||||||
table_port.adr.eq(phase >> (len(self.coarse_ts) - resolution)),
|
|
||||||
sample.eq(table_port.dat_r)
|
|
||||||
]
|
|
||||||
|
|
||||||
self.sync.rtio += [sink.eq(Cat(samples))
|
|
||||||
for sink in self.jesd.core.sink.flatten()]
|
|
||||||
|
|
||||||
|
|
||||||
class Satellite(SatelliteBase):
|
|
||||||
"""
|
|
||||||
DRTIO satellite with local DAC/SAWG channels, as well as TTL channels via FMC and VHDCI carrier.
|
|
||||||
"""
|
|
||||||
def __init__(self, jdcg_type, **kwargs):
|
|
||||||
SatelliteBase.__init__(self, 150e6,
|
|
||||||
identifier_suffix="." + jdcg_type,
|
|
||||||
**kwargs)
|
|
||||||
|
|
||||||
platform = self.platform
|
|
||||||
|
|
||||||
self.submodules += RTMUARTForward(platform)
|
|
||||||
|
|
||||||
# RTM bitstream upload
|
|
||||||
slave_fpga_cfg = self.platform.request("rtm_fpga_cfg")
|
|
||||||
self.submodules.slave_fpga_cfg = gpio.GPIOTristate([
|
|
||||||
slave_fpga_cfg.cclk,
|
|
||||||
slave_fpga_cfg.din,
|
|
||||||
slave_fpga_cfg.done,
|
|
||||||
slave_fpga_cfg.init_b,
|
|
||||||
slave_fpga_cfg.program_b,
|
|
||||||
])
|
|
||||||
self.csr_devices.append("slave_fpga_cfg")
|
|
||||||
self.config["SLAVE_FPGA_GATEWARE"] = 0x200000
|
|
||||||
|
|
||||||
self.rtio_channels = rtio_channels = []
|
|
||||||
for i in range(4):
|
|
||||||
phy = ttl_simple.Output(platform.request("user_led", i))
|
|
||||||
self.submodules += phy
|
|
||||||
rtio_channels.append(rtio.Channel.from_phy(phy))
|
|
||||||
mcx_io = platform.request("mcx_io", 0)
|
|
||||||
phy = ttl_serdes_ultrascale.InOut(4, mcx_io.level)
|
|
||||||
self.comb += mcx_io.direction.eq(phy.oe)
|
|
||||||
self.submodules += phy
|
|
||||||
rtio_channels.append(rtio.Channel.from_phy(phy))
|
|
||||||
mcx_io = platform.request("mcx_io", 1)
|
|
||||||
phy = ttl_serdes_ultrascale.InOut(4, mcx_io.level)
|
|
||||||
self.comb += mcx_io.direction.eq(phy.oe)
|
|
||||||
self.submodules += phy
|
|
||||||
rtio_channels.append(rtio.Channel.from_phy(phy))
|
|
||||||
|
|
||||||
self.submodules.jesd_crg = jesd204_tools.UltrascaleCRG(
|
|
||||||
platform, use_rtio_clock=True)
|
|
||||||
cls = {
|
|
||||||
"sawg": JDCGSAWG,
|
|
||||||
"pattern": JDCGPattern,
|
|
||||||
"syncdds": JDCGSyncDDS
|
|
||||||
}[jdcg_type]
|
|
||||||
self.submodules.jdcg_0 = cls(platform, self.crg, self.jesd_crg, 0)
|
|
||||||
self.submodules.jdcg_1 = cls(platform, self.crg, self.jesd_crg, 1)
|
|
||||||
self.csr_devices.append("jesd_crg")
|
|
||||||
self.csr_devices.append("jdcg_0")
|
|
||||||
self.csr_devices.append("jdcg_1")
|
|
||||||
self.config["HAS_JDCG"] = None
|
|
||||||
self.add_csr_group("jdcg", ["jdcg_0", "jdcg_1"])
|
|
||||||
self.config["RTIO_FIRST_SAWG_CHANNEL"] = len(rtio_channels)
|
|
||||||
rtio_channels.extend(rtio.Channel.from_phy(phy)
|
|
||||||
for sawg in self.jdcg_0.sawgs +
|
|
||||||
self.jdcg_1.sawgs
|
|
||||||
for phy in sawg.phys)
|
|
||||||
|
|
||||||
# FMC-VHDCI-EEM DIOs x 2 (all OUTPUTs)
|
|
||||||
platform.add_connectors(fmcdio_vhdci_eem.connectors)
|
|
||||||
output_4x = partial(ttl_serdes_ultrascale.Output, 4)
|
|
||||||
eem.DIO.add_std(self, 0,
|
|
||||||
output_4x, output_4x,
|
|
||||||
iostandard=lambda eem: IOStandard("LVDS"))
|
|
||||||
eem.DIO.add_std(self, 1,
|
|
||||||
output_4x, output_4x,
|
|
||||||
iostandard=lambda eem: IOStandard("LVDS"))
|
|
||||||
# FMC-DIO-32ch-LVDS-a Direction Control Pins (via shift register) as TTLs x 3
|
|
||||||
platform.add_extension(fmcdio_vhdci_eem.io)
|
|
||||||
print("fmcdio_vhdci_eem.[CLK, SER, LATCH] starting at RTIO channel 0x{:06x}"
|
|
||||||
.format(len(rtio_channels)))
|
|
||||||
fmcdio_dirctl = platform.request("fmcdio_dirctl", 0)
|
|
||||||
fmcdio_dirctl_phys = [
|
|
||||||
ttl_simple.Output(fmcdio_dirctl.clk),
|
|
||||||
ttl_simple.Output(fmcdio_dirctl.ser),
|
|
||||||
ttl_simple.Output(fmcdio_dirctl.latch)
|
|
||||||
]
|
|
||||||
for phy in fmcdio_dirctl_phys:
|
|
||||||
self.submodules += phy
|
|
||||||
rtio_channels.append(rtio.Channel.from_phy(phy))
|
|
||||||
workaround_us_lvds_tristate(platform)
|
|
||||||
|
|
||||||
self.add_rtio(rtio_channels)
|
|
||||||
|
|
||||||
self.submodules.sysref_sampler = jesd204_tools.SysrefSampler(
|
|
||||||
platform.request("amc_fpga_sysref", 0), self.rtio_tsc.coarse_ts)
|
|
||||||
self.csr_devices.append("sysref_sampler")
|
|
||||||
self.jdcg_0.jesd.core.register_jref(self.sysref_sampler.jref)
|
|
||||||
self.jdcg_1.jesd.core.register_jref(self.sysref_sampler.jref)
|
|
||||||
if jdcg_type == "syncdds":
|
|
||||||
self.comb += [
|
|
||||||
self.jdcg_0.coarse_ts.eq(self.rtio_tsc.coarse_ts),
|
|
||||||
self.jdcg_1.coarse_ts.eq(self.rtio_tsc.coarse_ts),
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class SimpleSatellite(SatelliteBase):
|
|
||||||
def __init__(self, **kwargs):
|
|
||||||
SatelliteBase.__init__(self, **kwargs)
|
|
||||||
|
|
||||||
platform = self.platform
|
|
||||||
|
|
||||||
self.submodules += RTMUARTForward(platform)
|
|
||||||
|
|
||||||
rtio_channels = []
|
|
||||||
for i in range(4):
|
|
||||||
phy = ttl_simple.Output(platform.request("user_led", i))
|
|
||||||
self.submodules += phy
|
|
||||||
rtio_channels.append(rtio.Channel.from_phy(phy))
|
|
||||||
mcx_io = platform.request("mcx_io", 0)
|
|
||||||
phy = ttl_serdes_ultrascale.InOut(4, mcx_io.level)
|
|
||||||
self.comb += mcx_io.direction.eq(phy.oe)
|
|
||||||
self.submodules += phy
|
|
||||||
rtio_channels.append(rtio.Channel.from_phy(phy))
|
|
||||||
mcx_io = platform.request("mcx_io", 1)
|
|
||||||
phy = ttl_serdes_ultrascale.InOut(4, mcx_io.level)
|
|
||||||
self.comb += mcx_io.direction.eq(phy.oe)
|
|
||||||
self.submodules += phy
|
|
||||||
rtio_channels.append(rtio.Channel.from_phy(phy))
|
|
||||||
|
|
||||||
self.add_rtio(rtio_channels)
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
|
||||||
parser = argparse.ArgumentParser(
|
|
||||||
description="Sayma AMC gateware and firmware builder")
|
|
||||||
builder_args(parser)
|
|
||||||
soc_sayma_amc_args(parser)
|
|
||||||
parser.set_defaults(output_dir="artiq_sayma")
|
|
||||||
parser.add_argument("-V", "--variant", default="satellite",
|
|
||||||
help="variant: satellite/simplesatellite "
|
|
||||||
"(default: %(default)s)")
|
|
||||||
parser.add_argument("--sfp", default=False, action="store_true",
|
|
||||||
help="use SFP port for DRTIO instead of uTCA backplane")
|
|
||||||
parser.add_argument("--rtm-csr-csv",
|
|
||||||
default=os.path.join("artiq_sayma", "rtm_gateware", "rtm_csr.csv"),
|
|
||||||
help="CSV file listing remote CSRs on RTM (default: %(default)s)")
|
|
||||||
parser.add_argument("--jdcg-type",
|
|
||||||
default="sawg",
|
|
||||||
help="Change type of signal generator. This is used exclusively for "
|
|
||||||
"development and debugging.")
|
|
||||||
parser.add_argument("--with-wrpll", default=False, action="store_true")
|
|
||||||
parser.add_argument("--gateware-identifier-str", default=None,
|
|
||||||
help="Override ROM identifier")
|
|
||||||
args = parser.parse_args()
|
|
||||||
|
|
||||||
variant = args.variant.lower()
|
|
||||||
if variant == "satellite":
|
|
||||||
soc = Satellite(
|
|
||||||
with_sfp=args.sfp,
|
|
||||||
jdcg_type=args.jdcg_type,
|
|
||||||
with_wrpll=args.with_wrpll,
|
|
||||||
gateware_identifier_str=args.gateware_identifier_str,
|
|
||||||
**soc_sayma_amc_argdict(args))
|
|
||||||
elif variant == "simplesatellite":
|
|
||||||
soc = SimpleSatellite(
|
|
||||||
with_sfp=args.sfp,
|
|
||||||
with_wrpll=args.with_wrpll,
|
|
||||||
gateware_identifier_str=args.gateware_identifier_str,
|
|
||||||
**soc_sayma_amc_argdict(args))
|
|
||||||
else:
|
|
||||||
raise SystemExit("Invalid variant (-V/--variant)")
|
|
||||||
|
|
||||||
build_artiq_soc(soc, builder_argdict(args))
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
main()
|
|
|
@ -1,280 +0,0 @@
|
||||||
#!/usr/bin/env python3
|
|
||||||
|
|
||||||
import argparse
|
|
||||||
import os
|
|
||||||
import subprocess
|
|
||||||
import struct
|
|
||||||
|
|
||||||
from migen import *
|
|
||||||
from migen.genlib.cdc import MultiReg
|
|
||||||
|
|
||||||
from misoc.interconnect.csr import *
|
|
||||||
from misoc.cores import gpio
|
|
||||||
from misoc.cores import spi2
|
|
||||||
from misoc.cores.a7_gtp import *
|
|
||||||
from misoc.targets.sayma_rtm import BaseSoC, soc_sayma_rtm_args, soc_sayma_rtm_argdict
|
|
||||||
from misoc.integration.builder import Builder, builder_args, builder_argdict
|
|
||||||
|
|
||||||
from artiq.gateware import rtio
|
|
||||||
from artiq.gateware import jesd204_tools
|
|
||||||
from artiq.gateware.rtio.phy import ttl_simple, ttl_serdes_7series
|
|
||||||
from artiq.gateware.rtio.xilinx_clocking import RTIOClockMultiplier, fix_serdes_timing_path
|
|
||||||
from artiq.gateware.drtio.transceiver import gtp_7series
|
|
||||||
from artiq.gateware.drtio.siphaser import SiPhaser7Series
|
|
||||||
from artiq.gateware.drtio.wrpll import WRPLL, DDMTDSamplerGTP
|
|
||||||
from artiq.gateware.drtio.rx_synchronizer import XilinxRXSynchronizer
|
|
||||||
from artiq.gateware.drtio import *
|
|
||||||
from artiq.build_soc import add_identifier
|
|
||||||
from artiq import __artiq_dir__ as artiq_dir
|
|
||||||
|
|
||||||
|
|
||||||
class _SatelliteBase(BaseSoC):
|
|
||||||
mem_map = {
|
|
||||||
"drtioaux": 0x50000000,
|
|
||||||
}
|
|
||||||
mem_map.update(BaseSoC.mem_map)
|
|
||||||
|
|
||||||
def __init__(self, rtio_clk_freq, *, with_wrpll, gateware_identifier_str, **kwargs):
|
|
||||||
BaseSoC.__init__(self,
|
|
||||||
cpu_type="vexriscv",
|
|
||||||
cpu_bus_width=64,
|
|
||||||
**kwargs)
|
|
||||||
add_identifier(self, gateware_identifier_str=gateware_identifier_str)
|
|
||||||
self.rtio_clk_freq = rtio_clk_freq
|
|
||||||
|
|
||||||
platform = self.platform
|
|
||||||
|
|
||||||
disable_cdrclkc_ibuf = Signal(reset=1)
|
|
||||||
disable_cdrclkc_ibuf.attr.add("no_retiming")
|
|
||||||
cdrclkc_clkout = platform.request("cdr_clk_clean")
|
|
||||||
cdrclkc_clkout_buf = Signal()
|
|
||||||
self.specials += Instance("IBUFDS_GTE2",
|
|
||||||
i_CEB=disable_cdrclkc_ibuf,
|
|
||||||
i_I=cdrclkc_clkout.p, i_IB=cdrclkc_clkout.n,
|
|
||||||
o_O=cdrclkc_clkout_buf)
|
|
||||||
qpll_drtio_settings = QPLLSettings(
|
|
||||||
refclksel=0b001,
|
|
||||||
fbdiv=4,
|
|
||||||
fbdiv_45=5,
|
|
||||||
refclk_div=1)
|
|
||||||
qpll = QPLL(cdrclkc_clkout_buf, qpll_drtio_settings)
|
|
||||||
self.submodules += qpll
|
|
||||||
|
|
||||||
self.submodules.drtio_transceiver = gtp_7series.GTP(
|
|
||||||
qpll_channel=qpll.channels[0],
|
|
||||||
data_pads=[platform.request("rtm_amc_link", 0)],
|
|
||||||
sys_clk_freq=self.clk_freq,
|
|
||||||
rtio_clk_freq=rtio_clk_freq)
|
|
||||||
self.csr_devices.append("drtio_transceiver")
|
|
||||||
self.sync += disable_cdrclkc_ibuf.eq(
|
|
||||||
~self.drtio_transceiver.stable_clkin.storage)
|
|
||||||
|
|
||||||
self.submodules.rtio_tsc = rtio.TSC("sync", glbl_fine_ts_width=3)
|
|
||||||
|
|
||||||
cdr = ClockDomainsRenamer({"rtio_rx": "rtio_rx0"})
|
|
||||||
|
|
||||||
self.submodules.rx_synchronizer = cdr(XilinxRXSynchronizer())
|
|
||||||
core = cdr(DRTIOSatellite(
|
|
||||||
self.rtio_tsc, self.drtio_transceiver.channels[0],
|
|
||||||
self.rx_synchronizer))
|
|
||||||
self.submodules.drtiosat = core
|
|
||||||
self.csr_devices.append("drtiosat")
|
|
||||||
|
|
||||||
coreaux = cdr(DRTIOAuxController(core.link_layer, self.cpu_dw))
|
|
||||||
self.submodules.drtioaux0 = coreaux
|
|
||||||
self.csr_devices.append("drtioaux0")
|
|
||||||
|
|
||||||
memory_address = self.mem_map["drtioaux"]
|
|
||||||
self.add_wb_slave(memory_address, 0x800,
|
|
||||||
coreaux.bus)
|
|
||||||
self.add_memory_region("drtioaux0_mem", memory_address | self.shadow_base, 0x800)
|
|
||||||
|
|
||||||
self.config["HAS_DRTIO"] = None
|
|
||||||
self.add_csr_group("drtioaux", ["drtioaux0"])
|
|
||||||
self.add_memory_group("drtioaux_mem", ["drtioaux0_mem"])
|
|
||||||
|
|
||||||
gtp = self.drtio_transceiver.gtps[0]
|
|
||||||
rtio_clk_period = 1e9/rtio_clk_freq
|
|
||||||
self.config["RTIO_FREQUENCY"] = str(rtio_clk_freq/1e6)
|
|
||||||
if with_wrpll:
|
|
||||||
self.comb += [
|
|
||||||
platform.request("filtered_clk_sel").eq(0),
|
|
||||||
platform.request("ddmtd_main_dcxo_oe").eq(1),
|
|
||||||
platform.request("ddmtd_helper_dcxo_oe").eq(1)
|
|
||||||
]
|
|
||||||
self.submodules.wrpll_sampler = DDMTDSamplerGTP(
|
|
||||||
self.drtio_transceiver,
|
|
||||||
platform.request("cdr_clk_clean_fabric"))
|
|
||||||
self.submodules.wrpll = WRPLL(
|
|
||||||
helper_clk_pads=platform.request("ddmtd_helper_clk"),
|
|
||||||
main_dcxo_i2c=platform.request("ddmtd_main_dcxo_i2c"),
|
|
||||||
helper_dxco_i2c=platform.request("ddmtd_helper_dcxo_i2c"),
|
|
||||||
ddmtd_inputs=self.wrpll_sampler)
|
|
||||||
self.csr_devices.append("wrpll")
|
|
||||||
platform.add_period_constraint(self.wrpll.cd_helper.clk, rtio_clk_period*0.99)
|
|
||||||
platform.add_false_path_constraints(self.crg.cd_sys.clk, self.wrpll.cd_helper.clk)
|
|
||||||
platform.add_false_path_constraints(self.wrpll.cd_helper.clk, gtp.rxoutclk)
|
|
||||||
else:
|
|
||||||
self.comb += platform.request("filtered_clk_sel").eq(1)
|
|
||||||
self.submodules.siphaser = SiPhaser7Series(
|
|
||||||
si5324_clkin=platform.request("si5324_clkin"),
|
|
||||||
rx_synchronizer=self.rx_synchronizer,
|
|
||||||
ref_clk=self.crg.cd_sys.clk, ref_div2=True,
|
|
||||||
rtio_clk_freq=rtio_clk_freq)
|
|
||||||
platform.add_false_path_constraints(
|
|
||||||
self.crg.cd_sys.clk, self.siphaser.mmcm_freerun_output)
|
|
||||||
self.csr_devices.append("siphaser")
|
|
||||||
self.submodules.si5324_rst_n = gpio.GPIOOut(platform.request("si5324").rst_n)
|
|
||||||
self.csr_devices.append("si5324_rst_n")
|
|
||||||
i2c = self.platform.request("i2c")
|
|
||||||
self.submodules.i2c = gpio.GPIOTristate([i2c.scl, i2c.sda])
|
|
||||||
self.csr_devices.append("i2c")
|
|
||||||
self.config["I2C_BUS_COUNT"] = 1
|
|
||||||
self.config["HAS_SI5324"] = None
|
|
||||||
|
|
||||||
platform.add_period_constraint(gtp.txoutclk, rtio_clk_period)
|
|
||||||
platform.add_period_constraint(gtp.rxoutclk, rtio_clk_period)
|
|
||||||
platform.add_false_path_constraints(
|
|
||||||
self.crg.cd_sys.clk,
|
|
||||||
gtp.txoutclk, gtp.rxoutclk)
|
|
||||||
|
|
||||||
self.submodules.rtio_crg = RTIOClockMultiplier(rtio_clk_freq)
|
|
||||||
self.csr_devices.append("rtio_crg")
|
|
||||||
fix_serdes_timing_path(platform)
|
|
||||||
|
|
||||||
def add_rtio(self, rtio_channels):
|
|
||||||
self.submodules.rtio_moninj = rtio.MonInj(rtio_channels)
|
|
||||||
self.csr_devices.append("rtio_moninj")
|
|
||||||
|
|
||||||
self.submodules.local_io = SyncRTIO(self.rtio_tsc, rtio_channels)
|
|
||||||
self.comb += self.drtiosat.async_errors.eq(self.local_io.async_errors)
|
|
||||||
self.comb += self.drtiosat.cri.connect(self.local_io.cri)
|
|
||||||
|
|
||||||
|
|
||||||
class Satellite(_SatelliteBase):
|
|
||||||
def __init__(self, **kwargs):
|
|
||||||
_SatelliteBase.__init__(self, **kwargs)
|
|
||||||
|
|
||||||
platform = self.platform
|
|
||||||
|
|
||||||
rtio_channels = []
|
|
||||||
for bm in range(2):
|
|
||||||
print("BaseMod{} RF switches starting at RTIO channel 0x{:06x}"
|
|
||||||
.format(bm, len(rtio_channels)))
|
|
||||||
for i in range(4):
|
|
||||||
phy = ttl_serdes_7series.Output_8X(platform.request("basemod{}_rfsw".format(bm), i),
|
|
||||||
invert=True)
|
|
||||||
self.submodules += phy
|
|
||||||
rtio_channels.append(rtio.Channel.from_phy(phy))
|
|
||||||
|
|
||||||
print("BaseMod{} attenuator starting at RTIO channel 0x{:06x}"
|
|
||||||
.format(bm, len(rtio_channels)))
|
|
||||||
basemod_att = platform.request("basemod{}_att".format(bm))
|
|
||||||
for name in "rst_n clk le".split():
|
|
||||||
signal = getattr(basemod_att, name)
|
|
||||||
for i in range(len(signal)):
|
|
||||||
phy = ttl_simple.Output(signal[i])
|
|
||||||
self.submodules += phy
|
|
||||||
rtio_channels.append(rtio.Channel.from_phy(phy))
|
|
||||||
phy = ttl_simple.Output(basemod_att.mosi[0])
|
|
||||||
self.submodules += phy
|
|
||||||
rtio_channels.append(rtio.Channel.from_phy(phy))
|
|
||||||
for i in range(3):
|
|
||||||
self.comb += basemod_att.mosi[i+1].eq(basemod_att.miso[i])
|
|
||||||
phy = ttl_simple.InOut(basemod_att.miso[3])
|
|
||||||
self.submodules += phy
|
|
||||||
rtio_channels.append(rtio.Channel.from_phy(phy))
|
|
||||||
|
|
||||||
self.add_rtio(rtio_channels)
|
|
||||||
|
|
||||||
self.comb += platform.request("clk_src_ext_sel").eq(0)
|
|
||||||
|
|
||||||
# HMC clock chip and DAC control
|
|
||||||
self.comb += [
|
|
||||||
platform.request("ad9154_rst_n", 0).eq(1),
|
|
||||||
platform.request("ad9154_rst_n", 1).eq(1)
|
|
||||||
]
|
|
||||||
self.submodules.converter_spi = spi2.SPIMaster(spi2.SPIInterface(
|
|
||||||
platform.request("hmc_spi"),
|
|
||||||
platform.request("ad9154_spi", 0),
|
|
||||||
platform.request("ad9154_spi", 1)))
|
|
||||||
self.csr_devices.append("converter_spi")
|
|
||||||
self.submodules.hmc7043_reset = gpio.GPIOOut(
|
|
||||||
platform.request("hmc7043_reset"), reset_out=1)
|
|
||||||
self.csr_devices.append("hmc7043_reset")
|
|
||||||
self.submodules.hmc7043_gpo = gpio.GPIOIn(
|
|
||||||
platform.request("hmc7043_gpo"))
|
|
||||||
self.csr_devices.append("hmc7043_gpo")
|
|
||||||
self.config["HAS_HMC830_7043"] = None
|
|
||||||
self.config["HAS_AD9154"] = None
|
|
||||||
self.config["AD9154_COUNT"] = 2
|
|
||||||
self.config["CONVERTER_SPI_HMC830_CS"] = 0
|
|
||||||
self.config["CONVERTER_SPI_HMC7043_CS"] = 1
|
|
||||||
self.config["CONVERTER_SPI_FIRST_AD9154_CS"] = 2
|
|
||||||
self.config["HMC830_REF"] = str(int(self.rtio_clk_freq/1e6))
|
|
||||||
|
|
||||||
# HMC workarounds
|
|
||||||
self.comb += platform.request("hmc830_pwr_en").eq(1)
|
|
||||||
self.submodules.hmc7043_out_en = gpio.GPIOOut(
|
|
||||||
platform.request("hmc7043_out_en"))
|
|
||||||
self.csr_devices.append("hmc7043_out_en")
|
|
||||||
|
|
||||||
# DDMTD
|
|
||||||
sysref_pads = platform.request("rtm_fpga_sysref", 0)
|
|
||||||
self.submodules.sysref_ddmtd = jesd204_tools.DDMTD(sysref_pads, self.rtio_clk_freq)
|
|
||||||
self.csr_devices.append("sysref_ddmtd")
|
|
||||||
platform.add_false_path_constraints(
|
|
||||||
self.sysref_ddmtd.cd_helper.clk, self.drtio_transceiver.gtps[0].txoutclk)
|
|
||||||
platform.add_false_path_constraints(
|
|
||||||
self.sysref_ddmtd.cd_helper.clk, self.crg.cd_sys.clk)
|
|
||||||
|
|
||||||
|
|
||||||
class SatmanSoCBuilder(Builder):
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
Builder.__init__(self, *args, **kwargs)
|
|
||||||
firmware_dir = os.path.join(artiq_dir, "firmware")
|
|
||||||
self.software_packages = []
|
|
||||||
self.add_software_package("satman", os.path.join(firmware_dir, "satman"))
|
|
||||||
|
|
||||||
def initialize_memory(self):
|
|
||||||
satman = os.path.join(self.output_dir, "software", "satman",
|
|
||||||
"satman.bin")
|
|
||||||
with open(satman, "rb") as boot_file:
|
|
||||||
boot_data = []
|
|
||||||
unpack_endian = "<I"
|
|
||||||
while True:
|
|
||||||
w = boot_file.read(4)
|
|
||||||
if not w:
|
|
||||||
break
|
|
||||||
boot_data.append(struct.unpack(unpack_endian, w)[0])
|
|
||||||
|
|
||||||
self.soc.main_ram.mem.init = boot_data
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
|
||||||
parser = argparse.ArgumentParser(
|
|
||||||
description="Sayma RTM gateware and firmware builder")
|
|
||||||
builder_args(parser)
|
|
||||||
soc_sayma_rtm_args(parser)
|
|
||||||
parser.add_argument("--rtio-clk-freq",
|
|
||||||
default=150, type=int, help="RTIO clock frequency in MHz")
|
|
||||||
parser.add_argument("--with-wrpll", default=False, action="store_true")
|
|
||||||
parser.add_argument("--gateware-identifier-str", default=None,
|
|
||||||
help="Override ROM identifier")
|
|
||||||
parser.set_defaults(output_dir=os.path.join("artiq_sayma", "rtm"))
|
|
||||||
args = parser.parse_args()
|
|
||||||
|
|
||||||
soc = Satellite(
|
|
||||||
rtio_clk_freq=1e6*args.rtio_clk_freq,
|
|
||||||
with_wrpll=args.with_wrpll,
|
|
||||||
gateware_identifier_str=args.gateware_identifier_str,
|
|
||||||
**soc_sayma_rtm_argdict(args))
|
|
||||||
builder = SatmanSoCBuilder(soc, **builder_argdict(args))
|
|
||||||
try:
|
|
||||||
builder.build()
|
|
||||||
except subprocess.CalledProcessError as e:
|
|
||||||
raise SystemExit("Command {} failed".format(" ".join(e.cmd)))
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
main()
|
|
|
@ -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()
|
|
|
@ -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()
|
|
|
@ -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()
|
|
|
@ -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()
|
|
|
@ -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()
|
|
|
@ -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()
|
|
|
@ -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()
|
|
|
@ -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()))
|
|
|
@ -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()
|
|
|
@ -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()
|
|
|
@ -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")
|
|
|
@ -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
|
|
|
@ -47,12 +47,6 @@ Digital I/O drivers
|
||||||
.. automodule:: artiq.coredevice.edge_counter
|
.. automodule:: artiq.coredevice.edge_counter
|
||||||
:members:
|
:members:
|
||||||
|
|
||||||
:mod:`artiq.coredevice.shiftreg` module
|
|
||||||
+++++++++++++++++++++++++++++++++++++++
|
|
||||||
|
|
||||||
.. automodule:: artiq.coredevice.shiftreg
|
|
||||||
:members:
|
|
||||||
|
|
||||||
:mod:`artiq.coredevice.spi2` module
|
:mod:`artiq.coredevice.spi2` module
|
||||||
+++++++++++++++++++++++++++++++++++
|
+++++++++++++++++++++++++++++++++++
|
||||||
|
|
||||||
|
@ -104,25 +98,6 @@ RF generation drivers
|
||||||
.. automodule:: artiq.coredevice.adf5356
|
.. automodule:: artiq.coredevice.adf5356
|
||||||
:members:
|
: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
|
:mod:`artiq.coredevice.phaser` module
|
||||||
+++++++++++++++++++++++++++++++++++++
|
+++++++++++++++++++++++++++++++++++++
|
||||||
|
|
||||||
|
@ -150,12 +125,6 @@ DAC/ADC drivers
|
||||||
.. automodule:: artiq.coredevice.sampler
|
.. automodule:: artiq.coredevice.sampler
|
||||||
:members:
|
:members:
|
||||||
|
|
||||||
:mod:`artiq.coredevice.novogorny` module
|
|
||||||
++++++++++++++++++++++++++++++++++++++++
|
|
||||||
|
|
||||||
.. automodule:: artiq.coredevice.novogorny
|
|
||||||
:members:
|
|
||||||
|
|
||||||
:mod:`artiq.coredevice.fastino` module
|
:mod:`artiq.coredevice.fastino` module
|
||||||
++++++++++++++++++++++++++++++++++++++++
|
++++++++++++++++++++++++++++++++++++++++
|
||||||
|
|
||||||
|
|
|
@ -120,9 +120,6 @@ Core device RTIO analyzer tool
|
||||||
:ref: artiq.frontend.artiq_coreanalyzer.get_argparser
|
:ref: artiq.frontend.artiq_coreanalyzer.get_argparser
|
||||||
:prog: artiq_coreanalyzer
|
:prog: artiq_coreanalyzer
|
||||||
|
|
||||||
.. note::
|
|
||||||
The RTIO analyzer does not support SAWG.
|
|
||||||
|
|
||||||
.. _routing-table-tool:
|
.. _routing-table-tool:
|
||||||
|
|
||||||
DRTIO routing table manipulation tool
|
DRTIO routing table manipulation tool
|
||||||
|
|
14
flake.nix
14
flake.nix
|
@ -135,18 +135,6 @@
|
||||||
propagatedBuildInputs = with pkgs.python3Packages; [ jinja2 numpy migen pyserial asyncserial ];
|
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 {
|
microscope = pkgs.python3Packages.buildPythonPackage rec {
|
||||||
pname = "microscope";
|
pname = "microscope";
|
||||||
version = "unstable-2020-12-28";
|
version = "unstable-2020-12-28";
|
||||||
|
@ -356,7 +344,7 @@
|
||||||
devShell.x86_64-linux = pkgs.mkShell {
|
devShell.x86_64-linux = pkgs.mkShell {
|
||||||
name = "artiq-dev-shell";
|
name = "artiq-dev-shell";
|
||||||
buildInputs = [
|
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.rustc
|
||||||
rustPlatform.rust.cargo
|
rustPlatform.rust.cargo
|
||||||
cargo-xbuild
|
cargo-xbuild
|
||||||
|
|
Loading…
Reference in New Issue