diff --git a/pulse_lib/base_pulse.py b/pulse_lib/base_pulse.py
index 5cbce149a9b895aa789785ef60113eef0d29311c..bdd94b171cfffab97c85c05d9d2e52918b877f4c 100644
--- a/pulse_lib/base_pulse.py
+++ b/pulse_lib/base_pulse.py
@@ -534,12 +534,12 @@ class pulselib:
                                  self.digitizer_channels.values(),
                                  name=name, sample_rate=sample_rate, hres=hres)
 
-    def mk_sequence(self,seq):
+    def mk_sequence(self, segments):
         '''
-        seq: list of segment_container.
+        segments: list of segment_container.
         '''
-        seq_obj = sequencer(self.uploader, self.digitizer_channels)
-        seq_obj.add_sequence(seq)
+        seq_obj = sequencer(self.uploader, self.digitizer_channels, self.awg_channels)
+        seq_obj.add_sequence(segments)
         seq_obj.configure_digitizer = self.configure_digitizer
         return seq_obj
 
diff --git a/pulse_lib/compiler/__init__.py b/pulse_lib/compiler/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/pulse_lib/compiler/condition_measurements.py b/pulse_lib/compiler/condition_measurements.py
new file mode 100644
index 0000000000000000000000000000000000000000..58c97b93c290415651f126b9a261399e4fc8aa05
--- /dev/null
+++ b/pulse_lib/compiler/condition_measurements.py
@@ -0,0 +1,160 @@
+from collections.abc import Iterable
+from collections import defaultdict
+import logging
+import copy
+
+import numpy as np
+
+from pulse_lib.segments.segment_measurements import measurement_acquisition
+from pulse_lib.segments.conditional_segment import conditional_segment
+
+
+logger = logging.getLogger(__name__)
+
+'''
+ConditionSegment -> Acquisition
+Acquisition: Segment#, end-time
+
+Feedback event:
+    * measurement: channel
+    * event set time: measurement end-time
+    * event clear time: condition end-time
+    * conditions
+
+* Qblox:
+    * latch-reset on segment start? latch idle time.
+    * if multiple measurements on sensor: latch-enable interval / latch-disable interval.
+* Keysight:
+    * pxi-trigger assignment
+    * Add support for multiple pxi-triggers
+    * Add pxi-triggers assignment to configuration: single condition could use high bit.
+'''
+
+class FeedbackEvent:
+    def __init__(self, measurement, end_times):
+        self.measurement = measurement
+        self._set_times = end_times
+        self._apply_times = np.full(1, np.nan)
+        self._reset_times = end_times
+
+    @property
+    def set_times(self):
+        return self._set_times
+
+    @property
+    def apply_times(self):
+        return self._apply_times
+
+    @property
+    def reset_times(self):
+        return self._reset_times
+
+    def add_condition(self, condition, start_times, end_times):
+        self._apply_times = np.fmin(self._apply_times, start_times)
+        self._reset_times = np.fmax(self._reset_times, end_times)
+
+
+
+class ConditionMeasurements:
+    def __init__(self, measurements_description, uploader, max_awg_to_dig_delay):
+        self._md = measurements_description
+        self._supports_conditionals = getattr(uploader, 'supports_conditionals', False)
+        if self._supports_conditionals:
+            self.min_feedback_time = uploader.get_roundtrip_latency() + max_awg_to_dig_delay
+        self._measurements = {}
+        self._feedback_events = {}
+        self._end_times = {}
+        self._n_segments = 0
+        self._acquisition_count = defaultdict(int)
+        self._channel_measurements = defaultdict(list)
+        self._conditional_measurements = {}
+
+    def add_segment(self, segment, seg_start_times):
+        self._n_segments += 1
+        self._add_measurements(segment, seg_start_times)
+
+        if not isinstance(segment, conditional_segment):
+            return
+
+        if not self._supports_conditionals:
+            raise Exception(f'Backend does not support conditional segments')
+
+        condition = segment.condition
+        seg_end = seg_start_times + segment.total_time
+
+        # Lookup acquistions for condition
+        acquisition_names = self._get_acquisition_names(condition)
+        logger.info(f'segment {self._n_segments-1} conditional on: {acquisition_names}')
+
+        # Add condition to feedback event
+        measurements = []
+        for name in acquisition_names:
+            # NOTE: lastest measurement with this name before acquisition
+            try:
+                m = self._measurements[name]
+            except KeyError:
+                raise Exception(f'measurement {name} not found before condition')
+            measurements.append(m)
+            try:
+                fb = self._feedback_events[id(m)]
+            except KeyError:
+                fb = FeedbackEvent(m, self._end_times[id(m)])
+                self._feedback_events[id(m)] = fb
+            fb.add_condition(condition, seg_start_times, seg_end)
+
+        self._conditional_measurements[id(segment)] = measurements
+
+    def _add_measurements(self, segment, seg_start_times):
+        if isinstance(segment, conditional_segment):
+            # Conditional branches must all have the same measurements.
+            # use 1st branch of conditional segment.
+            segment = segment.branches[0]
+        for measurement in segment.measurements:
+            if isinstance(measurement, measurement_acquisition):
+                m = copy.copy(measurement)
+                channel_acquisitions = self._channel_measurements[m.acquisition_channel]
+                m.index += len(channel_acquisitions)
+                channel_acquisitions.append(m)
+
+                acq_channel = segment[m.acquisition_channel]
+                end_times = np.zeros(seg_start_times.shape)
+                for index in np.ndindex(seg_start_times.shape):
+                    acq_data = acq_channel._get_data_all_at(index).data[measurement.index]
+                    t_measure = acq_data.t_measure if acq_data.t_measure is not None else 0
+                    end_times[index] = seg_start_times[index] + acq_data.start + t_measure
+                self._end_times[id(m)] = end_times
+            else:
+                m = measurement
+            self._measurements[m.name] = m
+
+    @property
+    def feedback_events(self):
+        return self._feedback_events
+
+    @property
+    def measurement_acquisitions(self):
+        return self._channel_measurements
+
+    def get_end_time(self, measurement, index):
+        return self._end_times[id(measurement)][tuple(index)]
+
+    def get_measurements(self, conditional_segment):
+        return self._conditional_measurements[id(conditional_segment)]
+
+    def check_feedback_timing(self):
+
+        required_time = self.min_feedback_time
+
+        for fb in self._feedback_events.values():
+            margin = fb.apply_times - fb.set_times - required_time
+            if np.min(margin) < 0:
+                raise Exception(f'Insufficient time between measurement {fb.measurement.name} and condition')
+
+    def _get_acquisition_names(self, condition):
+        if isinstance(condition, Iterable):
+            acquisition_names = set()
+            for ref in condition:
+                acquisition_names.update(ref.keys)
+            return list(acquisition_names)
+        return list(condition.keys)
+
diff --git a/pulse_lib/qblox/pulsar_sequencers.py b/pulse_lib/qblox/pulsar_sequencers.py
index e6208cf743d688f3227610b6973cb6ae51198072..7d499731a1fc0b0017275558615c649e5335d48a 100644
--- a/pulse_lib/qblox/pulsar_sequencers.py
+++ b/pulse_lib/qblox/pulsar_sequencers.py
@@ -2,11 +2,20 @@ import logging
 import math
 from numbers import Number
 from copy import copy
-from typing import Any, List, Dict, Callable
+from typing import Any, List, Dict, Callable, Optional
 from dataclasses import dataclass
+from contextlib import contextmanager
 
 import numpy as np
 
+from packaging.version import Version
+from q1pulse import __version__ as q1pulse_version
+
+if Version(q1pulse_version) < Version('0.9.0'):
+    raise Exception('Upgrade q1pulse to version 0.9+')
+
+from q1pulse.lang.conditions import CounterFlags
+
 logger = logging.getLogger(__name__)
 
 def iround(value):
@@ -31,6 +40,20 @@ class PulsarConfig:
         return math.floor(value / PulsarConfig.ALIGNMENT + 1e-8) * PulsarConfig.ALIGNMENT
 
 
+@dataclass
+class LatchEvent:
+    time: int
+    reset: bool = False
+    counters: Optional[List[str]] = None
+
+@dataclass
+class MarkerEvent:
+    time: int
+    enabled_markers: int
+    '''
+    every bit represents a physical marker output.
+    '''
+
 class SequenceBuilderBase:
     def __init__(self, name, sequencer):
         self.name = name
@@ -73,20 +96,24 @@ class SequenceBuilderBase:
     def add_markers(self, markers):
         self.markers = markers
         self.imarker = -1
-        self.set_next_marker()
+        self._set_next_marker()
 
-    def set_next_marker(self):
+    def _set_next_marker(self):
         self.imarker += 1
         if len(self.markers) > self.imarker:
-            self.t_next_marker = self.markers[self.imarker][0]
+            self.t_next_marker = self.markers[self.imarker].time
         else:
             self.t_next_marker = None
 
-    def insert_markers(self, t):
+    def _insert_markers(self, t):
         while self.t_next_marker is not None and t >= self.t_next_marker:
-            marker = self.markers[self.imarker]
-            self._set_markers(marker[0], marker[1])
-            self.set_next_marker()
+            self._add_next_marker()
+
+    def _add_next_marker(self):
+        self.t_end = self.t_next_marker
+        marker = self.markers[self.imarker]
+        self._set_markers(marker.time, marker.enabled_markers)
+        self._set_next_marker()
 
     def _set_markers(self, t, value):
         self.seq.set_markers(value, t_offset=t)
@@ -99,7 +126,7 @@ class SequenceBuilderBase:
     def _update_time_and_markers(self, t, duration):
         if t < self.t_end:
             raise Exception(f'Overlapping pulses {t} < {self.t_end} ({self.name})')
-        self.insert_markers(t)
+        self._insert_markers(t)
         self.t_end = t + duration
 
     def add_comment(self, comment):
@@ -108,12 +135,13 @@ class SequenceBuilderBase:
     def wait_till(self, t):
         t += self.offset_ns
         self._update_time(t, 0)
+        self.seq.add_comment(f'wait {t}')
         self.seq.wait(t)
 
     def finalize(self):
         for i in range(self.imarker, len(self.markers)):
             marker = self.markers[i]
-            self._set_markers(marker[0], marker[1])
+            self._set_markers(marker.time, marker.enabled_markers)
 
 
 # Range is -1.0 ... +1.0 with 16 bits => 2 / 2**16
@@ -440,8 +468,14 @@ class IQSequenceBuilder(SequenceBuilderBase):
                  mixer_gain=None, mixer_phase_offset=None):
         super().__init__(name, sequencer)
         self.nco_frequency = nco_frequency
-        self._square_waves = []
         self.add_comment(f'IQ: NCO={nco_frequency/1e6:7.2f} MHz')
+        self._square_waves = []
+        self._trigger_counters = {}
+        self._uses_feedback = False
+        self._in_conditional = False
+        self._t_next_latch_event = None
+        self._ilatch_event = 0
+        self._latch_events = []
 
         if mixer_gain is not None:
             self.seq.mixer_gain_ratio = mixer_gain[1]/mixer_gain[0]
@@ -491,13 +525,13 @@ class IQSequenceBuilder(SequenceBuilderBase):
                 t_start_offset = t_start % 4
                 t_end_offset = t_end % 4
                 duration = t_end-t_start
-                if t_start_offset == 0 and t_end_offset == 0:
-                    # Create aligned block pulse with offset
-                    self.seq.block_pulse(duration, ampI, ampQ, t_offset=t_start)
-                elif duration < 200:
+                if duration < 200:
                     # Create square waveform with offset
                     wave_id = self._register_squarewave(t_start_offset, duration)
                     self.seq.shaped_pulse(wave_id, ampI, wave_id, ampQ, t_offset=t_pulse)
+                elif t_start_offset == 0 and t_end_offset == 0:
+                    # Create aligned block pulse with offset
+                    self.seq.block_pulse(duration, ampI, ampQ, t_offset=t_start)
                 else:
                     # Create square waveform for start and end, and use offset in between.
                     if t_start_offset:
@@ -570,6 +604,116 @@ class IQSequenceBuilder(SequenceBuilderBase):
             raise Exception(f'{self.name}: NCO frequency {self.nco_frequency/1e6:5.1f} MHz out of range')
         self.seq.nco_frequency = self.nco_frequency
 
+    def add_trigger_counter(self, trigger):
+        self._uses_feedback = True
+        self._trigger_counters[trigger.sequencer_name] = self.seq.add_trigger_counter(trigger)
+
+    @contextmanager
+    def conditional(self, channels, t_min, t_max):
+        t_min += self.offset_ns
+        t_max += self.offset_ns
+        t = max(self.t_end+4, t_min, t_max-40)
+        t = PulsarConfig.ceil(t)
+        if t > t_max-8:
+            raise Exception(f'Failed to schedule conditional pulse on {self.name}. '
+                            f' t:{t} > {t_max-8}')
+        # add markers before conditional
+        self._update_time_and_markers(t, 0)
+        self._in_conditional = True
+        counters = [self._trigger_counters[ch] for ch in channels]
+        flags = CounterFlags(self)
+        with self.seq.conditional(counters, t_offset=t):
+            yield flags
+        self._in_conditional = False
+
+    @contextmanager
+    def condition(self, operator):
+        t_condition_start = self.t_end
+        self.seq.enter_condition(operator)
+        yield
+        self.seq.exit_condition(PulsarConfig.ceil(self.t_end))
+        # reset end time
+        self.t_end = t_condition_start
+
+    def _insert_markers(self, t):
+        '''
+        Override of insert_marker taking care of conditional blocks and
+        counter latching.
+        '''
+        if self._uses_feedback:
+            if self._in_conditional:
+                if self.t_next_marker is not None and t >= self.t_next_marker:
+                    raise Exception(f'Cannot set marker in conditional segment {self.name}, t:{t}')
+                if self._t_next_latch_event is not None and t >= self._t_next_latch_event:
+                    raise Exception(f'Cannot enable latches in conditional segment {self.name}, t:{t}')
+                return
+            else:
+                # add latch events, but not on same time as marker
+                loop = True
+                while loop:
+                    loop = False
+                    if (self._t_next_latch_event is not None
+                        and self._t_next_latch_event < t
+                        and (self.t_next_marker is None
+                             or self._t_next_latch_event < self.t_next_marker)):
+                        self._add_next_latch_event()
+                        loop = True
+                    elif self.t_next_marker is not None and t >= self.t_next_marker:
+                        self._add_next_marker()
+                        loop = True
+        else:
+            super()._insert_markers(t)
+
+    def add_latch_events(self, latch_events):
+        self._latch_events = []
+        for event in latch_events:
+            event = copy(event)
+            event.time = PulsarConfig.floor(event.time + self.offset_ns)
+            self._latch_events.append(event)
+        self._ilatch_event = -1
+        self._set_next_latch_event()
+
+    def _set_next_latch_event(self):
+        self._ilatch_event += 1
+        if len(self._latch_events) > self._ilatch_event:
+            self._t_next_latch_event = self._latch_events[self._ilatch_event].time
+        else:
+            self._t_next_latch_event = None
+
+    def _add_next_latch_event(self):
+        latch_event = self._latch_events[self._ilatch_event]
+        if latch_event.time + 20 < self.t_end:
+            raise Exception(f'Latch event {latch_event} on {self.name} scheduled too late t:{self.t_end}')
+        # Increment time. There could already be a phase shift, awg offset or marker on t_end
+        self.t_end += 4
+        t = PulsarConfig.ceil(max(self.t_end, latch_event.time))
+        logger.info(f'{latch_event} at t={self.t_end}')
+        if latch_event.reset:
+            self.seq.latch_reset(t_offset=t)
+        else:
+            counters = [self._trigger_counters[name] for name in latch_event.counters]
+            self.seq.latch_enable(counters, t_offset=t)
+        # Increment time with time used for latch instruction
+        self.t_end = t+4
+        self._set_next_latch_event()
+
+    def finalize(self):
+        if self._t_next_latch_event is not None:
+            for latch_event in self._latch_events[self._ilatch_event:]:
+                if latch_event.reset:
+                    logger.info(f'latch reset at end {latch_event} ({self.t_end})')
+                    if latch_event.time == np.inf:
+                        t = PulsarConfig.ceil(self.t_end+4)
+                    else:
+                        t = latch_event.time
+                    self.seq.latch_reset(t_offset=t)
+                    self.t_end = max(self.t_end, t+4)
+                else:
+                    logger.info(f'Skipping latch event at end: {latch_event}')
+
+        super().finalize()
+
+
 @dataclass
 class _SeqCommand:
     time: int
@@ -597,6 +741,30 @@ class AcquisitionSequenceBuilder(SequenceBuilderBase):
             self._rf_amplitude = rf_source.amplitude * scaling
             self._n_out_ch = 1 if isinstance(rf_source.output[1], int) else 2
 
+    @property
+    def thresholded_acq_rotation(self):
+        return self.seq.thresholded_acq_rotation
+
+    @thresholded_acq_rotation.setter
+    def thresholded_acq_rotation(self, rotation):
+        self.seq.thresholded_acq_rotation = rotation
+
+    @property
+    def thresholded_acq_threshold(self):
+        return self.seq.thresholded_acq_threshold
+
+    @thresholded_acq_threshold.setter
+    def thresholded_acq_threshold(self, threshold):
+        self.seq.thresholded_acq_threshold = threshold
+
+    @property
+    def thresholded_acq_trigger_invert(self):
+        return self.seq.thresholded_acq_trigger_invert
+
+    @thresholded_acq_trigger_invert.setter
+    def thresholded_acq_trigger_invert(self, invert):
+        self.seq.thresholded_acq_trigger_invert = invert
+
     @property
     def integration_time(self):
         return self.seq.integration_length_acq
diff --git a/pulse_lib/qblox/pulsar_uploader.py b/pulse_lib/qblox/pulsar_uploader.py
index d427fead0eec2960cb103fc6b6a0218d244a0aaf..ee8360f42d75473d40d039ce2b8842796bdff239 100644
--- a/pulse_lib/qblox/pulsar_uploader.py
+++ b/pulse_lib/qblox/pulsar_uploader.py
@@ -1,4 +1,5 @@
 import time
+from collections import Iterable
 from uuid import UUID
 from datetime import datetime
 import numpy as np
@@ -8,6 +9,8 @@ from dataclasses import dataclass, field
 from typing import Dict, Optional, List, Union
 from numbers import Number
 
+from pulse_lib.segments.conditional_segment import conditional_segment
+
 from .rendering import SineWaveform, get_modulation
 from .pulsar_sequencers import (
         Voltage1nsSequenceBuilder,
@@ -15,7 +18,10 @@ from .pulsar_sequencers import (
         IQSequenceBuilder,
         AcquisitionSequenceBuilder,
         SequenceBuilderBase,
-        PulsarConfig)
+        PulsarConfig,
+        MarkerEvent,
+        LatchEvent)
+from .qblox_conditional import get_conditional_channel
 
 from q1pulse import Q1Instrument
 
@@ -66,7 +72,6 @@ class PulsarUploader:
             out_channels = [self.awg_channels[iq_out_ch.awg_channel_name]
                             for iq_out_ch in iq_out_channels]
             module_name = out_channels[0].awg_name
-            # TODO @@@ check I and Q phase.
             q1.add_control(name, module_name, [out_ch.channel_number for out_ch in out_channels])
 
         for name, dig_ch in self.digitizer_channels.items():
@@ -88,11 +93,18 @@ class PulsarUploader:
             if marker_ch.invert:
                 raise Exception(f'Marker channel inversion not (yet) supported')
 
-
     @staticmethod
     def set_output_dir(path):
         PulsarUploader.output_dir = path
 
+    @property
+    def supports_conditionals(self):
+        return True
+
+    def get_roundtrip_latency(self):
+        # TODO @@@ put in configuration file.
+        return 260
+
     def _get_voltage_channels(self):
         iq_out_channels = []
 
@@ -145,11 +157,6 @@ class PulsarUploader:
         self.seq_markers = seq_markers
         self.marker_sequencers = marker_sequencers
 
-
-    @property
-    def supports_conditionals(self):
-        return False
-
     def get_effective_sample_rate(self, sample_rate):
         """
         Returns the sample rate that will be used by the AWG.
@@ -245,12 +252,12 @@ class PulsarUploader:
         total_seconds = job.playback_time * n_rep * 1e-9
         timeout_minutes = int(total_seconds*1.1 / 60) + 1
 
-        # update resonator frequency and amplitude
+        # update resonator frequency and phase
         for ch_name, dig_channel in self.digitizer_channels.items():
             nco_freq = dig_channel.frequency
-            if nco_freq is None:
-                continue
-            job.program[ch_name].nco_frequency = nco_freq
+            if nco_freq is not None:
+                job.program[ch_name].nco_frequency = nco_freq
+            job.program[ch_name].thresholded_acq_rotation = dig_channel.phase
 
         self.q1instrument.start_program(job.program)
         self.q1instrument.wait_stopped(timeout_minutes=timeout_minutes)
@@ -291,7 +298,6 @@ class PulsarUploader:
             if dig_ch.frequency or len(in_ch) == 2:
 
                 if dig_ch.frequency or dig_ch.iq_input:
-                    # @@@ if frequency, set phase in QRM.sequencer phase_rotation_acq
                     raw_ch = (raw[0] + 1j * raw[1]) * np.exp(1j*dig_ch.phase)
                     if not dig_ch.iq_out:
                         raw_ch = raw_ch.real
@@ -380,6 +386,7 @@ class Job(object):
         self.playback_time = 0 #total playtime of the waveform
         self.acquisition_conf = None
         self.acq_data_scaling = {}
+        self.feedback_channels = set()
 
         self.released = False
 
@@ -399,6 +406,59 @@ class Job(object):
     def set_acquisition_conf(self, conf):
         self.acquisition_conf = conf
 
+    def set_feedback(self, condition_measurements):
+        self.condition_measurements = condition_measurements
+        # process feedback events
+        events = []
+        for channel_name, measurements in condition_measurements.measurement_acquisitions.items():
+            for m in measurements:
+                try:
+                    fb = condition_measurements.feedback_events[id(m)]
+                    events.append((self._at_index(fb.set_times), channel_name, 'latch-enable'))
+                    events.append((self._at_index(fb.reset_times), channel_name, 'reset'))
+                except KeyError:
+                    t = condition_measurements.get_end_time(m, self.index)
+                    events.append((t, channel_name, 'latch-disable'))
+
+        latch_events = []
+        feedback_channels = set()
+        active_counters = set()
+        latching_counters = set()
+        pending_resets = set()
+        enabled_latches = set()
+        last_t = 0
+        for t, channel_name, action in sorted(events):
+            if t != last_t and latching_counters != enabled_latches:
+                enabled_latches = latching_counters.copy()
+                latch_events.append(LatchEvent(last_t, counters=list(enabled_latches)))
+                feedback_channels.add(channel_name)
+                last_t = t
+            if action == 'latch-enable':
+                if channel_name in pending_resets:
+                    raise Exception(f'Qblox feedback error: counter not reset before measurement '
+                                    f'{channel_name}, t={t}')
+                active_counters.add(channel_name)
+                latching_counters.add(channel_name)
+            elif action == 'latch-disable':
+                latching_counters.discard(channel_name)
+            elif action == 'reset':
+                active_counters.remove(channel_name)
+                pending_resets.add(channel_name)
+                if not active_counters:
+                    latch_events.append(LatchEvent(t, reset=True))
+                    pending_resets.clear()
+        if pending_resets:
+            latch_events.append(LatchEvent(np.inf, reset=True))
+        self.latch_events = latch_events
+        self.feedback_channels = feedback_channels
+        logger.info(f'Feedback events {latch_events}')
+
+    def _at_index(self, data):
+        try:
+            return data[tuple(self.index[-len(data.shape):])]
+        except AttributeError:
+            return data
+
     def release(self):
         if self.released:
             logger.warning(f'job {self.seq_id}-{self.index} already released')
@@ -515,7 +575,10 @@ class UploadAggregator:
                     channel_info.integral = 0
 
                 if channel_info.dc_compensation:
-                    seg_ch = seg[channel_name]
+                    if isinstance(seg, conditional_segment):
+                        seg_ch = get_conditional_channel(seg, channel_name)
+                    else:
+                        seg_ch = seg[channel_name]
                     channel_info.integral += seg_ch.integrate(job.index, sample_rate)
                     logger.debug(f'Integral seg:{iseg} {channel_name} integral:{channel_info.integral}')
 
@@ -548,11 +611,16 @@ class UploadAggregator:
     def get_markers(self, job, marker_channel):
         # Marker on periods can overlap, also across segments.
         # Get all start/stop times and merge them.
+        channel_name = marker_channel.name
         start_stop = []
         segments = self.segments
         for iseg,(seg,seg_render) in enumerate(zip(job.sequence,segments)):
             offset = seg_render.t_start + marker_channel.delay + self.max_pre_start_ns
-            seg_ch = seg[marker_channel.name]
+            if isinstance(seg, conditional_segment):
+                logger.debug(f'conditional for {channel_name}')
+                seg_ch = get_conditional_channel(seg, channel_name)
+            else:
+                seg_ch = seg[channel_name]
             ch_data = seg_ch._get_data_all_at(job.index)
 
             for pulse in ch_data.my_marker_data:
@@ -561,7 +629,7 @@ class UploadAggregator:
 
         # merge markers
         marker_value = 1 << marker_channel.channel_number
-        return merge_markers(marker_channel.name, start_stop, marker_value, min_off_ns=20)
+        return merge_markers(channel_name, start_stop, marker_value, min_off_ns=20)
 
     def get_markers_seq(self, job, seq_name):
         marker_names = self.seq_markers.get(seq_name, [])
@@ -581,9 +649,9 @@ class UploadAggregator:
             s += value
             if last is not None and t == last:
                 # multiple markers on same time
-                seq_markers[-1] = (t,s)
+                seq_markers[-1] = MarkerEvent(t,s)
             else:
-                seq_markers.append((t,s))
+                seq_markers.append(MarkerEvent(t,s))
                 last = t
 
         return seq_markers
@@ -608,7 +676,11 @@ class UploadAggregator:
 
         for iseg,(seg,seg_render) in enumerate(zip(job.sequence,segments)):
             seg_start = seg_render.t_start
-            seg_ch = seg[channel_name]
+            if isinstance(seg, conditional_segment):
+                logger.debug(f'conditional for {channel_name}')
+                seg_ch = get_conditional_channel(seg, channel_name)
+            else:
+                seg_ch = seg[channel_name]
             data = seg_ch._get_data_all_at(job.index)
             entries = data.get_data_elements(break_ramps=True)
             for e in entries:
@@ -675,17 +747,56 @@ class UploadAggregator:
                                 nco_freq,
                                 mixer_gain=qubit_channel.correction_gain,
                                 mixer_phase_offset=qubit_channel.correction_phase)
-        seq.set_time_offset(t_offset)
-        attenuation = 1.0 # TODO @@@ check if this is always true..
+
+        # lookup attenuation of AWG channels
+        iq_out_channels = qubit_channel.iq_channel.IQ_out_channels
+        att = [self.channels[output.awg_channel_name].attenuation for output in iq_out_channels]
+        if min(att) != max(att):
+            raise Exception('Attenuation for IQ output is not equal for channels '
+                            f'{[[output.awg_channel_name] for output in iq_out_channels]}')
+        attenuation = min(att)
         scaling = 1/(attenuation * seq.max_output_voltage*1000)
 
+        seq.set_time_offset(t_offset)
+        if self.feedback_triggers:
+            for trigger in self.feedback_triggers.values():
+                seq.add_trigger_counter(trigger)
+            seq.add_latch_events(job.latch_events)
+
         markers = self.get_markers_seq(job, channel_name)
         seq.add_markers(markers)
 
         for iseg,(seg,seg_render) in enumerate(zip(job.sequence,segments)):
             seg_start = seg_render.t_start
+            if not isinstance(seg, conditional_segment):
+                seg_ch = seg[channel_name]
+                self._add_iq_data(job, seg_ch, seg_start, scaling, seq, lo_freq)
+            else:
+                measurements = job.condition_measurements.get_measurements(seg)
+                logger.info(f'conditional segment {iseg}, cond:{[m.name for m in measurements]}')
+                if len(measurements) != 1:
+                    raise Exception('Only 1 condition based on 1 measurement is supported.')
+                # TODO @@@ allow inversion of measurement!
+                m = measurements[0]
+                dig_channel = m.acquisition_channel
+                m_time = job.condition_measurements.get_end_time(m, job.index)
+                t_min = m_time+job.condition_measurements.min_feedback_time
+                with seq.conditional([dig_channel], t_min=t_min, t_max=seg_start) as cond:
+                    branches = seg.branches
+                    with cond.all_set():
+                        seg_ch = branches[1][channel_name]
+                        self._add_iq_data(job, seg_ch, seg_start, scaling, seq, lo_freq)
+                    with cond.none_set():
+                        seg_ch = branches[0][channel_name]
+                        self._add_iq_data(job, seg_ch, seg_start, scaling, seq, lo_freq)
+
+
+        t_end = PulsarConfig.ceil(seg_render.t_end)
+        seq.wait_till(t_end)
+        # add final markers
+        seq.finalize()
 
-            seg_ch = seg[channel_name]
+    def _add_iq_data(self, job, seg_ch, seg_start, scaling, seq, lo_freq):
             data = seg_ch._get_data_all_at(job.index)
 
             entries = data.get_data_elements()
@@ -715,12 +826,6 @@ class UploadAggregator:
                 else:
                     raise Exception('Unknown pulse element {type(e)}')
 
-        t_end = PulsarConfig.ceil(seg_render.t_end)
-        seq.wait_till(t_end)
-        # add final markers
-        seq.finalize()
-
-
     def add_acquisition_channel(self, job, digitizer_channel):
         for name in job.schedule_params:
             if name.startswith('dig_trigger_') or name.startswith('dig_wait'):
@@ -741,6 +846,8 @@ class UploadAggregator:
                                          nco_frequency=nco_freq,
                                          rf_source=digitizer_channel.rf_source)
         seq.set_time_offset(t_offset)
+        seq.thresholded_acq_rotation = digitizer_channel.phase
+
         if digitizer_channel.rf_source is not None:
             seq.offset_rf_ns = PulsarConfig.align(self.max_pre_start_ns + digitizer_channel.rf_source.delay)
 
@@ -749,9 +856,16 @@ class UploadAggregator:
         if acq_conf.average_repetitions:
             seq.reset_bin_counter(t=0)
 
+        acq_threshold = None
+        acq_threshold_trigger_invert = False
+        use_feedback = channel_name in self.feedback_triggers
         for iseg, (seg, seg_render) in enumerate(zip(job.sequence, self.segments)):
             seg_start = seg_render.t_start
-            seg_ch = seg[channel_name]
+            if isinstance(seg, conditional_segment):
+                logger.debug(f'conditional for {channel_name}')
+                seg_ch = get_conditional_channel(seg, channel_name)
+            else:
+                seg_ch = seg[channel_name]
             acquisition_data = seg_ch._get_data_all_at(job.index).get_data()
 
             for acquisition in acquisition_data:
@@ -776,6 +890,14 @@ class UploadAggregator:
                     seq.repeated_acquire(t, trigger_period, n_cycles, trigger_period)
                 else:
                     seq.acquire(t, t_measure)
+                if acquisition.threshold is not None and use_feedback:
+                    if acq_threshold is None:
+                        acq_threshold = acquisition.threshold
+                        acq_threshold_trigger_invert = acquisition.zero_on_high
+                    else:
+                        if (acq_threshold != acquisition.threshold
+                            or acq_threshold_trigger_invert != acquisition.zero_on_high):
+                            raise Exception(f'With feedback all thresholds on a channel must be equal')
 
         t_end = PulsarConfig.ceil(seg_render.t_end)
         try:
@@ -783,6 +905,9 @@ class UploadAggregator:
         except:
             raise Exception(f"Acquisition doesn't fit in sequence. Add a wait to extend the sequence.")
         seq.finalize()
+        if acq_threshold is not None:
+            seq.thresholded_acq_threshold = acq_threshold
+            self.feedback_triggers[channel_name].invert = acq_threshold_trigger_invert
         job.acq_data_scaling[channel_name] = seq.get_data_scaling()
 
     def add_marker_seq(self, job, channel_name):
@@ -798,11 +923,17 @@ class UploadAggregator:
         times.append(['start', time.perf_counter()])
 
         name = datetime.now().strftime("%Y%m%d_%H%M%S_%f")
-        self.program = self.q1instrument.new_program(name)
-        job.program = self.program
-        self.program.repetitions = job.n_rep if job.n_rep else 1
+        program = self.q1instrument.new_program(name)
+        self.program = program
+        job.program = program
+        program.repetitions = job.n_rep if job.n_rep else 1
 
-        self.program._timeline.disable_update() # @@@ Yuk
+        self.feedback_triggers = {}
+        for channel in job.feedback_channels:
+            trigger = program.configure_trigger(channel)
+            self.feedback_triggers[channel] = trigger
+
+        program._timeline.disable_update() # @@@ Yuk
 
         times.append(['init', time.perf_counter()])
 
@@ -834,23 +965,23 @@ class UploadAggregator:
 
         times.append(['marker', time.perf_counter()])
 
-        self.program._timeline.enable_update() # @@@ Yuk
+        program._timeline.enable_update() # @@@ Yuk
 
         times.append(['done', time.perf_counter()])
 
         # NOTE: compilation is 20...30% faster with listing=False, add_comments=False
         if UploadAggregator.verbose:
-            self.program.compile(listing=True, json=True)
+            program.compile(listing=True, json=True)
         else:
             retry = False
             try:
-                self.program.compile(add_comments=False, listing=False, json=False)
+                program.compile(add_comments=False, listing=False, json=False)
             except Exception as ex:
                 retry = True
                 print(f'Exception {ex} was raised during compilation. Compiling again with comments.')
             if retry:
                 # retry with listing and comments.
-                self.program.compile(add_comments=True, listing=True, json=True)
+                program.compile(add_comments=True, listing=True, json=True)
 
         times.append(['compile', time.perf_counter()])
 
@@ -891,4 +1022,3 @@ class UploadAggregator:
             result = -channel_info.integral / channel_info.dc_compensation_min
         return result
 
-
diff --git a/pulse_lib/sequencer.py b/pulse_lib/sequencer.py
index a7bafc6129764445adaeb4058142a10d9e8af3c3..4a56dc0d504fbabadb7606d608e1c1f380f0e901 100644
--- a/pulse_lib/sequencer.py
+++ b/pulse_lib/sequencer.py
@@ -10,16 +10,14 @@ from .segments.segment_measurements import measurement_acquisition
 from .segments.utility.data_handling_functions import find_common_dimension, update_dimension
 from .segments.utility.setpoint_mgr import setpoint_mgr, setpoint
 from .segments.utility.looping import loop_obj
-from .segments.utility.measurement_ref import MeasurementRef
 from .measurements_description import measurements_description
 from .acquisition.acquisition_conf import AcquisitionConf
 from .acquisition.player import SequencePlayer
 from .acquisition.measurement_converter import MeasurementConverter, DataSelection, MeasurementParameter
+from .compiler.condition_measurements import ConditionMeasurements
 
 from si_prefix import si_format
 
-from typing import List
-from collections.abc import Iterable
 from numbers import Number
 import numpy as np
 import uuid
@@ -31,7 +29,7 @@ class sequencer():
     """
     Class to make sequences for segments.
     """
-    def __init__(self, upload_module, digitizer_channels):
+    def __init__(self, upload_module, digitizer_channels, awg_channels):
         '''
         make a new sequence object.
         Args:
@@ -51,6 +49,7 @@ class sequencer():
         self.sequence = list()
         self.uploader = upload_module
         self._digitizer_channels = digitizer_channels
+        self._awg_channels = awg_channels
 
         self._measurements_description = measurements_description(digitizer_channels)
 
@@ -219,6 +218,11 @@ class sequencer():
         self._HVI_variables = data_container(marker_HVI_variable())
         self._HVI_variables = update_dimension(self._HVI_variables, self.shape)
 
+        dig_awg_delay = self._calculate_max_dig_delay()
+        self._condition_measurements = ConditionMeasurements(self._measurements_description,
+                                                             self.uploader,
+                                                             dig_awg_delay)
+
         # enforce master clock for the current segments (affects the IQ channels (translated into a phase shift) and and the marker channels (time shifts))
         t_tot = np.zeros(self.shape)
 
@@ -229,16 +233,30 @@ class sequencer():
             lp_time.add_data(t_tot, axis=list(range(self.ndim -1,-1,-1)))
             seg_container.add_master_clock(lp_time)
             self._HVI_variables += seg_container.software_markers.pulse_data_all
-            if isinstance(seg_container, conditional_segment):
-                self._check_conditional(seg_container, t_tot)
+            self._condition_measurements.add_segment(seg_container, t_tot)
             self._measurements_description.add_segment(seg_container, t_tot)
-
             t_tot += seg_container.total_time
         self._measurements_description.add_HVI_variables(self._HVI_variables)
         self._total_time = t_tot
+        self._condition_measurements.check_feedback_timing()
         self._generate_sweep_params()
         self._create_metadata()
 
+    def _calculate_max_dig_delay(self):
+        '''
+        Returns the maximum configured delay from AWG channel to digitizer channel.
+        '''
+        awg_delays = []
+        for channel in self._awg_channels.values():
+            awg_delays.append(channel.delay)
+
+        dig_delays = []
+        for channel in self._digitizer_channels.values():
+            dig_delays.append(channel.delay)
+
+        return max(0, *dig_delays) - min(0, *awg_delays)
+
+
     def _generate_sweep_params(self):
         self.params =[]
 
@@ -261,45 +279,6 @@ class sequencer():
                 LOdict[name] = iq.LO
         self.metadata['LOs'] = LOdict
 
-
-    def _check_conditional(self, conditional:conditional_segment, total_time):
-
-        if not getattr(self.uploader, 'supports_conditionals', False):
-            raise Exception(f'Backend does not support conditional segments')
-
-        condition = conditional.condition
-        refs = condition if isinstance(condition, Iterable) else [condition]
-
-        # Lookup acquistions for condition
-        acquisition_names = self._get_acquisition_names(refs)
-        logger.info(f'acquisitions: {acquisition_names}')
-
-        # check start of conditional pulse
-        min_slack = self._get_min_slack(acquisition_names, total_time)
-        logger.info(f'min slack for conditional {min_slack} ns. (Must be < 0)')
-        if min_slack < 0:
-            raise Exception(f'condition triggered {-min_slack} ns too early')
-
-        pass
-
-    def _get_acquisition_names(self, refs:List[MeasurementRef]):
-        acquisition_names = set()
-        for ref in refs:
-            acquisition_names.update(ref.keys)
-
-        return list(acquisition_names)
-
-    def _get_min_slack(self, acquisition_names, seg_start_times):
-        # calculate slack for all sequence indices
-        slack = np.empty((len(acquisition_names), ) + seg_start_times.shape)
-
-        for i, name in enumerate(acquisition_names):
-            slack[i] = seg_start_times - self._measurements_description.end_times[name]
-
-        slack -= self.uploader.get_roundtrip_latency()
-
-        return np.min(slack)
-
     def voltage_compensation(self, compensate):
         '''
         add a voltage compensation at the end of the sequence
@@ -564,6 +543,8 @@ class sequencer():
                 if self.hw_schedule is not None:
                     hvi_markers = self._HVI_variables.item(tuple(index)).HVI_markers
                     upload_job.add_hw_schedule(self.hw_schedule, hvi_markers)
+                if self._condition_measurements.feedback_events:
+                    upload_job.set_feedback(self._condition_measurements)
 
                 self.uploader.add_upload_job(upload_job)
                 return upload_job