forked from M-Labs/artiq
added typing and reformatted driver for ad9910, ad9912, and urukul
Signed-off-by: Leon Riesebos <leon.riesebos@duke.edu>
This commit is contained in:
parent
5ba22c11c3
commit
c22f731a61
|
@ -3,15 +3,15 @@ from numpy import int32, int64
|
|||
from artiq.language.core import (
|
||||
kernel, delay, portable, delay_mu, now_mu, at_mu)
|
||||
from artiq.language.units import us, ms
|
||||
from artiq.language.types import *
|
||||
from artiq.language.types import TBool, TInt32, TInt64, TFloat, TList, TTuple
|
||||
|
||||
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",
|
||||
|
@ -20,7 +20,6 @@ __all__ = [
|
|||
"RAM_MODE_CONT_BIDIR_RAMP", "RAM_MODE_CONT_RAMPUP",
|
||||
]
|
||||
|
||||
|
||||
_PHASE_MODE_DEFAULT = -1
|
||||
PHASE_MODE_CONTINUOUS = 0
|
||||
PHASE_MODE_ABSOLUTE = 1
|
||||
|
@ -124,15 +123,15 @@ class AD9910:
|
|||
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`).
|
||||
Can be a string of the form "eeprom_device:byte_offset" to read the value
|
||||
from a I2C EEPROM; in which case, `io_update_delay` must be set to the
|
||||
same string value.
|
||||
Can be a string of the form "eeprom_device:byte_offset" to read the
|
||||
value from a I2C EEPROM; in which case, `io_update_delay` must be set
|
||||
to the same string value.
|
||||
: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.
|
||||
Can be a string of the form "eeprom_device:byte_offset" to read the value
|
||||
from a I2C EEPROM; in which case, `sync_delay_seed` must be set to the
|
||||
same string value.
|
||||
Can be a string of the form "eeprom_device:byte_offset" to read the
|
||||
value from a I2C EEPROM; in which case, `sync_delay_seed` must be set
|
||||
to the same string value.
|
||||
"""
|
||||
kernel_invariants = {"chip_select", "cpld", "core", "bus",
|
||||
"ftw_per_hz", "sysclk_per_mu"}
|
||||
|
@ -169,17 +168,20 @@ class AD9910:
|
|||
self.sysclk_per_mu = int(round(sysclk * self.core.ref_period))
|
||||
self.sysclk = sysclk
|
||||
|
||||
if isinstance(sync_delay_seed, str) or isinstance(io_update_delay, str):
|
||||
if isinstance(sync_delay_seed, str) or isinstance(io_update_delay,
|
||||
str):
|
||||
if sync_delay_seed != io_update_delay:
|
||||
raise ValueError("When using EEPROM, sync_delay_seed must be equal to io_update_delay")
|
||||
raise ValueError("When using EEPROM, sync_delay_seed must be "
|
||||
"equal to io_update_delay")
|
||||
self.sync_data = SyncDataEeprom(dmgr, self.core, sync_delay_seed)
|
||||
else:
|
||||
self.sync_data = SyncDataUser(self.core, sync_delay_seed, io_update_delay)
|
||||
self.sync_data = SyncDataUser(self.core, sync_delay_seed,
|
||||
io_update_delay)
|
||||
|
||||
self.phase_mode = PHASE_MODE_CONTINUOUS
|
||||
|
||||
@kernel
|
||||
def set_phase_mode(self, phase_mode):
|
||||
def set_phase_mode(self, phase_mode: TInt32):
|
||||
r"""Set the default phase mode.
|
||||
|
||||
for future calls to :meth:`set` and
|
||||
|
@ -224,7 +226,7 @@ class AD9910:
|
|||
self.phase_mode = phase_mode
|
||||
|
||||
@kernel
|
||||
def write16(self, addr, data):
|
||||
def write16(self, addr: TInt32, data: TInt32):
|
||||
"""Write to 16 bit register.
|
||||
|
||||
:param addr: Register address
|
||||
|
@ -235,7 +237,7 @@ class AD9910:
|
|||
self.bus.write((addr << 24) | (data << 8))
|
||||
|
||||
@kernel
|
||||
def write32(self, addr, data):
|
||||
def write32(self, addr: TInt32, data: TInt32):
|
||||
"""Write to 32 bit register.
|
||||
|
||||
:param addr: Register address
|
||||
|
@ -249,7 +251,7 @@ class AD9910:
|
|||
self.bus.write(data)
|
||||
|
||||
@kernel
|
||||
def read16(self, addr):
|
||||
def read16(self, addr: TInt32) -> TInt32:
|
||||
"""Read from 16 bit register.
|
||||
|
||||
:param addr: Register address
|
||||
|
@ -264,7 +266,7 @@ class AD9910:
|
|||
return self.bus.read()
|
||||
|
||||
@kernel
|
||||
def read32(self, addr):
|
||||
def read32(self, addr: TInt32) -> TInt32:
|
||||
"""Read from 32 bit register.
|
||||
|
||||
:param addr: Register address
|
||||
|
@ -279,7 +281,7 @@ class AD9910:
|
|||
return self.bus.read()
|
||||
|
||||
@kernel
|
||||
def read64(self, addr):
|
||||
def read64(self, addr: TInt32) -> TInt64:
|
||||
"""Read from 64 bit register.
|
||||
|
||||
:param addr: Register address
|
||||
|
@ -302,7 +304,7 @@ class AD9910:
|
|||
return (int64(hi) << 32) | lo
|
||||
|
||||
@kernel
|
||||
def write64(self, addr, data_high, data_low):
|
||||
def write64(self, addr: TInt32, data_high: TInt32, data_low: TInt32):
|
||||
"""Write to 64 bit register.
|
||||
|
||||
:param addr: Register address
|
||||
|
@ -320,14 +322,14 @@ class AD9910:
|
|||
self.bus.write(data_low)
|
||||
|
||||
@kernel
|
||||
def write_ram(self, data):
|
||||
def write_ram(self, data: TList(int32)):
|
||||
"""Write data to RAM.
|
||||
|
||||
The profile to write to and the step, start, and end address
|
||||
need to be configured before and separately using
|
||||
:meth:`set_profile_ram` and the parent CPLD `set_profile`.
|
||||
|
||||
:param data List(int32): Data to be written to RAM.
|
||||
:param data: Data to be written to RAM.
|
||||
"""
|
||||
self.bus.set_config_mu(urukul.SPI_CONFIG, 8, urukul.SPIT_DDS_WR,
|
||||
self.chip_select)
|
||||
|
@ -341,14 +343,14 @@ class AD9910:
|
|||
self.bus.write(data[len(data) - 1])
|
||||
|
||||
@kernel
|
||||
def read_ram(self, data):
|
||||
def read_ram(self, data: TList(int32)):
|
||||
"""Read data from RAM.
|
||||
|
||||
The profile to read from and the step, start, and end address
|
||||
need to be configured before and separately using
|
||||
:meth:`set_profile_ram` and the parent CPLD `set_profile`.
|
||||
|
||||
:param data List(int32): List to be filled with data read from RAM.
|
||||
:param data: List to be filled with data read from RAM.
|
||||
"""
|
||||
self.bus.set_config_mu(urukul.SPI_CONFIG, 8, urukul.SPIT_DDS_WR,
|
||||
self.chip_select)
|
||||
|
@ -370,10 +372,12 @@ class AD9910:
|
|||
data[(n - preload) + i] = self.bus.read()
|
||||
|
||||
@kernel
|
||||
def set_cfr1(self, power_down=0b0000, phase_autoclear=0,
|
||||
drg_load_lrr=0, drg_autoclear=0,
|
||||
internal_profile=0, ram_destination=0, ram_enable=0,
|
||||
manual_osk_external=0, osk_enable=0, select_auto_osk=0):
|
||||
def set_cfr1(self, power_down: TInt32 = 0b0000,
|
||||
phase_autoclear: TInt32 = 0,
|
||||
drg_load_lrr: TInt32 = 0, drg_autoclear: TInt32 = 0,
|
||||
internal_profile: TInt32 = 0, ram_destination: TInt32 = 0,
|
||||
ram_enable: TInt32 = 0, manual_osk_external: TInt32 = 0,
|
||||
osk_enable: TInt32 = 0, select_auto_osk: TInt32 = 0):
|
||||
"""Set CFR1. See the AD9910 datasheet for parameter meanings.
|
||||
|
||||
This method does not pulse IO_UPDATE.
|
||||
|
@ -405,7 +409,7 @@ class AD9910:
|
|||
2) # SDIO input only, MSB first
|
||||
|
||||
@kernel
|
||||
def init(self, blind=False):
|
||||
def init(self, blind: TBool = False):
|
||||
"""Initialize and configure the DDS.
|
||||
|
||||
Sets up SPI mode, confirms chip presence, powers down unused blocks,
|
||||
|
@ -464,7 +468,7 @@ class AD9910:
|
|||
delay(1 * ms)
|
||||
|
||||
@kernel
|
||||
def power_down(self, bits=0b1111):
|
||||
def power_down(self, bits: TInt32 = 0b1111):
|
||||
"""Power down DDS.
|
||||
|
||||
:param bits: Power down bits, see datasheet
|
||||
|
@ -472,16 +476,15 @@ class AD9910:
|
|||
self.set_cfr1(power_down=bits)
|
||||
self.cpld.io_update.pulse(1 * us)
|
||||
|
||||
# KLUDGE: ref_time_mu default argument is explicitly marked int64() to
|
||||
# avoid silent truncation of explicitly passed timestamps. (Compiler bug?)
|
||||
@kernel
|
||||
def set_mu(self, ftw, pow_=0, asf=0x3fff, phase_mode=_PHASE_MODE_DEFAULT,
|
||||
ref_time_mu=int64(-1), profile=0):
|
||||
def set_mu(self, ftw: TInt32, pow_: TInt32 = 0, asf: TInt32 = 0x3fff,
|
||||
phase_mode: TInt32 = _PHASE_MODE_DEFAULT,
|
||||
ref_time_mu: TInt64 = int64(-1), profile: TInt32 = 0):
|
||||
"""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.
|
||||
scale factor width is 14.
|
||||
|
||||
After the SPI transfer, the shared IO update pin is pulsed to
|
||||
activate the data.
|
||||
|
@ -530,8 +533,9 @@ class AD9910:
|
|||
return pow_
|
||||
|
||||
@kernel
|
||||
def set_profile_ram(self, start, end, step=1, profile=0, nodwell_high=0,
|
||||
zero_crossing=0, mode=1):
|
||||
def set_profile_ram(self, start: TInt32, end: TInt32, step: TInt32 = 1,
|
||||
profile: TInt32 = 0, nodwell_high: TInt32 = 0,
|
||||
zero_crossing: TInt32 = 0, mode: TInt32 = 1):
|
||||
"""Set the RAM profile settings.
|
||||
|
||||
:param start: Profile start address in RAM.
|
||||
|
@ -555,57 +559,60 @@ class AD9910:
|
|||
self.write64(_AD9910_REG_PROFILE0 + profile, hi, lo)
|
||||
|
||||
@kernel
|
||||
def set_ftw(self, ftw):
|
||||
"""Set the value stored to the AD9910's frequency tuning word (FTW) register.
|
||||
def set_ftw(self, ftw: TInt32):
|
||||
"""Set the value stored to the AD9910's frequency tuning word (FTW)
|
||||
register.
|
||||
|
||||
:param ftw: Frequency tuning word to be stored, range: 0 to 0xffffffff.
|
||||
"""
|
||||
self.write32(_AD9910_REG_FTW, ftw)
|
||||
|
||||
@kernel
|
||||
def set_asf(self, asf):
|
||||
"""Set the value stored to the AD9910's amplitude scale factor (ASF) register.
|
||||
def set_asf(self, asf: TInt32):
|
||||
"""Set the value stored to the AD9910's amplitude scale factor (ASF)
|
||||
register.
|
||||
|
||||
:param asf: Amplitude scale factor to be stored, range: 0 to 0x3fff.
|
||||
"""
|
||||
self.write32(_AD9910_REG_ASF, asf << 2)
|
||||
|
||||
@kernel
|
||||
def set_pow(self, pow_):
|
||||
"""Set the value stored to the AD9910's phase offset word (POW) register.
|
||||
def set_pow(self, pow_: TInt32):
|
||||
"""Set the value stored to the AD9910's phase offset word (POW)
|
||||
register.
|
||||
|
||||
:param pow_: Phase offset word to be stored, range: 0 to 0xffff.
|
||||
"""
|
||||
self.write16(_AD9910_REG_POW, pow_)
|
||||
|
||||
@portable(flags={"fast-math"})
|
||||
def frequency_to_ftw(self, frequency) -> TInt32:
|
||||
def frequency_to_ftw(self, frequency: TFloat) -> TInt32:
|
||||
"""Return the 32-bit frequency tuning word corresponding to the given
|
||||
frequency.
|
||||
"""
|
||||
return int32(round(self.ftw_per_hz * frequency))
|
||||
|
||||
@portable(flags={"fast-math"})
|
||||
def ftw_to_frequency(self, ftw):
|
||||
def ftw_to_frequency(self, ftw: TInt32) -> TFloat:
|
||||
"""Return the frequency corresponding to the given frequency tuning
|
||||
word.
|
||||
"""
|
||||
return ftw / self.ftw_per_hz
|
||||
|
||||
@portable(flags={"fast-math"})
|
||||
def turns_to_pow(self, turns) -> TInt32:
|
||||
def turns_to_pow(self, turns: TFloat) -> TInt32:
|
||||
"""Return the 16-bit phase offset word corresponding to the given phase
|
||||
in turns."""
|
||||
return int32(round(turns * 0x10000)) & int32(0xffff)
|
||||
|
||||
@portable(flags={"fast-math"})
|
||||
def pow_to_turns(self, pow_):
|
||||
def pow_to_turns(self, pow_: TInt32) -> TFloat:
|
||||
"""Return the phase in turns corresponding to a given phase offset
|
||||
word."""
|
||||
return pow_ / 0x10000
|
||||
|
||||
@portable(flags={"fast-math"})
|
||||
def amplitude_to_asf(self, amplitude) -> TInt32:
|
||||
def amplitude_to_asf(self, amplitude: TFloat) -> TInt32:
|
||||
"""Return 14-bit amplitude scale factor corresponding to given
|
||||
fractional amplitude."""
|
||||
code = int32(round(amplitude * 0x3fff))
|
||||
|
@ -614,13 +621,13 @@ class AD9910:
|
|||
return code
|
||||
|
||||
@portable(flags={"fast-math"})
|
||||
def asf_to_amplitude(self, asf):
|
||||
def asf_to_amplitude(self, asf: TInt32) -> TFloat:
|
||||
"""Return amplitude as a fraction of full scale corresponding to given
|
||||
amplitude scale factor."""
|
||||
return asf / float(0x3fff)
|
||||
|
||||
@portable(flags={"fast-math"})
|
||||
def frequency_to_ram(self, frequency, ram):
|
||||
def frequency_to_ram(self, frequency: TList(TFloat), ram: TList(TInt32)):
|
||||
"""Convert frequency values to RAM profile data.
|
||||
|
||||
To be used with :const:`RAM_DEST_FTW`.
|
||||
|
@ -633,7 +640,7 @@ class AD9910:
|
|||
ram[i] = self.frequency_to_ftw(frequency[i])
|
||||
|
||||
@portable(flags={"fast-math"})
|
||||
def turns_to_ram(self, turns, ram):
|
||||
def turns_to_ram(self, turns: TList(TFloat), ram: TList(TInt32)):
|
||||
"""Convert phase values to RAM profile data.
|
||||
|
||||
To be used with :const:`RAM_DEST_POW`.
|
||||
|
@ -646,7 +653,7 @@ class AD9910:
|
|||
ram[i] = self.turns_to_pow(turns[i]) << 16
|
||||
|
||||
@portable(flags={"fast-math"})
|
||||
def amplitude_to_ram(self, amplitude, ram):
|
||||
def amplitude_to_ram(self, amplitude: TList(TFloat), ram: TList(TInt32)):
|
||||
"""Convert amplitude values to RAM profile data.
|
||||
|
||||
To be used with :const:`RAM_DEST_ASF`.
|
||||
|
@ -659,7 +666,8 @@ class AD9910:
|
|||
ram[i] = self.amplitude_to_asf(amplitude[i]) << 18
|
||||
|
||||
@portable(flags={"fast-math"})
|
||||
def turns_amplitude_to_ram(self, turns, amplitude, ram):
|
||||
def turns_amplitude_to_ram(self, turns: TList(TFloat),
|
||||
amplitude: TList(TFloat), ram: TList(TInt32)):
|
||||
"""Convert phase and amplitude values to RAM profile data.
|
||||
|
||||
To be used with :const:`RAM_DEST_POWASF`.
|
||||
|
@ -674,32 +682,36 @@ class AD9910:
|
|||
self.amplitude_to_asf(amplitude[i]) << 2)
|
||||
|
||||
@kernel
|
||||
def set_frequency(self, frequency):
|
||||
"""Set the value stored to the AD9910's frequency tuning word (FTW) register.
|
||||
def set_frequency(self, frequency: TFloat):
|
||||
"""Set the value stored to the AD9910's frequency tuning word (FTW)
|
||||
register.
|
||||
|
||||
:param frequency: frequency to be stored, in Hz.
|
||||
"""
|
||||
return self.set_ftw(self.frequency_to_ftw(frequency))
|
||||
self.set_ftw(self.frequency_to_ftw(frequency))
|
||||
|
||||
@kernel
|
||||
def set_amplitude(self, amplitude):
|
||||
"""Set the value stored to the AD9910's amplitude scale factor (ASF) register.
|
||||
def set_amplitude(self, amplitude: TFloat):
|
||||
"""Set the value stored to the AD9910's amplitude scale factor (ASF)
|
||||
register.
|
||||
|
||||
:param amplitude: amplitude to be stored, in units of full scale.
|
||||
"""
|
||||
return self.set_asf(self.amplitude_to_asf(amplitude))
|
||||
self.set_asf(self.amplitude_to_asf(amplitude))
|
||||
|
||||
@kernel
|
||||
def set_phase(self, turns):
|
||||
"""Set the value stored to the AD9910's phase offset word (POW) register.
|
||||
def set_phase(self, turns: TFloat):
|
||||
"""Set the value stored to the AD9910's phase offset word (POW)
|
||||
register.
|
||||
|
||||
:param turns: phase offset to be stored, in turns.
|
||||
"""
|
||||
return self.set_pow(self.turns_to_pow(turns))
|
||||
self.set_pow(self.turns_to_pow(turns))
|
||||
|
||||
@kernel
|
||||
def set(self, frequency, phase=0.0, amplitude=1.0,
|
||||
phase_mode=_PHASE_MODE_DEFAULT, ref_time_mu=int64(-1), profile=0):
|
||||
def set(self, frequency: TFloat, phase: TFloat = 0.0,
|
||||
amplitude: TFloat = 1.0, phase_mode: TInt32 = _PHASE_MODE_DEFAULT,
|
||||
ref_time_mu: TInt64 = int64(-1), profile: TInt32 = 0):
|
||||
"""Set profile 0 data in SI units.
|
||||
|
||||
.. seealso:: :meth:`set_mu`
|
||||
|
@ -718,7 +730,7 @@ class AD9910:
|
|||
profile))
|
||||
|
||||
@kernel
|
||||
def set_att_mu(self, att):
|
||||
def set_att_mu(self, att: TInt32):
|
||||
"""Set digital step attenuator in machine units.
|
||||
|
||||
This method will write the attenuator settings of all four channels.
|
||||
|
@ -730,7 +742,7 @@ class AD9910:
|
|||
self.cpld.set_att_mu(self.chip_select - 4, att)
|
||||
|
||||
@kernel
|
||||
def set_att(self, att):
|
||||
def set_att(self, att: TFloat):
|
||||
"""Set digital step attenuator in SI units.
|
||||
|
||||
This method will write the attenuator settings of all four channels.
|
||||
|
@ -742,7 +754,7 @@ class AD9910:
|
|||
self.cpld.set_att(self.chip_select - 4, att)
|
||||
|
||||
@kernel
|
||||
def cfg_sw(self, state):
|
||||
def cfg_sw(self, state: TInt32):
|
||||
"""Set CPLD CFG RF switch state. The RF switch is controlled by the
|
||||
logical or of the CPLD configuration shift register
|
||||
RF switch bit and the SW TTL line (if used).
|
||||
|
@ -752,7 +764,7 @@ class AD9910:
|
|||
self.cpld.cfg_sw(self.chip_select - 4, state)
|
||||
|
||||
@kernel
|
||||
def set_sync(self, in_delay, window):
|
||||
def set_sync(self, in_delay: TInt32, window: TInt32):
|
||||
"""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
|
||||
|
@ -787,7 +799,8 @@ class AD9910:
|
|||
self.cpld.io_update.pulse(1 * us)
|
||||
|
||||
@kernel
|
||||
def tune_sync_delay(self, search_seed=15):
|
||||
def tune_sync_delay(self,
|
||||
search_seed: TInt32 = 15) -> TTuple([TInt32, TInt32]):
|
||||
"""Find a stable SYNC_IN delay.
|
||||
|
||||
This method first locates a valid SYNC_IN delay at zero validation
|
||||
|
@ -843,7 +856,8 @@ class AD9910:
|
|||
raise ValueError("no valid window/delay")
|
||||
|
||||
@kernel
|
||||
def measure_io_update_alignment(self, delay_start, delay_stop):
|
||||
def measure_io_update_alignment(self, delay_start: TInt64,
|
||||
delay_stop: TInt64) -> TInt32:
|
||||
"""Use the digital ramp generator to locate the alignment between
|
||||
IO_UPDATE and SYNC_CLK.
|
||||
|
||||
|
@ -885,7 +899,7 @@ class AD9910:
|
|||
return ftw & 1
|
||||
|
||||
@kernel
|
||||
def tune_io_update_delay(self):
|
||||
def tune_io_update_delay(self) -> TInt32:
|
||||
"""Find a stable IO_UPDATE delay alignment.
|
||||
|
||||
Scan through increasing IO_UPDATE delays until a delay is found that
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
from numpy import int32, int64
|
||||
|
||||
from artiq.language.types import TInt32, TInt64
|
||||
from artiq.language.types import TInt32, TInt64, TFloat, TTuple
|
||||
from artiq.language.core import kernel, delay, portable
|
||||
from artiq.language.units import ms, us, ns
|
||||
from artiq.coredevice.ad9912_reg import *
|
||||
|
@ -44,7 +44,7 @@ class AD9912:
|
|||
self.ftw_per_hz = 1 / sysclk * (int64(1) << 48)
|
||||
|
||||
@kernel
|
||||
def write(self, addr, data, length):
|
||||
def write(self, addr: TInt32, data: TInt32, length: TInt32):
|
||||
"""Variable length write to a register.
|
||||
Up to 4 bytes.
|
||||
|
||||
|
@ -62,7 +62,7 @@ class AD9912:
|
|||
self.bus.write(data << (32 - length * 8))
|
||||
|
||||
@kernel
|
||||
def read(self, addr, length):
|
||||
def read(self, addr: TInt32, length: TInt32) -> TInt32:
|
||||
"""Variable length read from a register.
|
||||
Up to 4 bytes.
|
||||
|
||||
|
@ -111,7 +111,7 @@ class AD9912:
|
|||
delay(1 * ms)
|
||||
|
||||
@kernel
|
||||
def set_att_mu(self, att):
|
||||
def set_att_mu(self, att: TInt32):
|
||||
"""Set digital step attenuator in machine units.
|
||||
|
||||
This method will write the attenuator settings of all four channels.
|
||||
|
@ -123,7 +123,7 @@ class AD9912:
|
|||
self.cpld.set_att_mu(self.chip_select - 4, att)
|
||||
|
||||
@kernel
|
||||
def set_att(self, att):
|
||||
def set_att(self, att: TFloat):
|
||||
"""Set digital step attenuator in SI units.
|
||||
|
||||
This method will write the attenuator settings of all four channels.
|
||||
|
@ -135,14 +135,14 @@ class AD9912:
|
|||
self.cpld.set_att(self.chip_select - 4, att)
|
||||
|
||||
@kernel
|
||||
def set_mu(self, ftw, pow):
|
||||
def set_mu(self, ftw: TInt64, pow_: TInt32):
|
||||
"""Set profile 0 data in machine units.
|
||||
|
||||
After the SPI transfer, the shared IO update pin is pulsed to
|
||||
activate the data.
|
||||
|
||||
:param ftw: Frequency tuning word: 48 bit unsigned.
|
||||
:param pow: Phase tuning word: 16 bit unsigned.
|
||||
:param pow_: Phase tuning word: 16 bit unsigned.
|
||||
"""
|
||||
# streaming transfer of FTW and POW
|
||||
self.bus.set_config_mu(urukul.SPI_CONFIG, 16,
|
||||
|
@ -150,47 +150,58 @@ class AD9912:
|
|||
self.bus.write((AD9912_POW1 << 16) | (3 << 29))
|
||||
self.bus.set_config_mu(urukul.SPI_CONFIG, 32,
|
||||
urukul.SPIT_DDS_WR, self.chip_select)
|
||||
self.bus.write((pow << 16) | (int32(ftw >> 32) & 0xffff))
|
||||
self.bus.write((pow_ << 16) | (int32(ftw >> 32) & 0xffff))
|
||||
self.bus.set_config_mu(urukul.SPI_CONFIG | spi.SPI_END, 32,
|
||||
urukul.SPIT_DDS_WR, self.chip_select)
|
||||
self.bus.write(int32(ftw))
|
||||
self.cpld.io_update.pulse(10 * ns)
|
||||
|
||||
@portable(flags={"fast-math"})
|
||||
def frequency_to_ftw(self, frequency) -> TInt64:
|
||||
def frequency_to_ftw(self, frequency: TFloat) -> TInt64:
|
||||
"""Returns the 48-bit frequency tuning word corresponding to the given
|
||||
frequency.
|
||||
"""
|
||||
return int64(round(self.ftw_per_hz*frequency)) & ((int64(1) << 48) - 1)
|
||||
return int64(round(self.ftw_per_hz * frequency)) & (
|
||||
(int64(1) << 48) - 1)
|
||||
|
||||
@portable(flags={"fast-math"})
|
||||
def ftw_to_frequency(self, ftw):
|
||||
def ftw_to_frequency(self, ftw: TInt64) -> TFloat:
|
||||
"""Returns the frequency corresponding to the given
|
||||
frequency tuning word.
|
||||
"""
|
||||
return ftw / self.ftw_per_hz
|
||||
|
||||
@portable(flags={"fast-math"})
|
||||
def turns_to_pow(self, phase) -> TInt32:
|
||||
def turns_to_pow(self, phase: TFloat) -> TInt32:
|
||||
"""Returns the 16-bit phase offset word corresponding to the given
|
||||
phase.
|
||||
"""
|
||||
return int32(round((1 << 14) * phase)) & 0xffff
|
||||
|
||||
@portable(flags={"fast-math"})
|
||||
def pow_to_turns(self, pow_: TInt32) -> TFloat:
|
||||
"""Return the phase in turns corresponding to a given phase offset
|
||||
word.
|
||||
|
||||
:param pow_: Phase offset word.
|
||||
:return: Phase in turns.
|
||||
"""
|
||||
return pow_ / (1 << 14)
|
||||
|
||||
@kernel
|
||||
def set(self, frequency, phase=0.0):
|
||||
def set(self, frequency: TFloat, phase: TFloat = 0.0):
|
||||
"""Set profile 0 data in SI units.
|
||||
|
||||
.. seealso:: :meth:`set_mu`
|
||||
|
||||
:param ftw: Frequency in Hz
|
||||
:param pow: Phase tuning word in turns
|
||||
:param frequency: Frequency in Hz
|
||||
:param phase: Phase tuning word in turns
|
||||
"""
|
||||
self.set_mu(self.frequency_to_ftw(frequency),
|
||||
self.turns_to_pow(phase))
|
||||
|
||||
@kernel
|
||||
def cfg_sw(self, state):
|
||||
def cfg_sw(self, state: TInt32):
|
||||
"""Set CPLD CFG RF switch state. The RF switch is controlled by the
|
||||
logical or of the CPLD configuration shift register
|
||||
RF switch bit and the SW TTL line (if used).
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
from artiq.language.core import kernel, delay, portable, at_mu, now_mu
|
||||
from artiq.language.units import us, ms
|
||||
|
||||
from numpy import int32, int64
|
||||
|
||||
from artiq.coredevice import spi2 as spi
|
||||
from artiq.language.core import kernel, delay, portable, at_mu, now_mu
|
||||
from artiq.language.units import us, ms
|
||||
from artiq.language.types import TInt32, TFloat, TBool
|
||||
|
||||
from artiq.coredevice import spi2 as spi
|
||||
|
||||
SPI_CONFIG = (0 * spi.SPI_OFFLINE | 0 * spi.SPI_END |
|
||||
0 * spi.SPI_INPUT | 1 * spi.SPI_CS_POLARITY |
|
||||
|
@ -105,7 +105,7 @@ class _RegIOUpdate:
|
|||
self.cpld = cpld
|
||||
|
||||
@kernel
|
||||
def pulse(self, t):
|
||||
def pulse(self, t: TFloat):
|
||||
cfg = self.cpld.cfg_reg
|
||||
self.cpld.cfg_write(cfg | (1 << CFG_IO_UPDATE))
|
||||
delay(t)
|
||||
|
@ -117,7 +117,7 @@ class _DummySync:
|
|||
self.cpld = cpld
|
||||
|
||||
@kernel
|
||||
def set_mu(self, ftw):
|
||||
def set_mu(self, ftw: TInt32):
|
||||
pass
|
||||
|
||||
|
||||
|
@ -196,12 +196,12 @@ class CPLD:
|
|||
self.sync_div = sync_div
|
||||
|
||||
@kernel
|
||||
def cfg_write(self, cfg):
|
||||
def cfg_write(self, cfg: TInt32):
|
||||
"""Write to the configuration register.
|
||||
|
||||
See :func:`urukul_cfg` for possible flags.
|
||||
|
||||
:param data: 24 bit data to be written. Will be stored at
|
||||
:param cfg: 24 bit data to be written. Will be stored at
|
||||
:attr:`cfg_reg`.
|
||||
"""
|
||||
self.bus.set_config_mu(SPI_CONFIG | spi.SPI_END, 24,
|
||||
|
@ -210,7 +210,7 @@ class CPLD:
|
|||
self.cfg_reg = cfg
|
||||
|
||||
@kernel
|
||||
def sta_read(self):
|
||||
def sta_read(self) -> TInt32:
|
||||
"""Read the status register.
|
||||
|
||||
Use any of the following functions to extract values:
|
||||
|
@ -229,7 +229,7 @@ class CPLD:
|
|||
return self.bus.read()
|
||||
|
||||
@kernel
|
||||
def init(self, blind=False):
|
||||
def init(self, blind: TBool = False):
|
||||
"""Initialize and detect Urukul.
|
||||
|
||||
Resets the DDS I/O interface and verifies correct CPLD gateware
|
||||
|
@ -261,7 +261,7 @@ class CPLD:
|
|||
self.cfg_write(self.cfg_reg & ~(1 << CFG_IO_RST))
|
||||
|
||||
@kernel
|
||||
def cfg_sw(self, channel, on):
|
||||
def cfg_sw(self, channel: TInt32, on: TBool):
|
||||
"""Configure the RF switches through the configuration register.
|
||||
|
||||
These values are logically OR-ed with the LVDS lines on EEM1.
|
||||
|
@ -277,7 +277,7 @@ class CPLD:
|
|||
self.cfg_write(c)
|
||||
|
||||
@kernel
|
||||
def cfg_switches(self, state):
|
||||
def cfg_switches(self, state: TInt32):
|
||||
"""Configure all four RF switches through the configuration register.
|
||||
|
||||
:param state: RF switch state as a 4 bit integer.
|
||||
|
@ -285,11 +285,12 @@ class CPLD:
|
|||
self.cfg_write((self.cfg_reg & ~0xf) | state)
|
||||
|
||||
@kernel
|
||||
def set_att_mu(self, channel, att):
|
||||
def set_att_mu(self, channel: TInt32, att: TInt32):
|
||||
"""Set digital step attenuator in machine units.
|
||||
|
||||
This method will also write the attenuator settings of the three other channels. Use
|
||||
:meth:`get_att_mu` to retrieve the hardware state set in previous experiments.
|
||||
This method will also write the attenuator settings of the three
|
||||
other channels. Use :meth:`get_att_mu` to retrieve the hardware
|
||||
state set in previous experiments.
|
||||
|
||||
:param channel: Attenuator channel (0-3).
|
||||
:param att: 8-bit digital attenuation setting:
|
||||
|
@ -300,7 +301,7 @@ class CPLD:
|
|||
self.set_all_att_mu(a)
|
||||
|
||||
@kernel
|
||||
def set_all_att_mu(self, att_reg):
|
||||
def set_all_att_mu(self, att_reg: TInt32):
|
||||
"""Set all four digital step attenuators (in machine units).
|
||||
|
||||
.. seealso:: :meth:`set_att_mu`
|
||||
|
@ -313,7 +314,7 @@ class CPLD:
|
|||
self.att_reg = att_reg
|
||||
|
||||
@kernel
|
||||
def set_att(self, channel, att):
|
||||
def set_att(self, channel: TInt32, att: TFloat):
|
||||
"""Set digital step attenuator in SI units.
|
||||
|
||||
This method will write the attenuator settings of all four channels.
|
||||
|
@ -325,16 +326,16 @@ class CPLD:
|
|||
attenuation. Minimum attenuation is 0*dB, maximum attenuation is
|
||||
31.5*dB.
|
||||
"""
|
||||
code = 255 - int32(round(att*8))
|
||||
if code < 0 or code > 255:
|
||||
raise ValueError("Invalid urukul.CPLD attenuation!")
|
||||
self.set_att_mu(channel, code)
|
||||
self.set_att_mu(channel, self.att_to_mu(att))
|
||||
|
||||
@kernel
|
||||
def get_att_mu(self):
|
||||
def get_att_mu(self) -> TInt32:
|
||||
"""Return the digital step attenuator settings in machine units.
|
||||
|
||||
The result is stored and will be used in future calls of :meth:`set_att_mu`.
|
||||
The result is stored and will be used in future calls of
|
||||
:meth:`set_att_mu` and :meth:`set_att`.
|
||||
|
||||
.. seealso:: :meth:`get_channel_att_mu`
|
||||
|
||||
:return: 32 bit attenuator settings
|
||||
"""
|
||||
|
@ -349,7 +350,7 @@ class CPLD:
|
|||
return self.att_reg
|
||||
|
||||
@kernel
|
||||
def set_sync_div(self, div):
|
||||
def set_sync_div(self, div: TInt32):
|
||||
"""Set the SYNC_IN AD9910 pulse generator frequency
|
||||
and align it to the current RTIO timestamp.
|
||||
|
||||
|
@ -366,7 +367,7 @@ class CPLD:
|
|||
self.sync.set_mu(ftw)
|
||||
|
||||
@kernel
|
||||
def set_profile(self, profile):
|
||||
def set_profile(self, profile: TInt32):
|
||||
"""Set the PROFILE pins.
|
||||
|
||||
The PROFILE pins are common to all four DDS channels.
|
||||
|
|
Loading…
Reference in New Issue