forked from M-Labs/artiq
Merge branch 'urukul-sync'
* urukul-sync: (29 commits) urukul: flake8 [nfc] ad9910: flake8 [nfc] urukul/ad9910 test: remove unused import test_urukul: relax speed urukul,ad9910: print speed metrics kasli: add PTB2 (external clock and SYNC) kasli: add sync to LUH, HUB, Opticlock kasli_tester: urukul0 mmcx clock defunct test_ad9910: relax ifc mode read tests: add Urukul-AD9910 HITL unittests including SYNC ad9910: add init bit explanation test: add Urukul CPLD HITL tests ad9910: fiducial timestamp for tracking phase mode ad9910: add phase modes ad9910: fix pll timeout loop tester: add urukul sync ptb: back out urukul-sync ad9910: add IO_UPDATE alignment and tuning urukul: set up sync_in generator ad9910: add io_update alignment measurement ... close #1143
This commit is contained in:
commit
31f68ddf6c
|
@ -1,13 +1,27 @@
|
|||
from numpy import int32, int64
|
||||
|
||||
from artiq.language.core import kernel, delay, portable
|
||||
from artiq.language.units import us, ns, ms
|
||||
from artiq.language.core import (
|
||||
kernel, delay, portable, delay_mu, now_mu, at_mu)
|
||||
from artiq.language.units import us, ms
|
||||
|
||||
from artiq.coredevice import spi2 as spi
|
||||
from artiq.coredevice import urukul
|
||||
# Work around ARTIQ-Python import machinery
|
||||
urukul_sta_pll_lock = urukul.urukul_sta_pll_lock
|
||||
urukul_sta_smp_err = urukul.urukul_sta_smp_err
|
||||
|
||||
|
||||
__all__ = [
|
||||
"AD9910",
|
||||
"PHASE_MODE_CONTINUOUS", "PHASE_MODE_ABSOLUTE", "PHASE_MODE_TRACKING"
|
||||
]
|
||||
|
||||
|
||||
_PHASE_MODE_DEFAULT = -1
|
||||
PHASE_MODE_CONTINUOUS = 0
|
||||
PHASE_MODE_ABSOLUTE = 1
|
||||
PHASE_MODE_TRACKING = 2
|
||||
|
||||
_AD9910_REG_CFR1 = 0x00
|
||||
_AD9910_REG_CFR2 = 0x01
|
||||
_AD9910_REG_CFR3 = 0x02
|
||||
|
@ -49,12 +63,21 @@ class AD9910:
|
|||
Urukul CPLD instance).
|
||||
:param pll_cp: DDS PLL charge pump setting.
|
||||
:param pll_vco: DDS PLL VCO range selection.
|
||||
:param sync_delay_seed: SYNC_IN delay tuning starting value.
|
||||
To stabilize the SYNC_IN delay tuning, run :meth:`tune_sync_delay` once
|
||||
and set this to the delay tap number returned (default: -1 to signal no
|
||||
synchronization and no tuning during :meth:`init`).
|
||||
:param io_update_delay: IO_UPDATE pulse alignment delay.
|
||||
To align IO_UPDATE to SYNC_CLK, run :meth:`tune_io_update_delay` and
|
||||
set this to the delay tap number returned.
|
||||
"""
|
||||
kernel_invariants = {"chip_select", "cpld", "core", "bus",
|
||||
"ftw_per_hz", "pll_n", "pll_cp", "pll_vco"}
|
||||
"ftw_per_hz", "pll_n", "io_update_delay",
|
||||
"sysclk_per_mu"}
|
||||
|
||||
def __init__(self, dmgr, chip_select, cpld_device, sw_device=None,
|
||||
pll_n=40, pll_cp=7, pll_vco=5):
|
||||
pll_n=40, pll_cp=7, pll_vco=5, sync_delay_seed=-1,
|
||||
io_update_delay=0):
|
||||
self.cpld = dmgr.get(cpld_device)
|
||||
self.core = self.cpld.core
|
||||
self.bus = self.cpld.bus
|
||||
|
@ -68,7 +91,9 @@ class AD9910:
|
|||
assert self.cpld.refclk/4 <= 60e6
|
||||
sysclk = self.cpld.refclk*pll_n/4 # Urukul clock fanout divider
|
||||
assert sysclk <= 1e9
|
||||
self.ftw_per_hz = 1./sysclk*(int64(1) << 32)
|
||||
self.ftw_per_hz = (1 << 32)/sysclk
|
||||
self.sysclk_per_mu = int(round(sysclk*self.core.ref_period))
|
||||
assert self.sysclk_per_mu == sysclk*self.core.ref_period
|
||||
assert 0 <= pll_vco <= 5
|
||||
vco_min, vco_max = [(370, 510), (420, 590), (500, 700),
|
||||
(600, 880), (700, 950), (820, 1150)][pll_vco]
|
||||
|
@ -76,6 +101,54 @@ class AD9910:
|
|||
self.pll_vco = pll_vco
|
||||
assert 0 <= pll_cp <= 7
|
||||
self.pll_cp = pll_cp
|
||||
self.sync_delay_seed = sync_delay_seed
|
||||
self.io_update_delay = io_update_delay
|
||||
self.phase_mode = PHASE_MODE_CONTINUOUS
|
||||
|
||||
@kernel
|
||||
def set_phase_mode(self, phase_mode):
|
||||
"""Set the default phase mode.
|
||||
|
||||
for future calls to :meth:`set` and
|
||||
:meth:`set_mu`. Supported phase modes are:
|
||||
|
||||
* :const:`PHASE_MODE_CONTINUOUS`: the phase accumulator is unchanged
|
||||
when changing frequency or phase. The DDS phase is the sum of the
|
||||
phase accumulator and the phase offset. The only discontinuous
|
||||
changes in the DDS output phase come from changes to the phase
|
||||
offset. This mode is also knows as "relative phase mode".
|
||||
:math:`\phi(t) = q(t^\prime) + p + (t - t^\prime) f`
|
||||
|
||||
* :const:`PHASE_MODE_ABSOLUTE`: the phase accumulator is reset when
|
||||
changing frequency or phase. Thus, the phase of the DDS at the
|
||||
time of the change is equal to the specified phase offset.
|
||||
:math:`\phi(t) = p + (t - t^\prime) f`
|
||||
|
||||
* :const:`PHASE_MODE_TRACKING`: when changing frequency or phase,
|
||||
the phase accumulator is cleared and the phase offset is offset
|
||||
by the value the phase accumulator would have if the DDS had been
|
||||
running at the specified frequency since a given fiducial
|
||||
time stamp. This is functionally equivalent to
|
||||
:const:`PHASE_MODE_ABSOLUTE`. The only difference is the fiducial
|
||||
time stamp. This mode is also known as "coherent phase mode".
|
||||
The default fiducial time stamp is 0.
|
||||
:math:`\phi(t) = p + (t - T) f`
|
||||
|
||||
Where:
|
||||
|
||||
* :math:`\phi(t)`: the DDS output phase
|
||||
* :math:`q(t) = \phi(t) - p`: DDS internal phase accumulator
|
||||
* :math:`p`: phase offset
|
||||
* :math:`f`: frequency
|
||||
* :math:`t^\prime`: time stamp of setting :math:`p`, :math:`f`
|
||||
* :math:`T`: fiducial time stamp
|
||||
* :math:`t`: running time
|
||||
|
||||
.. warning:: This setting may become inconsistent when used as part of
|
||||
a DMA recording. When using DMA, it is recommended to specify the
|
||||
phase mode explicitly when calling :meth:`set` or :meth:`set_mu`.
|
||||
"""
|
||||
self.phase_mode = phase_mode
|
||||
|
||||
@kernel
|
||||
def write32(self, addr, data):
|
||||
|
@ -100,9 +173,9 @@ class AD9910:
|
|||
self.bus.set_config_mu(urukul.SPI_CONFIG, 8,
|
||||
urukul.SPIT_DDS_WR, self.chip_select)
|
||||
self.bus.write((addr | 0x80) << 24)
|
||||
self.bus.set_config_mu(urukul.SPI_CONFIG | spi.SPI_END
|
||||
| spi.SPI_INPUT, 32,
|
||||
urukul.SPIT_DDS_RD, self.chip_select)
|
||||
self.bus.set_config_mu(
|
||||
urukul.SPI_CONFIG | spi.SPI_END | spi.SPI_INPUT,
|
||||
32, urukul.SPIT_DDS_RD, self.chip_select)
|
||||
self.bus.write(0)
|
||||
return self.bus.read()
|
||||
|
||||
|
@ -136,7 +209,7 @@ class AD9910:
|
|||
"""
|
||||
# Set SPI mode
|
||||
self.write32(_AD9910_REG_CFR1, 0x00000002)
|
||||
self.cpld.io_update.pulse(2*us)
|
||||
self.cpld.io_update.pulse(1*us)
|
||||
delay(1*ms)
|
||||
if not blind:
|
||||
# Use the AUX DAC setting to identify and confirm presence
|
||||
|
@ -145,60 +218,121 @@ class AD9910:
|
|||
raise ValueError("Urukul AD9910 AUX_DAC mismatch")
|
||||
delay(50*us) # slack
|
||||
# Configure PLL settings and bring up PLL
|
||||
self.write32(_AD9910_REG_CFR2, 0x01400020)
|
||||
self.cpld.io_update.pulse(2*us)
|
||||
# enable amplitude scale from profiles
|
||||
# read effective FTW
|
||||
# sync timing validation disable (enabled later)
|
||||
self.write32(_AD9910_REG_CFR2, 0x01010020)
|
||||
self.cpld.io_update.pulse(1*us)
|
||||
cfr3 = (0x0807c100 | (self.pll_vco << 24) |
|
||||
(self.pll_cp << 19) | (self.pll_n << 1))
|
||||
self.write32(_AD9910_REG_CFR3, cfr3 | 0x400) # PFD reset
|
||||
self.cpld.io_update.pulse(100*us)
|
||||
self.cpld.io_update.pulse(1*us)
|
||||
self.write32(_AD9910_REG_CFR3, cfr3)
|
||||
self.cpld.io_update.pulse(100*us)
|
||||
self.cpld.io_update.pulse(1*us)
|
||||
if blind:
|
||||
delay(100*ms)
|
||||
return
|
||||
else:
|
||||
# Wait for PLL lock, up to 100 ms
|
||||
for i in range(100):
|
||||
sta = self.cpld.sta_read()
|
||||
lock = urukul_sta_pll_lock(sta)
|
||||
delay(1*ms)
|
||||
if lock & (1 << self.chip_select - 4):
|
||||
return
|
||||
break
|
||||
if i >= 100 - 1:
|
||||
raise ValueError("PLL lock timeout")
|
||||
if self.sync_delay_seed >= 0:
|
||||
self.tune_sync_delay(self.sync_delay_seed)
|
||||
delay(1*ms)
|
||||
|
||||
@kernel
|
||||
def set_mu(self, ftw, pow=0, asf=0x3fff):
|
||||
def power_down(self, bits=0b1111):
|
||||
"""Power down DDS.
|
||||
|
||||
:param bits: power down bits, see datasheet
|
||||
"""
|
||||
self.write32(_AD9910_REG_CFR1, 0x00000002 | (bits << 4))
|
||||
self.cpld.io_update.pulse(1*us)
|
||||
|
||||
@kernel
|
||||
def set_mu(self, ftw, pow=0, asf=0x3fff, phase_mode=_PHASE_MODE_DEFAULT,
|
||||
ref_time=-1):
|
||||
"""Set profile 0 data in machine units.
|
||||
|
||||
This uses machine units (FTW, POW, ASF). The frequency tuning word
|
||||
width is 32, the phase offset word width is 16, and the amplitude
|
||||
scale factor width is 12.
|
||||
|
||||
After the SPI transfer, the shared IO update pin is pulsed to
|
||||
activate the data.
|
||||
|
||||
.. seealso: :meth:`set_phase_mode` for a definition of the different
|
||||
phase modes.
|
||||
|
||||
:param ftw: Frequency tuning word: 32 bit.
|
||||
:param pow: Phase tuning word: 16 bit unsigned.
|
||||
:param asf: Amplitude scale factor: 14 bit unsigned.
|
||||
:param phase_mode: If specified, overrides the default phase mode set
|
||||
by :meth:`set_phase_mode` for this call.
|
||||
:param ref_time: Fiducial time used to compute absolute or tracking
|
||||
phase updates. In machine units as obtained by `now_mu()`.
|
||||
:return: Resulting phase offset word after application of phase
|
||||
tracking offset. When using :const:`PHASE_MODE_CONTINUOUS` in
|
||||
subsequent calls, use this value as the "current" phase.
|
||||
"""
|
||||
if phase_mode == _PHASE_MODE_DEFAULT:
|
||||
phase_mode = self.phase_mode
|
||||
# Align to coarse RTIO which aligns SYNC_CLK
|
||||
at_mu(now_mu() & ~0xf)
|
||||
if phase_mode != PHASE_MODE_CONTINUOUS:
|
||||
# Auto-clear phase accumulator on IO_UPDATE.
|
||||
# This is active already for the next IO_UPDATE
|
||||
self.write32(_AD9910_REG_CFR1, 0x00002002)
|
||||
if phase_mode == PHASE_MODE_TRACKING and ref_time < 0:
|
||||
# set default fiducial time stamp
|
||||
ref_time = 0
|
||||
if ref_time >= 0:
|
||||
# 32 LSB are sufficient.
|
||||
# Also no need to use IO_UPDATE time as this
|
||||
# is equivalent to an output pipeline latency.
|
||||
dt = int32(now_mu()) - int32(ref_time)
|
||||
pow += dt*ftw*self.sysclk_per_mu >> 16
|
||||
self.write64(_AD9910_REG_PR0, (asf << 16) | pow, ftw)
|
||||
self.cpld.io_update.pulse(10*ns)
|
||||
delay_mu(int64(self.io_update_delay))
|
||||
self.cpld.io_update.pulse_mu(8) # assumes 8 mu > t_SYSCLK
|
||||
at_mu(now_mu() & ~0xf)
|
||||
if phase_mode != PHASE_MODE_CONTINUOUS:
|
||||
self.write32(_AD9910_REG_CFR1, 0x00000002)
|
||||
# future IO_UPDATE will activate
|
||||
return pow
|
||||
|
||||
@portable(flags={"fast-math"})
|
||||
def frequency_to_ftw(self, frequency):
|
||||
"""Returns the frequency tuning word corresponding to the given
|
||||
"""Return the frequency tuning word corresponding to the given
|
||||
frequency.
|
||||
"""
|
||||
return int32(round(self.ftw_per_hz*frequency))
|
||||
|
||||
@portable(flags={"fast-math"})
|
||||
def turns_to_pow(self, turns):
|
||||
"""Returns the phase offset word corresponding to the given phase
|
||||
"""Return the phase offset word corresponding to the given phase
|
||||
in turns."""
|
||||
return int32(round(turns*0x10000))
|
||||
|
||||
@portable(flags={"fast-math"})
|
||||
def amplitude_to_asf(self, amplitude):
|
||||
"""Returns amplitude scale factor corresponding to given amplitude."""
|
||||
"""Return amplitude scale factor corresponding to given amplitude."""
|
||||
return int32(round(amplitude*0x3ffe))
|
||||
|
||||
@portable(flags={"fast-math"})
|
||||
def pow_to_turns(self, pow):
|
||||
"""Return the phase in turns corresponding to a given phase offset
|
||||
word."""
|
||||
return pow/0x10000
|
||||
|
||||
@kernel
|
||||
def set(self, frequency, phase=0.0, amplitude=1.0):
|
||||
def set(self, frequency, phase=0.0, amplitude=1.0,
|
||||
phase_mode=_PHASE_MODE_DEFAULT, ref_time=-1):
|
||||
"""Set profile 0 data in SI units.
|
||||
|
||||
.. seealso:: :meth:`set_mu`
|
||||
|
@ -206,10 +340,13 @@ class AD9910:
|
|||
:param ftw: Frequency in Hz
|
||||
:param pow: Phase tuning word in turns
|
||||
:param asf: Amplitude in units of full scale
|
||||
:param phase_mode: Phase mode constant
|
||||
:param ref_time: Fiducial time stamp in machine units
|
||||
:return: Resulting phase offset in turns
|
||||
"""
|
||||
self.set_mu(self.frequency_to_ftw(frequency),
|
||||
self.turns_to_pow(phase),
|
||||
self.amplitude_to_asf(amplitude))
|
||||
return self.pow_to_turns(self.set_mu(
|
||||
self.frequency_to_ftw(frequency), self.turns_to_pow(phase),
|
||||
self.amplitude_to_asf(amplitude), phase_mode, ref_time))
|
||||
|
||||
@kernel
|
||||
def set_att_mu(self, att):
|
||||
|
@ -240,3 +377,148 @@ class AD9910:
|
|||
:param state: CPLD CFG RF switch bit
|
||||
"""
|
||||
self.cpld.cfg_sw(self.chip_select - 4, state)
|
||||
|
||||
@kernel
|
||||
def set_sync(self, in_delay, window):
|
||||
"""Set the relevant parameters in the multi device synchronization
|
||||
register. See the AD9910 datasheet for details. The SYNC clock
|
||||
generator preset value is set to zero, and the SYNC_OUT generator is
|
||||
disabled.
|
||||
|
||||
:param in_delay: SYNC_IN delay tap (0-31) in steps of ~75ps
|
||||
:param window: Symmetric SYNC_IN validation window (0-15) in
|
||||
steps of ~75ps for both hold and setup margin.
|
||||
"""
|
||||
self.write32(_AD9910_REG_MSYNC,
|
||||
(window << 28) | # SYNC S/H validation delay
|
||||
(1 << 27) | # SYNC receiver enable
|
||||
(0 << 26) | # SYNC generator disable
|
||||
(0 << 25) | # SYNC generator SYS rising edge
|
||||
(0 << 18) | # SYNC preset
|
||||
(0 << 11) | # SYNC output delay
|
||||
(in_delay << 3)) # SYNC receiver delay
|
||||
|
||||
@kernel
|
||||
def clear_smp_err(self):
|
||||
"""Clear the SMP_ERR flag and enables SMP_ERR validity monitoring.
|
||||
|
||||
Violations of the SYNC_IN sample and hold margins will result in
|
||||
SMP_ERR being asserted. This then also activates the red LED on
|
||||
the respective Urukul channel.
|
||||
|
||||
Also modifies CFR2.
|
||||
"""
|
||||
self.write32(_AD9910_REG_CFR2, 0x01010020) # clear SMP_ERR
|
||||
self.cpld.io_update.pulse(1*us)
|
||||
self.write32(_AD9910_REG_CFR2, 0x01010000) # enable SMP_ERR
|
||||
self.cpld.io_update.pulse(1*us)
|
||||
|
||||
@kernel
|
||||
def tune_sync_delay(self, sync_delay_seed):
|
||||
"""Find a stable SYNC_IN delay.
|
||||
|
||||
This method first locates the smallest SYNC_IN validity window at
|
||||
minimum window size and then increases the window a bit to provide some
|
||||
slack and stability.
|
||||
|
||||
It starts scanning delays around `sync_delay_seed` (see the
|
||||
device database arguments and :class:`AD9910`) at maximum validation
|
||||
window size and decreases the window size until a valid delay is found.
|
||||
|
||||
:param sync_delay_seed: Start value for valid SYNC_IN delay search.
|
||||
:return: Tuple of optimal delay and window size.
|
||||
"""
|
||||
dt = 14 # 1/(f_SYSCLK*75ps) taps per SYSCLK period
|
||||
max_delay = dt # 14*75ps > 1ns
|
||||
max_window = dt//4 + 1 # 2*75ps*4 = 600ps high > 1ns/2
|
||||
min_window = max(0, max_window - 2) # 2*75ps hold, 2*75ps setup
|
||||
for window in range(max_window - min_window + 1):
|
||||
window = max_window - window
|
||||
for in_delay in range(max_delay):
|
||||
# alternate search direction around seed_delay
|
||||
if in_delay & 1:
|
||||
in_delay = -in_delay
|
||||
in_delay = sync_delay_seed + (in_delay >> 1)
|
||||
if in_delay < 0:
|
||||
in_delay = 0
|
||||
elif in_delay > 31:
|
||||
in_delay = 31
|
||||
self.set_sync(in_delay, window)
|
||||
self.clear_smp_err()
|
||||
# integrate SMP_ERR statistics for a few hundred cycles
|
||||
delay(10*us)
|
||||
err = urukul_sta_smp_err(self.cpld.sta_read())
|
||||
err = (err >> (self.chip_select - 4)) & 1
|
||||
delay(40*us) # slack
|
||||
if not err:
|
||||
window -= min_window # add margin
|
||||
self.set_sync(in_delay, window)
|
||||
self.clear_smp_err()
|
||||
delay(40*us) # slack
|
||||
return in_delay, window
|
||||
raise ValueError("no valid window/delay")
|
||||
|
||||
@kernel
|
||||
def measure_io_update_alignment(self, io_up_delay):
|
||||
"""Use the digital ramp generator to locate the alignment between
|
||||
IO_UPDATE and SYNC_CLK.
|
||||
|
||||
The ramp generator is set up to a linear frequency ramp
|
||||
(dFTW/t_SYNC_CLK=1) and started at a RTIO time stamp.
|
||||
|
||||
After scanning the alignment, an IO_UPDATE delay midway between two
|
||||
edges should be chosen.
|
||||
|
||||
:return: odd/even SYNC_CLK cycle indicator
|
||||
"""
|
||||
# set up DRG
|
||||
# DRG ACC autoclear and LRR on io update
|
||||
self.write32(_AD9910_REG_CFR1, 0x0000c002)
|
||||
# DRG -> FTW, DRG enable
|
||||
self.write32(_AD9910_REG_CFR2, 0x01090000)
|
||||
# no limits
|
||||
self.write64(_AD9910_REG_DRAMPL, -1, 0)
|
||||
# DRCTL=0, dt=1 t_SYNC_CLK
|
||||
self.write32(_AD9910_REG_DRAMPR, 0x00010000)
|
||||
# dFTW = 1, (work around negative slope)
|
||||
self.write64(_AD9910_REG_DRAMPS, -1, 0)
|
||||
at_mu(now_mu() + 0x10 & ~0xf) # align to RTIO/2
|
||||
self.cpld.io_update.pulse_mu(8)
|
||||
# disable DRG autoclear and LRR on io_update
|
||||
self.write32(_AD9910_REG_CFR1, 0x00000002)
|
||||
# stop DRG
|
||||
self.write64(_AD9910_REG_DRAMPS, 0, 0)
|
||||
at_mu((now_mu() + 0x10 & ~0xf) + io_up_delay) # delay
|
||||
self.cpld.io_update.pulse_mu(32 - io_up_delay) # realign
|
||||
ftw = self.read32(_AD9910_REG_FTW) # read out effective FTW
|
||||
delay(100*us) # slack
|
||||
# disable DRG
|
||||
self.write32(_AD9910_REG_CFR2, 0x01010000)
|
||||
self.cpld.io_update.pulse_mu(8)
|
||||
return ftw & 1
|
||||
|
||||
@kernel
|
||||
def tune_io_update_delay(self):
|
||||
"""Find a stable IO_UPDATE delay alignment.
|
||||
|
||||
Scan through increasing IO_UPDATE delays until a delay is found that
|
||||
lets IO_UPDATE be registered in the next SYNC_CLK cycle. Return a
|
||||
IO_UPDATE delay that is midway between two such SYNC_CLK transitions.
|
||||
|
||||
This method assumes that the IO_UPDATE TTLOut device has one machine
|
||||
unit resolution (SERDES) and that the ratio between fine RTIO frequency
|
||||
(RTIO time machine units) and SYNC_CLK is 4.
|
||||
|
||||
:return: Stable IO_UPDATE delay to be passed to the constructor
|
||||
:class:`AD9910` via the device database.
|
||||
"""
|
||||
period = 4 # f_RTIO/f_SYNC = 4
|
||||
max_delay = 8 # mu, 1 ns
|
||||
d0 = self.io_update_delay
|
||||
t0 = int32(self.measure_io_update_alignment(d0))
|
||||
for i in range(max_delay - 1):
|
||||
t = self.measure_io_update_alignment(
|
||||
(d0 + i + 1) & (max_delay - 1))
|
||||
if t != t0:
|
||||
return (d0 + i + period//2) & (period - 1)
|
||||
raise ValueError("no IO_UPDATE-SYNC_CLK alignment edge found")
|
||||
|
|
|
@ -430,14 +430,15 @@ class TTLClockGen:
|
|||
The time cursor is not modified by any function in this class.
|
||||
|
||||
:param channel: channel number
|
||||
:param acc_width: accumulator width in bits
|
||||
"""
|
||||
kernel_invariants = {"core", "channel", "acc_width"}
|
||||
|
||||
def __init__(self, dmgr, channel, core_device="core"):
|
||||
def __init__(self, dmgr, channel, acc_width=24, core_device="core"):
|
||||
self.core = dmgr.get(core_device)
|
||||
self.channel = channel
|
||||
|
||||
self.acc_width = numpy.int64(24)
|
||||
self.acc_width = numpy.int64(acc_width)
|
||||
|
||||
@portable
|
||||
def frequency_to_ftw(self, frequency):
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
from artiq.language.core import kernel, delay, portable
|
||||
from artiq.language.core import kernel, delay, portable, at_mu, now_mu
|
||||
from artiq.language.units import us, ms
|
||||
|
||||
from numpy import int32
|
||||
|
@ -109,12 +109,22 @@ class _RegIOUpdate:
|
|||
self.cpld.cfg_write(cfg)
|
||||
|
||||
|
||||
class _DummySync:
|
||||
def __init__(self, cpld):
|
||||
self.cpld = cpld
|
||||
|
||||
@kernel
|
||||
def set_mu(self, ftw):
|
||||
pass
|
||||
|
||||
|
||||
class CPLD:
|
||||
"""Urukul CPLD SPI router and configuration interface.
|
||||
|
||||
:param spi_device: SPI bus device name
|
||||
:param io_update_device: IO update RTIO TTLOut channel name
|
||||
:param dds_reset_device: DDS reset RTIO TTLOut channel name
|
||||
:param sync_device: AD9910 SYNC_IN RTIO TTLClockGen channel name
|
||||
:param refclk: Reference clock (SMA, MMCX or on-board 100 MHz oscillator)
|
||||
frequency in Hz
|
||||
:param clk_sel: Reference clock selection. For hardware revision >= 1.3
|
||||
|
@ -122,20 +132,25 @@ class CPLD:
|
|||
internal MMCX. For hardware revision <= v1.2 valid options are: 0 -
|
||||
either XO or MMCX dependent on component population; 1 SMA. Unsupported
|
||||
clocking options are silently ignored.
|
||||
:param sync_sel: SYNC clock selection. 0 corresponds to SYNC clock over EEM
|
||||
from FPGA. 1 corresponds to SYNC clock from DDS0.
|
||||
:param sync_sel: SYNC_IN selection. 0 corresponds to SYNC_IN over EEM
|
||||
from FPGA. 1 corresponds to SYNC_IN from DDS0.
|
||||
:param rf_sw: Initial CPLD RF switch register setting (default: 0x0).
|
||||
Knowledge of this state is not transferred between experiments.
|
||||
:param att: Initial attenuator setting shift register (default:
|
||||
0x00000000). See also: :meth:`set_all_att_mu`. Knowledge of this state
|
||||
is not transferred between experiments.
|
||||
:param sync_div: SYNC_IN generator divider. The ratio between the coarse
|
||||
RTIO frequency and the SYNC_IN generator frequency (default: 2 if
|
||||
`sync_device` was specified).
|
||||
:param core_device: Core device name
|
||||
"""
|
||||
kernel_invariants = {"refclk", "bus", "core", "io_update"}
|
||||
|
||||
def __init__(self, dmgr, spi_device, io_update_device=None,
|
||||
dds_reset_device=None, sync_sel=0, clk_sel=0, rf_sw=0,
|
||||
refclk=125e6, att=0x00000000, core_device="core"):
|
||||
dds_reset_device=None, sync_device=None,
|
||||
sync_sel=0, clk_sel=0, rf_sw=0,
|
||||
refclk=125e6, att=0x00000000, sync_div=None,
|
||||
core_device="core"):
|
||||
|
||||
self.core = dmgr.get(core_device)
|
||||
self.refclk = refclk
|
||||
|
@ -147,11 +162,20 @@ class CPLD:
|
|||
self.io_update = _RegIOUpdate(self)
|
||||
if dds_reset_device is not None:
|
||||
self.dds_reset = dmgr.get(dds_reset_device)
|
||||
if sync_device is not None:
|
||||
self.sync = dmgr.get(sync_device)
|
||||
if sync_div is None:
|
||||
sync_div = 2
|
||||
else:
|
||||
self.sync = _DummySync(self)
|
||||
assert sync_div is None
|
||||
sync_div = 0
|
||||
|
||||
self.cfg_reg = urukul_cfg(rf_sw=rf_sw, led=0, profile=0,
|
||||
io_update=0, mask_nu=0, clk_sel=clk_sel,
|
||||
sync_sel=sync_sel, rst=0, io_rst=0)
|
||||
self.att_reg = att
|
||||
self.sync_div = sync_div
|
||||
|
||||
@kernel
|
||||
def cfg_write(self, cfg):
|
||||
|
@ -207,6 +231,9 @@ class CPLD:
|
|||
raise ValueError("Urukul proto_rev mismatch")
|
||||
delay(100*us) # reset, slack
|
||||
self.cfg_write(cfg)
|
||||
if self.sync_div:
|
||||
at_mu(now_mu() & ~0xf) # align to RTIO/2
|
||||
self.set_sync_div(self.sync_div) # 125 MHz/2 = 1 GHz/16
|
||||
delay(1*ms) # DDS wake up
|
||||
|
||||
@kernel
|
||||
|
@ -289,3 +316,20 @@ class CPLD:
|
|||
SPIT_ATT_RD, CS_ATT)
|
||||
self.bus.write(self.att_reg)
|
||||
return self.bus.read()
|
||||
|
||||
@kernel
|
||||
def set_sync_div(self, div):
|
||||
"""Set the SYNC_IN AD9910 pulse generator frequency
|
||||
and align it to the current RTIO timestamp.
|
||||
|
||||
The SYNC_IN signal is derived from the coarse RTIO clock
|
||||
and the divider must be a power of two two.
|
||||
Configure ``sync_sel == 0``.
|
||||
|
||||
:param div: SYNC_IN frequency divider. Must be a power of two.
|
||||
Minimum division ratio is 2. Maximum division ratio is 16.
|
||||
"""
|
||||
ftw_max = 1 << 4
|
||||
ftw = ftw_max//div
|
||||
assert ftw*div == ftw_max
|
||||
self.sync.set_mu(ftw)
|
||||
|
|
|
@ -86,13 +86,19 @@ for j in range(3):
|
|||
"type": "local",
|
||||
"module": "artiq.coredevice.spi2",
|
||||
"class": "SPIMaster",
|
||||
"arguments": {"channel": 27 + 2*j}
|
||||
"arguments": {"channel": 27 + 3*j}
|
||||
},
|
||||
"ttl_urukul{}_sync".format(j): {
|
||||
"type": "local",
|
||||
"module": "artiq.coredevice.ttl",
|
||||
"class": "TTLClockGen",
|
||||
"arguments": {"channel": 28 + 3*j, "acc_width": 4}
|
||||
},
|
||||
"ttl_urukul{}_io_update".format(j): {
|
||||
"type": "local",
|
||||
"module": "artiq.coredevice.ttl",
|
||||
"class": "TTLOut",
|
||||
"arguments": {"channel": 28 + 2*j}
|
||||
"arguments": {"channel": 29 + 3*j}
|
||||
},
|
||||
"urukul{}_cpld".format(j): {
|
||||
"type": "local",
|
||||
|
@ -100,6 +106,7 @@ for j in range(3):
|
|||
"class": "CPLD",
|
||||
"arguments": {
|
||||
"spi_device": "spi_urukul{}".format(j),
|
||||
"sync_device": "ttl_urukul{}_sync".format(j),
|
||||
"io_update_device": "ttl_urukul{}_io_update".format(j),
|
||||
"refclk": 100e6,
|
||||
"clk_sel": 0
|
||||
|
@ -126,13 +133,13 @@ device_db.update({
|
|||
"type": "local",
|
||||
"module": "artiq.coredevice.ttl",
|
||||
"class": "TTLOut",
|
||||
"arguments": {"channel": 33}
|
||||
"arguments": {"channel": 36}
|
||||
},
|
||||
"led1": {
|
||||
"type": "local",
|
||||
"module": "artiq.coredevice.ttl",
|
||||
"class": "TTLOut",
|
||||
"arguments": {"channel": 34}
|
||||
"arguments": {"channel": 37}
|
||||
}
|
||||
})
|
||||
|
||||
|
@ -142,19 +149,19 @@ device_db.update({
|
|||
"type": "local",
|
||||
"module": "artiq.coredevice.spi2",
|
||||
"class": "SPIMaster",
|
||||
"arguments": {"channel": 35}
|
||||
"arguments": {"channel": 38}
|
||||
},
|
||||
"ttl_zotino0_ldac": {
|
||||
"type": "local",
|
||||
"module": "artiq.coredevice.ttl",
|
||||
"class": "TTLOut",
|
||||
"arguments": {"channel": 36}
|
||||
"arguments": {"channel": 39}
|
||||
},
|
||||
"ttl_zotino0_clr": {
|
||||
"type": "local",
|
||||
"module": "artiq.coredevice.ttl",
|
||||
"class": "TTLOut",
|
||||
"arguments": {"channel": 37}
|
||||
"arguments": {"channel": 40}
|
||||
},
|
||||
"zotino0": {
|
||||
"type": "local",
|
||||
|
|
|
@ -0,0 +1,183 @@
|
|||
core_addr = "staging.ber.quartiq.de"
|
||||
|
||||
device_db = {
|
||||
"core": {
|
||||
"type": "local",
|
||||
"module": "artiq.coredevice.core",
|
||||
"class": "Core",
|
||||
"arguments": {"host": core_addr, "ref_period": 1e-9}
|
||||
},
|
||||
"core_log": {
|
||||
"type": "controller",
|
||||
"host": "::1",
|
||||
"port": 1068,
|
||||
"command": "aqctl_corelog -p {port} --bind {bind} " + core_addr
|
||||
},
|
||||
"core_cache": {
|
||||
"type": "local",
|
||||
"module": "artiq.coredevice.cache",
|
||||
"class": "CoreCache"
|
||||
},
|
||||
"core_dma": {
|
||||
"type": "local",
|
||||
"module": "artiq.coredevice.dma",
|
||||
"class": "CoreDMA"
|
||||
},
|
||||
|
||||
"i2c_switch0": {
|
||||
"type": "local",
|
||||
"module": "artiq.coredevice.i2c",
|
||||
"class": "PCA9548",
|
||||
"arguments": {"address": 0xe0}
|
||||
},
|
||||
"i2c_switch1": {
|
||||
"type": "local",
|
||||
"module": "artiq.coredevice.i2c",
|
||||
"class": "PCA9548",
|
||||
"arguments": {"address": 0xe2}
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
device_db.update({
|
||||
"ttl" + str(i): {
|
||||
"type": "local",
|
||||
"module": "artiq.coredevice.ttl",
|
||||
"class": "TTLInOut" if i < 4 else "TTLOut",
|
||||
"arguments": {"channel": i},
|
||||
} for i in range(24)
|
||||
})
|
||||
|
||||
|
||||
device_db.update({
|
||||
"spi_sampler0_adc": {
|
||||
"type": "local",
|
||||
"module": "artiq.coredevice.spi2",
|
||||
"class": "SPIMaster",
|
||||
"arguments": {"channel": 24}
|
||||
},
|
||||
"spi_sampler0_pgia": {
|
||||
"type": "local",
|
||||
"module": "artiq.coredevice.spi2",
|
||||
"class": "SPIMaster",
|
||||
"arguments": {"channel": 25}
|
||||
},
|
||||
"spi_sampler0_cnv": {
|
||||
"type": "local",
|
||||
"module": "artiq.coredevice.ttl",
|
||||
"class": "TTLOut",
|
||||
"arguments": {"channel": 26},
|
||||
},
|
||||
"sampler0": {
|
||||
"type": "local",
|
||||
"module": "artiq.coredevice.sampler",
|
||||
"class": "Sampler",
|
||||
"arguments": {
|
||||
"spi_adc_device": "spi_sampler0_adc",
|
||||
"spi_pgia_device": "spi_sampler0_pgia",
|
||||
"cnv_device": "spi_sampler0_cnv"
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
for j in range(2):
|
||||
device_db.update({
|
||||
"spi_urukul{}".format(j): {
|
||||
"type": "local",
|
||||
"module": "artiq.coredevice.spi2",
|
||||
"class": "SPIMaster",
|
||||
"arguments": {"channel": 27 + 3*j}
|
||||
},
|
||||
"ttl_urukul{}_sync".format(j): {
|
||||
"type": "local",
|
||||
"module": "artiq.coredevice.ttl",
|
||||
"class": "TTLClockGen",
|
||||
"arguments": {"channel": 28 + 3*j, "acc_width": 4}
|
||||
},
|
||||
"ttl_urukul{}_io_update".format(j): {
|
||||
"type": "local",
|
||||
"module": "artiq.coredevice.ttl",
|
||||
"class": "TTLOut",
|
||||
"arguments": {"channel": 29 + 3*j}
|
||||
},
|
||||
"urukul{}_cpld".format(j): {
|
||||
"type": "local",
|
||||
"module": "artiq.coredevice.urukul",
|
||||
"class": "CPLD",
|
||||
"arguments": {
|
||||
"spi_device": "spi_urukul{}".format(j),
|
||||
"sync_device": "ttl_urukul{}_sync".format(j),
|
||||
"io_update_device": "ttl_urukul{}_io_update".format(j),
|
||||
"refclk": 100e6,
|
||||
"clk_sel": 0
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
device_db.update({
|
||||
"urukul{}_ch{}".format(j, i): {
|
||||
"type": "local",
|
||||
"module": "artiq.coredevice.ad9910",
|
||||
"class": "AD9910",
|
||||
"arguments": {
|
||||
"pll_n": 40,
|
||||
"chip_select": 4 + i,
|
||||
"cpld_device": "urukul{}_cpld".format(j)
|
||||
}
|
||||
} for i in range(4)
|
||||
})
|
||||
|
||||
device_db.update({
|
||||
"grabber0": {
|
||||
"type": "local",
|
||||
"module": "artiq.coredevice.grabber",
|
||||
"class": "grabber",
|
||||
"arguments": {"channel_base": 33}
|
||||
}
|
||||
})
|
||||
|
||||
device_db.update({
|
||||
"led0": {
|
||||
"type": "local",
|
||||
"module": "artiq.coredevice.ttl",
|
||||
"class": "TTLOut",
|
||||
"arguments": {"channel": 35}
|
||||
},
|
||||
"led1": {
|
||||
"type": "local",
|
||||
"module": "artiq.coredevice.ttl",
|
||||
"class": "TTLOut",
|
||||
"arguments": {"channel": 36}
|
||||
}
|
||||
})
|
||||
|
||||
device_db.update({
|
||||
"spi_zotino0": {
|
||||
"type": "local",
|
||||
"module": "artiq.coredevice.spi2",
|
||||
"class": "SPIMaster",
|
||||
"arguments": {"channel": 37}
|
||||
},
|
||||
"ttl_zotino0_ldac": {
|
||||
"type": "local",
|
||||
"module": "artiq.coredevice.ttl",
|
||||
"class": "TTLOut",
|
||||
"arguments": {"channel": 38}
|
||||
},
|
||||
"ttl_zotino0_clr": {
|
||||
"type": "local",
|
||||
"module": "artiq.coredevice.ttl",
|
||||
"class": "TTLOut",
|
||||
"arguments": {"channel": 39}
|
||||
},
|
||||
"zotino0": {
|
||||
"type": "local",
|
||||
"module": "artiq.coredevice.zotino",
|
||||
"class": "Zotino",
|
||||
"arguments": {
|
||||
"spi_device": "spi_zotino0",
|
||||
"ldac_device": "ttl_zotino0_ldac",
|
||||
"clr_device": "ttl_zotino0_clr"
|
||||
}
|
||||
}
|
||||
})
|
|
@ -313,23 +313,94 @@ device_db = {
|
|||
"arguments": {"channel": 33}
|
||||
},
|
||||
|
||||
"spi_urukul1": {
|
||||
"type": "local",
|
||||
"module": "artiq.coredevice.spi2",
|
||||
"class": "SPIMaster",
|
||||
"arguments": {"channel": 34}
|
||||
},
|
||||
"ttl_urukul1_sync": {
|
||||
"type": "local",
|
||||
"module": "artiq.coredevice.ttl",
|
||||
"class": "TTLClockGen",
|
||||
"arguments": {"channel": 35, "acc_width": 4}
|
||||
},
|
||||
"ttl_urukul1_io_update": {
|
||||
"type": "local",
|
||||
"module": "artiq.coredevice.ttl",
|
||||
"class": "TTLOut",
|
||||
"arguments": {"channel": 36}
|
||||
},
|
||||
"urukul1_cpld": {
|
||||
"type": "local",
|
||||
"module": "artiq.coredevice.urukul",
|
||||
"class": "CPLD",
|
||||
"arguments": {
|
||||
"spi_device": "spi_urukul1",
|
||||
"sync_device": "ttl_urukul1_sync",
|
||||
"io_update_device": "ttl_urukul1_io_update",
|
||||
"refclk": 100e6,
|
||||
"clk_sel": 1
|
||||
}
|
||||
},
|
||||
"urukul1_ch0": {
|
||||
"type": "local",
|
||||
"module": "artiq.coredevice.ad9910",
|
||||
"class": "AD9910",
|
||||
"arguments": {
|
||||
"pll_n": 40,
|
||||
"chip_select": 4,
|
||||
"cpld_device": "urukul1_cpld"
|
||||
}
|
||||
},
|
||||
"urukul1_ch1": {
|
||||
"type": "local",
|
||||
"module": "artiq.coredevice.ad9910",
|
||||
"class": "AD9910",
|
||||
"arguments": {
|
||||
"pll_n": 40,
|
||||
"chip_select": 5,
|
||||
"cpld_device": "urukul1_cpld"
|
||||
}
|
||||
},
|
||||
"urukul1_ch2": {
|
||||
"type": "local",
|
||||
"module": "artiq.coredevice.ad9910",
|
||||
"class": "AD9910",
|
||||
"arguments": {
|
||||
"pll_n": 40,
|
||||
"chip_select": 6,
|
||||
"cpld_device": "urukul1_cpld"
|
||||
}
|
||||
},
|
||||
"urukul1_ch3": {
|
||||
"type": "local",
|
||||
"module": "artiq.coredevice.ad9910",
|
||||
"class": "AD9910",
|
||||
"arguments": {
|
||||
"pll_n": 40,
|
||||
"chip_select": 7,
|
||||
"cpld_device": "urukul1_cpld"
|
||||
}
|
||||
},
|
||||
|
||||
"spi_zotino0": {
|
||||
"type": "local",
|
||||
"module": "artiq.coredevice.spi2",
|
||||
"class": "SPIMaster",
|
||||
"arguments": {"channel": 36}
|
||||
"arguments": {"channel": 37}
|
||||
},
|
||||
"ttl_zotino0_ldac": {
|
||||
"type": "local",
|
||||
"module": "artiq.coredevice.ttl",
|
||||
"class": "TTLOut",
|
||||
"arguments": {"channel": 37}
|
||||
"arguments": {"channel": 38}
|
||||
},
|
||||
"ttl_zotino0_clr": {
|
||||
"type": "local",
|
||||
"module": "artiq.coredevice.ttl",
|
||||
"class": "TTLOut",
|
||||
"arguments": {"channel": 38}
|
||||
"arguments": {"channel": 39}
|
||||
},
|
||||
"zotino0": {
|
||||
"type": "local",
|
||||
|
|
|
@ -0,0 +1,240 @@
|
|||
core_addr = "staging.ber.quartiq.de"
|
||||
|
||||
device_db = {
|
||||
"core": {
|
||||
"type": "local",
|
||||
"module": "artiq.coredevice.core",
|
||||
"class": "Core",
|
||||
"arguments": {"host": core_addr, "ref_period": 1e-9}
|
||||
},
|
||||
"core_log": {
|
||||
"type": "controller",
|
||||
"host": "::1",
|
||||
"port": 1068,
|
||||
"command": "aqctl_corelog -p {port} --bind {bind} " + core_addr
|
||||
},
|
||||
"core_cache": {
|
||||
"type": "local",
|
||||
"module": "artiq.coredevice.cache",
|
||||
"class": "CoreCache"
|
||||
},
|
||||
"core_dma": {
|
||||
"type": "local",
|
||||
"module": "artiq.coredevice.dma",
|
||||
"class": "CoreDMA"
|
||||
},
|
||||
|
||||
"i2c_switch0": {
|
||||
"type": "local",
|
||||
"module": "artiq.coredevice.i2c",
|
||||
"class": "PCA9548",
|
||||
"arguments": {"address": 0xe0}
|
||||
},
|
||||
"i2c_switch1": {
|
||||
"type": "local",
|
||||
"module": "artiq.coredevice.i2c",
|
||||
"class": "PCA9548",
|
||||
"arguments": {"address": 0xe2}
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
device_db.update({
|
||||
"ttl" + str(i): {
|
||||
"type": "local",
|
||||
"module": "artiq.coredevice.ttl",
|
||||
"class": "TTLInOut" if i < 4 else "TTLOut",
|
||||
"arguments": {"channel": i},
|
||||
} for i in range(24)
|
||||
})
|
||||
|
||||
|
||||
device_db.update({
|
||||
"spi_sampler0_adc": {
|
||||
"type": "local",
|
||||
"module": "artiq.coredevice.spi2",
|
||||
"class": "SPIMaster",
|
||||
"arguments": {"channel": 24}
|
||||
},
|
||||
"spi_sampler0_pgia": {
|
||||
"type": "local",
|
||||
"module": "artiq.coredevice.spi2",
|
||||
"class": "SPIMaster",
|
||||
"arguments": {"channel": 25}
|
||||
},
|
||||
"spi_sampler0_cnv": {
|
||||
"type": "local",
|
||||
"module": "artiq.coredevice.ttl",
|
||||
"class": "TTLOut",
|
||||
"arguments": {"channel": 26},
|
||||
},
|
||||
"sampler0": {
|
||||
"type": "local",
|
||||
"module": "artiq.coredevice.sampler",
|
||||
"class": "Sampler",
|
||||
"arguments": {
|
||||
"spi_adc_device": "spi_sampler0_adc",
|
||||
"spi_pgia_device": "spi_sampler0_pgia",
|
||||
"cnv_device": "spi_sampler0_cnv"
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
device_db.update({
|
||||
"spi_urukul0": {
|
||||
"type": "local",
|
||||
"module": "artiq.coredevice.spi2",
|
||||
"class": "SPIMaster",
|
||||
"arguments": {"channel": 27}
|
||||
},
|
||||
"ttl_urukul0_io_update": {
|
||||
"type": "local",
|
||||
"module": "artiq.coredevice.ttl",
|
||||
"class": "TTLOut",
|
||||
"arguments": {"channel": 28}
|
||||
},
|
||||
"ttl_urukul0_sw0": {
|
||||
"type": "local",
|
||||
"module": "artiq.coredevice.ttl",
|
||||
"class": "TTLOut",
|
||||
"arguments": {"channel": 29}
|
||||
},
|
||||
"ttl_urukul0_sw1": {
|
||||
"type": "local",
|
||||
"module": "artiq.coredevice.ttl",
|
||||
"class": "TTLOut",
|
||||
"arguments": {"channel": 30}
|
||||
},
|
||||
"ttl_urukul0_sw2": {
|
||||
"type": "local",
|
||||
"module": "artiq.coredevice.ttl",
|
||||
"class": "TTLOut",
|
||||
"arguments": {"channel": 31}
|
||||
},
|
||||
"ttl_urukul0_sw3": {
|
||||
"type": "local",
|
||||
"module": "artiq.coredevice.ttl",
|
||||
"class": "TTLOut",
|
||||
"arguments": {"channel": 32}
|
||||
},
|
||||
"urukul0_cpld": {
|
||||
"type": "local",
|
||||
"module": "artiq.coredevice.urukul",
|
||||
"class": "CPLD",
|
||||
"arguments": {
|
||||
"spi_device": "spi_urukul0",
|
||||
"io_update_device": "ttl_urukul0_io_update",
|
||||
"refclk": 100e6,
|
||||
"clk_sel": 0
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
device_db.update({
|
||||
"urukul0_ch" + str(i): {
|
||||
"type": "local",
|
||||
"module": "artiq.coredevice.ad9912",
|
||||
"class": "AD9912",
|
||||
"arguments": {
|
||||
"pll_n": 10,
|
||||
"chip_select": 4 + i,
|
||||
"cpld_device": "urukul0_cpld",
|
||||
"sw_device": "ttl_urukul0_sw" + str(i)
|
||||
}
|
||||
} for i in range(4)
|
||||
})
|
||||
|
||||
|
||||
device_db.update({
|
||||
"spi_urukul1": {
|
||||
"type": "local",
|
||||
"module": "artiq.coredevice.spi2",
|
||||
"class": "SPIMaster",
|
||||
"arguments": {"channel": 33}
|
||||
},
|
||||
"ttl_urukul1_sync": {
|
||||
"type": "local",
|
||||
"module": "artiq.coredevice.ttl",
|
||||
"class": "TTLClockGen",
|
||||
"arguments": {"channel": 34, "acc_width": 4}
|
||||
},
|
||||
"ttl_urukul1_io_update": {
|
||||
"type": "local",
|
||||
"module": "artiq.coredevice.ttl",
|
||||
"class": "TTLOut",
|
||||
"arguments": {"channel": 35}
|
||||
},
|
||||
"urukul1_cpld": {
|
||||
"type": "local",
|
||||
"module": "artiq.coredevice.urukul",
|
||||
"class": "CPLD",
|
||||
"arguments": {
|
||||
"spi_device": "spi_urukul1",
|
||||
"sync_device": "ttl_urukul1_sync",
|
||||
"io_update_device": "ttl_urukul1_io_update",
|
||||
"refclk": 125e6,
|
||||
"clk_sel": 0
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
device_db.update({
|
||||
"urukul1_ch" + str(i): {
|
||||
"type": "local",
|
||||
"module": "artiq.coredevice.ad9910",
|
||||
"class": "AD9910",
|
||||
"arguments": {
|
||||
"pll_n": 32,
|
||||
"chip_select": 4 + i,
|
||||
"cpld_device": "urukul1_cpld"
|
||||
}
|
||||
} for i in range(4)
|
||||
})
|
||||
|
||||
|
||||
device_db.update({
|
||||
"led0": {
|
||||
"type": "local",
|
||||
"module": "artiq.coredevice.ttl",
|
||||
"class": "TTLOut",
|
||||
"arguments": {"channel": 36}
|
||||
},
|
||||
"led1": {
|
||||
"type": "local",
|
||||
"module": "artiq.coredevice.ttl",
|
||||
"class": "TTLOut",
|
||||
"arguments": {"channel": 37}
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
device_db.update({
|
||||
"spi_zotino0": {
|
||||
"type": "local",
|
||||
"module": "artiq.coredevice.spi2",
|
||||
"class": "SPIMaster",
|
||||
"arguments": {"channel": 38}
|
||||
},
|
||||
"ttl_zotino0_ldac": {
|
||||
"type": "local",
|
||||
"module": "artiq.coredevice.ttl",
|
||||
"class": "TTLOut",
|
||||
"arguments": {"channel": 39}
|
||||
},
|
||||
"ttl_zotino0_clr": {
|
||||
"type": "local",
|
||||
"module": "artiq.coredevice.ttl",
|
||||
"class": "TTLOut",
|
||||
"arguments": {"channel": 40}
|
||||
},
|
||||
"zotino0": {
|
||||
"type": "local",
|
||||
"module": "artiq.coredevice.zotino",
|
||||
"class": "Zotino",
|
||||
"arguments": {
|
||||
"spi_device": "spi_zotino0",
|
||||
"ldac_device": "ttl_zotino0_ldac",
|
||||
"clr_device": "ttl_zotino0_clr"
|
||||
}
|
||||
}
|
||||
})
|
|
@ -57,36 +57,42 @@ device_db.update(
|
|||
"class": "SPIMaster",
|
||||
"arguments": {"channel": 8}
|
||||
},
|
||||
ttl_urukul0_io_update={
|
||||
ttl_urukul0_sync={
|
||||
"type": "local",
|
||||
"module": "artiq.coredevice.ttl",
|
||||
"class": "TTLOut",
|
||||
"arguments": {"channel": 9}
|
||||
"class": "TTLClockGen",
|
||||
"arguments": {"channel": 9, "acc_width": 4}
|
||||
},
|
||||
ttl_urukul0_sw0={
|
||||
ttl_urukul0_io_update={
|
||||
"type": "local",
|
||||
"module": "artiq.coredevice.ttl",
|
||||
"class": "TTLOut",
|
||||
"arguments": {"channel": 10}
|
||||
},
|
||||
ttl_urukul0_sw1={
|
||||
ttl_urukul0_sw0={
|
||||
"type": "local",
|
||||
"module": "artiq.coredevice.ttl",
|
||||
"class": "TTLOut",
|
||||
"arguments": {"channel": 11}
|
||||
},
|
||||
ttl_urukul0_sw2={
|
||||
ttl_urukul0_sw1={
|
||||
"type": "local",
|
||||
"module": "artiq.coredevice.ttl",
|
||||
"class": "TTLOut",
|
||||
"arguments": {"channel": 12}
|
||||
},
|
||||
ttl_urukul0_sw3={
|
||||
ttl_urukul0_sw2={
|
||||
"type": "local",
|
||||
"module": "artiq.coredevice.ttl",
|
||||
"class": "TTLOut",
|
||||
"arguments": {"channel": 13}
|
||||
},
|
||||
ttl_urukul0_sw3={
|
||||
"type": "local",
|
||||
"module": "artiq.coredevice.ttl",
|
||||
"class": "TTLOut",
|
||||
"arguments": {"channel": 14}
|
||||
},
|
||||
urukul0_cpld={
|
||||
"type": "local",
|
||||
"module": "artiq.coredevice.urukul",
|
||||
|
@ -94,6 +100,7 @@ device_db.update(
|
|||
"arguments": {
|
||||
"spi_device": "spi_urukul0",
|
||||
"io_update_device": "ttl_urukul0_io_update",
|
||||
"sync_device": "ttl_urukul0_sync",
|
||||
"refclk": 125e6,
|
||||
"clk_sel": 0
|
||||
}
|
||||
|
@ -119,19 +126,19 @@ device_db["spi_sampler0_adc"] = {
|
|||
"type": "local",
|
||||
"module": "artiq.coredevice.spi2",
|
||||
"class": "SPIMaster",
|
||||
"arguments": {"channel": 14}
|
||||
"arguments": {"channel": 15}
|
||||
}
|
||||
device_db["spi_sampler0_pgia"] = {
|
||||
"type": "local",
|
||||
"module": "artiq.coredevice.spi2",
|
||||
"class": "SPIMaster",
|
||||
"arguments": {"channel": 15}
|
||||
"arguments": {"channel": 16}
|
||||
}
|
||||
device_db["spi_sampler0_cnv"] = {
|
||||
"type": "local",
|
||||
"module": "artiq.coredevice.ttl",
|
||||
"class": "TTLOut",
|
||||
"arguments": {"channel": 16},
|
||||
"arguments": {"channel": 17},
|
||||
}
|
||||
device_db["sampler0"] = {
|
||||
"type": "local",
|
||||
|
@ -150,19 +157,19 @@ device_db["spi_zotino0"] = {
|
|||
"type": "local",
|
||||
"module": "artiq.coredevice.spi2",
|
||||
"class": "SPIMaster",
|
||||
"arguments": {"channel": 17}
|
||||
"arguments": {"channel": 18}
|
||||
}
|
||||
device_db["ttl_zotino0_ldac"] = {
|
||||
"type": "local",
|
||||
"module": "artiq.coredevice.ttl",
|
||||
"class": "TTLOut",
|
||||
"arguments": {"channel": 18}
|
||||
"arguments": {"channel": 19}
|
||||
}
|
||||
device_db["ttl_zotino0_clr"] = {
|
||||
"type": "local",
|
||||
"module": "artiq.coredevice.ttl",
|
||||
"class": "TTLOut",
|
||||
"arguments": {"channel": 19}
|
||||
"arguments": {"channel": 20}
|
||||
}
|
||||
device_db["zotino0"] = {
|
||||
"type": "local",
|
||||
|
@ -181,7 +188,7 @@ device_db["grabber0"] = {
|
|||
"type": "local",
|
||||
"module": "artiq.coredevice.grabber",
|
||||
"class": "Grabber",
|
||||
"arguments": {"channel_base": 20}
|
||||
"arguments": {"channel_base": 21}
|
||||
}
|
||||
|
||||
|
||||
|
@ -191,13 +198,19 @@ device_db.update(
|
|||
"type": "local",
|
||||
"module": "artiq.coredevice.spi2",
|
||||
"class": "SPIMaster",
|
||||
"arguments": {"channel": 22}
|
||||
"arguments": {"channel": 23}
|
||||
},
|
||||
ttl_urukul1_sync={
|
||||
"type": "local",
|
||||
"module": "artiq.coredevice.ttl",
|
||||
"class": "TTLClockGen",
|
||||
"arguments": {"channel": 24, "acc_width": 4}
|
||||
},
|
||||
ttl_urukul1_io_update={
|
||||
"type": "local",
|
||||
"module": "artiq.coredevice.ttl",
|
||||
"class": "TTLOut",
|
||||
"arguments": {"channel": 23}
|
||||
"arguments": {"channel": 25}
|
||||
},
|
||||
urukul1_cpld={
|
||||
"type": "local",
|
||||
|
@ -206,6 +219,7 @@ device_db.update(
|
|||
"arguments": {
|
||||
"spi_device": "spi_urukul1",
|
||||
"io_update_device": "ttl_urukul1_io_update",
|
||||
"sync_device": "ttl_urukul1_sync",
|
||||
"refclk": 100e6,
|
||||
"clk_sel": 1
|
||||
}
|
||||
|
@ -231,7 +245,7 @@ for i in range(8):
|
|||
"type": "local",
|
||||
"module": "artiq.coredevice.ttl",
|
||||
"class": "TTLOut",
|
||||
"arguments": {"channel": 24 + i},
|
||||
"arguments": {"channel": 26 + i},
|
||||
}
|
||||
|
||||
|
||||
|
@ -241,7 +255,7 @@ for i in range(8):
|
|||
"type": "local",
|
||||
"module": "artiq.coredevice.ttl",
|
||||
"class": "TTLOut",
|
||||
"arguments": {"channel": 32 + i},
|
||||
"arguments": {"channel": 34 + i},
|
||||
}
|
||||
|
||||
|
||||
|
@ -250,19 +264,19 @@ device_db["spi_sampler1_adc"] = {
|
|||
"type": "local",
|
||||
"module": "artiq.coredevice.spi2",
|
||||
"class": "SPIMaster",
|
||||
"arguments": {"channel": 40}
|
||||
"arguments": {"channel": 42}
|
||||
}
|
||||
device_db["spi_sampler1_pgia"] = {
|
||||
"type": "local",
|
||||
"module": "artiq.coredevice.spi2",
|
||||
"class": "SPIMaster",
|
||||
"arguments": {"channel": 41}
|
||||
"arguments": {"channel": 43}
|
||||
}
|
||||
device_db["spi_sampler1_cnv"] = {
|
||||
"type": "local",
|
||||
"module": "artiq.coredevice.ttl",
|
||||
"class": "TTLOut",
|
||||
"arguments": {"channel": 42},
|
||||
"arguments": {"channel": 44},
|
||||
}
|
||||
device_db["sampler1"] = {
|
||||
"type": "local",
|
||||
|
@ -281,19 +295,19 @@ device_db["spi_zotino1"] = {
|
|||
"type": "local",
|
||||
"module": "artiq.coredevice.spi2",
|
||||
"class": "SPIMaster",
|
||||
"arguments": {"channel": 43}
|
||||
"arguments": {"channel": 45}
|
||||
}
|
||||
device_db["ttl_zotino1_ldac"] = {
|
||||
"type": "local",
|
||||
"module": "artiq.coredevice.ttl",
|
||||
"class": "TTLOut",
|
||||
"arguments": {"channel": 44}
|
||||
"arguments": {"channel": 46}
|
||||
}
|
||||
device_db["ttl_zotino1_clr"] = {
|
||||
"type": "local",
|
||||
"module": "artiq.coredevice.ttl",
|
||||
"class": "TTLOut",
|
||||
"arguments": {"channel": 45}
|
||||
"arguments": {"channel": 47}
|
||||
}
|
||||
device_db["zotino1"] = {
|
||||
"type": "local",
|
||||
|
@ -312,13 +326,13 @@ device_db.update(
|
|||
"type": "local",
|
||||
"module": "artiq.coredevice.ttl",
|
||||
"class": "TTLOut",
|
||||
"arguments": {"channel": 46}
|
||||
"arguments": {"channel": 48}
|
||||
},
|
||||
led1={
|
||||
"type": "local",
|
||||
"module": "artiq.coredevice.ttl",
|
||||
"class": "TTLOut",
|
||||
"arguments": {"channel": 47}
|
||||
"arguments": {"channel": 49}
|
||||
},
|
||||
)
|
||||
|
||||
|
@ -328,4 +342,10 @@ device_db.update(
|
|||
|
||||
loop_out="ttl4",
|
||||
loop_in="ttl0",
|
||||
|
||||
# Urukul CPLD with sync and io_update, IFC MODE 0b1000
|
||||
urukul_cpld="urukul0_cpld",
|
||||
# Urukul AD9910 with switch TTL, internal 125 MHz MMCX connection
|
||||
# FIXME: MMCX not connected
|
||||
# urukul_ad9910="urukul0_ch0",
|
||||
)
|
||||
|
|
|
@ -75,7 +75,7 @@ class Urukul(_EEM):
|
|||
),
|
||||
]
|
||||
ttls = [(6, eem, "io_update"),
|
||||
(7, eem, "dds_reset")]
|
||||
(7, eem, "dds_reset_sync_in")]
|
||||
if eem_aux is not None:
|
||||
ttls += [(0, eem_aux, "sync_clk"),
|
||||
(1, eem_aux, "sync_in"),
|
||||
|
@ -113,7 +113,7 @@ class Urukul(_EEM):
|
|||
),
|
||||
]
|
||||
ttls = [(6, eem0, "io_update"),
|
||||
(7, eem0, "dds_reset"),
|
||||
(7, eem0, "dds_reset_sync_in"),
|
||||
(4, eem1, "sw0"),
|
||||
(5, eem1, "sw1"),
|
||||
(6, eem1, "sw2"),
|
||||
|
@ -148,7 +148,8 @@ class Urukul(_EEM):
|
|||
return ios
|
||||
|
||||
@classmethod
|
||||
def add_std(cls, target, eem, eem_aux, ttl_out_cls, iostandard="LVDS_25"):
|
||||
def add_std(cls, target, eem, eem_aux, ttl_out_cls, sync_gen_cls=None,
|
||||
iostandard="LVDS_25"):
|
||||
cls.add_extension(target, eem, eem_aux, iostandard=iostandard)
|
||||
|
||||
phy = spi2.SPIMaster(target.platform.request("urukul{}_spi_p".format(eem)),
|
||||
|
@ -156,8 +157,13 @@ class Urukul(_EEM):
|
|||
target.submodules += phy
|
||||
target.rtio_channels.append(rtio.Channel.from_phy(phy, ififo_depth=4))
|
||||
|
||||
pads = target.platform.request("urukul{}_dds_reset".format(eem))
|
||||
target.specials += DifferentialOutput(0, pads.p, pads.n)
|
||||
pads = target.platform.request("urukul{}_dds_reset_sync_in".format(eem))
|
||||
pad = Signal(reset=0)
|
||||
target.specials += DifferentialOutput(pad, pads.p, pads.n)
|
||||
if sync_gen_cls is not None: # AD9910 variant and SYNC_IN from EEM
|
||||
phy = sync_gen_cls(pad, ftw_width=4)
|
||||
target.submodules += phy
|
||||
target.rtio_channels.append(rtio.Channel.from_phy(phy))
|
||||
|
||||
pads = target.platform.request("urukul{}_io_update".format(eem))
|
||||
phy = ttl_out_cls(pads.p, pads.n)
|
||||
|
@ -170,7 +176,6 @@ class Urukul(_EEM):
|
|||
target.submodules += phy
|
||||
target.rtio_channels.append(rtio.Channel.from_phy(phy))
|
||||
|
||||
|
||||
class Sampler(_EEM):
|
||||
@staticmethod
|
||||
def io(eem, eem_aux, iostandard="LVDS_25"):
|
||||
|
@ -522,7 +527,7 @@ class SUServo(_EEM):
|
|||
target.submodules += phy
|
||||
target.rtio_channels.append(rtio.Channel.from_phy(phy, ififo_depth=4))
|
||||
|
||||
pads = target.platform.request("{}_dds_reset".format(eem_urukul0))
|
||||
pads = target.platform.request("{}_dds_reset_sync_in".format(eem_urukul0))
|
||||
target.specials += DifferentialOutput(0, pads.p, pads.n)
|
||||
|
||||
for i, signal in enumerate("sw0 sw1 sw2 sw3".split()):
|
||||
|
|
|
@ -175,7 +175,8 @@ class Opticlock(_StandaloneBase):
|
|||
self.submodules += phy
|
||||
self.rtio_channels.append(rtio.Channel.from_phy(phy))
|
||||
|
||||
eem.Urukul.add_std(self, 6, None, ttl_serdes_7series.Output_8X)
|
||||
eem.Urukul.add_std(self, 6, None, ttl_serdes_7series.Output_8X,
|
||||
ttl_simple.ClockGen)
|
||||
eem.Zotino.add_std(self, 7, ttl_serdes_7series.Output_8X)
|
||||
|
||||
self.config["HAS_RTIO_LOG"] = None
|
||||
|
@ -574,6 +575,47 @@ class PTB(_StandaloneBase):
|
|||
self.add_rtio(self.rtio_channels)
|
||||
|
||||
|
||||
class PTB2(_StandaloneBase):
|
||||
"""PTB Kasli variant with Urukul1 SYNC and external reference clock"""
|
||||
def __init__(self, hw_rev=None, **kwargs):
|
||||
if hw_rev is None:
|
||||
hw_rev = "v1.1"
|
||||
_StandaloneBase.__init__(self, hw_rev=hw_rev, **kwargs)
|
||||
|
||||
self.config["SI5324_AS_SYNTHESIZER"] = None
|
||||
self.config["SI5324_EXT_REF"] = None
|
||||
self.config["RTIO_FREQUENCY"] = "125.0"
|
||||
if hw_rev == "v1.0":
|
||||
# EEM clock fan-out from Si5324, not MMCX
|
||||
self.comb += self.platform.request("clk_sel").eq(1)
|
||||
|
||||
self.rtio_channels = []
|
||||
eem.DIO.add_std(self, 0,
|
||||
ttl_serdes_7series.InOut_8X, ttl_serdes_7series.Output_8X)
|
||||
eem.DIO.add_std(self, 1,
|
||||
ttl_serdes_7series.Output_8X, ttl_serdes_7series.Output_8X)
|
||||
eem.DIO.add_std(self, 2,
|
||||
ttl_serdes_7series.Output_8X, ttl_serdes_7series.Output_8X)
|
||||
eem.Sampler.add_std(self, 3, None, ttl_serdes_7series.Output_8X)
|
||||
eem.Urukul.add_std(self, 5, 4, ttl_serdes_7series.Output_8X)
|
||||
eem.Urukul.add_std(self, 6, None, ttl_serdes_7series.Output_8X,
|
||||
ttl_simple.ClockGen)
|
||||
|
||||
for i in (1, 2):
|
||||
sfp_ctl = self.platform.request("sfp_ctl", i)
|
||||
phy = ttl_simple.Output(sfp_ctl.led)
|
||||
self.submodules += phy
|
||||
self.rtio_channels.append(rtio.Channel.from_phy(phy))
|
||||
|
||||
eem.Zotino.add_std(self, 7, ttl_serdes_7series.Output_8X)
|
||||
|
||||
self.config["HAS_RTIO_LOG"] = None
|
||||
self.config["RTIO_LOG_CHANNEL"] = len(self.rtio_channels)
|
||||
self.rtio_channels.append(rtio.LogChannel())
|
||||
|
||||
self.add_rtio(self.rtio_channels)
|
||||
|
||||
|
||||
class HUB(_StandaloneBase):
|
||||
"""HUB Kasli variant
|
||||
|
||||
|
@ -600,9 +642,12 @@ class HUB(_StandaloneBase):
|
|||
eem.DIO.add_std(self, 2,
|
||||
ttl_serdes_7series.Output_8X, ttl_serdes_7series.Output_8X)
|
||||
eem.Sampler.add_std(self, 3, None, ttl_serdes_7series.Output_8X)
|
||||
eem.Urukul.add_std(self, 4, None, ttl_serdes_7series.Output_8X)
|
||||
eem.Urukul.add_std(self, 5, None, ttl_serdes_7series.Output_8X)
|
||||
eem.Urukul.add_std(self, 6, None, ttl_serdes_7series.Output_8X)
|
||||
eem.Urukul.add_std(self, 4, None, ttl_serdes_7series.Output_8X,
|
||||
ttl_simple.ClockGen)
|
||||
eem.Urukul.add_std(self, 5, None, ttl_serdes_7series.Output_8X,
|
||||
ttl_simple.ClockGen)
|
||||
eem.Urukul.add_std(self, 6, None, ttl_serdes_7series.Output_8X,
|
||||
ttl_simple.ClockGen)
|
||||
|
||||
for i in (1, 2):
|
||||
sfp_ctl = self.platform.request("sfp_ctl", i)
|
||||
|
@ -645,8 +690,10 @@ class LUH(_StandaloneBase):
|
|||
eem.DIO.add_std(self, 2,
|
||||
ttl_serdes_7series.Output_8X, ttl_serdes_7series.Output_8X)
|
||||
eem.Sampler.add_std(self, 3, None, ttl_serdes_7series.Output_8X)
|
||||
eem.Urukul.add_std(self, 4, None, ttl_serdes_7series.Output_8X)
|
||||
eem.Urukul.add_std(self, 5, None, ttl_serdes_7series.Output_8X)
|
||||
eem.Urukul.add_std(self, 4, None, ttl_serdes_7series.Output_8X,
|
||||
ttl_simple.ClockGen)
|
||||
eem.Urukul.add_std(self, 5, None, ttl_serdes_7series.Output_8X,
|
||||
ttl_simple.ClockGen)
|
||||
eem.Grabber.add_std(self, 6)
|
||||
|
||||
for i in (1, 2):
|
||||
|
@ -688,11 +735,13 @@ class Tester(_StandaloneBase):
|
|||
self.grabber_csr_group = []
|
||||
eem.DIO.add_std(self, 5,
|
||||
ttl_serdes_7series.InOut_8X, ttl_serdes_7series.Output_8X)
|
||||
eem.Urukul.add_std(self, 0, 1, ttl_serdes_7series.Output_8X)
|
||||
eem.Urukul.add_std(self, 0, 1, ttl_serdes_7series.Output_8X,
|
||||
ttl_simple.ClockGen)
|
||||
eem.Sampler.add_std(self, 3, 2, ttl_serdes_7series.Output_8X)
|
||||
eem.Zotino.add_std(self, 4, ttl_serdes_7series.Output_8X)
|
||||
eem.Grabber.add_std(self, 6)
|
||||
eem.Urukul.add_std(self, 7, None, ttl_serdes_7series.Output_8X)
|
||||
eem.Urukul.add_std(self, 7, None, ttl_serdes_7series.Output_8X,
|
||||
ttl_simple.ClockGen)
|
||||
eem.DIO.add_std(self, 8,
|
||||
ttl_serdes_7series.Output_8X, ttl_serdes_7series.Output_8X)
|
||||
eem.DIO.add_std(self, 9,
|
||||
|
@ -1079,8 +1128,8 @@ def main():
|
|||
soc_kasli_args(parser)
|
||||
parser.set_defaults(output_dir="artiq_kasli")
|
||||
variants = {cls.__name__.lower(): cls for cls in [
|
||||
Opticlock, SUServo, SYSU, MITLL, MITLL2, USTC,
|
||||
Tsinghua, Tsinghua2, WIPM, NUDT, PTB, HUB, LUH,
|
||||
Opticlock, SUServo, PTB, PTB2, HUB, LUH,
|
||||
SYSU, MITLL, MITLL2, USTC, Tsinghua, Tsinghua2, WIPM, NUDT,
|
||||
VLBAIMaster, VLBAISatellite, Tester, Master, Satellite]}
|
||||
parser.add_argument("-V", "--variant", default="opticlock",
|
||||
help="variant: {} (default: %(default)s)".format(
|
||||
|
|
|
@ -0,0 +1,180 @@
|
|||
from artiq.experiment import *
|
||||
from artiq.test.hardware_testbench import ExperimentCase
|
||||
from artiq.coredevice.ad9910 import _AD9910_REG_FTW
|
||||
from artiq.coredevice.urukul import (
|
||||
urukul_sta_smp_err, CFG_CLK_SEL0, CFG_CLK_SEL1)
|
||||
|
||||
|
||||
class AD9910Exp(EnvExperiment):
|
||||
def build(self, runner):
|
||||
self.setattr_device("core")
|
||||
self.dev = self.get_device("urukul_ad9910")
|
||||
self.runner = runner
|
||||
|
||||
def run(self):
|
||||
getattr(self, self.runner)()
|
||||
|
||||
@kernel
|
||||
def instantiate(self):
|
||||
pass
|
||||
|
||||
@kernel
|
||||
def init(self):
|
||||
self.core.break_realtime()
|
||||
self.dev.cpld.init()
|
||||
self.dev.init()
|
||||
|
||||
@kernel
|
||||
def init_fail(self):
|
||||
self.core.break_realtime()
|
||||
self.dev.cpld.init()
|
||||
cfg = self.dev.cpld.cfg_reg
|
||||
cfg &= ~(1 << CFG_CLK_SEL1)
|
||||
cfg |= 1 << CFG_CLK_SEL0
|
||||
self.dev.cpld.cfg_write(cfg)
|
||||
# clk_sel=1, external SMA, should fail PLL lock
|
||||
self.dev.init()
|
||||
|
||||
@kernel
|
||||
def set_get(self):
|
||||
self.core.break_realtime()
|
||||
self.dev.cpld.init()
|
||||
self.dev.init()
|
||||
self.dev.set_att(20*dB)
|
||||
f = 81.2345*MHz
|
||||
self.dev.set(frequency=f, phase=.33, amplitude=.89)
|
||||
self.set_dataset("ftw_set", self.dev.frequency_to_ftw(f))
|
||||
self.set_dataset("ftw_get", self.dev.read32(_AD9910_REG_FTW))
|
||||
|
||||
@kernel
|
||||
def set_speed(self):
|
||||
self.core.break_realtime()
|
||||
self.dev.cpld.init()
|
||||
self.dev.init()
|
||||
f = 81.2345*MHz
|
||||
n = 10
|
||||
t0 = self.core.get_rtio_counter_mu()
|
||||
for i in range(n):
|
||||
self.dev.set(frequency=f, phase=.33, amplitude=.89)
|
||||
self.set_dataset("dt", self.core.mu_to_seconds(
|
||||
self.core.get_rtio_counter_mu() - t0)/n)
|
||||
|
||||
@kernel
|
||||
def set_speed_mu(self):
|
||||
self.core.break_realtime()
|
||||
self.dev.cpld.init()
|
||||
self.dev.init()
|
||||
n = 10
|
||||
t0 = self.core.get_rtio_counter_mu()
|
||||
for i in range(n):
|
||||
self.dev.set_mu(0x12345678, 0x1234, 0x4321)
|
||||
self.set_dataset("dt", self.core.mu_to_seconds(
|
||||
self.core.get_rtio_counter_mu() - t0)/n)
|
||||
|
||||
@kernel
|
||||
def sync_window(self):
|
||||
self.core.break_realtime()
|
||||
self.dev.cpld.init()
|
||||
self.dev.init()
|
||||
dly, win = self.dev.tune_sync_delay(self.dev.sync_delay_seed)
|
||||
err = [0] * 32
|
||||
self.sync_scan(err, win=win + 1) # tighten window by 2*75ps
|
||||
self.set_dataset("dly", dly)
|
||||
self.set_dataset("win", win)
|
||||
self.set_dataset("err", err)
|
||||
|
||||
@kernel
|
||||
def sync_scan(self, err, win):
|
||||
for in_delay in range(len(err)):
|
||||
self.dev.set_sync(in_delay=in_delay, window=win)
|
||||
self.dev.clear_smp_err()
|
||||
delay(10*us) # integrate SMP_ERR statistics
|
||||
e = urukul_sta_smp_err(self.dev.cpld.sta_read())
|
||||
err[in_delay] = (e >> (self.dev.chip_select - 4)) & 1
|
||||
delay(50*us) # slack
|
||||
|
||||
@kernel
|
||||
def io_update_delay(self):
|
||||
self.core.break_realtime()
|
||||
self.dev.cpld.init()
|
||||
self.dev.init()
|
||||
bins = [0]*8
|
||||
self.scan_io_delay(bins)
|
||||
self.set_dataset("bins", bins)
|
||||
self.set_dataset("dly", self.dev.io_update_delay)
|
||||
|
||||
@kernel
|
||||
def scan_io_delay(self, bins):
|
||||
delay(100*us)
|
||||
n = 100
|
||||
for i in range(n):
|
||||
for phase in range(len(bins)):
|
||||
bins[phase] += self.dev.measure_io_update_alignment(phase)
|
||||
delay(10*ms)
|
||||
|
||||
@kernel
|
||||
def sw_readback(self):
|
||||
self.core.break_realtime()
|
||||
self.dev.cpld.init()
|
||||
self.dev.init()
|
||||
self.dev.cfg_sw(0)
|
||||
self.dev.sw.on()
|
||||
sw_on = (self.dev.cpld.sta_read() >> (self.dev.chip_select - 4)) & 1
|
||||
delay(10*us)
|
||||
self.dev.sw.off()
|
||||
sw_off = (self.dev.cpld.sta_read() >> (self.dev.chip_select - 4)) & 1
|
||||
self.set_dataset("sw", (sw_on, sw_off))
|
||||
|
||||
|
||||
class AD9910Test(ExperimentCase):
|
||||
def test_instantiate(self):
|
||||
self.execute(AD9910Exp, "instantiate")
|
||||
|
||||
def test_init(self):
|
||||
self.execute(AD9910Exp, "init")
|
||||
|
||||
def test_init_fail(self):
|
||||
with self.assertRaises(ValueError):
|
||||
self.execute(AD9910Exp, "init_fail")
|
||||
|
||||
def test_set_get(self):
|
||||
self.execute(AD9910Exp, "set_get")
|
||||
ftw_get = self.dataset_mgr.get("ftw_get")
|
||||
ftw_set = self.dataset_mgr.get("ftw_set")
|
||||
self.assertEqual(ftw_get, ftw_set)
|
||||
|
||||
def test_set_speed(self):
|
||||
self.execute(AD9910Exp, "set_speed")
|
||||
dt = self.dataset_mgr.get("dt")
|
||||
print(dt)
|
||||
self.assertLess(dt, 70*us)
|
||||
|
||||
def test_set_speed_mu(self):
|
||||
self.execute(AD9910Exp, "set_speed_mu")
|
||||
dt = self.dataset_mgr.get("dt")
|
||||
print(dt)
|
||||
self.assertLess(dt, 10*us)
|
||||
|
||||
def test_sync_window(self):
|
||||
self.execute(AD9910Exp, "sync_window")
|
||||
err = self.dataset_mgr.get("err")
|
||||
dly = self.dataset_mgr.get("dly")
|
||||
win = self.dataset_mgr.get("win")
|
||||
print(dly, win, err)
|
||||
# make sure one tap margin on either side of optimal delay
|
||||
for i in -1, 0, 1:
|
||||
self.assertEqual(err[i + dly], 0)
|
||||
|
||||
def test_io_update_delay(self):
|
||||
self.execute(AD9910Exp, "io_update_delay")
|
||||
dly = self.dataset_mgr.get("dly")
|
||||
bins = self.dataset_mgr.get("bins")
|
||||
print(dly, bins)
|
||||
n = max(bins)
|
||||
# test for 4-periodicity (SYNC_CLK) and maximal contrast
|
||||
for i in range(len(bins)):
|
||||
self.assertEqual(abs(bins[i] - bins[(i + 4) % 8]), n)
|
||||
|
||||
def test_sw_readback(self):
|
||||
self.execute(AD9910Exp, "sw_readback")
|
||||
self.assertEqual(self.dataset_mgr.get("sw"), (1, 0))
|
|
@ -0,0 +1,149 @@
|
|||
from artiq.experiment import *
|
||||
from artiq.test.hardware_testbench import ExperimentCase
|
||||
from artiq.coredevice import urukul
|
||||
|
||||
|
||||
class UrukulExp(EnvExperiment):
|
||||
def build(self, runner):
|
||||
self.setattr_device("core")
|
||||
self.dev = self.get_device("urukul_cpld")
|
||||
self.runner = runner
|
||||
|
||||
def run(self):
|
||||
getattr(self, self.runner)()
|
||||
|
||||
@kernel
|
||||
def instantiate(self):
|
||||
pass
|
||||
|
||||
@kernel
|
||||
def init(self):
|
||||
self.core.break_realtime()
|
||||
self.dev.init()
|
||||
|
||||
@kernel
|
||||
def cfg_write(self):
|
||||
self.core.break_realtime()
|
||||
self.dev.init()
|
||||
self.dev.cfg_write(self.dev.cfg_reg)
|
||||
|
||||
@kernel
|
||||
def sta_read(self):
|
||||
self.core.break_realtime()
|
||||
self.dev.init()
|
||||
sta = self.dev.sta_read()
|
||||
self.set_dataset("sta", sta)
|
||||
|
||||
@kernel
|
||||
def switches(self):
|
||||
self.core.break_realtime()
|
||||
self.dev.init()
|
||||
self.dev.io_rst()
|
||||
self.dev.cfg_sw(0, 0)
|
||||
self.dev.cfg_sw(0, 1)
|
||||
self.dev.cfg_sw(3, 1)
|
||||
self.dev.cfg_switches(0b1010)
|
||||
|
||||
@kernel
|
||||
def switch_speed(self):
|
||||
self.core.break_realtime()
|
||||
self.dev.init()
|
||||
n = 10
|
||||
t0 = self.core.get_rtio_counter_mu()
|
||||
for i in range(n):
|
||||
self.dev.cfg_sw(3, i & 1)
|
||||
self.set_dataset("dt", self.core.mu_to_seconds(
|
||||
self.core.get_rtio_counter_mu() - t0)/n)
|
||||
|
||||
@kernel
|
||||
def switches_readback(self):
|
||||
self.core.reset() # clear switch TTLs
|
||||
self.dev.init()
|
||||
sw_set = 0b1010
|
||||
self.dev.cfg_switches(sw_set)
|
||||
sta_get = self.dev.sta_read()
|
||||
self.set_dataset("sw_set", sw_set)
|
||||
self.set_dataset("sta_get", sta_get)
|
||||
|
||||
@kernel
|
||||
def att(self):
|
||||
self.core.break_realtime()
|
||||
self.dev.init()
|
||||
att_set = 0x12345678
|
||||
self.dev.set_all_att_mu(att_set)
|
||||
att_get = self.dev.get_att_mu()
|
||||
self.set_dataset("att_set", att_set)
|
||||
self.set_dataset("att_get", att_get)
|
||||
|
||||
@kernel
|
||||
def att_speed(self):
|
||||
self.core.break_realtime()
|
||||
self.dev.init()
|
||||
n = 10
|
||||
t0 = self.core.get_rtio_counter_mu()
|
||||
for i in range(n):
|
||||
self.dev.set_att(3, 30*dB)
|
||||
self.set_dataset("dt", self.core.mu_to_seconds(
|
||||
self.core.get_rtio_counter_mu() - t0)/n)
|
||||
|
||||
@kernel
|
||||
def io_update(self):
|
||||
self.core.break_realtime()
|
||||
self.dev.init()
|
||||
self.dev.io_update.pulse_mu(8)
|
||||
|
||||
@kernel
|
||||
def sync(self):
|
||||
self.core.break_realtime()
|
||||
self.dev.init()
|
||||
self.dev.set_sync_div(2)
|
||||
|
||||
|
||||
class UrukulTest(ExperimentCase):
|
||||
def test_instantiate(self):
|
||||
self.execute(UrukulExp, "instantiate")
|
||||
|
||||
def test_init(self):
|
||||
self.execute(UrukulExp, "init")
|
||||
|
||||
def test_cfg_write(self):
|
||||
self.execute(UrukulExp, "cfg_write")
|
||||
|
||||
def test_sta_read(self):
|
||||
self.execute(UrukulExp, "sta_read")
|
||||
sta = self.dataset_mgr.get("sta")
|
||||
print(hex(sta))
|
||||
# self.assertEqual(urukul.urukul_sta_ifc_mode(sta), 0b0001)
|
||||
|
||||
def test_switches(self):
|
||||
self.execute(UrukulExp, "switches")
|
||||
|
||||
def test_switch_speed(self):
|
||||
self.execute(UrukulExp, "switch_speed")
|
||||
dt = self.dataset_mgr.get("dt")
|
||||
print(dt)
|
||||
self.assertLess(dt, 5*us)
|
||||
|
||||
def test_switches_readback(self):
|
||||
self.execute(UrukulExp, "switches_readback")
|
||||
sw_get = urukul.urukul_sta_rf_sw(self.dataset_mgr.get("sta_get"))
|
||||
sw_set = self.dataset_mgr.get("sw_set")
|
||||
self.assertEqual(sw_get, sw_set)
|
||||
|
||||
def test_att(self):
|
||||
self.execute(UrukulExp, "att")
|
||||
att_set = self.dataset_mgr.get("att_set")
|
||||
att_get = self.dataset_mgr.get("att_get")
|
||||
self.assertEqual(att_set, att_get)
|
||||
|
||||
def test_att_speed(self):
|
||||
self.execute(UrukulExp, "att_speed")
|
||||
dt = self.dataset_mgr.get("dt")
|
||||
print(dt)
|
||||
self.assertLess(dt, 5*us)
|
||||
|
||||
def test_io_update(self):
|
||||
self.execute(UrukulExp, "io_update")
|
||||
|
||||
def test_sync(self):
|
||||
self.execute(UrukulExp, "sync")
|
Loading…
Reference in New Issue