From 6d98ae023f7906366c6a3850931640fcf480c6b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Niederpr=C3=BCm?= Date: Mon, 8 Jan 2024 15:40:43 +0100 Subject: [PATCH 1/4] UrukulChannel: Remove custom init --- src/atomiq/components/sinara/urukul.py | 74 +------------------------- 1 file changed, 1 insertion(+), 73 deletions(-) diff --git a/src/atomiq/components/sinara/urukul.py b/src/atomiq/components/sinara/urukul.py index cb4045e..6232482 100644 --- a/src/atomiq/components/sinara/urukul.py +++ b/src/atomiq/components/sinara/urukul.py @@ -6,7 +6,7 @@ from atomiq.helper import identity_float, identity_float_int32 from artiq.experiment import kernel, delay, delay_mu, parallel from artiq.language.types import TFloat, TBool, TInt32, TList -from artiq.language.units import us, ms +from artiq.language.units import us from artiq.coredevice import ad9910 from artiq.coredevice import urukul from artiq.coredevice import spi2 as spi @@ -77,7 +77,6 @@ class UrukulChannel(RFSource, Switchable): @kernel def _prerun(self): # use local override of init method - # self.init(blind=False) self.device.init() delay(250 * us) # ATH, 150 -> 250 @@ -95,77 +94,6 @@ class UrukulChannel(RFSource, Switchable): frequency=self.frequency, phase=0.0, amplitude=self.amplitude, profile=-1 ) - @kernel - def init(self, blind: TBool = False): - """ - This method was copied from upstream 8b45f917d1010ee7f1891b18d0073fcb316b68b2 - - Some details regarding the delays needed to be changed. See: - https://forum.m-labs.hk/d/464-ad9910set-cause-random-rtio-underflow-in-dds-init/3 - - Initialize and configure the DDS. - Sets up SPI mode, confirms chip presence, powers down unused blocks, - configures the PLL, waits for PLL lock. Uses the - IO_UPDATE signal multiple times. - :param blind: Do not read back DDS identity and do not wait for lock. - """ - self.device.sync_data.init() - if self.device.sync_data.sync_delay_seed >= 0 and not self.device.cpld.sync_div: - raise ValueError("parent cpld does not drive SYNC") - if self.device.sync_data.sync_delay_seed >= 0: - if self.device.sysclk_per_mu != self.device.sysclk * self.core.ref_period: - raise ValueError("incorrect clock ratio for synchronization") - delay(50 * ms) # slack - - # Set SPI mode - self.device.set_cfr1() - self.device.cpld.io_update.pulse(1 * us) - delay(1 * ms) - if not blind: - # Use the AUX DAC setting to identify and confirm presence - aux_dac = self.device.read32(ad9910._AD9910_REG_AUX_DAC) - if aux_dac & 0xFF != 0x7F: - raise ValueError("Urukul AD9910 AUX_DAC mismatch") - delay(50 * us) # slack - # Configure PLL settings and bring up PLL - # enable amplitude scale from profiles - # read effective FTW - # sync timing validation disable (enabled later) - self.device.set_cfr2(sync_validation_disable=1) - self.device.cpld.io_update.pulse(1 * us) - cfr3 = ( - 0x0807C000 - | (self.device.pll_vco << 24) - | (self.device.pll_cp << 19) - | (self.device.pll_en << 8) - | (self.device.pll_n << 1) - ) - self.device.write32(ad9910._AD9910_REG_CFR3, cfr3 | 0x400) # PFD reset - self.device.cpld.io_update.pulse(1 * us) - if self.device.pll_en: - delay(1 * ms) # SP - self.device.write32(ad9910._AD9910_REG_CFR3, cfr3) - delay(1 * ms) # SP - self.device.cpld.io_update.pulse(1 * us) - delay(1 * ms) # SP - if blind: - delay(100 * ms) - else: - # Wait for PLL lock, up to 100 ms - for i in range(100): - sta = self.device.cpld.sta_read() - lock = (sta >> 8) & 0xF - # lock = ad9910.urukul_sta_pll_lock(sta) - delay(1 * ms) - if lock & (1 << self.device.chip_select - 4): - break - if i >= 100 - 1: - raise ValueError("PLL lock timeout") - delay(10 * us) # slack - if self.device.sync_data.sync_delay_seed >= 0 and not blind: - self.device.tune_sync_delay(self.device.sync_data.sync_delay_seed) - delay(1 * ms) - @kernel def set( self, -- GitLab From 72274757819c341aa8abdbdb834dad985b8abea5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Niederpr=C3=BCm?= Date: Mon, 8 Jan 2024 15:42:28 +0100 Subject: [PATCH 2/4] UrukulChannel: Allow to switch by CPLD only --- src/atomiq/components/sinara/urukul.py | 39 ++++++++++++++++++++++---- 1 file changed, 34 insertions(+), 5 deletions(-) diff --git a/src/atomiq/components/sinara/urukul.py b/src/atomiq/components/sinara/urukul.py index 6232482..cd618b9 100644 --- a/src/atomiq/components/sinara/urukul.py +++ b/src/atomiq/components/sinara/urukul.py @@ -63,12 +63,19 @@ class UrukulChannel(RFSource, Switchable): """ kernel_invariants = {"urukul", "device", "ttl", "profile_default", "profile_arb"} - def __init__(self, urukul: Urukul, device, ttl, default_attenuation=19.0, profile_arb: TInt32 = 0, *args, **kwargs): + def __init__(self, urukul: Urukul, device, ttl=None, default_attenuation=19.0, profile_arb: TInt32 = 0, + *args, **kwargs): RFSource.__init__(self, *args, **kwargs) Switchable.__init__(self, ["RF_on"]) self.urukul = urukul self.device = device - self.ttl = ttl + if ttl is not None: + self.ttl = ttl + self.on = self._on_ttl + self.off = self._off_ttl + else: + self.on = self._on_cpld + self.off = self._off_cpld self.attenuation = default_attenuation self.profile_arb = profile_arb @@ -81,7 +88,7 @@ class UrukulChannel(RFSource, Switchable): delay(250 * us) # ATH, 150 -> 250 - self.ttl.off() + self.off() self.device.set_phase_mode(ad9910.PHASE_MODE_CONTINUOUS) # attenuation @@ -155,7 +162,7 @@ class UrukulChannel(RFSource, Switchable): self.set(phase=phase) @kernel - def on(self): + def _on_ttl(self): """ Turn on via ttl. @@ -166,7 +173,18 @@ class UrukulChannel(RFSource, Switchable): delay_mu(4) @kernel - def off(self): + def _on_cpld(self): + """ + Turn on via CPLD. + + :return + """ + delay_mu(4) + self.device.cpld.cfg_sw(self.device.chip_select-4, True) + delay_mu(4) + + @kernel + def _off_ttl(self): """ Turn off via ttl. @@ -176,6 +194,17 @@ class UrukulChannel(RFSource, Switchable): self.ttl.off() delay_mu(4) + @kernel + def _off_cpld(self): + """ + Turn off via CPLD. + + :return + """ + delay_mu(4) + self.device.cpld.cfg_sw(self.device.chip_select-4, False) + delay_mu(4) + @kernel(flags={"fast-math"}) def _ramp(self, duration: TFloat, -- GitLab From 91a05ac6fa2ba9bd7e8d0a9bd4bc56b618a0200f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Niederpr=C3=BCm?= Date: Mon, 8 Jan 2024 15:43:35 +0100 Subject: [PATCH 3/4] Minor formatting --- src/atomiq/components/sinara/urukul.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/atomiq/components/sinara/urukul.py b/src/atomiq/components/sinara/urukul.py index cd618b9..d6ac8bd 100644 --- a/src/atomiq/components/sinara/urukul.py +++ b/src/atomiq/components/sinara/urukul.py @@ -219,15 +219,15 @@ class UrukulChannel(RFSource, Switchable): """ if ramp_timestep < 3.0*us: ramp_timestep = 3.0 * us - self.experiment.log.warning(self.identifier + ": Trying to ramp with timestep {0:.3f} us which is below the\ - lower limit of 1.5us. Reducing number of steps...", [ramp_timestep*1e6]) + self.experiment.log.warning(self.identifier + ": Ramp timestep {0:.3f} below limit (3.0us). Reducing...", + [ramp_timestep*1e6]) n = int(duration / ramp_timestep) ramp_timestep = duration / float(n) if n < 2: - self.experiment.log.error(self.identifier + ": No ramp possible with duration {0} us and ramp_timestep {1}\ - us", [duration*1e6, ramp_timestep*1e6]) + self.experiment.log.error(self.identifier + ": Ramp impossible with duration {0}us and ramp_timestep {1}us", + [duration*1e6, ramp_timestep*1e6]) freq_step = (frequency_end - frequency_start) / n amp_step = (amplitude_end - amplitude_start) / n @@ -404,7 +404,8 @@ class UrukulChannel(RFSource, Switchable): if ram_destination != ad9910.RAM_DEST_ASF: # work around for https://github.com/m-labs/artiq/issues/1554: Activate OSK - self.device.set_cfr1(ram_enable=1, ram_destination=ram_destination, manual_osk_external=0, osk_enable=1, select_auto_osk=0) + self.device.set_cfr1(ram_enable=1, ram_destination=ram_destination, manual_osk_external=0, + osk_enable=1, select_auto_osk=0) else: self.device.set_cfr1(ram_enable=1, ram_destination=ram_destination) -- GitLab From edc1779caa5e8bb24bd6d01d0602bbe08a31bdbc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Niederpr=C3=BCm?= Date: Tue, 9 Jan 2024 14:36:59 +0100 Subject: [PATCH 4/4] NOT WORKING YET: Enable arb on Urukuls in SUServo this creates a common code base for ramps and ARBs between UrukulChannel and SUServoChannel. First attempts are made to enable RAM mode for ARB on SUServo. The idea is to set the nu_mask register in the Urukul CPLD to be able to talk to the Urukul directly. No success so far... --- src/atomiq/__init__.py | 21 +- src/atomiq/components/sinara/suservo.py | 55 ++-- src/atomiq/components/sinara/urukul.py | 326 +++++++++++++----------- 3 files changed, 207 insertions(+), 195 deletions(-) diff --git a/src/atomiq/__init__.py b/src/atomiq/__init__.py index bf8ab71..a81be62 100644 --- a/src/atomiq/__init__.py +++ b/src/atomiq/__init__.py @@ -3,6 +3,9 @@ from .atomiq import AtomiqExperiment, AtomiqBlock import artiq from inspect import isclass from artiq.language.environment import Experiment, EnvExperiment +from artiq.experiment import delay_mu, kernel +from artiq.language.types import TInt64 +from artiq.coredevice.urukul import CFG_IO_UPDATE def is_experiment(o): @@ -23,5 +26,21 @@ def is_experiment(o): artiq.language.environment.is_experiment = is_experiment # delete atomiq.artiq and atomiq.is_experiment namespace to rule out side effects -del artiq del is_experiment + + +@kernel +def pulse_mu(self, t: TInt64): + cfg = self.cpld.cfg_reg + self.cpld.cfg_write(cfg | (1 << CFG_IO_UPDATE)) + delay_mu(t) + self.cpld.cfg_write(cfg) + + +artiq.coredevice.urukul._RegIOUpdate.pulse_mu = pulse_mu + +del artiq +del pulse_mu +del kernel +del delay_mu +del TInt64 diff --git a/src/atomiq/components/sinara/suservo.py b/src/atomiq/components/sinara/suservo.py index 957a0aa..5758b96 100644 --- a/src/atomiq/components/sinara/suservo.py +++ b/src/atomiq/components/sinara/suservo.py @@ -2,6 +2,7 @@ from __future__ import annotations from atomiq.components.primitives import Component, Switchable from atomiq.components.electronics.rfsource import RFSource +from atomiq.components.sinara.urukul import UrukulRampARBMixin from atomiq.components.electronics.adc import ADCChannel from atomiq.components.optoelectronics.photodiode import AnalogPhotodiode, CalibratedPhotodiode from atomiq.components.optoelectronics.lightmodulator import RFLightModulator @@ -11,7 +12,8 @@ from atomiq.components.laser import ContinuouslyStabilizedModulatedLaser from artiq.experiment import kernel, portable from artiq.language.types import TFloat, TBool, TInt32 from artiq.language.units import us -from artiq.language.core import now_mu, delay, delay_mu, parallel +from artiq.language.core import now_mu, delay, delay_mu +from artiq.coredevice import ad9910 from artiq.master.worker_db import DummyDevice import numpy as np @@ -59,7 +61,7 @@ class SUServo(Component): self.suservo_device.set_pgia_mu(channel, gain) -class SUServoChannel(RFSource, Switchable): +class SUServoChannel(UrukulRampARBMixin, RFSource, Switchable): """A Urukul DDS channel in SUServo configuration as RF Source If not in servo mode, this can be used just like a normal :class:UrukulChannel @@ -71,11 +73,12 @@ class SUServoChannel(RFSource, Switchable): servo_divider: Todo: Why do we need this? (default 10.0) """ - kernel_invariants = {"suservo", "suservo_channel", "cpld"} + kernel_invariants = {"suservo", "suservo_channel", "cpld", "channel", "profile_arb", "profile_default"} def __init__(self, suservo: SUServo, suservo_channel, default_attenuation=19.0, - default_profile=0, + default_profile=ad9910.DEFAULT_PROFILE, + profile_arb: TInt32 = 0, servo_divider: TFloat = 10.0, *args, **kwargs): @@ -83,12 +86,17 @@ class SUServoChannel(RFSource, Switchable): Switchable.__init__(self, ["RF_on"]) self.suservo = suservo self.suservo_channel = suservo_channel + self.channel = self.suservo_channel.servo_channel // 4 if not isinstance(self.suservo.suservo_device, DummyDevice): - self.cpld = self.suservo.suservo_device.cplds[self.suservo_channel.servo_channel // 4] + self.cpld = self.suservo.suservo_device.cplds[self.channel] + self.device = self.suservo.suservo_device.ddses[self.channel] self.profile = default_profile self.attenuation = default_attenuation self.servo_on = False + self.profile_arb = profile_arb + self.profile_default = default_profile + self.servo_divider = servo_divider self.next_on = np.int64(0) @@ -166,8 +174,10 @@ class SUServoChannel(RFSource, Switchable): if self.servo_on: self.suservo_channel.set_dds_offset(self.profile, -1*amplitude/self.servo_divider) + self.suservo_channel.set_dds_offset(self.profile_arb, -1*amplitude/self.servo_divider) else: self.suservo_channel.set_y(self.profile, y=amplitude) + self.suservo_channel.set_y(self.profile_arb, y=amplitude) self.amplitude = amplitude @kernel @@ -234,41 +244,6 @@ class SUServoChannel(RFSource, Switchable): # we need some time for the servo to settle delay(0.03*us) - @kernel(flags={"fast-math"}) - def _ramp(self, duration: TFloat, - frequency_start: TFloat, - frequency_end: TFloat, - amplitude_start: TFloat, - amplitude_end: TFloat, - ramp_timestep: TFloat = 200e-6): - """ - At some point, we want to replace this by code that uses the digital ramp generator (DRG) on the AD9910 because - this allows for much faster ramps - """ - if ramp_timestep < 1.5*us: - ramp_timestep = 1.5 * us - self.experiment.log.warning(self.identifier + ": Ramp timestep {0:.3f} below limit (1.4us). Reducing...", - [ramp_timestep*1e6]) - - n = int(duration / ramp_timestep) - ramp_timestep = duration / float(n) - - if n < 2: - self.experiment.log.error(self.identifier + ": Ramp impossible with duration {0}us and ramp_timestep {1}us", - [duration*1e6, ramp_timestep*1e6]) - - freq_step = (frequency_end - frequency_start) / n - amp_step = (amplitude_end - amplitude_start) / n - - delay(-1.31*us) - - for i in range(n): - _freq = frequency_start + i * freq_step - _amp = amplitude_start + i * amp_step - with parallel: - self.set(frequency=_freq, amplitude=_amp) - delay(ramp_timestep) - class SUServoADCChannel(ADCChannel): """Analog Input of a Sampler in an SUServo configuration diff --git a/src/atomiq/components/sinara/urukul.py b/src/atomiq/components/sinara/urukul.py index d6ac8bd..31528b6 100644 --- a/src/atomiq/components/sinara/urukul.py +++ b/src/atomiq/components/sinara/urukul.py @@ -51,160 +51,7 @@ class Urukul(Component): self.cpld.io_update.pulse_mu(8) -class UrukulChannel(RFSource, Switchable): - """Single DDS Channel of a Sinara Urukul - - Args: - urukul: The Urukul component this channel belongs to - device: The ARTIQ device from the `device_db` representing the Urukul channel, e.g. `@urukul0_ch0` - ttl: The ARTIQ device from the `device_db` representing the Urukul fast RF switch, e.g. `@ttl_urukul0_sw0` - default_attenuation: Default attenuation to set for the channel on startup. (default -19dBm) - profile_arb: Profile on the DDS to use for arbitrary function generation. (default 0) - """ - kernel_invariants = {"urukul", "device", "ttl", "profile_default", "profile_arb"} - - def __init__(self, urukul: Urukul, device, ttl=None, default_attenuation=19.0, profile_arb: TInt32 = 0, - *args, **kwargs): - RFSource.__init__(self, *args, **kwargs) - Switchable.__init__(self, ["RF_on"]) - self.urukul = urukul - self.device = device - if ttl is not None: - self.ttl = ttl - self.on = self._on_ttl - self.off = self._off_ttl - else: - self.on = self._on_cpld - self.off = self._off_cpld - - self.attenuation = default_attenuation - self.profile_arb = profile_arb - self.profile_default = ad9910.DEFAULT_PROFILE - - @kernel - def _prerun(self): - # use local override of init method - self.device.init() - - delay(250 * us) # ATH, 150 -> 250 - - self.off() - self.device.set_phase_mode(ad9910.PHASE_MODE_CONTINUOUS) - - # attenuation - delay(150 * us) - self.set_att(self.attenuation) - - # DDS - delay(150 * us) - self.set( - frequency=self.frequency, phase=0.0, amplitude=self.amplitude, profile=-1 - ) - - @kernel - def set( - self, - frequency: TFloat = -1.0, - amplitude: TFloat = -1.0, - phase: TFloat = 0.0, - profile: TInt32 = -1, - ): - """ - Set the frequency and amplitude. - Frequency/amplitude are set to the last known value if -1 is given. - - Args: - frequency: frequency [Hz] - amplitude: amplitude - """ - self.frequency = self.frequency if frequency < 0 else frequency - self.amplitude = self.amplitude if amplitude < 0 else amplitude - - profile = self.profile_default if profile < 0 else profile - - self.device.set( - frequency=self.frequency, - amplitude=self.amplitude, - phase=phase, - profile=profile, - ) - - # also write to the arb profile - self.device.set( - frequency=self.frequency, - amplitude=self.amplitude, - phase=phase, - profile=self.profile_arb, - ) - - @kernel - def set_att(self, attenuation: TFloat): - """ - Set the hardware attenuation for this urukul channel via cpld. - - Args: - attenuation: channel attenuation (0. to 31.0 in 0.5 increments) [dB] - """ - self.attenuation = attenuation - self.device.set_att(attenuation) - # self.kernel_log(self.attenuation) - - @kernel - def _set_frequency(self, frequency: TFloat): - self.set(frequency=frequency) - - @kernel - def _set_amplitude(self, amplitude: TFloat): - self.set(amplitude=amplitude) - - @kernel - def _set_phase(self, phase: TFloat): - self.set(phase=phase) - - @kernel - def _on_ttl(self): - """ - Turn on via ttl. - - :return - """ - delay_mu(4) - self.ttl.on() - delay_mu(4) - - @kernel - def _on_cpld(self): - """ - Turn on via CPLD. - - :return - """ - delay_mu(4) - self.device.cpld.cfg_sw(self.device.chip_select-4, True) - delay_mu(4) - - @kernel - def _off_ttl(self): - """ - Turn off via ttl. - - :return - """ - delay_mu(4) - self.ttl.off() - delay_mu(4) - - @kernel - def _off_cpld(self): - """ - Turn off via CPLD. - - :return - """ - delay_mu(4) - self.device.cpld.cfg_sw(self.device.chip_select-4, False) - delay_mu(4) - +class UrukulRampARBMixin(): @kernel(flags={"fast-math"}) def _ramp(self, duration: TFloat, @@ -371,6 +218,13 @@ class UrukulChannel(RFSource, Switchable): ram_offset: Address offset of the RAM storage address. (default 0) profile: Profile of the DDS to use for the RAM mode. If none is given, profile 0 is used. """ + + # SUServo: set nu_mask register in CPLD so that we can talk to the Urukul directly + prev_cpld_cfg = self.device.cpld.cfg_reg + self.experiment.log.info("nu_mask: 0x{:02x}", [(prev_cpld_cfg & (0xf << urukul.CFG_MASK_NU)) >> urukul.CFG_MASK_NU]) + self.device.cpld.cfg_write(prev_cpld_cfg | (0x1 << (urukul.CFG_MASK_NU + self.channel // 4))) + self.experiment.log.info("nu_mask: 0x{0:02x}", [(self.device.cpld.cfg_reg & (0xf << urukul.CFG_MASK_NU)) >> urukul.CFG_MASK_NU]) + num_samples = len(samples) # disable RAM for writing data @@ -393,11 +247,16 @@ class UrukulChannel(RFSource, Switchable): # self.device.write_ram(samples[:num_samples]) self._write_ram(samples[:num_samples], transform=transform) + self.device.cpld.cfg_write(prev_cpld_cfg) + return num_samples - 1 + ram_offset @kernel def _run_arb(self, duration: TFloat, ram_destination: TInt32, trigger: TBool = True, profile: TInt32 = -1): + prev_cpld_cfg = self.device.cpld.cfg_reg + self.device.cpld.cfg_write(prev_cpld_cfg | (0x1 << (urukul.CFG_MASK_NU + self.channel // 4))) + self.device.set(frequency=self.frequency, amplitude=self.amplitude, phase=self.phase, ram_destination=ram_destination) @@ -426,3 +285,162 @@ class UrukulChannel(RFSource, Switchable): if trigger: self.device.cpld.io_update.pulse_mu(8) + + self.device.cpld.cfg_write(prev_cpld_cfg) + delay(2*us) + + +class UrukulChannel(UrukulRampARBMixin, RFSource, Switchable): + """Single DDS Channel of a Sinara Urukul + + Args: + urukul: The Urukul component this channel belongs to + device: The ARTIQ device from the `device_db` representing the Urukul channel, e.g. `@urukul0_ch0` + ttl: The ARTIQ device from the `device_db` representing the Urukul fast RF switch, e.g. `@ttl_urukul0_sw0` + default_attenuation: Default attenuation to set for the channel on startup. (default -19dBm) + profile_arb: Profile on the DDS to use for arbitrary function generation. (default 0) + """ + kernel_invariants = {"urukul", "device", "ttl", "channel", "profile_default", "profile_arb"} + + def __init__(self, urukul: Urukul, device, ttl=None, default_attenuation=19.0, profile_arb: TInt32 = 0, + *args, **kwargs): + RFSource.__init__(self, *args, **kwargs) + Switchable.__init__(self, ["RF_on"]) + self.urukul = urukul + self.device = device + self.channel = self.device.chip_select-4 + if ttl is not None: + self.ttl = ttl + self.on = self._on_ttl + self.off = self._off_ttl + else: + self.on = self._on_cpld + self.off = self._off_cpld + + self.attenuation = default_attenuation + self.profile_arb = profile_arb + self.profile_default = ad9910.DEFAULT_PROFILE + + @kernel + def _prerun(self): + # use local override of init method + self.device.init() + + delay(250 * us) # ATH, 150 -> 250 + + self.off() + self.device.set_phase_mode(ad9910.PHASE_MODE_CONTINUOUS) + + # attenuation + delay(150 * us) + self.set_att(self.attenuation) + + # DDS + delay(150 * us) + self.set( + frequency=self.frequency, phase=0.0, amplitude=self.amplitude, profile=-1 + ) + + @kernel + def set( + self, + frequency: TFloat = -1.0, + amplitude: TFloat = -1.0, + phase: TFloat = 0.0, + profile: TInt32 = -1, + ): + """ + Set the frequency and amplitude. + Frequency/amplitude are set to the last known value if -1 is given. + + Args: + frequency: frequency [Hz] + amplitude: amplitude + """ + self.frequency = self.frequency if frequency < 0 else frequency + self.amplitude = self.amplitude if amplitude < 0 else amplitude + + profile = self.profile_default if profile < 0 else profile + + self.device.set( + frequency=self.frequency, + amplitude=self.amplitude, + phase=phase, + profile=profile, + ) + + # also write to the arb profile + self.device.set( + frequency=self.frequency, + amplitude=self.amplitude, + phase=phase, + profile=self.profile_arb, + ) + + @kernel + def set_att(self, attenuation: TFloat): + """ + Set the hardware attenuation for this urukul channel via cpld. + + Args: + attenuation: channel attenuation (0. to 31.0 in 0.5 increments) [dB] + """ + self.attenuation = attenuation + self.device.set_att(attenuation) + # self.kernel_log(self.attenuation) + + @kernel + def _set_frequency(self, frequency: TFloat): + self.set(frequency=frequency) + + @kernel + def _set_amplitude(self, amplitude: TFloat): + self.set(amplitude=amplitude) + + @kernel + def _set_phase(self, phase: TFloat): + self.set(phase=phase) + + @kernel + def _on_ttl(self): + """ + Turn on via ttl. + + :return + """ + delay_mu(4) + self.ttl.on() + delay_mu(4) + + @kernel + def _on_cpld(self): + """ + Turn on via CPLD. + + :return + """ + delay_mu(4) + self.device.cpld.cfg_sw(self.channel, True) + delay_mu(4) + + @kernel + def _off_ttl(self): + """ + Turn off via ttl. + + :return + """ + delay_mu(4) + self.ttl.off() + delay_mu(4) + + @kernel + def _off_cpld(self): + """ + Turn off via CPLD. + + :return + """ + delay_mu(4) + self.device.cpld.cfg_sw(self.channel, False) + delay_mu(4) -- GitLab