Source code for qblox_instruments.qcodes_drivers.sequencer

# ----------------------------------------------------------------------------
# Description    : Sequencer QCoDeS interface
# Git repository : https://gitlab.com/qblox/packages/software/qblox_instruments.git
# Copyright (C) Qblox BV (2020)
# ----------------------------------------------------------------------------

import re
import warnings
from functools import partial
from typing import Any, Literal, NoReturn, Optional, Union

from qcodes import Instrument, InstrumentChannel, Parameter
from qcodes import validators as vals

from qblox_instruments.native.definitions import (
    BINNED_ACQUISITION_BIT_WIDTH_QRM,
    BINNED_ACQUISITION_SCALING_QRC,
    ChannelType,
)
from qblox_instruments.native.helpers import ValidatedChannelConnection
from qblox_instruments.qcodes_drivers.component import Component
from qblox_instruments.qcodes_drivers.misc_validators import TupleValidator
from qblox_instruments.qcodes_drivers.real_mode_validators import (
    RealModeCompatibleValidator,
)
from qblox_instruments.qcodes_drivers.registration_mixin import ParentAttributeRegistrationMixin
from qblox_instruments.types import FrequencyParameter

_CHANNEL_CONNECTION_REGEX = re.compile(
    r"(?P<direction>in|out|io)(?P<i_channel>0|[1-9][0-9]*)(?:_(?P<q_channel>0|[1-9][0-9]*))?"
)


[docs] class Sequencer(Component, ParentAttributeRegistrationMixin): """ Represents a single sequencer. It combines all sequencer specific parameters and functions into a single QCoDes InstrumentChannel. """
[docs] def __init__( self, parent: Union[Instrument, InstrumentChannel], name: str, seq_idx: int, ) -> None: """ Creates a sequencer class and adds all relevant parameters for the sequencer. Parameters ---------- parent : Union[Instrument, InstrumentChannel] The QCoDeS class to which this sequencer belongs. name : str Name of this sequencer channel seq_idx : int The index of this sequencer in the parent instrument, representing which sequencer is controlled by this class. """ # Initialize instrument channel super().__init__(parent, name) # Store sequencer index self._seq_idx = seq_idx # Add required parent attributes for the QCoDeS parameters to function for attr_name in Sequencer._get_required_parent_attr_names(): self._register(attr_name) # Add dummy-specific attributes if parent is a dummy if hasattr(self.parent, "is_dummy") and self.parent.is_dummy: for attr_name in Sequencer._get_dummy_parent_attr_names(): self._register(attr_name) if self.parent.is_qrm_type or self.parent.is_qrc_type or self.parent.is_qtm_type: for attr_name in Sequencer._get_acquisition_parent_attr_names(): self._register(attr_name) # Add parameters # Channel map self._num_analog_outputs: Optional[int] = None if not any( ( self.parent.is_qtm_type, self.parent.is_qdm_type, self.parent.is_linq_type, ) ): if self.parent.is_rf_type: states = ["off", "IQ", False, True] if self.parent.is_qrm_type: num_outputs = 1 elif self.parent.is_qrc_type: num_outputs = 6 else: num_outputs = 2 else: states = ["off", "I", "Q"] if self.parent.is_qrm_type: num_outputs = 2 elif self.parent.is_qrc_type: num_outputs = 12 else: num_outputs = 4 self._num_analog_outputs = num_outputs for output in range(num_outputs): self.add_parameter( f"connect_out{output}", label=f"Sequencer to output {output} connection configuration", docstring="Sets/gets whether this sequencer is connected to " f"output {output}, and if so which component.", unit="", vals=vals.MultiTypeAnd( vals.Enum(*states), RealModeCompatibleValidator(self, "Q", True) ), set_cmd=partial(self._set_sequencer_connect_out, output), get_cmd=partial(self._get_sequencer_connect_out, output), ) # TODO: handle control sequencers differently when the module has control sequencers # in the future (see SRM-936) if self.parent.is_qrm_type or self.parent.is_qrc_type: if self.parent.is_rf_type: self.add_parameter( "connect_acq", label="Sequencer acquisition input connection configuration", docstring="Sets/gets which input the acquisition path of " "this sequencer is connected to, if any.", unit="", vals=vals.Enum("off", "in0", "in1", False, True), set_cmd=partial(self._set_sequencer_connect_acq, 0), get_cmd=partial(self._get_sequencer_connect_acq, 0), ) else: self.add_parameter( "connect_acq_I", label="Sequencer acquisition I input connection configuration", docstring="Sets/gets which input the I input of the acquisition " "path of this sequencer is connected to, if any.", unit="", vals=vals.Enum("off", "in0", "in1"), set_cmd=partial(self._set_sequencer_connect_acq, 0), get_cmd=partial(self._get_sequencer_connect_acq, 0), ) self.add_parameter( "connect_acq_Q", label="Sequencer acquisition Q input connection configuration", docstring="Sets/gets which input the Q input of the acquisition " "path of this sequencer is connected to, if any.", unit="", vals=vals.Enum("off", "in0", "in1"), set_cmd=partial(self._set_sequencer_connect_acq, 1), get_cmd=partial(self._get_sequencer_connect_acq, 1), ) # Sequencer (All modules) self.add_parameter( "sync_en", label="Sequencer synchronization enable", docstring="Sets/gets sequencer synchronization enable which " "enables party-line synchronization.", unit="", vals=vals.Bool(), set_parser=bool, get_parser=bool, set_cmd=partial(self._set_sequencer_config_val, ["seq_proc", "sync_en"]), get_cmd=partial(self._get_sequencer_config_val, ["seq_proc", "sync_en"]), ) if not any((self.parent.is_qtm_type, self.parent.is_qdm_type, self.parent.is_linq_type)): self.add_parameter( "nco_freq_cal_type_default", label="Default automatic mixer calibration setting", docstring="Sets/gets the Default automatic mixer" "calibration while setting the nco frequency", unit="Hz", vals=vals.Enum("off", "sideband"), set_cmd=None, get_cmd=None, initial_value="off", ) nco_freq = Parameter( "_nco_freq", label="Sequencer NCO frequency", unit="Hz", vals=vals.Numbers(-500e6, 500e6), set_parser=float, get_parser=float, set_cmd=partial(self._set_sequencer_config_val, ["awg", "nco", "freq_hz"]), get_cmd=partial(self._get_sequencer_config_val, ["awg", "nco", "freq_hz"]), ) self.add_parameter( "nco_freq", label="Sequencer NCO frequency", docstring=( "Sets/gets sequencer NCO frequency in Hz with a resolution of 0.25 Hz. " "Be cautious with the unexpected behaviors when mixing static and dynamic " "parameters. Any sequencer parameter change, such as upd_param, will reload " "the NCO frequency set via QCoDeS. A Q1ASM command set_freq will update the " "NCO frequency, but without communicating this value to QCoDeS." ), parameter_class=FrequencyParameter, source=nco_freq, calibration_function=self._calibrate_sideband, ) if self.parent.is_rf_type: def sideband_cal_wrapper(inst: "Sequencer") -> None: inst._run_mixer_sidebands_calib() # Invalidate QCoDeS cache to force parameters to be polled from the instrument # after automatic mixer calibration, instead of relying on outdated values. self.parameters["mixer_corr_gain_ratio"].cache.invalidate() self.parameters["mixer_corr_phase_offset_degree"].cache.invalidate() self.sideband_cal = partial(sideband_cal_wrapper, self) self.add_parameter( "nco_phase_offs", label="Sequencer NCO phase offset", docstring=( "Sets/gets sequencer NCO phase offset in degrees with a resolution of 3.6e-7 " "degrees. Be cautious with the unexpected behaviors when mixing " "static and dynamic parameters. Any sequencer parameter change, " "such as upd_param and reset_ph, will reload " "the static phase offset control register, " "and will not clear the QCoDeS parameter." ), unit="Degrees", vals=vals.Numbers(0, 360), set_parser=float, get_parser=float, set_cmd=partial(self._set_sequencer_config_val, ["awg", "nco", "po"]), get_cmd=partial(self._get_sequencer_config_val, ["awg", "nco", "po"]), ) default_nco_prop_delay_comp = 146 if self.parent.is_qrm_type else 267 self.add_parameter( "nco_prop_delay_comp", label="Sequencer NCO propagation delay compensation", docstring=f"Sets/gets a delay that compensates the NCO phase " f"to the input path with respect to the instrument's " f"combined output and input propagation delay. This " f"delay is applied on top of a default delay of {default_nco_prop_delay_comp} ns.", unit="ns", vals=vals.Numbers(-50, 109), set_parser=int, get_parser=int, set_cmd=partial(self._set_sequencer_config_val, ["awg", "nco", "delay_comp"]), get_cmd=partial(self._get_sequencer_config_val, ["awg", "nco", "delay_comp"]), ) self.add_parameter( "nco_prop_delay_comp_en", label="Sequencer NCO propagation delay compensation enable", docstring="Sets/gets the enable for a delay that compensates " "the NCO phase to the input path with respect to the " "instrument's combined output and input propagation " "delay. This delays the frequency update as well.", unit="ns", vals=vals.Bool(), set_parser=bool, get_parser=bool, set_cmd=partial(self._set_sequencer_config_val, ["awg", "nco", "delay_comp_en"]), get_cmd=partial(self._get_sequencer_config_val, ["awg", "nco", "delay_comp_en"]), ) self.add_parameter( "marker_ovr_en", label="Sequencer marker override enable", docstring="Sets/gets sequencer marker override enable. See `marker_ovr_value`.", unit="", vals=vals.Bool(), set_parser=bool, get_parser=bool, set_cmd=partial(self._set_sequencer_config_val, ["awg", "marker_ovr", "en"]), get_cmd=partial(self._get_sequencer_config_val, ["awg", "marker_ovr", "en"]), ) self.add_parameter( "marker_ovr_value", label="Sequencer marker override value", docstring=( "Sets/gets sequencer marker override value. Bit index " "corresponds to marker channel index. " "Be cautious when mixing static and dynamic parameters. " "marker_ovr_en() and marker_ovr_value() have priority and will " "overwrite set_mrk in Q1ASM completely. Set value OR'ed by " "other sequencers." ), unit="", vals=vals.Numbers(0, 15), set_parser=int, get_parser=int, set_cmd=partial(self._set_sequencer_config_val, ["awg", "marker_ovr", "val"]), get_cmd=partial(self._get_sequencer_config_val, ["awg", "marker_ovr", "val"]), ) self.add_parameter( "isa_version", label="ISA version", docstring="Get sequencer's ISA version in (major, minor).", vals=TupleValidator((vals.Ints(), vals.Ints())), get_cmd=self.get_sequencer_isa_version, snapshot_exclude=True, ) self.add_parameter( "sequence", label="Sequence", docstring="Sets sequencer's AWG waveforms, acquisition weights, " "acquisitions and Q1ASM program. Valid input is a " "string representing the JSON filename or a JSON " "compatible dictionary.", vals=vals.MultiType(vals.Dict(), vals.Strings()), set_cmd=self._set_sequence, get_cmd=Sequencer._get_sequence_raise, snapshot_exclude=True, ) for x in range(1, 16): self.add_parameter( f"trigger{x}_count_threshold", label=f"Counter threshold for trigger address T{x}", docstring=( f"Sets/gets threshold for counter on trigger address T{x}. " f"Thresholding condition used: greater than or equal." ), unit="", vals=vals.Numbers(0, 65535), set_parser=int, get_parser=int, set_cmd=partial( self._set_sequencer_config_val, ["seq_proc", "trg", x - 1, "count_threshold"], ), get_cmd=partial( self._get_sequencer_config_val, ["seq_proc", "trg", x - 1, "count_threshold"], ), ) self.add_parameter( f"trigger{x}_threshold_invert", label=f"Comparison result inversion for trigger address {x}", docstring=f"Sets/gets comparison result inversion for triggeraddress {x}.", unit="", vals=vals.Bool(), set_parser=bool, get_parser=bool, set_cmd=partial( self._set_sequencer_config_val, ["seq_proc", "trg", x - 1, "threshold_invert"], ), get_cmd=partial( self._get_sequencer_config_val, ["seq_proc", "trg", x - 1, "threshold_invert"], ), ) # AWG settings (QCM/QRM) if not any( ( self.parent.is_qtm_type, self.parent.is_qdm_type, self.parent.is_linq_type, ) ): for x in range(0, 2): self.add_parameter( f"cont_mode_en_awg_path{x}", label="Sequencer continuous waveform mode enable for AWG path 0", docstring=( f"Sets/gets sequencer continuous waveform mode enable for AWG path {x}." ), unit="", vals=vals.Bool(), set_parser=bool, get_parser=bool, set_cmd=partial( self._set_sequencer_config_val, ["awg", "cont_mode", "en_path", x], ), get_cmd=partial( self._get_sequencer_config_val, ["awg", "cont_mode", "en_path", x], ), ) self.add_parameter( f"cont_mode_waveform_idx_awg_path{x}", label=f"Sequencer continuous waveform mode waveform index for AWG path {x}", docstring=( f"Sets/gets sequencer continuous waveform mode waveform index " f"or AWG path {x}." ), unit="", vals=vals.Numbers(0, 2**10 - 1), set_parser=int, get_parser=int, set_cmd=partial( self._set_sequencer_config_val, ["awg", "cont_mode", "wave_idx_path", x], ), get_cmd=partial( self._get_sequencer_config_val, ["awg", "cont_mode", "wave_idx_path", x], ), ) self.add_parameter( f"upsample_rate_awg_path{x}", label=f"Sequencer upsample rate for AWG path {x}", docstring=f"Sets/gets sequencer upsample rate for AWG path {x}.", unit="", vals=vals.Numbers(0, 2**16 - 1), set_parser=int, get_parser=int, set_cmd=partial( self._set_sequencer_config_val, ["awg", "upsample_rate_path", x] ), get_cmd=partial( self._get_sequencer_config_val, ["awg", "upsample_rate_path", x] ), ) self.add_parameter( f"gain_awg_path{x}", label=f"Sequencer gain for AWG path {x}", docstring=( f"Sets/gets sequencer gain for AWG path {x}. " "Be cautious when mixing static and dynamic parameters. " f"The total offset on path {x} is a multiplication of set_awg_gain and " f"gain_awg_path{x}." ), unit="", vals=vals.Numbers(-1.0, 1.0), set_parser=float, get_parser=float, set_cmd=partial(self._set_sequencer_config_val, ["awg", "gain_path", x]), get_cmd=partial(self._get_sequencer_config_val, ["awg", "gain_path", x]), ) self.add_parameter( f"offset_awg_path{x}", label=f"Sequencer offset for AWG path {x}", docstring=( f"Sets/gets sequencer offset for AWG path {x}. " "Be cautious when mixing static and dynamic parameters. " f"The total offset on path {x} is an addition of set_awg_offs and " f"offset_awg_path{x}." ), unit="", vals=vals.Numbers(-1.0, 1.0), set_parser=float, get_parser=float, set_cmd=partial(self._set_sequencer_config_val, ["awg", "offs_path", x]), get_cmd=partial(self._get_sequencer_config_val, ["awg", "offs_path", x]), ) if not self.parent.is_qrc_type: self.add_parameter( "mod_en_awg", label="Sequencer modulation enable", docstring="Sets/gets sequencer modulation enable for AWG.", unit="", vals=vals.Bool(), set_parser=bool, get_parser=bool, set_cmd=partial(self._set_sequencer_config_val, ["awg", "mixer", "en"]), get_cmd=partial(self._get_sequencer_config_val, ["awg", "mixer", "en"]), ) self.add_parameter( "mixer_corr_phase_offset_degree", label="Sequencer mixer phase imbalance correction", docstring="Sets/gets sequencer mixer phase imbalance correction " "for AWG; applied to AWG path 1 relative to AWG path 0 " "and measured in degrees", unit="", vals=vals.MultiTypeAnd( vals.Numbers(-45.0, 45.0), RealModeCompatibleValidator(self, 0.0, False) ), set_parser=float, get_parser=float, set_cmd=partial( self._set_sequencer_config_val, ["awg", "mixer", "corr_phase_offset_degree"], ), get_cmd=partial( self._get_sequencer_config_val, ["awg", "mixer", "corr_phase_offset_degree"], ), ) self.add_parameter( "mixer_corr_gain_ratio", label="Sequencer mixer gain imbalance correction", docstring="Sets/gets sequencer mixer gain imbalance correction " "for AWG; equal to AWG path 1 amplitude divided by " "AWG path 0 amplitude.", unit="", vals=vals.MultiTypeAnd( vals.Numbers(0.5, 2.0), RealModeCompatibleValidator(self, 1.0, False) ), set_parser=float, get_parser=float, set_cmd=partial( self._set_sequencer_config_val, ["awg", "mixer", "corr_gain_ratio"] ), get_cmd=partial( self._get_sequencer_config_val, ["awg", "mixer", "corr_gain_ratio"] ), ) if (self.parent.is_qrm_type or self.parent.is_qcm_type) and not self.parent.is_rf_type: self.add_parameter( "real_mode_en", label="Sequencer mixer real mode enable", docstring="Sets/gets sequencer real mode enable for AWG.", unit="", vals=vals.Bool(), set_parser=bool, get_parser=bool, set_cmd=self._real_mode_en, get_cmd=partial(self._get_sequencer_config_val, ["awg", "real_mode_en"]), ) # Acquisition settings (QRM and QRC modules only) if self.parent.is_qrm_type or self.parent.is_qrc_type: if not self.parent.is_qrc_type: self.add_parameter( "demod_en_acq", label="Sequencer demodulation enable", docstring="Sets/gets sequencer demodulation enable for acquisition.", unit="", vals=vals.Bool(), set_parser=bool, get_parser=bool, set_cmd=partial(self._set_sequencer_config_val, ["acq", "demod", "en"]), get_cmd=partial(self._get_sequencer_config_val, ["acq", "demod", "en"]), ) self.add_parameter( "integration_length_acq", label="Sequencer integration length", docstring="Sets/gets sequencer integration length in number " "of samples for non-weighed acquisitions on paths " "0 and 1. Must be a multiple of 4." "The default value is 1024.", unit="", vals=vals.Numbers(4, 2**24 - 4), set_parser=int, get_parser=int, set_cmd=partial( self._set_sequencer_config_val, ["acq", "th_acq", "non_weighed_integration_len"], ), get_cmd=partial( self._get_sequencer_config_val, ["acq", "th_acq", "non_weighed_integration_len"], ), ) self.add_parameter( "thresholded_acq_rotation", label="Sequencer integration result phase rotation", docstring="Sets/gets sequencer integration result phase rotation in degrees.", unit="Degrees", vals=vals.Numbers(0, 360), set_parser=float, get_parser=float, set_cmd=self._set_sequencer_config_rotation_matrix, get_cmd=self._get_sequencer_config_rotation_matrix, ) threshold_scaling = 1 if not self.parent.is_qrc_type else BINNED_ACQUISITION_SCALING_QRC max_threshold = 2 ** (2 * BINNED_ACQUISITION_BIT_WIDTH_QRM) - 4 self.add_parameter( "thresholded_acq_threshold", label="Sequencer discretization threshold", docstring="Sets/gets sequencer discretization threshold for " "discretizing the phase rotation result. " "Discretization is done by comparing the threshold " "to the rotated integration result of path 0. " "This comparison is applied before normalization " "(i.e. division) of the rotated value with the " "integration length and therefore the threshold " "needs to be compensated (i.e. multiplied) with " "this length for the discretization to function " "properly.", unit="", vals=vals.Numbers(-1.0 * max_threshold, 1.0 * max_threshold), set_parser=float, get_parser=float, set_cmd=lambda x: ( self._set_sequencer_config_val( ["acq", "th_acq", "discr_threshold"], x / threshold_scaling ) ), get_cmd=lambda: ( self._get_sequencer_config_val(["acq", "th_acq", "discr_threshold"]) * threshold_scaling ), ) self.add_parameter( "thresholded_acq_marker_en", label="Thresholded acquisition marker enable", docstring="Enable mapping of thresholded acquisition result to markers.", unit="", vals=vals.Bool(), set_parser=bool, get_parser=bool, set_cmd=partial(self._set_sequencer_config_val, ["acq", "th_acq_mrk_map", "en"]), get_cmd=partial(self._get_sequencer_config_val, ["acq", "th_acq_mrk_map", "en"]), ) self.add_parameter( "thresholded_acq_marker_address", label="Marker mask which maps the thresholded acquisition result to the markers", docstring="Sets/gets the marker mask which maps the thresholded acquisition " "result to the markers (M1 to M4).", unit="", vals=(vals.Numbers(0, 3) if parent.is_rf_type else vals.Numbers(0, 15)), set_parser=int, get_parser=int, set_cmd=partial(self._set_sequencer_config_val, ["acq", "th_acq_mrk_map", "addr"]), get_cmd=partial(self._get_sequencer_config_val, ["acq", "th_acq_mrk_map", "addr"]), ) self.add_parameter( "thresholded_acq_marker_invert", label="Inversion of the thresholded acquisition result before it is masked " "onto the markers", docstring="Sets/gets inversion of the thresholded acquisition result before " "it is masked onto the markers.", unit="", vals=vals.Bool(), set_parser=bool, get_parser=bool, set_cmd=partial(self._set_sequencer_config_val, ["acq", "th_acq_mrk_map", "inv"]), get_cmd=partial(self._get_sequencer_config_val, ["acq", "th_acq_mrk_map", "inv"]), ) self.add_parameter( "thresholded_acq_trigger_en", label="Thresholded acquisition result enable", docstring="Sets/gets mapping of thresholded acquisition result to trigger network.", unit="", vals=vals.Bool(), set_parser=bool, get_parser=bool, set_cmd=partial(self._set_sequencer_config_val, ["acq", "th_acq_trg_map", "en"]), get_cmd=partial(self._get_sequencer_config_val, ["acq", "th_acq_trg_map", "en"]), ) self.add_parameter( "thresholded_acq_trigger_address", label="Trigger address to which the thresholded acquisition result is mapped to " "the trigger network", docstring="Sets/gets the trigger address to which the thresholded " "acquisition result is mapped to the trigger network (T1 to T15)", unit="", vals=vals.Numbers(1, 15), set_parser=int, get_parser=int, set_cmd=partial(self._set_sequencer_config_val, ["acq", "th_acq_trg_map", "addr"]), get_cmd=partial(self._get_sequencer_config_val, ["acq", "th_acq_trg_map", "addr"]), ) self.add_parameter( "thresholded_acq_trigger_invert", label="Inversion of the thresholded acquisition result before it is masked " "onto the trigger network node", docstring="Sets/gets the inversion of the thresholded acquisition result " "before it is mapped to the trigger network.", unit="", vals=vals.Bool(), set_parser=bool, get_parser=bool, set_cmd=partial(self._set_sequencer_config_val, ["acq", "th_acq_trg_map", "inv"]), get_cmd=partial(self._get_sequencer_config_val, ["acq", "th_acq_trg_map", "inv"]), ) if not self.parent.is_rf_type: self.add_parameter( "ttl_acq_auto_bin_incr_en", label="TTL trigger acquisition automatic bin increase.", docstring="Sets/gets whether the bin index is automatically " "incremented when acquiring multiple triggers. " "Disabling the TTL trigger acquisition path " "resets the bin index.", unit="", vals=vals.Bool(), set_parser=bool, get_parser=bool, set_cmd=partial( self._set_sequencer_config_val, ["acq", "ttl", "auto_bin_incr_en"], ), get_cmd=partial( self._get_sequencer_config_val, ["acq", "ttl", "auto_bin_incr_en"], ), ) self.add_parameter( "ttl_acq_threshold", label="TTL trigger acquisition threshold", docstring="Sets/gets the threshold value with which to compare " "the input ADC values of the selected input path.", unit="", vals=vals.Numbers(-1.0, 1.0), set_parser=float, get_parser=float, set_cmd=partial(self._set_sequencer_config_val, ["acq", "ttl", "threshold"]), get_cmd=partial(self._get_sequencer_config_val, ["acq", "ttl", "threshold"]), ) self.add_parameter( "ttl_acq_input_select", label="TTL trigger acquisition input", docstring="Sets/gets the input used to compare against " "the threshold value in the TTL trigger acquisition " "path.", unit="", vals=vals.Numbers(0, 1), set_parser=int, get_parser=int, set_cmd=partial(self._set_sequencer_config_val, ["acq", "ttl", "in"]), get_cmd=partial(self._get_sequencer_config_val, ["acq", "ttl", "in"]), )
def _get_bind_index(self) -> int: """ Return the sequencer index to bind to parent methods. Returns ------- int The sequencer index for this sequencer. """ return self._seq_idx @property def seq_idx(self) -> int: """ Get sequencer index. Returns ------- int Sequencer index """ return self._seq_idx @staticmethod def _get_required_parent_attr_names() -> list[str]: """ Return list of parent attribute names that are required for the QCoDeS parameters to function, so that they can be registered to this object using the _register method. Returns ------- list List of parent attribute names to register. """ # Sequencer attributes attr_names = [] for operation in ["set", "get"]: attr_names += [ f"_{operation}_sequencer_connect_out", f"_{operation}_sequencer_connect_acq", f"_{operation}_sequencer_config", f"_{operation}_sequencer_config_val", f"_{operation}_sequencer_config_rotation_matrix", ] attr_names += [ "connect_sequencer", "get_sequencer_isa_version", "_set_sequencer_program", "_set_sequence", "_validate_sequence", "_update_sequence", "update_sequence", "arm_sequencer", "start_sequencer", "stop_sequencer", "get_sequencer_status", "clear_sequencer_flags", "get_sequencer_registers", "set_sequencer_registers", "_add_waveforms", "_delete_waveform", "get_waveforms", "_run_mixer_sidebands_calib", ] return attr_names @staticmethod def _get_dummy_parent_attr_names() -> list[str]: """ Return list of parent attribute names that are dummy-specific. """ return [ "set_dummy_binned_acquisition_data", "delete_dummy_binned_acquisition_data", "set_dummy_scope_acquisition_data", "delete_dummy_scope_acquisition_data", ] @staticmethod def _get_acquisition_parent_attr_names() -> list[str]: """ Return list of parent attribute names that are related to acquisitions. """ return [ "_add_acquisitions", "_delete_acquisition", "delete_acquisition_data", "get_acquisitions", "_add_weights", "_delete_weight", "get_weights", "store_scope_acquisition", "_get_acq_acquisition_data", "get_acquisition_status", ] @staticmethod def _get_sequence_raise() -> NoReturn: raise RuntimeError( "The `sequence` parameter cannot be queried from the instrument. " "Use `sequencer.sequence.cache()` instead. " "If this exception was raised by `sequencer.sequence.cache()`, " "then the cache was invalid and the instrument state is unknown " "(after startup or reset)." ) def _get_cached_qcodes_parameter(self, parameter_name: str) -> Any: parameter = self.parameters.get(parameter_name) if parameter is None: return None return parameter.cache()
[docs] def set_trigger_thresholding(self, address: int, count: int, invert: bool) -> None: """ Sets threshold for designated trigger address counter, together with the inversion condition. Thresholding condition used: greater than or equal. Parameters ---------- address: int Trigger address to which the settings are applied count: int Threshold invert: bool Comparison result inversion Raises ------ NotImplementedError Functionality not available on this module. """ self.parameters[f"trigger{address}_count_threshold"].set(count) self.parameters[f"trigger{address}_threshold_invert"].set(invert)
def _real_mode_en(self, value: bool) -> None: if value: def conflicts(parameter_name: str, ref_value: Any, invert: bool) -> bool: # Check the value of the qcodes parameter (referenced by its # name). Return True if the value does not equals the expected value and # `invert is False`, or it equals the expected value and `invert is False`. return ( param := self._get_cached_qcodes_parameter(parameter_name) ) is not None and ((param == ref_value) ^ invert) error = False message = "Found conflicting settings while setting real_mode_en to True." if conflicts("mixer_corr_phase_offset_degree", 0, True): self.parameters["mixer_corr_phase_offset_degree"](0.0) error = True message += "\nsetting mixer_corr_phase_offset_degree to 0." if conflicts("mixer_corr_gain_ratio", 1, True): self.parameters["mixer_corr_gain_ratio"](1.0) error = True message += "\nsetting mixer_corr_gain_ratio to 1." for i in range(self._num_analog_outputs or 0): if conflicts(f"connect_out{i}", "Q", False): self.parameters[f"connect_out{i}"]("off") error = True message += f"\nsetting connect_out{i} to 'off'." if error: warnings.warn(message) self._set_sequencer_config_val(["awg", "real_mode_en"], value)
[docs] def get_trigger_thresholding(self, address: int) -> tuple: """ Gets threshold for designated trigger address counter, together with the inversion condition. Thresholding condition used: greater than or equal. Parameters ---------- address: int Trigger address to which the settings are applied Returns ------- int Threshold bool Comparison result inversion Raises ------ NotImplementedError Functionality not available on this module. """ count = self.parameters[f"trigger{address}_count_threshold"].get() invert = self.parameters[f"trigger{address}_threshold_invert"].get() return (count, invert)
[docs] def reset_trigger_thresholding(self) -> None: """ Resets trigger thresholds for all trigger address counters (T1 to T15) back to 0. Also resets inversion back to false. Raises ------ NotImplementedError Functionality not available on this module. """ for x in range(1, 16): self.parameters[f"trigger{x}_count_threshold"].set(1) self.parameters[f"trigger{x}_threshold_invert"].set(False)
def _calibrate_sideband( self, cal_type: Optional[Literal["off", "sideband"]] = None, ) -> None: """ Calibrate the mixer according to the calibration type. Parameters ---------- cal_type : Optional[str] Automatic mixer calibration to perform after setting the frequency. Can be one of 'off', 'sideband'. Raises ------ ValueError cal_type is not one of 'off', 'sideband'. """ if cal_type is None: cal_type = self.parameters["nco_freq_cal_type_default"]() if cal_type == "sideband": self.sideband_cal() # Invalidate QCoDeS cache to force parameters to be polled from the instrument # after automatic mixer calibration, instead of relying on outdated cached values. self.parameters["mixer_corr_gain_ratio"].cache.invalidate() self.parameters["mixer_corr_phase_offset_degree"].cache.invalidate() return if cal_type != "off": raise ValueError("cal_type must be one of 'off', 'sideband'.")
[docs] def validate_connections(self, *connections: str) -> list[ValidatedChannelConnection]: is_rf = self.parent.is_rf_type real_mode_en: bool = self._get_cached_qcodes_parameter("real_mode_en") validated_connections: list[ValidatedChannelConnection] = [] for connection in connections: # Parse syntax. m = _CHANNEL_CONNECTION_REGEX.fullmatch(connection) if not m: raise ValueError("syntax error") # Decode direction. directions: list[ChannelType] = [] if m.group("direction") != "in": directions.append(ChannelType.AWG) if m.group("direction") != "out": directions.append(ChannelType.ACQ) # Decode channel indices. i_channel = int(m.group("i_channel")) q_channel = m.group("q_channel") if q_channel is not None: q_channel = int(q_channel) # Catch some expected mistakes gracefully. if i_channel == q_channel: suggestion = m.group("i_channel") + m.group("i_channel") raise ValueError( "cannot connect I and Q path to the same I/O port " f"(did you mean {suggestion!r}?)" ) if is_rf and q_channel is not None: message = "for RF connections, only one I/O port should be specified" if i_channel % 2 == 0 and q_channel == i_channel + 1: # they're probably thinking in terms of DAC/ADC indices suggestion = f"{m.group('direction')}{i_channel // 2}" message += ( f" (you may be confused with DAC/ADC indices, did you mean {suggestion!r}?)" ) raise ValueError(message) # Convert from I/O indices to DAC/ADC indices on RF devices. if is_rf: q_channel = i_channel * 2 + 1 i_channel = i_channel * 2 if real_mode_en and q_channel is not None and ChannelType.AWG in directions: raise ValueError( f"Invalid connection specified for {self.full_name}: {connection}\n" "When real mode is enabled, only the I output path can be connected." ) validated_connections.append( ValidatedChannelConnection(directions, i_channel, q_channel) ) return validated_connections
[docs] def set_register( self, register: str, value: int, timeout: int = 0, time_poll_res: float = 0.02 ) -> None: """ Wait for the sequencer to stop, then set a single register to the given value. Parameters ---------- register : str Name of the register to set, e.g. "R1". value : int Value to set the register to. timeout : int How long to wait for the sequencer to stop. time_poll_res : float How often to poll the sequencer status while waiting for it to stop. """ return self.set_registers({register: value}, timeout, time_poll_res)
[docs] def set_registers( self, registers: dict[str, int], timeout: int = 0, time_poll_res: float = 0.02 ) -> None: """ Wait for the sequencer to stop, then set multiple registers to given values. Parameters ---------- registers : dict[str, int] Mapping of register name to register values to set. timeout : int How long to wait for the sequencer to stop. time_poll_res : float How often to poll the sequencer status while waiting for it to stop. """ _ = self.get_sequencer_status(timeout, time_poll_res) self.set_sequencer_registers(registers)
[docs] def get_register(self, register: str, timeout: int = 0, time_poll_res: float = 0.02) -> int: """ Wait for the sequencer to stop, then obtain the value of a single register. Parameters ---------- register : str Name of the register to get the value of, e.g. "R1". timeout : int How long to wait for the sequencer to stop. time_poll_res : float How often to poll the sequencer status while waiting for it to stop. Returns ------- int The register value. """ registers = self.get_registers([register], timeout, time_poll_res) return registers[register]
[docs] def get_registers( self, registers: Optional[list[str]] = None, timeout: int = 0, time_poll_res: float = 0.02, ) -> dict[str, int]: """ Wait for the sequencer to stop, then obtain the value of multiple registers. Parameters ---------- registers : Optional[list[str]] List of names of registers to get the value of. If `None`, get all register values. timeout : int How long to wait for the sequencer to stop. time_poll_res : float How often to poll the sequencer status while waiting for it to stop. Returns ------- dict[str, int] A mapping of register names to values. """ _ = self.get_sequencer_status(timeout, time_poll_res) return self.get_sequencer_registers(registers)
def _set_default_selfcast_max_id(self, max_selfcast_id: int) -> None: """ Sets the default range for automatic selfcast on a single sequencer. Parameters ---------- max_selfcast_id : int The maximum selfcast ID. Raises ------ Exception Invalid input parameter type. """ self.parent.parent._set_selfcast_max_id(self.parent.slot_idx, self.seq_idx, max_selfcast_id)