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)
# ----------------------------------------------------------------------------


# -- include -----------------------------------------------------------------

from typing import List, Union
from functools import partial
from qcodes import validators as vals
from qcodes import Instrument, InstrumentChannel


# -- class -------------------------------------------------------------------

[docs]class Sequencer(InstrumentChannel): """ This class 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, ): """ 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. Returns ---------- Raises ---------- """ # 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 parameters # -- Channel map (All modules) --------------------------------------- self.add_parameter( "channel_map_path0_out0_en", label="Sequencer path 0 output 0 enable", docstring="Sets/gets sequencer channel map enable of path 0 to " "output 0.", unit="", vals=vals.Bool(), set_parser=bool, get_parser=bool, set_cmd=partial(self._set_sequencer_channel_map, 0), get_cmd=partial(self._get_sequencer_channel_map, 0), ) self.add_parameter( "channel_map_path1_out1_en", label="Sequencer path 1 output 1 enable", docstring="Sets/gets sequencer channel map enable of path 1 to " "output 1.", unit="", vals=vals.Bool(), set_parser=bool, get_parser=bool, set_cmd=partial(self._set_sequencer_channel_map, 1), get_cmd=partial(self._get_sequencer_channel_map, 1), ) if self.parent.is_qcm_type: self.add_parameter( "channel_map_path0_out2_en", label="Sequencer path 0 output 2 enable.", docstring="Sets/gets sequencer channel map enable of path 0 " "to output 2.", unit="", vals=vals.Bool(), set_parser=bool, get_parser=bool, set_cmd=partial(self._set_sequencer_channel_map, 2), get_cmd=partial(self._get_sequencer_channel_map, 2), ) self.add_parameter( "channel_map_path1_out3_en", label="Sequencer path 1 output 3 enable.", docstring="Sets/gets sequencer channel map enable of path 1 " "to output 3.", unit="", vals=vals.Bool(), set_parser=bool, get_parser=bool, set_cmd=partial(self._set_sequencer_channel_map, 3), get_cmd=partial(self._get_sequencer_channel_map, 3), ) # -- 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, "sync_en" ), get_cmd=partial( self._get_sequencer_config_val, "sync_en" ), ) self.add_parameter( "nco_freq", label="Sequencer NCO frequency", docstring="Sets/gets sequencer NCO frequency in Hz with a " "resolution of 0.25 Hz.", unit="Hz", vals=vals.Numbers(-300e6, 300e6), set_parser=float, get_parser=float, set_cmd=partial( self._set_sequencer_config_val, "freq_hz" ), get_cmd=partial( self._get_sequencer_config_val, "freq_hz" ), ) 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.", unit="Degrees", vals=vals.Numbers(0, 360), set_parser=float, get_parser=float, set_cmd=partial( self._set_sequencer_config_val, "phase_offs_degree" ), get_cmd=partial( self._get_sequencer_config_val, "phase_offs_degree" ), ) self.add_parameter( "marker_ovr_en", label="Sequencer marker override enable", docstring="Sets/gets sequencer marker override enable.", unit="", vals=vals.Bool(), set_parser=bool, get_parser=bool, set_cmd=partial( self._set_sequencer_config_val, "mrk_ovr_en" ), get_cmd=partial( self._get_sequencer_config_val, "mrk_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.", unit="", vals=vals.Numbers(0, 15), set_parser=int, get_parser=int, set_cmd=partial( self._set_sequencer_config_val, "mrk_ovr_val" ), get_cmd=partial( self._get_sequencer_config_val, "mrk_ovr_val" ), ) self.add_parameter( "sequence", label="Sequence", docstring="Sets sequencer's AWG waveforms, acquistion weights, " "acquisitions and Q1ASM program. Valid input is a " "string representing the JSON filename or a JSON " "compatible dictionary.", vals=vals.MultiType(vals.Strings(), vals.Dict()), set_cmd=self._set_sequence, ) # -- AWG settings (All modules) -------------------------------------- self.add_parameter( "cont_mode_en_awg_path0", label="Sequencer continous waveform mode enable for AWG path 0", docstring="Sets/gets sequencer continous waveform mode enable " "for AWG path 0.", unit="", vals=vals.Bool(), set_parser=bool, get_parser=bool, set_cmd=partial( self._set_sequencer_config_val, "cont_mode_en_awg_path_0" ), get_cmd=partial( self._get_sequencer_config_val, "cont_mode_en_awg_path_0" ), ) self.add_parameter( "cont_mode_en_awg_path1", label="Sequencer continous waveform mode enable for AWG path 1", docstring="Sets/gets sequencer continous waveform mode enable " "for AWG path 1.", unit="", vals=vals.Bool(), set_parser=bool, get_parser=bool, set_cmd=partial( self._set_sequencer_config_val, "cont_mode_en_awg_path_1" ), get_cmd=partial( self._get_sequencer_config_val, "cont_mode_en_awg_path_1" ), ) self.add_parameter( "cont_mode_waveform_idx_awg_path0", label="Sequencer continous waveform mode waveform index for " "AWG path 0", docstring="Sets/gets sequencer continous waveform mode waveform " "index or AWG path 0.", unit="", vals=vals.Numbers(0, 2 ** 10 - 1), set_parser=int, get_parser=int, set_cmd=partial( self._set_sequencer_config_val, "cont_mode_waveform_idx_awg_path_0" ), get_cmd=partial( self._get_sequencer_config_val, "cont_mode_waveform_idx_awg_path_0" ), ) self.add_parameter( "cont_mode_waveform_idx_awg_path1", label="Sequencer continous waveform mode waveform index for " "AWG path 1", docstring="Sets/gets sequencer continous waveform mode waveform " "index or AWG path 1.", unit="", vals=vals.Numbers(0, 2 ** 10 - 1), set_parser=int, get_parser=int, set_cmd=partial( self._set_sequencer_config_val, "cont_mode_waveform_idx_awg_path_1" ), get_cmd=partial( self._get_sequencer_config_val, "cont_mode_waveform_idx_awg_path_1" ), ) self.add_parameter( "upsample_rate_awg_path0", label="Sequencer upsample rate for AWG path 0", docstring="Sets/gets sequencer upsample rate for AWG path 0.", unit="", vals=vals.Numbers(0, 2 ** 16 - 1), set_parser=int, get_parser=int, set_cmd=partial( self._set_sequencer_config_val, "upsample_rate_awg_path_0" ), get_cmd=partial( self._get_sequencer_config_val, "upsample_rate_awg_path_0" ), ) self.add_parameter( "upsample_rate_awg_path1", label="Sequencer upsample rate for AWG path 1", docstring="Sets/gets sequencer upsample rate for AWG path 1.", unit="", vals=vals.Numbers(0, 2 ** 16 - 1), set_parser=int, get_parser=int, set_cmd=partial( self._set_sequencer_config_val, "upsample_rate_awg_path_1" ), get_cmd=partial( self._get_sequencer_config_val, "upsample_rate_awg_path_1" ), ) self.add_parameter( "gain_awg_path0", label="Sequencer gain for AWG path 0", docstring="Sets/gets sequencer gain for AWG path 0.", unit="", vals=vals.Numbers(-1.0, 1.0), set_parser=float, get_parser=float, set_cmd=partial( self._set_sequencer_config_val, "gain_awg_path_0_float" ), get_cmd=partial( self._get_sequencer_config_val, "gain_awg_path_0_float" ), ) self.add_parameter( "gain_awg_path1", label="Sequencer gain for AWG path 1", docstring="Sets/gets sequencer gain for AWG path 1.", unit="", vals=vals.Numbers(-1.0, 1.0), set_parser=float, get_parser=float, set_cmd=partial( self._set_sequencer_config_val, "gain_awg_path_1_float" ), get_cmd=partial( self._get_sequencer_config_val, "gain_awg_path_1_float" ), ) self.add_parameter( "offset_awg_path0", label="Sequencer offset for AWG path 0", docstring="Sets/gets sequencer offset for AWG path 0.", unit="", vals=vals.Numbers(-1.0, 1.0), set_parser=float, get_parser=float, set_cmd=partial( self._set_sequencer_config_val, "offset_awg_path_0_float" ), get_cmd=partial( self._get_sequencer_config_val, "offset_awg_path_0_float" ), ) self.add_parameter( "offset_awg_path1", label="Sequencer offset for AWG path 1", docstring="Sets/gets sequencer offset for AWG path 1.", unit="", vals=vals.Numbers(-1.0, 1.0), set_parser=float, get_parser=float, set_cmd=partial( self._set_sequencer_config_val, "offset_awg_path_1_float" ), get_cmd=partial( self._get_sequencer_config_val, "offset_awg_path_1_float" ), ) 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.Numbers(-45.0, 45.0), set_parser=float, get_parser=float, set_cmd=partial( self._set_sequencer_config_val, "mixer_corr_phase_offset_degree_float" ), get_cmd=partial( self._get_sequencer_config_val, "mixer_corr_phase_offset_degree_float" ), ) 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.Numbers(0.5, 2.0), set_parser=float, get_parser=float, set_cmd=partial( self._set_sequencer_config_val, "mixer_corr_gain_ratio_float" ), get_cmd=partial( self._get_sequencer_config_val, "mixer_corr_gain_ratio_float" ), ) 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, "mod_en_awg" ), get_cmd=partial( self._get_sequencer_config_val, "mod_en_awg" ), ) # -- Acquisition settings (QRM modules only) ------------------------- if self.parent.is_qrm_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, "demod_en_acq" ), get_cmd=partial( self._get_sequencer_config_val, "demod_en_acq" ), ) 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", unit="", vals=vals.Multiples(4, min_value=4, max_value=2 ** 24 - 4), set_parser=int, get_parser=int, set_cmd=partial( self._set_sequencer_config_val, "non_weighed_integration_len" ), get_cmd=partial( self._get_sequencer_config_val, "non_weighed_integration_len" ), ) self.add_parameter( "phase_rotation_acq", 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, ) self.add_parameter( "discretization_threshold_acq", 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 * (2 ** 24 - 4), 1.0 * (2 ** 24 - 4)), set_parser=float, get_parser=float, set_cmd=partial( self._set_sequencer_config_val, "discr_threshold" ), get_cmd=partial( self._get_sequencer_config_val, "discr_threshold" ), )
# ------------------------------------------------------------------------ @property def seq_idx(self) -> int: """ Get sequencer index. Parameters ---------- Returns ---------- int Sequencer index Raises ---------- """ return self._seq_idx # ------------------------------------------------------------------------ @staticmethod def _get_required_parent_attr_names() -> List: """ Return list of parent attribute names that are required for the QCoDeS parameters to function, so that the can be registered to this object using the _register method. Parameters ---------- Returns ---------- List List of parent attribute names to register. Raises ---------- """ # Sequencer attributes attr_names = [] for operation in ["set", "get"]: attr_names.append("_{}_sequencer_channel_map".format(operation)) attr_names.append("_{}_sequencer_config".format(operation)) attr_names.append("_{}_sequencer_config_val".format(operation)) attr_names.append("_{}_sequencer_config_rotation_matrix".format(operation)) attr_names.append("_set_sequencer_program") attr_names.append("_set_sequence") attr_names.append("arm_sequencer") attr_names.append("start_sequencer") attr_names.append("stop_sequencer") attr_names.append("get_sequencer_state") # Waveform, weight and acquisition attributes for component in ["waveform", "weight", "acquisition"]: attr_names.append("_add_{}s".format(component)) attr_names.append("_delete_{}".format(component)) attr_names.append("get_{}s".format(component)) attr_names.append("store_scope_acquisition") attr_names.append("_get_acq_acquisition_data") attr_names.append("get_acquisition_state") return attr_names # ------------------------------------------------------------------------ def _register(self, attr_name: str) -> None: """ Register parent attribute to this sequencer using functools.partial to pre-select the sequencer index. If the attribute does not exist in the parent class, a method that raises a `NotImplementedError` exception is registered instead. The docstring of the parent attribute is also copied to the registered attribute. Parameters ---------- attr_name : str Attribute name of parent to register. Returns ---------- Raises ---------- """ if hasattr(self.parent, attr_name): parent_attr = getattr(self.parent, attr_name) partial_func = partial(parent_attr, self.seq_idx) partial_func.__doc__ = ( "Important:\n" + "This method calls {0} using functools.partial to set the " + "sequencer index. The following docstring is of {1}.{0}:\n\n" ).format(attr_name, type(self.parent).__name__) partial_func.__doc__ += parent_attr.__doc__ setattr(self, attr_name, partial_func) else: def raise_not_implemented_error(*args, **kwargs) -> None: raise NotImplementedError( '{} does not have "{}" attribute.'.format(self.parent.name, attr_name) ) setattr(self, attr_name, raise_not_implemented_error) # ------------------------------------------------------------------------ def _invalidate_qcodes_parameter_cache(self) -> None: """ Marks the cache of all QCoDeS parameters on this sequencer as invalid. Parameters ---------- Returns ---------- Raises ---------- """ for param in self.parameters.values(): param.cache.invalidate()