diff --git a/src/qibolab/instruments/qm/config.py b/src/qibolab/instruments/qm/config.py index bc5f8d663..15152a21f 100644 --- a/src/qibolab/instruments/qm/config.py +++ b/src/qibolab/instruments/qm/config.py @@ -50,18 +50,26 @@ def register_port(self, port): is_octave = isinstance(port, (OctaveOutput, OctaveInput)) controllers = self.octaves if is_octave else self.controllers if port.device not in controllers: - controllers[port.device] = {} - if not is_octave: - controllers[port.device]["analog_inputs"] = DEFAULT_INPUTS + if is_octave: + controllers[port.device] = {} + else: + controllers[port.device] = { + "analog_inputs": DEFAULT_INPUTS, + "digital_outputs": {}, + } device = controllers[port.device] if port.key in device: device[port.key].update(port.config) else: device[port.key] = port.config + if is_octave: - device["connectivity"] = port.opx_port.i.device + con = port.opx_port.i.device + number = port.opx_port.i.number + device["connectivity"] = con self.register_port(port.opx_port) + self.controllers[con]["digital_outputs"][number] = {} @staticmethod def iq_imbalance(g, phi): @@ -116,6 +124,7 @@ def register_drive_element(self, qubit, intermediate_frequency=0): else: self.elements[f"drive{qubit.name}"] = { "RF_inputs": {"port": qubit.drive.port.pair}, + "digitalInputs": qubit.drive.port.digital_inputs, } self.elements[f"drive{qubit.name}"].update( { @@ -168,9 +177,11 @@ def register_readout_element( } ] else: + self.elements[f"readout{qubit.name}"] = { "RF_inputs": {"port": qubit.readout.port.pair}, "RF_outputs": {"port": qubit.feedback.port.pair}, + "digitalInputs": qubit.readout.port.digital_inputs, } self.elements[f"readout{qubit.name}"].update( { @@ -252,6 +263,7 @@ def register_pulse(self, qubit, qmpulse): "operation": "control", "length": pulse.duration, "waveforms": {"I": serial_i, "Q": serial_q}, + "digital_marker": "ON", } # register drive pulse in elements self.elements[f"drive{qubit.name}"]["operations"][ diff --git a/src/qibolab/instruments/qm/ports.py b/src/qibolab/instruments/qm/ports.py index 028cdbe9e..1d6ce2d44 100644 --- a/src/qibolab/instruments/qm/ports.py +++ b/src/qibolab/instruments/qm/ports.py @@ -1,6 +1,15 @@ from dataclasses import dataclass, field, fields from typing import ClassVar, Dict, Optional, Union +DIGITAL_DELAY = 57 +DIGITAL_BUFFER = 18 +"""Default calibration parameters for digital pulses. + +https://docs.quantum-machines.co/1.1.7/qm-qua-sdk/docs/Guides/octave/#calibrating-the-digital-pulse + +Digital markers are used for LO triggering. +""" + @dataclass class QMPort: @@ -140,12 +149,32 @@ class OctaveOutput(QMOutput): Can be external or internal. """ - output_mode: str = field(default="always_on", metadata={"config": "output_mode"}) + output_mode: str = field(default="triggered", metadata={"config": "output_mode"}) """Can be: "always_on" / "always_off"/ "triggered" / "triggered_reversed".""" + digital_delay: int = DIGITAL_DELAY + """Delay for digital output channel.""" + digital_buffer: int = DIGITAL_BUFFER + """Buffer for digital output channel.""" opx_port: Optional[OPXOutput] = None """OPX+ port that is connected to the Octave port.""" + @property + def digital_inputs(self): + """Generates `digitalInputs` entry for elements in QM config. + + Digital markers are used to switch LOs on in triggered mode. + """ + opx = self.opx_port.i.device + number = self.opx_port.i.number + return { + "output_switch": { + "port": (opx, number), + "delay": self.digital_delay, + "buffer": self.digital_buffer, + } + } + @dataclass class OctaveInput(QMInput): diff --git a/tests/test_instruments_qm.py b/tests/test_instruments_qm.py index b013801de..4769ea375 100644 --- a/tests/test_instruments_qm.py +++ b/tests/test_instruments_qm.py @@ -160,6 +160,7 @@ def test_qm_register_port(qmcontroller, offset): "con1": { "analog_inputs": {1: {}, 2: {}}, "analog_outputs": {1: {"offset": offset, "filter": {}}}, + "digital_outputs": {}, } } @@ -179,6 +180,7 @@ def test_qm_register_port_filter(qmcontroller): "offset": 0.005, } }, + "digital_outputs": {}, } } @@ -234,6 +236,9 @@ def test_qm_register_drive_element(qmplatform): else: target_element = { "RF_inputs": {"port": ("octave3", 1)}, + "digitalInputs": { + "output_switch": {"buffer": 18, "delay": 57, "port": ("con3", 1)} + }, "intermediate_frequency": 1000000, "operations": {}, } @@ -278,6 +283,9 @@ def test_qm_register_readout_element(qmplatform): target_element = { "RF_inputs": {"port": ("octave2", 5)}, "RF_outputs": {"port": ("octave2", 1)}, + "digitalInputs": { + "output_switch": {"buffer": 18, "delay": 57, "port": ("con2", 9)} + }, "intermediate_frequency": 1000000, "operations": {}, "time_of_flight": 280, @@ -296,6 +304,7 @@ def test_qm_register_pulse(qmplatform, pulse_type, qubit): target_pulse = { "operation": "control", "length": pulse.duration, + "digital_marker": "ON", "waveforms": { "I": pulse.envelope_waveform_i().serial, "Q": pulse.envelope_waveform_q().serial,