From 8a8c54dff626b439919436ed3f00607bc0110552 Mon Sep 17 00:00:00 2001
From: Sander de Snoo <59472150+sldesnoo-Delft@users.noreply.github.com>
Date: Thu, 12 Jan 2023 10:53:17 +0100
Subject: [PATCH] Made QS more robust and use configurable offset for sequencer
 start

---
 pulse_lib/keysight/qs_uploader.py      | 37 +++++++++++++++-----------
 pulse_lib/keysight/sequencer_device.py |  7 ++++-
 2 files changed, 28 insertions(+), 16 deletions(-)

diff --git a/pulse_lib/keysight/qs_uploader.py b/pulse_lib/keysight/qs_uploader.py
index 0e329e98..3d96a8e5 100644
--- a/pulse_lib/keysight/qs_uploader.py
+++ b/pulse_lib/keysight/qs_uploader.py
@@ -115,7 +115,8 @@ class QsUploader:
         '''
         start = time.perf_counter()
 
-        aggregator = UploadAggregator(self.AWGs, self.awg_channels, self.marker_channels, self.digitizer_channels,
+        aggregator = UploadAggregator(self.AWGs, self.digitizers, self.awg_channels,
+                                      self.marker_channels, self.digitizer_channels,
                                       self.qubit_channels, self.sequencer_channels, self.sequencer_out_channels)
 
         aggregator.upload_job(job, self.__upload_to_awg) # @@@ TODO split generation and upload
@@ -507,9 +508,10 @@ class RfMarkerPulse:
 class UploadAggregator:
     verbose = False
 
-    def __init__(self, AWGs, awg_channels, marker_channels, digitizer_channels,
+    def __init__(self, AWGs, digitizers, awg_channels, marker_channels, digitizer_channels,
                  qubit_channels, sequencer_channels, sequencer_out_channels):
         self.AWGs = AWGs
+        self.digitizers = digitizers
         self.npt = 0
         self.marker_channels = marker_channels
         self.digitizer_channels = digitizer_channels
@@ -946,6 +948,9 @@ class UploadAggregator:
         segments = self.segments
 
         for channel_name, qubit_channel in self.qubit_channels.items():
+            if channel_name not in self.sequencer_channels:
+                logging.warning(f'QS driver (M3202A_QS) not loaded for qubit channel {channel_name}')
+                continue
             start = time.perf_counter()
             delays = []
             for i in range(2):
@@ -954,9 +959,10 @@ class UploadAggregator:
             if delays[0] != delays[1]:
                 raise Exception(f'I/Q Channel delays must be equal ({channel_name})')
 
+            sequencer_offset = self.sequencer_channels[channel_name].sequencer_offset
             # TODO improve for alignment on 1 ns. -> set channel delay in FPGA
-            # subtract 10 ns, because it's started 10 ns before 'classical' queued waveform
-            t_start = int((-self.max_pre_start_ns - delays[0]) / 5) * 5 -10
+            # subtract offset, because it's started before 'classical' queued waveform
+            t_start = int((-self.max_pre_start_ns - delays[0]) / 5) * 5 - sequencer_offset
 
             sequence = IQSequenceBuilder(channel_name, t_start,
                                          qubit_channel.iq_channel.LO)
@@ -1017,7 +1023,6 @@ class UploadAggregator:
     def _generate_digitizer_triggers(self, job):
         trigger_channels = {}
         digitizer_trigger_channels = {}
-        self.rf_marker_pulses = {}
         has_HVI_triggers = False
 
         for name, value in job.schedule_params.items():
@@ -1053,17 +1058,16 @@ class UploadAggregator:
                     if rf_source is not None and rf_source.mode != 'continuous':
                         rf_marker_pulses.append(RfMarkerPulse(t, t_end))
 
-            if (rf_source is not None
-                and rf_source.mode == 'continuous'
-                and t_end is not None):
+            if rf_source is not None:
+                if rf_source.mode == 'continuous' and t_end is not None:
                     rf_marker_pulses.append(RfMarkerPulse(0, t_end))
 
-            for rf_pulse in rf_marker_pulses:
-                rf_pulse.start += rf_source.delay
-                rf_pulse.stop += rf_source.delay
-                if rf_source.mode in ['pulsed', 'continuous']:
-                    rf_pulse.start -= rf_source.startup_time_ns
-                    rf_pulse.stop += rf_source.prolongation_ns
+                for rf_pulse in rf_marker_pulses:
+                    rf_pulse.start += rf_source.delay
+                    rf_pulse.stop += rf_source.delay
+                    if rf_source.mode in ['pulsed', 'continuous']:
+                        rf_pulse.start -= rf_source.startup_time_ns
+                        rf_pulse.stop += rf_source.prolongation_ns
 
         job.digitizer_triggers = list(trigger_channels.keys())
         job.digitizer_triggers.sort()
@@ -1092,9 +1096,11 @@ class UploadAggregator:
 
         logging.debug(f'PXI triggers: {pxi_triggers}')
 
-        self.rf_marker_pulses = {}
         segments = self.segments
         for channel_name, channel in self.digitizer_channels.items():
+            dig = self.digitizers[channel.module_name]
+            if not hasattr(dig, 'get_sequencer'):
+                raise Exception(f'QS driver (M3102A_QS) not configured for digitizer {channel.module_name}')
             sequence = AcquisitionSequenceBuilder(channel_name)
             job.digitizer_sequences[channel_name] = sequence
             rf_source = channel.rf_source
@@ -1158,6 +1164,7 @@ class UploadAggregator:
         job.digitizer_sequences = {}
         job.digitizer_triggers = {}
         job.digitizer_trigger_channels = {}
+        self.rf_marker_pulses = {}
 
         self._integrate(job)
 
diff --git a/pulse_lib/keysight/sequencer_device.py b/pulse_lib/keysight/sequencer_device.py
index bdd8dfd8..a0c2cbb6 100644
--- a/pulse_lib/keysight/sequencer_device.py
+++ b/pulse_lib/keysight/sequencer_device.py
@@ -9,6 +9,7 @@ class SequencerInfo:
     channel_name: str # awg_channel or qubit_channel
     phases: List[float]
     gain_correction: List[float]
+    sequencer_offset: int
 
 
 # TODO @@@ retrieve sequencer configuration from M3202A_QS object
@@ -18,10 +19,13 @@ class SequencerDevice:
     name: str
     iq_channels: List[object] = field(default_factory=list) # iq channel objects
     sequencers: List[SequencerInfo] = field(default_factory=list)
+    sequencer_offset: int = 10
 
     def __post_init__(self):
         self.iq_channels = [None]*2
         self.sequencers = [None]*12
+        if hasattr(self.awg, 'get_sequencer_offset'):
+            self.sequencer_offset = self.awg.get_sequencer_offset()
 
     def add_iq_channel(self, IQ_channel, channel_numbers):
         if not channel_numbers in [[1,2], [2,1], [3,4], [4,3]]:
@@ -62,7 +66,8 @@ class SequencerDevice:
             #print(f'{qubit_channel.channel_name} {IQ_comps} {qubit_phases}')
 
             sequencer = SequencerInfo(self.name, seq_num,
-                                      qubit_channel.channel_name, qubit_phases, gain_correction)
+                                      qubit_channel.channel_name, qubit_phases,
+                                      gain_correction, self.sequencer_offset)
             self.sequencers[seq_num-1] = sequencer
             sequencers.append(sequencer)
 
-- 
GitLab