mirror of https://github.com/m-labs/artiq.git
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 numpy import int32, int64
|
||||||
|
|
||||||
from artiq.language.core import kernel, delay, portable
|
from artiq.language.core import (
|
||||||
from artiq.language.units import us, ns, ms
|
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 spi2 as spi
|
||||||
from artiq.coredevice import urukul
|
from artiq.coredevice import urukul
|
||||||
|
# Work around ARTIQ-Python import machinery
|
||||||
urukul_sta_pll_lock = urukul.urukul_sta_pll_lock
|
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_CFR1 = 0x00
|
||||||
_AD9910_REG_CFR2 = 0x01
|
_AD9910_REG_CFR2 = 0x01
|
||||||
_AD9910_REG_CFR3 = 0x02
|
_AD9910_REG_CFR3 = 0x02
|
||||||
|
@ -49,12 +63,21 @@ class AD9910:
|
||||||
Urukul CPLD instance).
|
Urukul CPLD instance).
|
||||||
:param pll_cp: DDS PLL charge pump setting.
|
:param pll_cp: DDS PLL charge pump setting.
|
||||||
:param pll_vco: DDS PLL VCO range selection.
|
: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",
|
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,
|
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.cpld = dmgr.get(cpld_device)
|
||||||
self.core = self.cpld.core
|
self.core = self.cpld.core
|
||||||
self.bus = self.cpld.bus
|
self.bus = self.cpld.bus
|
||||||
|
@ -68,7 +91,9 @@ class AD9910:
|
||||||
assert self.cpld.refclk/4 <= 60e6
|
assert self.cpld.refclk/4 <= 60e6
|
||||||
sysclk = self.cpld.refclk*pll_n/4 # Urukul clock fanout divider
|
sysclk = self.cpld.refclk*pll_n/4 # Urukul clock fanout divider
|
||||||
assert sysclk <= 1e9
|
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
|
assert 0 <= pll_vco <= 5
|
||||||
vco_min, vco_max = [(370, 510), (420, 590), (500, 700),
|
vco_min, vco_max = [(370, 510), (420, 590), (500, 700),
|
||||||
(600, 880), (700, 950), (820, 1150)][pll_vco]
|
(600, 880), (700, 950), (820, 1150)][pll_vco]
|
||||||
|
@ -76,6 +101,54 @@ class AD9910:
|
||||||
self.pll_vco = pll_vco
|
self.pll_vco = pll_vco
|
||||||
assert 0 <= pll_cp <= 7
|
assert 0 <= pll_cp <= 7
|
||||||
self.pll_cp = pll_cp
|
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
|
@kernel
|
||||||
def write32(self, addr, data):
|
def write32(self, addr, data):
|
||||||
|
@ -100,9 +173,9 @@ class AD9910:
|
||||||
self.bus.set_config_mu(urukul.SPI_CONFIG, 8,
|
self.bus.set_config_mu(urukul.SPI_CONFIG, 8,
|
||||||
urukul.SPIT_DDS_WR, self.chip_select)
|
urukul.SPIT_DDS_WR, self.chip_select)
|
||||||
self.bus.write((addr | 0x80) << 24)
|
self.bus.write((addr | 0x80) << 24)
|
||||||
self.bus.set_config_mu(urukul.SPI_CONFIG | spi.SPI_END
|
self.bus.set_config_mu(
|
||||||
| spi.SPI_INPUT, 32,
|
urukul.SPI_CONFIG | spi.SPI_END | spi.SPI_INPUT,
|
||||||
urukul.SPIT_DDS_RD, self.chip_select)
|
32, urukul.SPIT_DDS_RD, self.chip_select)
|
||||||
self.bus.write(0)
|
self.bus.write(0)
|
||||||
return self.bus.read()
|
return self.bus.read()
|
||||||
|
|
||||||
|
@ -136,7 +209,7 @@ class AD9910:
|
||||||
"""
|
"""
|
||||||
# Set SPI mode
|
# Set SPI mode
|
||||||
self.write32(_AD9910_REG_CFR1, 0x00000002)
|
self.write32(_AD9910_REG_CFR1, 0x00000002)
|
||||||
self.cpld.io_update.pulse(2*us)
|
self.cpld.io_update.pulse(1*us)
|
||||||
delay(1*ms)
|
delay(1*ms)
|
||||||
if not blind:
|
if not blind:
|
||||||
# Use the AUX DAC setting to identify and confirm presence
|
# Use the AUX DAC setting to identify and confirm presence
|
||||||
|
@ -145,60 +218,121 @@ class AD9910:
|
||||||
raise ValueError("Urukul AD9910 AUX_DAC mismatch")
|
raise ValueError("Urukul AD9910 AUX_DAC mismatch")
|
||||||
delay(50*us) # slack
|
delay(50*us) # slack
|
||||||
# Configure PLL settings and bring up PLL
|
# Configure PLL settings and bring up PLL
|
||||||
self.write32(_AD9910_REG_CFR2, 0x01400020)
|
# enable amplitude scale from profiles
|
||||||
self.cpld.io_update.pulse(2*us)
|
# 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) |
|
cfr3 = (0x0807c100 | (self.pll_vco << 24) |
|
||||||
(self.pll_cp << 19) | (self.pll_n << 1))
|
(self.pll_cp << 19) | (self.pll_n << 1))
|
||||||
self.write32(_AD9910_REG_CFR3, cfr3 | 0x400) # PFD reset
|
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.write32(_AD9910_REG_CFR3, cfr3)
|
||||||
self.cpld.io_update.pulse(100*us)
|
self.cpld.io_update.pulse(1*us)
|
||||||
if blind:
|
if blind:
|
||||||
delay(100*ms)
|
delay(100*ms)
|
||||||
return
|
else:
|
||||||
# Wait for PLL lock, up to 100 ms
|
# Wait for PLL lock, up to 100 ms
|
||||||
for i in range(100):
|
for i in range(100):
|
||||||
sta = self.cpld.sta_read()
|
sta = self.cpld.sta_read()
|
||||||
lock = urukul_sta_pll_lock(sta)
|
lock = urukul_sta_pll_lock(sta)
|
||||||
delay(1*ms)
|
delay(1*ms)
|
||||||
if lock & (1 << self.chip_select - 4):
|
if lock & (1 << self.chip_select - 4):
|
||||||
return
|
break
|
||||||
|
if i >= 100 - 1:
|
||||||
raise ValueError("PLL lock timeout")
|
raise ValueError("PLL lock timeout")
|
||||||
|
if self.sync_delay_seed >= 0:
|
||||||
|
self.tune_sync_delay(self.sync_delay_seed)
|
||||||
|
delay(1*ms)
|
||||||
|
|
||||||
@kernel
|
@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.
|
"""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
|
After the SPI transfer, the shared IO update pin is pulsed to
|
||||||
activate the data.
|
activate the data.
|
||||||
|
|
||||||
|
.. seealso: :meth:`set_phase_mode` for a definition of the different
|
||||||
|
phase modes.
|
||||||
|
|
||||||
:param ftw: Frequency tuning word: 32 bit.
|
:param ftw: Frequency tuning word: 32 bit.
|
||||||
:param pow: Phase tuning word: 16 bit unsigned.
|
:param pow: Phase tuning word: 16 bit unsigned.
|
||||||
:param asf: Amplitude scale factor: 14 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.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"})
|
@portable(flags={"fast-math"})
|
||||||
def frequency_to_ftw(self, frequency):
|
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.
|
frequency.
|
||||||
"""
|
"""
|
||||||
return int32(round(self.ftw_per_hz*frequency))
|
return int32(round(self.ftw_per_hz*frequency))
|
||||||
|
|
||||||
@portable(flags={"fast-math"})
|
@portable(flags={"fast-math"})
|
||||||
def turns_to_pow(self, turns):
|
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."""
|
in turns."""
|
||||||
return int32(round(turns*0x10000))
|
return int32(round(turns*0x10000))
|
||||||
|
|
||||||
@portable(flags={"fast-math"})
|
@portable(flags={"fast-math"})
|
||||||
def amplitude_to_asf(self, amplitude):
|
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))
|
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
|
@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.
|
"""Set profile 0 data in SI units.
|
||||||
|
|
||||||
.. seealso:: :meth:`set_mu`
|
.. seealso:: :meth:`set_mu`
|
||||||
|
@ -206,10 +340,13 @@ class AD9910:
|
||||||
:param ftw: Frequency in Hz
|
:param ftw: Frequency in Hz
|
||||||
:param pow: Phase tuning word in turns
|
:param pow: Phase tuning word in turns
|
||||||
:param asf: Amplitude in units of full scale
|
: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),
|
return self.pow_to_turns(self.set_mu(
|
||||||
self.turns_to_pow(phase),
|
self.frequency_to_ftw(frequency), self.turns_to_pow(phase),
|
||||||
self.amplitude_to_asf(amplitude))
|
self.amplitude_to_asf(amplitude), phase_mode, ref_time))
|
||||||
|
|
||||||
@kernel
|
@kernel
|
||||||
def set_att_mu(self, att):
|
def set_att_mu(self, att):
|
||||||
|
@ -240,3 +377,148 @@ class AD9910:
|
||||||
:param state: CPLD CFG RF switch bit
|
:param state: CPLD CFG RF switch bit
|
||||||
"""
|
"""
|
||||||
self.cpld.cfg_sw(self.chip_select - 4, state)
|
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.
|
The time cursor is not modified by any function in this class.
|
||||||
|
|
||||||
:param channel: channel number
|
:param channel: channel number
|
||||||
|
:param acc_width: accumulator width in bits
|
||||||
"""
|
"""
|
||||||
kernel_invariants = {"core", "channel", "acc_width"}
|
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.core = dmgr.get(core_device)
|
||||||
self.channel = channel
|
self.channel = channel
|
||||||
|
|
||||||
self.acc_width = numpy.int64(24)
|
self.acc_width = numpy.int64(acc_width)
|
||||||
|
|
||||||
@portable
|
@portable
|
||||||
def frequency_to_ftw(self, frequency):
|
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 artiq.language.units import us, ms
|
||||||
|
|
||||||
from numpy import int32
|
from numpy import int32
|
||||||
|
@ -61,7 +61,7 @@ def urukul_cfg(rf_sw, led, profile, io_update, mask_nu,
|
||||||
(io_update << CFG_IO_UPDATE) |
|
(io_update << CFG_IO_UPDATE) |
|
||||||
(mask_nu << CFG_MASK_NU) |
|
(mask_nu << CFG_MASK_NU) |
|
||||||
((clk_sel & 0x01) << CFG_CLK_SEL0) |
|
((clk_sel & 0x01) << CFG_CLK_SEL0) |
|
||||||
((clk_sel & 0x02) << (CFG_CLK_SEL1-1)) |
|
((clk_sel & 0x02) << (CFG_CLK_SEL1 - 1)) |
|
||||||
(sync_sel << CFG_SYNC_SEL) |
|
(sync_sel << CFG_SYNC_SEL) |
|
||||||
(rst << CFG_RST) |
|
(rst << CFG_RST) |
|
||||||
(io_rst << CFG_IO_RST))
|
(io_rst << CFG_IO_RST))
|
||||||
|
@ -109,12 +109,22 @@ class _RegIOUpdate:
|
||||||
self.cpld.cfg_write(cfg)
|
self.cpld.cfg_write(cfg)
|
||||||
|
|
||||||
|
|
||||||
|
class _DummySync:
|
||||||
|
def __init__(self, cpld):
|
||||||
|
self.cpld = cpld
|
||||||
|
|
||||||
|
@kernel
|
||||||
|
def set_mu(self, ftw):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
class CPLD:
|
class CPLD:
|
||||||
"""Urukul CPLD SPI router and configuration interface.
|
"""Urukul CPLD SPI router and configuration interface.
|
||||||
|
|
||||||
:param spi_device: SPI bus device name
|
:param spi_device: SPI bus device name
|
||||||
:param io_update_device: IO update RTIO TTLOut channel name
|
:param io_update_device: IO update RTIO TTLOut channel name
|
||||||
:param dds_reset_device: DDS reset 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)
|
:param refclk: Reference clock (SMA, MMCX or on-board 100 MHz oscillator)
|
||||||
frequency in Hz
|
frequency in Hz
|
||||||
:param clk_sel: Reference clock selection. For hardware revision >= 1.3
|
: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 -
|
internal MMCX. For hardware revision <= v1.2 valid options are: 0 -
|
||||||
either XO or MMCX dependent on component population; 1 SMA. Unsupported
|
either XO or MMCX dependent on component population; 1 SMA. Unsupported
|
||||||
clocking options are silently ignored.
|
clocking options are silently ignored.
|
||||||
:param sync_sel: SYNC clock selection. 0 corresponds to SYNC clock over EEM
|
:param sync_sel: SYNC_IN selection. 0 corresponds to SYNC_IN over EEM
|
||||||
from FPGA. 1 corresponds to SYNC clock from DDS0.
|
from FPGA. 1 corresponds to SYNC_IN from DDS0.
|
||||||
:param rf_sw: Initial CPLD RF switch register setting (default: 0x0).
|
:param rf_sw: Initial CPLD RF switch register setting (default: 0x0).
|
||||||
Knowledge of this state is not transferred between experiments.
|
Knowledge of this state is not transferred between experiments.
|
||||||
:param att: Initial attenuator setting shift register (default:
|
:param att: Initial attenuator setting shift register (default:
|
||||||
0x00000000). See also: :meth:`set_all_att_mu`. Knowledge of this state
|
0x00000000). See also: :meth:`set_all_att_mu`. Knowledge of this state
|
||||||
is not transferred between experiments.
|
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
|
:param core_device: Core device name
|
||||||
"""
|
"""
|
||||||
kernel_invariants = {"refclk", "bus", "core", "io_update"}
|
kernel_invariants = {"refclk", "bus", "core", "io_update"}
|
||||||
|
|
||||||
def __init__(self, dmgr, spi_device, io_update_device=None,
|
def __init__(self, dmgr, spi_device, io_update_device=None,
|
||||||
dds_reset_device=None, sync_sel=0, clk_sel=0, rf_sw=0,
|
dds_reset_device=None, sync_device=None,
|
||||||
refclk=125e6, att=0x00000000, core_device="core"):
|
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.core = dmgr.get(core_device)
|
||||||
self.refclk = refclk
|
self.refclk = refclk
|
||||||
|
@ -147,11 +162,20 @@ class CPLD:
|
||||||
self.io_update = _RegIOUpdate(self)
|
self.io_update = _RegIOUpdate(self)
|
||||||
if dds_reset_device is not None:
|
if dds_reset_device is not None:
|
||||||
self.dds_reset = dmgr.get(dds_reset_device)
|
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,
|
self.cfg_reg = urukul_cfg(rf_sw=rf_sw, led=0, profile=0,
|
||||||
io_update=0, mask_nu=0, clk_sel=clk_sel,
|
io_update=0, mask_nu=0, clk_sel=clk_sel,
|
||||||
sync_sel=sync_sel, rst=0, io_rst=0)
|
sync_sel=sync_sel, rst=0, io_rst=0)
|
||||||
self.att_reg = att
|
self.att_reg = att
|
||||||
|
self.sync_div = sync_div
|
||||||
|
|
||||||
@kernel
|
@kernel
|
||||||
def cfg_write(self, cfg):
|
def cfg_write(self, cfg):
|
||||||
|
@ -207,6 +231,9 @@ class CPLD:
|
||||||
raise ValueError("Urukul proto_rev mismatch")
|
raise ValueError("Urukul proto_rev mismatch")
|
||||||
delay(100*us) # reset, slack
|
delay(100*us) # reset, slack
|
||||||
self.cfg_write(cfg)
|
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
|
delay(1*ms) # DDS wake up
|
||||||
|
|
||||||
@kernel
|
@kernel
|
||||||
|
@ -289,3 +316,20 @@ class CPLD:
|
||||||
SPIT_ATT_RD, CS_ATT)
|
SPIT_ATT_RD, CS_ATT)
|
||||||
self.bus.write(self.att_reg)
|
self.bus.write(self.att_reg)
|
||||||
return self.bus.read()
|
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",
|
"type": "local",
|
||||||
"module": "artiq.coredevice.spi2",
|
"module": "artiq.coredevice.spi2",
|
||||||
"class": "SPIMaster",
|
"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): {
|
"ttl_urukul{}_io_update".format(j): {
|
||||||
"type": "local",
|
"type": "local",
|
||||||
"module": "artiq.coredevice.ttl",
|
"module": "artiq.coredevice.ttl",
|
||||||
"class": "TTLOut",
|
"class": "TTLOut",
|
||||||
"arguments": {"channel": 28 + 2*j}
|
"arguments": {"channel": 29 + 3*j}
|
||||||
},
|
},
|
||||||
"urukul{}_cpld".format(j): {
|
"urukul{}_cpld".format(j): {
|
||||||
"type": "local",
|
"type": "local",
|
||||||
|
@ -100,6 +106,7 @@ for j in range(3):
|
||||||
"class": "CPLD",
|
"class": "CPLD",
|
||||||
"arguments": {
|
"arguments": {
|
||||||
"spi_device": "spi_urukul{}".format(j),
|
"spi_device": "spi_urukul{}".format(j),
|
||||||
|
"sync_device": "ttl_urukul{}_sync".format(j),
|
||||||
"io_update_device": "ttl_urukul{}_io_update".format(j),
|
"io_update_device": "ttl_urukul{}_io_update".format(j),
|
||||||
"refclk": 100e6,
|
"refclk": 100e6,
|
||||||
"clk_sel": 0
|
"clk_sel": 0
|
||||||
|
@ -126,13 +133,13 @@ device_db.update({
|
||||||
"type": "local",
|
"type": "local",
|
||||||
"module": "artiq.coredevice.ttl",
|
"module": "artiq.coredevice.ttl",
|
||||||
"class": "TTLOut",
|
"class": "TTLOut",
|
||||||
"arguments": {"channel": 33}
|
"arguments": {"channel": 36}
|
||||||
},
|
},
|
||||||
"led1": {
|
"led1": {
|
||||||
"type": "local",
|
"type": "local",
|
||||||
"module": "artiq.coredevice.ttl",
|
"module": "artiq.coredevice.ttl",
|
||||||
"class": "TTLOut",
|
"class": "TTLOut",
|
||||||
"arguments": {"channel": 34}
|
"arguments": {"channel": 37}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -142,19 +149,19 @@ device_db.update({
|
||||||
"type": "local",
|
"type": "local",
|
||||||
"module": "artiq.coredevice.spi2",
|
"module": "artiq.coredevice.spi2",
|
||||||
"class": "SPIMaster",
|
"class": "SPIMaster",
|
||||||
"arguments": {"channel": 35}
|
"arguments": {"channel": 38}
|
||||||
},
|
},
|
||||||
"ttl_zotino0_ldac": {
|
"ttl_zotino0_ldac": {
|
||||||
"type": "local",
|
"type": "local",
|
||||||
"module": "artiq.coredevice.ttl",
|
"module": "artiq.coredevice.ttl",
|
||||||
"class": "TTLOut",
|
"class": "TTLOut",
|
||||||
"arguments": {"channel": 36}
|
"arguments": {"channel": 39}
|
||||||
},
|
},
|
||||||
"ttl_zotino0_clr": {
|
"ttl_zotino0_clr": {
|
||||||
"type": "local",
|
"type": "local",
|
||||||
"module": "artiq.coredevice.ttl",
|
"module": "artiq.coredevice.ttl",
|
||||||
"class": "TTLOut",
|
"class": "TTLOut",
|
||||||
"arguments": {"channel": 37}
|
"arguments": {"channel": 40}
|
||||||
},
|
},
|
||||||
"zotino0": {
|
"zotino0": {
|
||||||
"type": "local",
|
"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"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
|
@ -198,7 +198,7 @@ device_db = {
|
||||||
"class": "TTLOut",
|
"class": "TTLOut",
|
||||||
"arguments": {"channel": 25}
|
"arguments": {"channel": 25}
|
||||||
},
|
},
|
||||||
"novogorny0" : {
|
"novogorny0": {
|
||||||
"type": "local",
|
"type": "local",
|
||||||
"module": "artiq.coredevice.novogorny",
|
"module": "artiq.coredevice.novogorny",
|
||||||
"class": "Novogorny",
|
"class": "Novogorny",
|
||||||
|
@ -313,23 +313,94 @@ device_db = {
|
||||||
"arguments": {"channel": 33}
|
"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": {
|
"spi_zotino0": {
|
||||||
"type": "local",
|
"type": "local",
|
||||||
"module": "artiq.coredevice.spi2",
|
"module": "artiq.coredevice.spi2",
|
||||||
"class": "SPIMaster",
|
"class": "SPIMaster",
|
||||||
"arguments": {"channel": 36}
|
"arguments": {"channel": 37}
|
||||||
},
|
},
|
||||||
"ttl_zotino0_ldac": {
|
"ttl_zotino0_ldac": {
|
||||||
"type": "local",
|
"type": "local",
|
||||||
"module": "artiq.coredevice.ttl",
|
"module": "artiq.coredevice.ttl",
|
||||||
"class": "TTLOut",
|
"class": "TTLOut",
|
||||||
"arguments": {"channel": 37}
|
"arguments": {"channel": 38}
|
||||||
},
|
},
|
||||||
"ttl_zotino0_clr": {
|
"ttl_zotino0_clr": {
|
||||||
"type": "local",
|
"type": "local",
|
||||||
"module": "artiq.coredevice.ttl",
|
"module": "artiq.coredevice.ttl",
|
||||||
"class": "TTLOut",
|
"class": "TTLOut",
|
||||||
"arguments": {"channel": 38}
|
"arguments": {"channel": 39}
|
||||||
},
|
},
|
||||||
"zotino0": {
|
"zotino0": {
|
||||||
"type": "local",
|
"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",
|
"class": "SPIMaster",
|
||||||
"arguments": {"channel": 8}
|
"arguments": {"channel": 8}
|
||||||
},
|
},
|
||||||
ttl_urukul0_io_update={
|
ttl_urukul0_sync={
|
||||||
"type": "local",
|
"type": "local",
|
||||||
"module": "artiq.coredevice.ttl",
|
"module": "artiq.coredevice.ttl",
|
||||||
"class": "TTLOut",
|
"class": "TTLClockGen",
|
||||||
"arguments": {"channel": 9}
|
"arguments": {"channel": 9, "acc_width": 4}
|
||||||
},
|
},
|
||||||
ttl_urukul0_sw0={
|
ttl_urukul0_io_update={
|
||||||
"type": "local",
|
"type": "local",
|
||||||
"module": "artiq.coredevice.ttl",
|
"module": "artiq.coredevice.ttl",
|
||||||
"class": "TTLOut",
|
"class": "TTLOut",
|
||||||
"arguments": {"channel": 10}
|
"arguments": {"channel": 10}
|
||||||
},
|
},
|
||||||
ttl_urukul0_sw1={
|
ttl_urukul0_sw0={
|
||||||
"type": "local",
|
"type": "local",
|
||||||
"module": "artiq.coredevice.ttl",
|
"module": "artiq.coredevice.ttl",
|
||||||
"class": "TTLOut",
|
"class": "TTLOut",
|
||||||
"arguments": {"channel": 11}
|
"arguments": {"channel": 11}
|
||||||
},
|
},
|
||||||
ttl_urukul0_sw2={
|
ttl_urukul0_sw1={
|
||||||
"type": "local",
|
"type": "local",
|
||||||
"module": "artiq.coredevice.ttl",
|
"module": "artiq.coredevice.ttl",
|
||||||
"class": "TTLOut",
|
"class": "TTLOut",
|
||||||
"arguments": {"channel": 12}
|
"arguments": {"channel": 12}
|
||||||
},
|
},
|
||||||
ttl_urukul0_sw3={
|
ttl_urukul0_sw2={
|
||||||
"type": "local",
|
"type": "local",
|
||||||
"module": "artiq.coredevice.ttl",
|
"module": "artiq.coredevice.ttl",
|
||||||
"class": "TTLOut",
|
"class": "TTLOut",
|
||||||
"arguments": {"channel": 13}
|
"arguments": {"channel": 13}
|
||||||
},
|
},
|
||||||
|
ttl_urukul0_sw3={
|
||||||
|
"type": "local",
|
||||||
|
"module": "artiq.coredevice.ttl",
|
||||||
|
"class": "TTLOut",
|
||||||
|
"arguments": {"channel": 14}
|
||||||
|
},
|
||||||
urukul0_cpld={
|
urukul0_cpld={
|
||||||
"type": "local",
|
"type": "local",
|
||||||
"module": "artiq.coredevice.urukul",
|
"module": "artiq.coredevice.urukul",
|
||||||
|
@ -94,6 +100,7 @@ device_db.update(
|
||||||
"arguments": {
|
"arguments": {
|
||||||
"spi_device": "spi_urukul0",
|
"spi_device": "spi_urukul0",
|
||||||
"io_update_device": "ttl_urukul0_io_update",
|
"io_update_device": "ttl_urukul0_io_update",
|
||||||
|
"sync_device": "ttl_urukul0_sync",
|
||||||
"refclk": 125e6,
|
"refclk": 125e6,
|
||||||
"clk_sel": 0
|
"clk_sel": 0
|
||||||
}
|
}
|
||||||
|
@ -119,19 +126,19 @@ device_db["spi_sampler0_adc"] = {
|
||||||
"type": "local",
|
"type": "local",
|
||||||
"module": "artiq.coredevice.spi2",
|
"module": "artiq.coredevice.spi2",
|
||||||
"class": "SPIMaster",
|
"class": "SPIMaster",
|
||||||
"arguments": {"channel": 14}
|
"arguments": {"channel": 15}
|
||||||
}
|
}
|
||||||
device_db["spi_sampler0_pgia"] = {
|
device_db["spi_sampler0_pgia"] = {
|
||||||
"type": "local",
|
"type": "local",
|
||||||
"module": "artiq.coredevice.spi2",
|
"module": "artiq.coredevice.spi2",
|
||||||
"class": "SPIMaster",
|
"class": "SPIMaster",
|
||||||
"arguments": {"channel": 15}
|
"arguments": {"channel": 16}
|
||||||
}
|
}
|
||||||
device_db["spi_sampler0_cnv"] = {
|
device_db["spi_sampler0_cnv"] = {
|
||||||
"type": "local",
|
"type": "local",
|
||||||
"module": "artiq.coredevice.ttl",
|
"module": "artiq.coredevice.ttl",
|
||||||
"class": "TTLOut",
|
"class": "TTLOut",
|
||||||
"arguments": {"channel": 16},
|
"arguments": {"channel": 17},
|
||||||
}
|
}
|
||||||
device_db["sampler0"] = {
|
device_db["sampler0"] = {
|
||||||
"type": "local",
|
"type": "local",
|
||||||
|
@ -150,19 +157,19 @@ device_db["spi_zotino0"] = {
|
||||||
"type": "local",
|
"type": "local",
|
||||||
"module": "artiq.coredevice.spi2",
|
"module": "artiq.coredevice.spi2",
|
||||||
"class": "SPIMaster",
|
"class": "SPIMaster",
|
||||||
"arguments": {"channel": 17}
|
"arguments": {"channel": 18}
|
||||||
}
|
}
|
||||||
device_db["ttl_zotino0_ldac"] = {
|
device_db["ttl_zotino0_ldac"] = {
|
||||||
"type": "local",
|
"type": "local",
|
||||||
"module": "artiq.coredevice.ttl",
|
"module": "artiq.coredevice.ttl",
|
||||||
"class": "TTLOut",
|
"class": "TTLOut",
|
||||||
"arguments": {"channel": 18}
|
"arguments": {"channel": 19}
|
||||||
}
|
}
|
||||||
device_db["ttl_zotino0_clr"] = {
|
device_db["ttl_zotino0_clr"] = {
|
||||||
"type": "local",
|
"type": "local",
|
||||||
"module": "artiq.coredevice.ttl",
|
"module": "artiq.coredevice.ttl",
|
||||||
"class": "TTLOut",
|
"class": "TTLOut",
|
||||||
"arguments": {"channel": 19}
|
"arguments": {"channel": 20}
|
||||||
}
|
}
|
||||||
device_db["zotino0"] = {
|
device_db["zotino0"] = {
|
||||||
"type": "local",
|
"type": "local",
|
||||||
|
@ -181,7 +188,7 @@ device_db["grabber0"] = {
|
||||||
"type": "local",
|
"type": "local",
|
||||||
"module": "artiq.coredevice.grabber",
|
"module": "artiq.coredevice.grabber",
|
||||||
"class": "Grabber",
|
"class": "Grabber",
|
||||||
"arguments": {"channel_base": 20}
|
"arguments": {"channel_base": 21}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -191,13 +198,19 @@ device_db.update(
|
||||||
"type": "local",
|
"type": "local",
|
||||||
"module": "artiq.coredevice.spi2",
|
"module": "artiq.coredevice.spi2",
|
||||||
"class": "SPIMaster",
|
"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={
|
ttl_urukul1_io_update={
|
||||||
"type": "local",
|
"type": "local",
|
||||||
"module": "artiq.coredevice.ttl",
|
"module": "artiq.coredevice.ttl",
|
||||||
"class": "TTLOut",
|
"class": "TTLOut",
|
||||||
"arguments": {"channel": 23}
|
"arguments": {"channel": 25}
|
||||||
},
|
},
|
||||||
urukul1_cpld={
|
urukul1_cpld={
|
||||||
"type": "local",
|
"type": "local",
|
||||||
|
@ -206,6 +219,7 @@ device_db.update(
|
||||||
"arguments": {
|
"arguments": {
|
||||||
"spi_device": "spi_urukul1",
|
"spi_device": "spi_urukul1",
|
||||||
"io_update_device": "ttl_urukul1_io_update",
|
"io_update_device": "ttl_urukul1_io_update",
|
||||||
|
"sync_device": "ttl_urukul1_sync",
|
||||||
"refclk": 100e6,
|
"refclk": 100e6,
|
||||||
"clk_sel": 1
|
"clk_sel": 1
|
||||||
}
|
}
|
||||||
|
@ -231,7 +245,7 @@ for i in range(8):
|
||||||
"type": "local",
|
"type": "local",
|
||||||
"module": "artiq.coredevice.ttl",
|
"module": "artiq.coredevice.ttl",
|
||||||
"class": "TTLOut",
|
"class": "TTLOut",
|
||||||
"arguments": {"channel": 24 + i},
|
"arguments": {"channel": 26 + i},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -241,7 +255,7 @@ for i in range(8):
|
||||||
"type": "local",
|
"type": "local",
|
||||||
"module": "artiq.coredevice.ttl",
|
"module": "artiq.coredevice.ttl",
|
||||||
"class": "TTLOut",
|
"class": "TTLOut",
|
||||||
"arguments": {"channel": 32 + i},
|
"arguments": {"channel": 34 + i},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -250,19 +264,19 @@ device_db["spi_sampler1_adc"] = {
|
||||||
"type": "local",
|
"type": "local",
|
||||||
"module": "artiq.coredevice.spi2",
|
"module": "artiq.coredevice.spi2",
|
||||||
"class": "SPIMaster",
|
"class": "SPIMaster",
|
||||||
"arguments": {"channel": 40}
|
"arguments": {"channel": 42}
|
||||||
}
|
}
|
||||||
device_db["spi_sampler1_pgia"] = {
|
device_db["spi_sampler1_pgia"] = {
|
||||||
"type": "local",
|
"type": "local",
|
||||||
"module": "artiq.coredevice.spi2",
|
"module": "artiq.coredevice.spi2",
|
||||||
"class": "SPIMaster",
|
"class": "SPIMaster",
|
||||||
"arguments": {"channel": 41}
|
"arguments": {"channel": 43}
|
||||||
}
|
}
|
||||||
device_db["spi_sampler1_cnv"] = {
|
device_db["spi_sampler1_cnv"] = {
|
||||||
"type": "local",
|
"type": "local",
|
||||||
"module": "artiq.coredevice.ttl",
|
"module": "artiq.coredevice.ttl",
|
||||||
"class": "TTLOut",
|
"class": "TTLOut",
|
||||||
"arguments": {"channel": 42},
|
"arguments": {"channel": 44},
|
||||||
}
|
}
|
||||||
device_db["sampler1"] = {
|
device_db["sampler1"] = {
|
||||||
"type": "local",
|
"type": "local",
|
||||||
|
@ -281,19 +295,19 @@ device_db["spi_zotino1"] = {
|
||||||
"type": "local",
|
"type": "local",
|
||||||
"module": "artiq.coredevice.spi2",
|
"module": "artiq.coredevice.spi2",
|
||||||
"class": "SPIMaster",
|
"class": "SPIMaster",
|
||||||
"arguments": {"channel": 43}
|
"arguments": {"channel": 45}
|
||||||
}
|
}
|
||||||
device_db["ttl_zotino1_ldac"] = {
|
device_db["ttl_zotino1_ldac"] = {
|
||||||
"type": "local",
|
"type": "local",
|
||||||
"module": "artiq.coredevice.ttl",
|
"module": "artiq.coredevice.ttl",
|
||||||
"class": "TTLOut",
|
"class": "TTLOut",
|
||||||
"arguments": {"channel": 44}
|
"arguments": {"channel": 46}
|
||||||
}
|
}
|
||||||
device_db["ttl_zotino1_clr"] = {
|
device_db["ttl_zotino1_clr"] = {
|
||||||
"type": "local",
|
"type": "local",
|
||||||
"module": "artiq.coredevice.ttl",
|
"module": "artiq.coredevice.ttl",
|
||||||
"class": "TTLOut",
|
"class": "TTLOut",
|
||||||
"arguments": {"channel": 45}
|
"arguments": {"channel": 47}
|
||||||
}
|
}
|
||||||
device_db["zotino1"] = {
|
device_db["zotino1"] = {
|
||||||
"type": "local",
|
"type": "local",
|
||||||
|
@ -312,13 +326,13 @@ device_db.update(
|
||||||
"type": "local",
|
"type": "local",
|
||||||
"module": "artiq.coredevice.ttl",
|
"module": "artiq.coredevice.ttl",
|
||||||
"class": "TTLOut",
|
"class": "TTLOut",
|
||||||
"arguments": {"channel": 46}
|
"arguments": {"channel": 48}
|
||||||
},
|
},
|
||||||
led1={
|
led1={
|
||||||
"type": "local",
|
"type": "local",
|
||||||
"module": "artiq.coredevice.ttl",
|
"module": "artiq.coredevice.ttl",
|
||||||
"class": "TTLOut",
|
"class": "TTLOut",
|
||||||
"arguments": {"channel": 47}
|
"arguments": {"channel": 49}
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -328,4 +342,10 @@ device_db.update(
|
||||||
|
|
||||||
loop_out="ttl4",
|
loop_out="ttl4",
|
||||||
loop_in="ttl0",
|
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"),
|
ttls = [(6, eem, "io_update"),
|
||||||
(7, eem, "dds_reset")]
|
(7, eem, "dds_reset_sync_in")]
|
||||||
if eem_aux is not None:
|
if eem_aux is not None:
|
||||||
ttls += [(0, eem_aux, "sync_clk"),
|
ttls += [(0, eem_aux, "sync_clk"),
|
||||||
(1, eem_aux, "sync_in"),
|
(1, eem_aux, "sync_in"),
|
||||||
|
@ -113,7 +113,7 @@ class Urukul(_EEM):
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
ttls = [(6, eem0, "io_update"),
|
ttls = [(6, eem0, "io_update"),
|
||||||
(7, eem0, "dds_reset"),
|
(7, eem0, "dds_reset_sync_in"),
|
||||||
(4, eem1, "sw0"),
|
(4, eem1, "sw0"),
|
||||||
(5, eem1, "sw1"),
|
(5, eem1, "sw1"),
|
||||||
(6, eem1, "sw2"),
|
(6, eem1, "sw2"),
|
||||||
|
@ -148,7 +148,8 @@ class Urukul(_EEM):
|
||||||
return ios
|
return ios
|
||||||
|
|
||||||
@classmethod
|
@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)
|
cls.add_extension(target, eem, eem_aux, iostandard=iostandard)
|
||||||
|
|
||||||
phy = spi2.SPIMaster(target.platform.request("urukul{}_spi_p".format(eem)),
|
phy = spi2.SPIMaster(target.platform.request("urukul{}_spi_p".format(eem)),
|
||||||
|
@ -156,8 +157,13 @@ class Urukul(_EEM):
|
||||||
target.submodules += phy
|
target.submodules += phy
|
||||||
target.rtio_channels.append(rtio.Channel.from_phy(phy, ififo_depth=4))
|
target.rtio_channels.append(rtio.Channel.from_phy(phy, ififo_depth=4))
|
||||||
|
|
||||||
pads = target.platform.request("urukul{}_dds_reset".format(eem))
|
pads = target.platform.request("urukul{}_dds_reset_sync_in".format(eem))
|
||||||
target.specials += DifferentialOutput(0, pads.p, pads.n)
|
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))
|
pads = target.platform.request("urukul{}_io_update".format(eem))
|
||||||
phy = ttl_out_cls(pads.p, pads.n)
|
phy = ttl_out_cls(pads.p, pads.n)
|
||||||
|
@ -170,7 +176,6 @@ class Urukul(_EEM):
|
||||||
target.submodules += phy
|
target.submodules += phy
|
||||||
target.rtio_channels.append(rtio.Channel.from_phy(phy))
|
target.rtio_channels.append(rtio.Channel.from_phy(phy))
|
||||||
|
|
||||||
|
|
||||||
class Sampler(_EEM):
|
class Sampler(_EEM):
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def io(eem, eem_aux, iostandard="LVDS_25"):
|
def io(eem, eem_aux, iostandard="LVDS_25"):
|
||||||
|
@ -522,7 +527,7 @@ class SUServo(_EEM):
|
||||||
target.submodules += phy
|
target.submodules += phy
|
||||||
target.rtio_channels.append(rtio.Channel.from_phy(phy, ififo_depth=4))
|
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)
|
target.specials += DifferentialOutput(0, pads.p, pads.n)
|
||||||
|
|
||||||
for i, signal in enumerate("sw0 sw1 sw2 sw3".split()):
|
for i, signal in enumerate("sw0 sw1 sw2 sw3".split()):
|
||||||
|
|
|
@ -175,7 +175,8 @@ class Opticlock(_StandaloneBase):
|
||||||
self.submodules += phy
|
self.submodules += phy
|
||||||
self.rtio_channels.append(rtio.Channel.from_phy(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)
|
eem.Zotino.add_std(self, 7, ttl_serdes_7series.Output_8X)
|
||||||
|
|
||||||
self.config["HAS_RTIO_LOG"] = None
|
self.config["HAS_RTIO_LOG"] = None
|
||||||
|
@ -574,6 +575,47 @@ class PTB(_StandaloneBase):
|
||||||
self.add_rtio(self.rtio_channels)
|
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):
|
class HUB(_StandaloneBase):
|
||||||
"""HUB Kasli variant
|
"""HUB Kasli variant
|
||||||
|
|
||||||
|
@ -600,9 +642,12 @@ class HUB(_StandaloneBase):
|
||||||
eem.DIO.add_std(self, 2,
|
eem.DIO.add_std(self, 2,
|
||||||
ttl_serdes_7series.Output_8X, ttl_serdes_7series.Output_8X)
|
ttl_serdes_7series.Output_8X, ttl_serdes_7series.Output_8X)
|
||||||
eem.Sampler.add_std(self, 3, None, 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, 4, None, ttl_serdes_7series.Output_8X,
|
||||||
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)
|
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):
|
for i in (1, 2):
|
||||||
sfp_ctl = self.platform.request("sfp_ctl", i)
|
sfp_ctl = self.platform.request("sfp_ctl", i)
|
||||||
|
@ -645,8 +690,10 @@ class LUH(_StandaloneBase):
|
||||||
eem.DIO.add_std(self, 2,
|
eem.DIO.add_std(self, 2,
|
||||||
ttl_serdes_7series.Output_8X, ttl_serdes_7series.Output_8X)
|
ttl_serdes_7series.Output_8X, ttl_serdes_7series.Output_8X)
|
||||||
eem.Sampler.add_std(self, 3, None, 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, 4, None, ttl_serdes_7series.Output_8X,
|
||||||
eem.Urukul.add_std(self, 5, 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)
|
eem.Grabber.add_std(self, 6)
|
||||||
|
|
||||||
for i in (1, 2):
|
for i in (1, 2):
|
||||||
|
@ -688,11 +735,13 @@ class Tester(_StandaloneBase):
|
||||||
self.grabber_csr_group = []
|
self.grabber_csr_group = []
|
||||||
eem.DIO.add_std(self, 5,
|
eem.DIO.add_std(self, 5,
|
||||||
ttl_serdes_7series.InOut_8X, ttl_serdes_7series.Output_8X)
|
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.Sampler.add_std(self, 3, 2, ttl_serdes_7series.Output_8X)
|
||||||
eem.Zotino.add_std(self, 4, ttl_serdes_7series.Output_8X)
|
eem.Zotino.add_std(self, 4, ttl_serdes_7series.Output_8X)
|
||||||
eem.Grabber.add_std(self, 6)
|
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,
|
eem.DIO.add_std(self, 8,
|
||||||
ttl_serdes_7series.Output_8X, ttl_serdes_7series.Output_8X)
|
ttl_serdes_7series.Output_8X, ttl_serdes_7series.Output_8X)
|
||||||
eem.DIO.add_std(self, 9,
|
eem.DIO.add_std(self, 9,
|
||||||
|
@ -1079,8 +1128,8 @@ def main():
|
||||||
soc_kasli_args(parser)
|
soc_kasli_args(parser)
|
||||||
parser.set_defaults(output_dir="artiq_kasli")
|
parser.set_defaults(output_dir="artiq_kasli")
|
||||||
variants = {cls.__name__.lower(): cls for cls in [
|
variants = {cls.__name__.lower(): cls for cls in [
|
||||||
Opticlock, SUServo, SYSU, MITLL, MITLL2, USTC,
|
Opticlock, SUServo, PTB, PTB2, HUB, LUH,
|
||||||
Tsinghua, Tsinghua2, WIPM, NUDT, PTB, HUB, LUH,
|
SYSU, MITLL, MITLL2, USTC, Tsinghua, Tsinghua2, WIPM, NUDT,
|
||||||
VLBAIMaster, VLBAISatellite, Tester, Master, Satellite]}
|
VLBAIMaster, VLBAISatellite, Tester, Master, Satellite]}
|
||||||
parser.add_argument("-V", "--variant", default="opticlock",
|
parser.add_argument("-V", "--variant", default="opticlock",
|
||||||
help="variant: {} (default: %(default)s)".format(
|
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