forked from M-Labs/artiq
add get_*() functions to ad9910, ad9912, and urukul. closes #1616
Signed-off-by: Leon Riesebos <leon.riesebos@duke.edu>
This commit is contained in:
parent
c22f731a61
commit
d04bcd8754
|
@ -8,6 +8,7 @@ ARTIQ-7
|
|||
|
||||
Highlights:
|
||||
* WRPLL
|
||||
* ``get()``, ``get_mu()``, ``get_att()``, and ``get_att_mu()`` functions added for AD9910 and AD9912
|
||||
|
||||
Breaking changes:
|
||||
|
||||
|
|
|
@ -532,6 +532,25 @@ class AD9910:
|
|||
# future IO_UPDATE will activate
|
||||
return pow_
|
||||
|
||||
@kernel
|
||||
def get_mu(self, profile: TInt32 = 0) -> TTuple([TInt32, TInt32, TInt32]):
|
||||
"""Get the frequency tuning word, phase offset word,
|
||||
and amplitude scale factor.
|
||||
|
||||
.. seealso:: :meth:`get`
|
||||
|
||||
:param profile: Profile number to get (0-7, default: 0)
|
||||
:return: A tuple ``(ftw, pow, asf)``
|
||||
"""
|
||||
|
||||
# Read data
|
||||
data = int64(self.read64(_AD9910_REG_PROFILE0 + profile))
|
||||
# Extract and return fields
|
||||
ftw = int32(data)
|
||||
pow_ = int32((data >> 32) & 0xffff)
|
||||
asf = int32((data >> 48) & 0x3fff)
|
||||
return ftw, pow_, asf
|
||||
|
||||
@kernel
|
||||
def set_profile_ram(self, start: TInt32, end: TInt32, step: TInt32 = 1,
|
||||
profile: TInt32 = 0, nodwell_high: TInt32 = 0,
|
||||
|
@ -585,6 +604,33 @@ class AD9910:
|
|||
"""
|
||||
self.write16(_AD9910_REG_POW, pow_)
|
||||
|
||||
@kernel
|
||||
def get_ftw(self) -> TInt32:
|
||||
"""Get the value stored to the AD9910's frequency tuning word (FTW)
|
||||
register.
|
||||
|
||||
:return: Frequency tuning word
|
||||
"""
|
||||
return self.read32(_AD9910_REG_FTW)
|
||||
|
||||
@kernel
|
||||
def get_asf(self) -> TInt32:
|
||||
"""Get the value stored to the AD9910's amplitude scale factor (ASF)
|
||||
register.
|
||||
|
||||
:return: Amplitude scale factor
|
||||
"""
|
||||
return self.read32(_AD9910_REG_ASF) >> 2
|
||||
|
||||
@kernel
|
||||
def get_pow(self) -> TInt32:
|
||||
"""Get the value stored to the AD9910's phase offset word (POW)
|
||||
register.
|
||||
|
||||
:return: Phase offset word
|
||||
"""
|
||||
return self.read16(_AD9910_REG_POW)
|
||||
|
||||
@portable(flags={"fast-math"})
|
||||
def frequency_to_ftw(self, frequency: TFloat) -> TInt32:
|
||||
"""Return the 32-bit frequency tuning word corresponding to the given
|
||||
|
@ -708,6 +754,33 @@ class AD9910:
|
|||
"""
|
||||
self.set_pow(self.turns_to_pow(turns))
|
||||
|
||||
@kernel
|
||||
def get_frequency(self) -> TFloat:
|
||||
"""Get the value stored to the AD9910's frequency tuning word (FTW)
|
||||
register.
|
||||
|
||||
:return: frequency in Hz.
|
||||
"""
|
||||
return self.ftw_to_frequency(self.get_ftw())
|
||||
|
||||
@kernel
|
||||
def get_amplitude(self) -> TFloat:
|
||||
"""Get the value stored to the AD9910's amplitude scale factor (ASF)
|
||||
register.
|
||||
|
||||
:return: amplitude in units of full scale.
|
||||
"""
|
||||
return self.asf_to_amplitude(self.get_asf())
|
||||
|
||||
@kernel
|
||||
def get_phase(self) -> TFloat:
|
||||
"""Get the value stored to the AD9910's phase offset word (POW)
|
||||
register.
|
||||
|
||||
:return: phase offset in turns.
|
||||
"""
|
||||
return self.pow_to_turns(self.get_pow())
|
||||
|
||||
@kernel
|
||||
def set(self, frequency: TFloat, phase: TFloat = 0.0,
|
||||
amplitude: TFloat = 1.0, phase_mode: TInt32 = _PHASE_MODE_DEFAULT,
|
||||
|
@ -729,6 +802,22 @@ class AD9910:
|
|||
self.amplitude_to_asf(amplitude), phase_mode, ref_time_mu,
|
||||
profile))
|
||||
|
||||
@kernel
|
||||
def get(self, profile: TInt32 = 0) -> TTuple([TFloat, TFloat, TFloat]):
|
||||
"""Get the frequency, phase, and amplitude.
|
||||
|
||||
.. seealso:: :meth:`get_mu`
|
||||
|
||||
:param profile: Profile number to get (0-7, default: 0)
|
||||
:return: A tuple ``(frequency, phase, amplitude)``
|
||||
"""
|
||||
|
||||
# Get values
|
||||
ftw, pow_, asf = self.get_mu(profile)
|
||||
# Convert and return
|
||||
return (self.ftw_to_frequency(ftw), self.pow_to_turns(pow_),
|
||||
self.asf_to_amplitude(asf))
|
||||
|
||||
@kernel
|
||||
def set_att_mu(self, att: TInt32):
|
||||
"""Set digital step attenuator in machine units.
|
||||
|
@ -753,6 +842,26 @@ class AD9910:
|
|||
"""
|
||||
self.cpld.set_att(self.chip_select - 4, att)
|
||||
|
||||
@kernel
|
||||
def get_att_mu(self) -> TInt32:
|
||||
"""Get digital step attenuator value in machine units.
|
||||
|
||||
.. seealso:: :meth:`artiq.coredevice.urukul.CPLD.get_channel_att_mu`
|
||||
|
||||
:return: Attenuation setting, 8 bit digital.
|
||||
"""
|
||||
return self.cpld.get_channel_att_mu(self.chip_select - 4)
|
||||
|
||||
@kernel
|
||||
def get_att(self) -> TFloat:
|
||||
"""Get digital step attenuator value in SI units.
|
||||
|
||||
.. seealso:: :meth:`artiq.coredevice.urukul.CPLD.get_channel_att`
|
||||
|
||||
:return: Attenuation in dB.
|
||||
"""
|
||||
return self.cpld.get_channel_att(self.chip_select - 4)
|
||||
|
||||
@kernel
|
||||
def cfg_sw(self, state: TInt32):
|
||||
"""Set CPLD CFG RF switch state. The RF switch is controlled by the
|
||||
|
|
|
@ -134,6 +134,26 @@ class AD9912:
|
|||
"""
|
||||
self.cpld.set_att(self.chip_select - 4, att)
|
||||
|
||||
@kernel
|
||||
def get_att_mu(self) -> TInt32:
|
||||
"""Get digital step attenuator value in machine units.
|
||||
|
||||
.. seealso:: :meth:`artiq.coredevice.urukul.CPLD.get_channel_att_mu`
|
||||
|
||||
:return: Attenuation setting, 8 bit digital.
|
||||
"""
|
||||
return self.cpld.get_channel_att_mu(self.chip_select - 4)
|
||||
|
||||
@kernel
|
||||
def get_att(self) -> TFloat:
|
||||
"""Get digital step attenuator value in SI units.
|
||||
|
||||
.. seealso:: :meth:`artiq.coredevice.urukul.CPLD.get_channel_att`
|
||||
|
||||
:return: Attenuation in dB.
|
||||
"""
|
||||
return self.cpld.get_channel_att(self.chip_select - 4)
|
||||
|
||||
@kernel
|
||||
def set_mu(self, ftw: TInt64, pow_: TInt32):
|
||||
"""Set profile 0 data in machine units.
|
||||
|
@ -156,6 +176,24 @@ class AD9912:
|
|||
self.bus.write(int32(ftw))
|
||||
self.cpld.io_update.pulse(10 * ns)
|
||||
|
||||
@kernel
|
||||
def get_mu(self) -> TTuple([TInt64, TInt32]):
|
||||
"""Get the frequency tuning word and phase offset word.
|
||||
|
||||
.. seealso:: :meth:`get`
|
||||
|
||||
:return: A tuple ``(ftw, pow)``.
|
||||
"""
|
||||
|
||||
# Read data
|
||||
high = self.read(AD9912_POW1, 4)
|
||||
self.core.break_realtime() # Regain slack to perform second read
|
||||
low = self.read(AD9912_FTW3, 4)
|
||||
# Extract and return fields
|
||||
ftw = (int64(high & 0xffff) << 32) | (int64(low) & int64(0xffffffff))
|
||||
pow_ = (high >> 16) & 0x3fff
|
||||
return ftw, pow_
|
||||
|
||||
@portable(flags={"fast-math"})
|
||||
def frequency_to_ftw(self, frequency: TFloat) -> TInt64:
|
||||
"""Returns the 48-bit frequency tuning word corresponding to the given
|
||||
|
@ -200,6 +238,20 @@ class AD9912:
|
|||
self.set_mu(self.frequency_to_ftw(frequency),
|
||||
self.turns_to_pow(phase))
|
||||
|
||||
@kernel
|
||||
def get(self) -> TTuple([TFloat, TFloat]):
|
||||
"""Get the frequency and phase.
|
||||
|
||||
.. seealso:: :meth:`get_mu`
|
||||
|
||||
:return: A tuple ``(frequency, phase)``.
|
||||
"""
|
||||
|
||||
# Get values
|
||||
ftw, pow_ = self.get_mu()
|
||||
# Convert and return
|
||||
return self.ftw_to_frequency(ftw), self.pow_to_turns(pow_)
|
||||
|
||||
@kernel
|
||||
def cfg_sw(self, state: TInt32):
|
||||
"""Set CPLD CFG RF switch state. The RF switch is controlled by the
|
||||
|
|
|
@ -284,6 +284,27 @@ class CPLD:
|
|||
"""
|
||||
self.cfg_write((self.cfg_reg & ~0xf) | state)
|
||||
|
||||
@portable(flags={"fast-math"})
|
||||
def mu_to_att(self, att_mu: TInt32) -> TFloat:
|
||||
"""Convert a digital attenuation setting to dB.
|
||||
|
||||
:param att_mu: Digital attenuation setting.
|
||||
:return: Attenuation setting in dB.
|
||||
"""
|
||||
return (255 - (att_mu & 0xff)) / 8
|
||||
|
||||
@portable(flags={"fast-math"})
|
||||
def att_to_mu(self, att: TFloat) -> TInt32:
|
||||
"""Convert an attenuation setting in dB to machine units.
|
||||
|
||||
:param att: Attenuation setting in dB.
|
||||
:return: Digital attenuation setting.
|
||||
"""
|
||||
code = int32(255) - int32(round(att * 8))
|
||||
if code < 0 or code > 255:
|
||||
raise ValueError("Invalid urukul.CPLD attenuation!")
|
||||
return code
|
||||
|
||||
@kernel
|
||||
def set_att_mu(self, channel: TInt32, att: TInt32):
|
||||
"""Set digital step attenuator in machine units.
|
||||
|
@ -349,6 +370,34 @@ class CPLD:
|
|||
self.bus.write(self.att_reg) # shift in current value again and latch
|
||||
return self.att_reg
|
||||
|
||||
@kernel
|
||||
def get_channel_att_mu(self, channel: TInt32) -> TInt32:
|
||||
"""Get digital step attenuator value for a channel in machine units.
|
||||
|
||||
The result is stored and will be used in future calls of
|
||||
:meth:`set_att_mu` and :meth:`set_att`.
|
||||
|
||||
.. seealso:: :meth:`get_att_mu`
|
||||
|
||||
:param channel: Attenuator channel (0-3).
|
||||
:return: 8-bit digital attenuation setting:
|
||||
255 minimum attenuation, 0 maximum attenuation (31.5 dB)
|
||||
"""
|
||||
return int32((self.get_att_mu() >> (channel * 8)) & 0xff)
|
||||
|
||||
@kernel
|
||||
def get_channel_att(self, channel: TInt32) -> TFloat:
|
||||
"""Get digital step attenuator value for a channel in SI units.
|
||||
|
||||
.. seealso:: :meth:`get_channel_att_mu`
|
||||
|
||||
:param channel: Attenuator channel (0-3).
|
||||
:return: Attenuation setting in dB. Higher value is more
|
||||
attenuation. Minimum attenuation is 0*dB, maximum attenuation is
|
||||
31.5*dB.
|
||||
"""
|
||||
return self.mu_to_att(self.get_channel_att_mu(channel))
|
||||
|
||||
@kernel
|
||||
def set_sync_div(self, div: TInt32):
|
||||
"""Set the SYNC_IN AD9910 pulse generator frequency
|
||||
|
|
|
@ -42,11 +42,52 @@ class AD9910Exp(EnvExperiment):
|
|||
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)
|
||||
p = .33
|
||||
a = .89
|
||||
att = 20*dB
|
||||
self.dev.set_att(att)
|
||||
self.dev.set(frequency=f, phase=p, amplitude=a)
|
||||
|
||||
self.core.break_realtime()
|
||||
ftw, pow_, asf = self.dev.get_mu()
|
||||
self.core.break_realtime()
|
||||
att_mu = self.dev.get_att_mu()
|
||||
|
||||
self.set_dataset("ftw_set", self.dev.frequency_to_ftw(f))
|
||||
self.set_dataset("ftw_get", self.dev.read32(_AD9910_REG_FTW))
|
||||
self.set_dataset("ftw_get", ftw)
|
||||
self.set_dataset("pow_set", self.dev.turns_to_pow(p))
|
||||
self.set_dataset("pow_get", pow_)
|
||||
self.set_dataset("asf_set", self.dev.amplitude_to_asf(a))
|
||||
self.set_dataset("asf_get", asf)
|
||||
self.set_dataset("att_set", self.dev.cpld.att_to_mu(att))
|
||||
self.set_dataset("att_get", att_mu)
|
||||
|
||||
@kernel
|
||||
def set_get_io_update_regs(self):
|
||||
self.core.break_realtime()
|
||||
self.dev.cpld.init()
|
||||
self.dev.init()
|
||||
f = 81.2345*MHz
|
||||
p = .33
|
||||
a = .89
|
||||
self.dev.set_frequency(f)
|
||||
self.dev.set_phase(p)
|
||||
self.dev.set_amplitude(a)
|
||||
|
||||
self.core.break_realtime()
|
||||
ftw = self.dev.get_ftw()
|
||||
self.core.break_realtime()
|
||||
pow_ = self.dev.get_pow()
|
||||
self.core.break_realtime()
|
||||
asf = self.dev.get_asf()
|
||||
|
||||
self.set_dataset("ftw_set", self.dev.frequency_to_ftw(f))
|
||||
self.set_dataset("ftw_get", ftw)
|
||||
self.set_dataset("pow_set", self.dev.turns_to_pow(p))
|
||||
self.set_dataset("pow_get", pow_)
|
||||
self.set_dataset("asf_set", self.dev.amplitude_to_asf(a))
|
||||
self.set_dataset("asf_get", asf)
|
||||
|
||||
@kernel
|
||||
def read_write64(self):
|
||||
|
@ -316,9 +357,19 @@ class AD9910Test(ExperimentCase):
|
|||
|
||||
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)
|
||||
for attr in ['ftw', 'pow', 'asf', 'att']:
|
||||
with self.subTest(attribute=attr):
|
||||
get = self.dataset_mgr.get("{}_get".format(attr))
|
||||
set_ = self.dataset_mgr.get("{}_set".format(attr))
|
||||
self.assertEqual(get, set_)
|
||||
|
||||
def test_set_get_io_update_regs(self):
|
||||
self.execute(AD9910Exp, "set_get_io_update_regs")
|
||||
for attr in ['ftw', 'pow', 'asf']:
|
||||
with self.subTest(attribute=attr):
|
||||
get = self.dataset_mgr.get("{}_get".format(attr))
|
||||
set_ = self.dataset_mgr.get("{}_set".format(attr))
|
||||
self.assertEqual(get, set_)
|
||||
|
||||
def test_read_write64(self):
|
||||
self.execute(AD9910Exp, "read_write64")
|
||||
|
|
|
@ -53,7 +53,7 @@ class UrukulExp(EnvExperiment):
|
|||
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)
|
||||
self.core.get_rtio_counter_mu() - t0) / n)
|
||||
|
||||
@kernel
|
||||
def switches_readback(self):
|
||||
|
@ -101,6 +101,28 @@ class UrukulExp(EnvExperiment):
|
|||
self.set_dataset("att_get", att_get)
|
||||
self.set_dataset("att_reg", att_reg)
|
||||
|
||||
@kernel
|
||||
def att_channel_get(self):
|
||||
self.core.break_realtime()
|
||||
self.dev.init()
|
||||
# clear backing state
|
||||
self.dev.att_reg = 0
|
||||
att_set = [int32(0x21), int32(0x43),
|
||||
int32(0x65), int32(0x87)]
|
||||
# set individual attenuators
|
||||
for i in range(len(att_set)):
|
||||
self.dev.set_att_mu(i, att_set[i])
|
||||
# confirm that we can set all attenuators and read back
|
||||
att_get = [0 for _ in range(len(att_set))]
|
||||
for i in range(len(att_set)):
|
||||
self.core.break_realtime()
|
||||
att_get[i] = self.dev.get_channel_att_mu(i)
|
||||
# confirm backing state
|
||||
att_reg = self.dev.att_reg
|
||||
self.set_dataset("att_set", att_set)
|
||||
self.set_dataset("att_get", att_get)
|
||||
self.set_dataset("att_reg", att_reg)
|
||||
|
||||
@kernel
|
||||
def att_speed(self):
|
||||
self.core.break_realtime()
|
||||
|
@ -108,9 +130,9 @@ class UrukulExp(EnvExperiment):
|
|||
n = 10
|
||||
t0 = self.core.get_rtio_counter_mu()
|
||||
for i in range(n):
|
||||
self.dev.set_att(3, 30*dB)
|
||||
self.dev.set_att(3, 30 * dB)
|
||||
self.set_dataset("dt", self.core.mu_to_seconds(
|
||||
self.core.get_rtio_counter_mu() - t0)/n)
|
||||
self.core.get_rtio_counter_mu() - t0) / n)
|
||||
|
||||
@kernel
|
||||
def io_update(self):
|
||||
|
@ -155,7 +177,7 @@ class UrukulTest(ExperimentCase):
|
|||
self.execute(UrukulExp, "switch_speed")
|
||||
dt = self.dataset_mgr.get("dt")
|
||||
print(dt)
|
||||
self.assertLess(dt, 5*us)
|
||||
self.assertLess(dt, 5 * us)
|
||||
|
||||
def test_switches_readback(self):
|
||||
self.execute(UrukulExp, "switches_readback")
|
||||
|
@ -175,11 +197,20 @@ class UrukulTest(ExperimentCase):
|
|||
self.assertEqual(att_set, self.dataset_mgr.get("att_get"))
|
||||
self.assertEqual(att_set, self.dataset_mgr.get("att_reg"))
|
||||
|
||||
def test_att_channel_get(self):
|
||||
self.execute(UrukulExp, "att_channel_get")
|
||||
att_set = self.dataset_mgr.get("att_set")
|
||||
self.assertListEqual(att_set, self.dataset_mgr.get("att_get"))
|
||||
att_reg = self.dataset_mgr.get("att_reg")
|
||||
for att in att_set:
|
||||
self.assertEqual(att, att_reg & 0xff)
|
||||
att_reg >>= 8
|
||||
|
||||
def test_att_speed(self):
|
||||
self.execute(UrukulExp, "att_speed")
|
||||
dt = self.dataset_mgr.get("dt")
|
||||
print(dt)
|
||||
self.assertLess(dt, 5*us)
|
||||
self.assertLess(dt, 5 * us)
|
||||
|
||||
def test_io_update(self):
|
||||
self.execute(UrukulExp, "io_update")
|
||||
|
|
Loading…
Reference in New Issue