Repository: Qiskit/qiskit

Issue:
Wrong definition of circuit scheduling for measurement
### Environment

- **Qiskit Terra version**: main
- **Python version**: whatever
- **Operating system**: whatever


### What is happening?

As discussed in #7006 currently we allow `measure` instruction to simultaneously write-access to the same classical registers.

```
qc = QuantumCircuit(2, 1)
qc.measure(0, 0)
qc.measure(1, 0)

     ┌─┐   
q_0: ┤M├───
     └╥┘┌─┐
q_1: ─╫─┤M├
      ║ └╥┘
c: 1/═╩══╩═
      0  0 
```

For example, the second measure instruction to q_1 is scheduled in parallel with q_0 (this is okey unless they don't share the classical register, since we can apply stimulus pulses on different qubits). However, once we schedule this circuit

```python
from qiskit.test.mock import FakeAlmaden
from qiskit import circuit, pulse, schedule

qc = circuit.QuantumCircuit(2, 1)
qc.measure(0, 0)
qc.measure(1, 0)

sched = schedule(qc, FakeAlmaden())
```

this yields

```
PulseError: "Schedule(name='') cannot be inserted into Schedule(name='Default measurement schedule for qubits [0, 1]') at time 0 because its instruction on channel MemorySlot(0) scheduled from time 0 to 19200 overlaps with an existing instruction."
```

because measurement trigger on `Acquire` channel overlaps. This can be scheduled by inserting the barrier.

```python
from qiskit.test.mock import FakeAlmaden
from qiskit import circuit, pulse, schedule
from qiskit.visualization.pulse_v2.stylesheet import IQXDebugging

qc = circuit.QuantumCircuit(2, 1)
qc.measure(0, 0)
qc.barrier()
qc.measure(1, 0)

sched = schedule(qc, FakeAlmaden())
sched.draw(style=IQXDebugging())
```

![image](https://user-images.githubusercontent.com/39517270/153680869-ebc7cd38-42b3-4904-86cc-43832552d5c0.png)

Regardless of how IBM implements control-flow, we should implement scheduler so that we have identical behavior with Qiskit Pulse since Qiskit is in principle backend-agnostic. 

In addition, this logic induces weird outcome.

```
circuit = QuantumCircuit(3, 1)
circuit.x(0)
circuit.measure(0, 0)
circuit.x(1).c_if(0, 1)
circuit.measure(2, 0)

     ┌───┐┌─┐              
q_0: ┤ X ├┤M├──────────────
     └───┘└╥┘   ┌───┐      
q_1: ──────╫────┤ X ├──────
           ║    └─╥─┘   ┌─┐
q_2: ──────╫──────╫─────┤M├
           ║ ┌────╨────┐└╥┘
c: 1/══════╩═╡ c_0=0x1 ╞═╩═
           0 └─────────┘ 0 
```

The ALAP (or ASAP) scheduled circuit will become
```
            ┌───┐       ┌─┐┌────────────────┐   
q_0: ───────┤ X ├───────┤M├┤ Delay(160[dt]) ├───
     ┌──────┴───┴──────┐└╥┘└─────┬───┬──────┘   
q_1: ┤ Delay(1760[dt]) ├─╫───────┤ X ├──────────
     └┬────────────────┤ ║       └─╥─┘       ┌─┐
q_2: ─┤ Delay(320[dt]) ├─╫─────────╫─────────┤M├
      └────────────────┘ ║    ┌────╨────┐    └╥┘
c: 1/════════════════════╩════╡ c_0=0x1 ╞═════╩═
                         0    └─────────┘     0 
```

Likely measurement on q_2 (let's say M2 node) start after t0=320 dt, which is computed based on execution time of X[0] (X0 node) and X[1] (X1 node), i.e. M2 depends on X1, and X1 depend on the measurement on q_0 (M0), and M0 is executed after X0. However, scheduler allows M0 and M2 to start at the same t0. However, actually this will never happen because M2 depends on X1 and X1 should be executed AFTER M0. So correct output should be:

```
            ┌───┐       ┌─┐              
q_0: ───────┤ X ├───────┤M├──────────────
     ┌──────┴───┴──────┐└╥┘   ┌───┐      
q_1: ┤ Delay(1760[dt]) ├─╫────┤ X ├──────
     ├─────────────────┤ ║    └─╥─┘   ┌─┐
q_2: ┤ Delay(1920[dt]) ├─╫──────╫─────┤M├
     └─────────────────┘ ║ ┌────╨────┐└╥┘
c: 1/════════════════════╩═╡ c_0=0x1 ╞═╩═
                         0 └─────────┘ 0 
```

Also scheduler outputs delays after end of circuit, which are basically redundant.

### How can we reproduce the issue?

see description above

### What should happen?

see description above

### Any suggestions?

_No response_


Discussion:

Resolving patch:
diff --git a/qiskit/transpiler/passes/scheduling/alap.py b/qiskit/transpiler/passes/scheduling/alap.py
--- a/qiskit/transpiler/passes/scheduling/alap.py
+++ b/qiskit/transpiler/passes/scheduling/alap.py
@@ -11,41 +11,20 @@
 # that they have been altered from the originals.
 
 """ALAP Scheduling."""
-import itertools
-from collections import defaultdict
-from typing import List
-
-from qiskit.circuit import Delay, Measure
-from qiskit.circuit.parameterexpression import ParameterExpression
+from qiskit.circuit import Delay, Qubit, Measure
 from qiskit.dagcircuit import DAGCircuit
-from qiskit.transpiler.basepasses import TransformationPass
 from qiskit.transpiler.exceptions import TranspilerError
-from qiskit.transpiler.passes.scheduling.time_unit_conversion import TimeUnitConversion
 
+from .base_scheduler import BaseScheduler
 
-class ALAPSchedule(TransformationPass):
-    """ALAP Scheduling pass, which schedules the **stop** time of instructions as late as possible.
 
-    For circuits with instructions writing or reading clbits (e.g. measurements, conditional gates),
-    the scheduler assumes clbits I/O operations take no time, ``measure`` locks clbits to be written
-    at its end and ``c_if`` locks clbits to be read at its beginning.
+class ALAPSchedule(BaseScheduler):
+    """ALAP Scheduling pass, which schedules the **stop** time of instructions as late as possible.
 
-    Notes:
-        The ALAP scheduler may not schedule a circuit exactly the same as any real backend does
-        when the circuit contains control flows (e.g. conditional instructions).
+    See :class:`~qiskit.transpiler.passes.scheduling.base_scheduler.BaseScheduler` for the
+    detailed behavior of the control flow operation, i.e. ``c_if``.
     """
 
-    def __init__(self, durations):
-        """ALAPSchedule initializer.
-
-        Args:
-            durations (InstructionDurations): Durations of instructions to be used in scheduling
-        """
-        super().__init__()
-        self.durations = durations
-        # ensure op node durations are attached and in consistent unit
-        self.requires.append(TimeUnitConversion(durations))
-
     def run(self, dag):
         """Run the ALAPSchedule pass on `dag`.
 
@@ -57,6 +36,7 @@ def run(self, dag):
 
         Raises:
             TranspilerError: if the circuit is not mapped on physical qubits.
+            TranspilerError: if conditional bit is added to non-supported instruction.
         """
         if len(dag.qregs) != 1 or dag.qregs.get("q", None) is None:
             raise TranspilerError("ALAP schedule runs on physical circuits only")
@@ -68,71 +48,95 @@ def run(self, dag):
         for creg in dag.cregs.values():
             new_dag.add_creg(creg)
 
-        qubit_time_available = defaultdict(int)
-        clbit_readable = defaultdict(int)
-        clbit_writeable = defaultdict(int)
-
-        def pad_with_delays(qubits: List[int], until, unit) -> None:
-            """Pad idle time-slots in ``qubits`` with delays in ``unit`` until ``until``."""
-            for q in qubits:
-                if qubit_time_available[q] < until:
-                    idle_duration = until - qubit_time_available[q]
-                    new_dag.apply_operation_front(Delay(idle_duration, unit), [q], [])
-
+        idle_before = {q: 0 for q in dag.qubits + dag.clbits}
         bit_indices = {bit: index for index, bit in enumerate(dag.qubits)}
         for node in reversed(list(dag.topological_op_nodes())):
-            # validate node.op.duration
-            if node.op.duration is None:
-                indices = [bit_indices[qarg] for qarg in node.qargs]
-                if dag.has_calibration_for(node):
-                    node.op.duration = dag.calibrations[node.op.name][
-                        (tuple(indices), tuple(float(p) for p in node.op.params))
-                    ].duration
-
-                if node.op.duration is None:
+            op_duration = self._get_node_duration(node, bit_indices, dag)
+
+            # compute t0, t1: instruction interval, note that
+            # t0: start time of instruction
+            # t1: end time of instruction
+
+            # since this is alap scheduling, node is scheduled in reversed topological ordering
+            # and nodes are packed from the very end of the circuit.
+            # the physical meaning of t0 and t1 is flipped here.
+            if isinstance(node.op, self.CONDITIONAL_SUPPORTED):
+                t0q = max(idle_before[q] for q in node.qargs)
+                if node.op.condition_bits:
+                    # conditional is bit tricky due to conditional_latency
+                    t0c = max(idle_before[c] for c in node.op.condition_bits)
+                    # Assume following case (t0c > t0q):
+                    #
+                    #                |t0q
+                    # Q ░░░░░░░░░░░░░▒▒▒
+                    # C ░░░░░░░░▒▒▒▒▒▒▒▒
+                    #           |t0c
+                    #
+                    # In this case, there is no actual clbit read before gate.
+                    #
+                    #             |t0q' = t0c - conditional_latency
+                    # Q ░░░░░░░░▒▒▒░░▒▒▒
+                    # C ░░░░░░▒▒▒▒▒▒▒▒▒▒
+                    #         |t1c' = t0c + conditional_latency
+                    #
+                    # rather than naively doing
+                    #
+                    #        |t1q' = t0c + duration
+                    # Q ░░░░░▒▒▒░░░░░▒▒▒
+                    # C ░░▒▒░░░░▒▒▒▒▒▒▒▒
+                    #     |t1c' = t0c + duration + conditional_latency
+                    #
+                    t0 = max(t0q, t0c - op_duration)
+                    t1 = t0 + op_duration
+                    for clbit in node.op.condition_bits:
+                        idle_before[clbit] = t1 + self.conditional_latency
+                else:
+                    t0 = t0q
+                    t1 = t0 + op_duration
+            else:
+                if node.op.condition_bits:
                     raise TranspilerError(
-                        f"Duration of {node.op.name} on qubits {indices} is not found."
+                        f"Conditional instruction {node.op.name} is not supported in ALAP scheduler."
                     )
-            if isinstance(node.op.duration, ParameterExpression):
-                indices = [bit_indices[qarg] for qarg in node.qargs]
-                raise TranspilerError(
-                    f"Parameterized duration ({node.op.duration}) "
-                    f"of {node.op.name} on qubits {indices} is not bounded."
-                )
-            # choose appropriate clbit available time depending on op
-            clbit_time_available = (
-                clbit_writeable if isinstance(node.op, Measure) else clbit_readable
-            )
-            # correction to change clbit start time to qubit start time
-            delta = 0 if isinstance(node.op, Measure) else node.op.duration
-            # must wait for op.condition_bits as well as node.cargs
-            start_time = max(
-                itertools.chain(
-                    (qubit_time_available[q] for q in node.qargs),
-                    (clbit_time_available[c] - delta for c in node.cargs + node.op.condition_bits),
-                )
-            )
-
-            pad_with_delays(node.qargs, until=start_time, unit=time_unit)
 
-            new_dag.apply_operation_front(node.op, node.qargs, node.cargs)
+                if isinstance(node.op, Measure):
+                    # clbit time is always right (alap) justified
+                    t0 = max(idle_before[bit] for bit in node.qargs + node.cargs)
+                    t1 = t0 + op_duration
+                    #
+                    #        |t1 = t0 + duration
+                    # Q ░░░░░▒▒▒▒▒▒▒▒▒▒▒
+                    # C ░░░░░░░░░▒▒▒▒▒▒▒
+                    #            |t0 + (duration - clbit_write_latency)
+                    #
+                    for clbit in node.cargs:
+                        idle_before[clbit] = t0 + (op_duration - self.clbit_write_latency)
+                else:
+                    # It happens to be directives such as barrier
+                    t0 = max(idle_before[bit] for bit in node.qargs + node.cargs)
+                    t1 = t0 + op_duration
+
+            for bit in node.qargs:
+                delta = t0 - idle_before[bit]
+                if delta > 0:
+                    new_dag.apply_operation_front(Delay(delta, time_unit), [bit], [])
+                idle_before[bit] = t1
 
-            stop_time = start_time + node.op.duration
-            # update time table
-            for q in node.qargs:
-                qubit_time_available[q] = stop_time
-            for c in node.cargs:  # measure
-                clbit_writeable[c] = clbit_readable[c] = start_time
-            for c in node.op.condition_bits:  # conditional op
-                clbit_writeable[c] = max(stop_time, clbit_writeable[c])
+            new_dag.apply_operation_front(node.op, node.qargs, node.cargs)
 
-        working_qubits = qubit_time_available.keys()
-        circuit_duration = max(qubit_time_available[q] for q in working_qubits)
-        pad_with_delays(new_dag.qubits, until=circuit_duration, unit=time_unit)
+        circuit_duration = max(idle_before.values())
+        for bit, before in idle_before.items():
+            delta = circuit_duration - before
+            if not (delta > 0 and isinstance(bit, Qubit)):
+                continue
+            new_dag.apply_operation_front(Delay(delta, time_unit), [bit], [])
 
         new_dag.name = dag.name
         new_dag.metadata = dag.metadata
+        new_dag.calibrations = dag.calibrations
+
         # set circuit duration and unit to indicate it is scheduled
         new_dag.duration = circuit_duration
         new_dag.unit = time_unit
+
         return new_dag
diff --git a/qiskit/transpiler/passes/scheduling/asap.py b/qiskit/transpiler/passes/scheduling/asap.py
--- a/qiskit/transpiler/passes/scheduling/asap.py
+++ b/qiskit/transpiler/passes/scheduling/asap.py
@@ -11,41 +11,20 @@
 # that they have been altered from the originals.
 
 """ASAP Scheduling."""
-import itertools
-from collections import defaultdict
-from typing import List
-
-from qiskit.circuit import Delay, Measure
-from qiskit.circuit.parameterexpression import ParameterExpression
+from qiskit.circuit import Delay, Qubit, Measure
 from qiskit.dagcircuit import DAGCircuit
-from qiskit.transpiler.basepasses import TransformationPass
 from qiskit.transpiler.exceptions import TranspilerError
-from qiskit.transpiler.passes.scheduling.time_unit_conversion import TimeUnitConversion
 
+from .base_scheduler import BaseScheduler
 
-class ASAPSchedule(TransformationPass):
-    """ASAP Scheduling pass, which schedules the start time of instructions as early as possible..
 
-    For circuits with instructions writing or reading clbits (e.g. measurements, conditional gates),
-    the scheduler assumes clbits I/O operations take no time, ``measure`` locks clbits to be written
-    at its end and ``c_if`` locks clbits to be read at its beginning.
+class ASAPSchedule(BaseScheduler):
+    """ASAP Scheduling pass, which schedules the start time of instructions as early as possible..
 
-    Notes:
-        The ASAP scheduler may not schedule a circuit exactly the same as any real backend does
-        when the circuit contains control flows (e.g. conditional instructions).
+    See :class:`~qiskit.transpiler.passes.scheduling.base_scheduler.BaseScheduler` for the
+    detailed behavior of the control flow operation, i.e. ``c_if``.
     """
 
-    def __init__(self, durations):
-        """ASAPSchedule initializer.
-
-        Args:
-            durations (InstructionDurations): Durations of instructions to be used in scheduling
-        """
-        super().__init__()
-        self.durations = durations
-        # ensure op node durations are attached and in consistent unit
-        self.requires.append(TimeUnitConversion(durations))
-
     def run(self, dag):
         """Run the ASAPSchedule pass on `dag`.
 
@@ -57,6 +36,7 @@ def run(self, dag):
 
         Raises:
             TranspilerError: if the circuit is not mapped on physical qubits.
+            TranspilerError: if conditional bit is added to non-supported instruction.
         """
         if len(dag.qregs) != 1 or dag.qregs.get("q", None) is None:
             raise TranspilerError("ASAP schedule runs on physical circuits only")
@@ -69,70 +49,105 @@ def run(self, dag):
         for creg in dag.cregs.values():
             new_dag.add_creg(creg)
 
-        qubit_time_available = defaultdict(int)
-        clbit_readable = defaultdict(int)
-        clbit_writeable = defaultdict(int)
-
-        def pad_with_delays(qubits: List[int], until, unit) -> None:
-            """Pad idle time-slots in ``qubits`` with delays in ``unit`` until ``until``."""
-            for q in qubits:
-                if qubit_time_available[q] < until:
-                    idle_duration = until - qubit_time_available[q]
-                    new_dag.apply_operation_back(Delay(idle_duration, unit), [q])
-
+        idle_after = {q: 0 for q in dag.qubits + dag.clbits}
         bit_indices = {q: index for index, q in enumerate(dag.qubits)}
         for node in dag.topological_op_nodes():
-            # validate node.op.duration
-            if node.op.duration is None:
-                indices = [bit_indices[qarg] for qarg in node.qargs]
-                if dag.has_calibration_for(node):
-                    node.op.duration = dag.calibrations[node.op.name][
-                        (tuple(indices), tuple(float(p) for p in node.op.params))
-                    ].duration
-
-                if node.op.duration is None:
+            op_duration = self._get_node_duration(node, bit_indices, dag)
+
+            # compute t0, t1: instruction interval, note that
+            # t0: start time of instruction
+            # t1: end time of instruction
+            if isinstance(node.op, self.CONDITIONAL_SUPPORTED):
+                t0q = max(idle_after[q] for q in node.qargs)
+                if node.op.condition_bits:
+                    # conditional is bit tricky due to conditional_latency
+                    t0c = max(idle_after[bit] for bit in node.op.condition_bits)
+                    if t0q > t0c:
+                        # This is situation something like below
+                        #
+                        #           |t0q
+                        # Q ▒▒▒▒▒▒▒▒▒░░
+                        # C ▒▒▒░░░░░░░░
+                        #     |t0c
+                        #
+                        # In this case, you can insert readout access before tq0
+                        #
+                        #           |t0q
+                        # Q ▒▒▒▒▒▒▒▒▒▒▒
+                        # C ▒▒▒░░░▒▒░░░
+                        #         |t0q - conditional_latency
+                        #
+                        t0c = max(t0q - self.conditional_latency, t0c)
+                    t1c = t0c + self.conditional_latency
+                    for bit in node.op.condition_bits:
+                        # Lock clbit until state is read
+                        idle_after[bit] = t1c
+                    # It starts after register read access
+                    t0 = max(t0q, t1c)
+                else:
+                    t0 = t0q
+                t1 = t0 + op_duration
+            else:
+                if node.op.condition_bits:
                     raise TranspilerError(
-                        f"Duration of {node.op.name} on qubits {indices} is not found."
+                        f"Conditional instruction {node.op.name} is not supported in ASAP scheduler."
                     )
-            if isinstance(node.op.duration, ParameterExpression):
-                indices = [bit_indices[qarg] for qarg in node.qargs]
-                raise TranspilerError(
-                    f"Parameterized duration ({node.op.duration}) "
-                    f"of {node.op.name} on qubits {indices} is not bounded."
-                )
-            # choose appropriate clbit available time depending on op
-            clbit_time_available = (
-                clbit_writeable if isinstance(node.op, Measure) else clbit_readable
-            )
-            # correction to change clbit start time to qubit start time
-            delta = node.op.duration if isinstance(node.op, Measure) else 0
-            # must wait for op.condition_bits as well as node.cargs
-            start_time = max(
-                itertools.chain(
-                    (qubit_time_available[q] for q in node.qargs),
-                    (clbit_time_available[c] - delta for c in node.cargs + node.op.condition_bits),
-                )
-            )
-
-            pad_with_delays(node.qargs, until=start_time, unit=time_unit)
 
-            new_dag.apply_operation_back(node.op, node.qargs, node.cargs)
+                if isinstance(node.op, Measure):
+                    # measure instruction handling is bit tricky due to clbit_write_latency
+                    t0q = max(idle_after[q] for q in node.qargs)
+                    t0c = max(idle_after[c] for c in node.cargs)
+                    # Assume following case (t0c > t0q)
+                    #
+                    #       |t0q
+                    # Q ▒▒▒▒░░░░░░░░░░░░
+                    # C ▒▒▒▒▒▒▒▒░░░░░░░░
+                    #           |t0c
+                    #
+                    # In this case, there is no actual clbit access until clbit_write_latency.
+                    # The node t0 can be push backward by this amount.
+                    #
+                    #         |t0q' = t0c - clbit_write_latency
+                    # Q ▒▒▒▒░░▒▒▒▒▒▒▒▒▒▒
+                    # C ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
+                    #           |t0c' = t0c
+                    #
+                    # rather than naively doing
+                    #
+                    #           |t0q' = t0c
+                    # Q ▒▒▒▒░░░░▒▒▒▒▒▒▒▒
+                    # C ▒▒▒▒▒▒▒▒░░░▒▒▒▒▒
+                    #              |t0c' = t0c + clbit_write_latency
+                    #
+                    t0 = max(t0q, t0c - self.clbit_write_latency)
+                    t1 = t0 + op_duration
+                    for clbit in node.cargs:
+                        idle_after[clbit] = t1
+                else:
+                    # It happens to be directives such as barrier
+                    t0 = max(idle_after[bit] for bit in node.qargs + node.cargs)
+                    t1 = t0 + op_duration
+
+            # Add delay to qubit wire
+            for bit in node.qargs:
+                delta = t0 - idle_after[bit]
+                if delta > 0 and isinstance(bit, Qubit):
+                    new_dag.apply_operation_back(Delay(delta, time_unit), [bit], [])
+                idle_after[bit] = t1
 
-            stop_time = start_time + node.op.duration
-            # update time table
-            for q in node.qargs:
-                qubit_time_available[q] = stop_time
-            for c in node.cargs:  # measure
-                clbit_writeable[c] = clbit_readable[c] = stop_time
-            for c in node.op.condition_bits:  # conditional op
-                clbit_writeable[c] = max(start_time, clbit_writeable[c])
+            new_dag.apply_operation_back(node.op, node.qargs, node.cargs)
 
-        working_qubits = qubit_time_available.keys()
-        circuit_duration = max(qubit_time_available[q] for q in working_qubits)
-        pad_with_delays(new_dag.qubits, until=circuit_duration, unit=time_unit)
+        circuit_duration = max(idle_after.values())
+        for bit, after in idle_after.items():
+            delta = circuit_duration - after
+            if not (delta > 0 and isinstance(bit, Qubit)):
+                continue
+            new_dag.apply_operation_back(Delay(delta, time_unit), [bit], [])
 
         new_dag.name = dag.name
         new_dag.metadata = dag.metadata
+        new_dag.calibrations = dag.calibrations
+
         # set circuit duration and unit to indicate it is scheduled
         new_dag.duration = circuit_duration
         new_dag.unit = time_unit
diff --git a/qiskit/transpiler/passes/scheduling/base_scheduler.py b/qiskit/transpiler/passes/scheduling/base_scheduler.py
new file mode 100644
--- /dev/null
+++ b/qiskit/transpiler/passes/scheduling/base_scheduler.py
@@ -0,0 +1,269 @@
+# This code is part of Qiskit.
+#
+# (C) Copyright IBM 2020.
+#
+# This code is licensed under the Apache License, Version 2.0. You may
+# obtain a copy of this license in the LICENSE.txt file in the root directory
+# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
+#
+# Any modifications or derivative works of this code must retain this
+# copyright notice, and modified files need to carry a notice indicating
+# that they have been altered from the originals.
+
+"""Base circuit scheduling pass."""
+
+from typing import Dict
+from qiskit.transpiler import InstructionDurations
+from qiskit.transpiler.basepasses import TransformationPass
+from qiskit.transpiler.passes.scheduling.time_unit_conversion import TimeUnitConversion
+from qiskit.dagcircuit import DAGOpNode, DAGCircuit
+from qiskit.circuit import Delay, Gate
+from qiskit.circuit.parameterexpression import ParameterExpression
+from qiskit.transpiler.exceptions import TranspilerError
+
+
+class BaseScheduler(TransformationPass):
+    """Base scheduler pass.
+
+    Policy of topological node ordering in scheduling
+
+        The DAG representation of ``QuantumCircuit`` respects the node ordering also in the
+        classical register wires, though theoretically two conditional instructions
+        conditioned on the same register are commute, i.e. read-access to the
+        classical register doesn't change its state.
+
+        .. parsed-literal::
+
+            qc = QuantumCircuit(2, 1)
+            qc.delay(100, 0)
+            qc.x(0).c_if(0, True)
+            qc.x(1).c_if(0, True)
+
+        The scheduler SHOULD comply with above topological ordering policy of the DAG circuit.
+        Accordingly, the `asap`-scheduled circuit will become
+
+        .. parsed-literal::
+
+                 ┌────────────────┐   ┌───┐
+            q_0: ┤ Delay(100[dt]) ├───┤ X ├──────────────
+                 ├────────────────┤   └─╥─┘      ┌───┐
+            q_1: ┤ Delay(100[dt]) ├─────╫────────┤ X ├───
+                 └────────────────┘     ║        └─╥─┘
+                                   ┌────╨────┐┌────╨────┐
+            c: 1/══════════════════╡ c_0=0x1 ╞╡ c_0=0x1 ╞
+                                   └─────────┘└─────────┘
+
+        Note that this scheduling might be inefficient in some cases,
+        because the second conditional operation can start without waiting the delay of 100 dt.
+        However, such optimization should be done by another pass,
+        otherwise scheduling may break topological ordering of the original circuit.
+
+    Realistic control flow scheduling respecting for microarcitecture
+
+        In the dispersive QND readout scheme, qubit is measured with microwave stimulus to qubit (Q)
+        followed by resonator ring-down (depopulation). This microwave signal is recorded
+        in the buffer memory (B) with hardware kernel, then a discriminated (D) binary value
+        is moved to the classical register (C).
+        The sequence from t0 to t1 of the measure instruction interval might be modeled as follows:
+
+        .. parsed-literal::
+
+            Q ░▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒░
+            B ░░▒▒▒▒▒▒▒▒░░░░░░░░░
+            D ░░░░░░░░░░▒▒▒▒▒▒░░░
+            C ░░░░░░░░░░░░░░░░▒▒░
+
+        However, ``QuantumCircuit`` representation is not enough accurate to represent
+        this model. In the circuit representation, thus ``Qubit`` is occupied by the
+        stimulus microwave signal during the first half of the interval,
+        and ``Clbit`` is only occupied at the very end of the interval.
+
+        This precise model may induce weird edge case.
+
+        .. parsed-literal::
+
+                    ┌───┐
+            q_0: ───┤ X ├──────
+                    └─╥─┘   ┌─┐
+            q_1: ─────╫─────┤M├
+                 ┌────╨────┐└╥┘
+            c: 1/╡ c_0=0x1 ╞═╩═
+                 └─────────┘ 0
+
+        In this example, user may intend to measure the state of ``q_1``, after ``XGate`` is
+        applied to the ``q_0``. This is correct interpretation from viewpoint of
+        the topological node ordering, i.e. x gate node come in front of the measure node.
+        However, according to the measurement model above, the data in the register
+        is unchanged during the stimulus, thus two nodes are simultaneously operated.
+        If one `alap`-schedule this circuit, it may return following circuit.
+
+        .. parsed-literal::
+
+                 ┌────────────────┐   ┌───┐
+            q_0: ┤ Delay(500[dt]) ├───┤ X ├──────
+                 └────────────────┘   └─╥─┘   ┌─┐
+            q_1: ───────────────────────╫─────┤M├
+                                   ┌────╨────┐└╥┘
+            c: 1/══════════════════╡ c_0=0x1 ╞═╩═
+                                   └─────────┘ 0
+
+        Note that there is no delay on ``q_1`` wire, and the measure instruction immediately
+        start after t=0, while the conditional gate starts after the delay.
+        It looks like the topological ordering between the nodes are flipped in the scheduled view.
+        This behavior can be understood by considering the control flow model described above,
+
+        .. parsed-literal::
+
+            : Quantum Circuit, first-measure
+            0 ░░░░░░░░░░░░▒▒▒▒▒▒░
+            1 ░▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒░
+
+            : In wire q0
+            Q ░░░░░░░░░░░░░░░▒▒▒░
+            C ░░░░░░░░░░░░▒▒░░░░░
+
+            : In wire q1
+            Q ░▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒░
+            B ░░▒▒▒▒▒▒▒▒░░░░░░░░░
+            D ░░░░░░░░░░▒▒▒▒▒▒░░░
+            C ░░░░░░░░░░░░░░░░▒▒░
+
+        Since there is no qubit register (Q0, Q1) overlap, the node ordering is determined by the
+        shared classical register C. As you can see, the execution order is still
+        preserved on C, i.e. read C then apply ``XGate``, finally store the measured outcome in C.
+        Because ``DAGOpNode`` cannot define different durations for associated registers,
+        the time ordering of two nodes is inverted anyways.
+
+        This behavior can be controlled by ``clbit_write_latency`` and ``conditional_latency``.
+        The former parameter determines the delay of the register write-access from
+        the beginning of the measure instruction t0, and another parameter determines
+        the delay of conditional gate operation from t0 which comes from the register read-access.
+
+        Since we usually expect topological ordering and time ordering are identical
+        without the context of microarchitecture, both latencies are set to zero by default.
+        In this case, ``Measure`` instruction immediately locks the register C.
+        Under this configuration, the `alap`-scheduled circuit of above example may become
+
+        .. parsed-literal::
+
+                    ┌───┐
+            q_0: ───┤ X ├──────
+                    └─╥─┘   ┌─┐
+            q_1: ─────╫─────┤M├
+                 ┌────╨────┐└╥┘
+            c: 1/╡ c_0=0x1 ╞═╩═
+                 └─────────┘ 0
+
+        If the backend microarchitecture supports smart scheduling of the control flow, i.e.
+        it may separately schedule qubit and classical register,
+        insertion of the delay yields unnecessary longer total execution time.
+
+        .. parsed-literal::
+            : Quantum Circuit, first-xgate
+            0 ░▒▒▒░░░░░░░░░░░░░░░
+            1 ░▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒░
+
+            : In wire q0
+            Q ░▒▒▒░░░░░░░░░░░░░░░
+            C ░░░░░░░░░░░░░░░░░░░ (zero latency)
+
+            : In wire q1
+            Q ░▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒░
+            C ░▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒░ (zero latency, scheduled after C0 read-access)
+
+        However this result is much more intuitive in the topological ordering view.
+        If finite conditional latency is provided, for example, 30 dt, the circuit
+        is scheduled as follows.
+
+        .. parsed-literal::
+
+                 ┌───────────────┐   ┌───┐
+            q_0: ┤ Delay(30[dt]) ├───┤ X ├──────
+                 ├───────────────┤   └─╥─┘   ┌─┐
+            q_1: ┤ Delay(30[dt]) ├─────╫─────┤M├
+                 └───────────────┘┌────╨────┐└╥┘
+            c: 1/═════════════════╡ c_0=0x1 ╞═╩═
+                                  └─────────┘ 0
+
+        with the timing model:
+
+        .. parsed-literal::
+            : Quantum Circuit, first-xgate
+            0 ░░▒▒▒░░░░░░░░░░░░░░░
+            1 ░░▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒░
+
+            : In wire q0
+            Q ░░▒▒▒░░░░░░░░░░░░░░░
+            C ░▒░░░░░░░░░░░░░░░░░░ (30dt latency)
+
+            : In wire q1
+            Q ░░▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒░
+            C ░░▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒░
+
+        See https://arxiv.org/abs/2102.01682 for more details.
+
+    """
+
+    CONDITIONAL_SUPPORTED = (Gate, Delay)
+
+    def __init__(
+        self,
+        durations: InstructionDurations,
+        clbit_write_latency: int = 0,
+        conditional_latency: int = 0,
+    ):
+        """Scheduler initializer.
+
+        Args:
+            durations: Durations of instructions to be used in scheduling
+            clbit_write_latency: A control flow constraints. Because standard superconducting
+                quantum processor implement dispersive QND readout, the actual data transfer
+                to the clbit happens after the round-trip stimulus signal is buffered
+                and discriminated into quantum state.
+                The interval ``[t0, t0 + clbit_write_latency]`` is regarded as idle time
+                for clbits associated with the measure instruction.
+                This defaults to 0 dt which is identical to Qiskit Pulse scheduler.
+            conditional_latency: A control flow constraints. This value represents
+                a latency of reading a classical register for the conditional operation.
+                The gate operation occurs after this latency. This appears as a delay
+                in front of the DAGOpNode of the gate.
+                This defaults to 0 dt.
+        """
+        super().__init__()
+        self.durations = durations
+
+        # Control flow constraints.
+        self.clbit_write_latency = clbit_write_latency
+        self.conditional_latency = conditional_latency
+
+        # Ensure op node durations are attached and in consistent unit
+        self.requires.append(TimeUnitConversion(durations))
+
+    @staticmethod
+    def _get_node_duration(
+        node: DAGOpNode,
+        bit_index_map: Dict,
+        dag: DAGCircuit,
+    ) -> int:
+        """A helper method to get duration from node or calibration."""
+        indices = [bit_index_map[qarg] for qarg in node.qargs]
+
+        if dag.has_calibration_for(node):
+            # If node has calibration, this value should be the highest priority
+            cal_key = tuple(indices), tuple(float(p) for p in node.op.params)
+            duration = dag.calibrations[node.op.name][cal_key].duration
+        else:
+            duration = node.op.duration
+
+        if isinstance(duration, ParameterExpression):
+            raise TranspilerError(
+                f"Parameterized duration ({duration}) "
+                f"of {node.op.name} on qubits {indices} is not bounded."
+            )
+        if duration is None:
+            raise TranspilerError(f"Duration of {node.op.name} on qubits {indices} is not found.")
+
+        return duration
+
+    def run(self, dag: DAGCircuit):
+        raise NotImplementedError


Test patch:
diff --git a/test/python/transpiler/test_instruction_alignments.py b/test/python/transpiler/test_instruction_alignments.py
--- a/test/python/transpiler/test_instruction_alignments.py
+++ b/test/python/transpiler/test_instruction_alignments.py
@@ -44,7 +44,13 @@ def setUp(self):
             ]
         )
         self.time_conversion_pass = TimeUnitConversion(inst_durations=instruction_durations)
-        self.scheduling_pass = ALAPSchedule(durations=instruction_durations)
+        # reproduce old behavior of 0.20.0 before #7655
+        # currently default write latency is 0
+        self.scheduling_pass = ALAPSchedule(
+            durations=instruction_durations,
+            clbit_write_latency=1600,
+            conditional_latency=0,
+        )
         self.align_measure_pass = AlignMeasures(alignment=16)
 
     def test_t1_experiment_type(self):
diff --git a/test/python/transpiler/test_scheduling_pass.py b/test/python/transpiler/test_scheduling_pass.py
--- a/test/python/transpiler/test_scheduling_pass.py
+++ b/test/python/transpiler/test_scheduling_pass.py
@@ -14,7 +14,7 @@
 
 import unittest
 
-from ddt import ddt, data
+from ddt import ddt, data, unpack
 from qiskit import QuantumCircuit
 from qiskit.test import QiskitTestCase
 from qiskit.transpiler.instruction_durations import InstructionDurations
@@ -51,7 +51,7 @@ def test_alap_agree_with_reverse_asap_reverse(self):
     @data(ALAPSchedule, ASAPSchedule)
     def test_classically_controlled_gate_after_measure(self, schedule_pass):
         """Test if ALAP/ASAP schedules circuits with c_if after measure with a common clbit.
-        See: https://github.com/Qiskit/qiskit-terra/issues/7006
+        See: https://github.com/Qiskit/qiskit-terra/issues/7654
 
         (input)
              ┌─┐
@@ -70,7 +70,7 @@ def test_classically_controlled_gate_after_measure(self, schedule_pass):
         q_1: ┤ Delay(1000[dt]) ├─╫───────┤ X ├───────
              └─────────────────┘ ║       └─╥─┘
                                  ║    ┌────╨────┐
-        c: 1/════════════════════╩════╡ c_0 = T ╞════
+        c: 1/════════════════════╩════╡ c_0=0x1 ╞════
                                  0    └─────────┘
         """
         qc = QuantumCircuit(2, 1)
@@ -83,16 +83,16 @@ def test_classically_controlled_gate_after_measure(self, schedule_pass):
 
         expected = QuantumCircuit(2, 1)
         expected.measure(0, 0)
-        expected.delay(200, 0)
         expected.delay(1000, 1)  # x.c_if starts after measure
         expected.x(1).c_if(0, True)
+        expected.delay(200, 0)
 
         self.assertEqual(expected, scheduled)
 
     @data(ALAPSchedule, ASAPSchedule)
     def test_measure_after_measure(self, schedule_pass):
         """Test if ALAP/ASAP schedules circuits with measure after measure with a common clbit.
-        See: https://github.com/Qiskit/qiskit-terra/issues/7006
+        See: https://github.com/Qiskit/qiskit-terra/issues/7654
 
         (input)
              ┌───┐┌─┐
@@ -104,13 +104,13 @@ def test_measure_after_measure(self, schedule_pass):
                    0  0
 
         (scheduled)
-                   ┌───┐       ┌─┐
-        q_0: ──────┤ X ├───────┤M├───
-             ┌─────┴───┴──────┐└╥┘┌─┐
-        q_1: ┤ Delay(200[dt]) ├─╫─┤M├
-             └────────────────┘ ║ └╥┘
-        c: 1/═══════════════════╩══╩═
-                                0  0
+                    ┌───┐       ┌─┐┌─────────────────┐
+        q_0: ───────┤ X ├───────┤M├┤ Delay(1000[dt]) ├
+             ┌──────┴───┴──────┐└╥┘└───────┬─┬───────┘
+        q_1: ┤ Delay(1200[dt]) ├─╫─────────┤M├────────
+             └─────────────────┘ ║         └╥┘
+        c: 1/════════════════════╩══════════╩═════════
+                                 0          0
         """
         qc = QuantumCircuit(2, 1)
         qc.x(0)
@@ -124,8 +124,9 @@ def test_measure_after_measure(self, schedule_pass):
         expected = QuantumCircuit(2, 1)
         expected.x(0)
         expected.measure(0, 0)
-        expected.delay(200, 1)  # 2nd measure starts at the same time as 1st measure starts
+        expected.delay(1200, 1)
         expected.measure(1, 0)
+        expected.delay(1000, 0)
 
         self.assertEqual(expected, scheduled)
 
@@ -146,6 +147,7 @@ def test_c_if_on_different_qubits(self, schedule_pass):
               0 └─────────┘└─────────┘
 
         (scheduled)
+
                                 ┌─┐┌────────────────┐
         q_0: ───────────────────┤M├┤ Delay(200[dt]) ├───────────
              ┌─────────────────┐└╥┘└─────┬───┬──────┘
@@ -154,7 +156,7 @@ def test_c_if_on_different_qubits(self, schedule_pass):
         q_2: ┤ Delay(1000[dt]) ├─╫─────────╫────────────┤ X ├───
              └─────────────────┘ ║         ║            └─╥─┘
                                  ║    ┌────╨────┐    ┌────╨────┐
-        c: 1/════════════════════╩════╡ c_0 = T ╞════╡ c_0 = T ╞
+        c: 1/════════════════════╩════╡ c_0=0x1 ╞════╡ c_0=0x1 ╞
                                  0    └─────────┘    └─────────┘
         """
         qc = QuantumCircuit(3, 1)
@@ -168,11 +170,11 @@ def test_c_if_on_different_qubits(self, schedule_pass):
 
         expected = QuantumCircuit(3, 1)
         expected.measure(0, 0)
-        expected.delay(200, 0)
         expected.delay(1000, 1)
         expected.delay(1000, 2)
         expected.x(1).c_if(0, True)
         expected.x(2).c_if(0, True)
+        expected.delay(200, 0)
 
         self.assertEqual(expected, scheduled)
 
@@ -190,30 +192,32 @@ def test_shorter_measure_after_measure(self, schedule_pass):
               0  0
 
         (scheduled)
-                               ┌─┐
-        q_0: ──────────────────┤M├───
-             ┌────────────────┐└╥┘┌─┐
-        q_1: ┤ Delay(300[dt]) ├─╫─┤M├
-             └────────────────┘ ║ └╥┘
-        c: 1/═══════════════════╩══╩═
-                                0  0
+                                ┌─┐┌────────────────┐
+        q_0: ───────────────────┤M├┤ Delay(700[dt]) ├
+             ┌─────────────────┐└╥┘└──────┬─┬───────┘
+        q_1: ┤ Delay(1000[dt]) ├─╫────────┤M├────────
+             └─────────────────┘ ║        └╥┘
+        c: 1/════════════════════╩═════════╩═════════
+                                 0         0
         """
         qc = QuantumCircuit(2, 1)
         qc.measure(0, 0)
         qc.measure(1, 0)
 
-        durations = InstructionDurations([("measure", 0, 1000), ("measure", 1, 700)])
+        durations = InstructionDurations([("measure", [0], 1000), ("measure", [1], 700)])
         pm = PassManager(schedule_pass(durations))
         scheduled = pm.run(qc)
 
         expected = QuantumCircuit(2, 1)
         expected.measure(0, 0)
-        expected.delay(300, 1)
+        expected.delay(1000, 1)
         expected.measure(1, 0)
+        expected.delay(700, 0)
 
         self.assertEqual(expected, scheduled)
 
-    def test_measure_after_c_if(self):
+    @data(ALAPSchedule, ASAPSchedule)
+    def test_measure_after_c_if(self, schedule_pass):
         """Test if ALAP/ASAP schedules circuits with c_if after measure with a common clbit.
 
         (input)
@@ -227,7 +231,186 @@ def test_measure_after_c_if(self):
         c: 1/═╩═╡ c_0 = T ╞═╩═
               0 └─────────┘ 0
 
-        (scheduled - ASAP)
+        (scheduled)
+                                ┌─┐┌─────────────────┐
+        q_0: ───────────────────┤M├┤ Delay(1000[dt]) ├──────────────────
+             ┌─────────────────┐└╥┘└──────┬───┬──────┘┌────────────────┐
+        q_1: ┤ Delay(1000[dt]) ├─╫────────┤ X ├───────┤ Delay(800[dt]) ├
+             ├─────────────────┤ ║        └─╥─┘       └──────┬─┬───────┘
+        q_2: ┤ Delay(1000[dt]) ├─╫──────────╫────────────────┤M├────────
+             └─────────────────┘ ║     ┌────╨────┐           └╥┘
+        c: 1/════════════════════╩═════╡ c_0=0x1 ╞════════════╩═════════
+                                 0     └─────────┘            0
+        """
+        qc = QuantumCircuit(3, 1)
+        qc.measure(0, 0)
+        qc.x(1).c_if(0, 1)
+        qc.measure(2, 0)
+
+        durations = InstructionDurations([("x", None, 200), ("measure", None, 1000)])
+        pm = PassManager(schedule_pass(durations))
+        scheduled = pm.run(qc)
+
+        expected = QuantumCircuit(3, 1)
+        expected.delay(1000, 1)
+        expected.delay(1000, 2)
+        expected.measure(0, 0)
+        expected.x(1).c_if(0, 1)
+        expected.measure(2, 0)
+        expected.delay(1000, 0)
+        expected.delay(800, 1)
+
+        self.assertEqual(expected, scheduled)
+
+    def test_parallel_gate_different_length(self):
+        """Test circuit having two parallel instruction with different length.
+
+        (input)
+             ┌───┐┌─┐
+        q_0: ┤ X ├┤M├───
+             ├───┤└╥┘┌─┐
+        q_1: ┤ X ├─╫─┤M├
+             └───┘ ║ └╥┘
+        c: 2/══════╩══╩═
+                   0  1
+
+        (expected, ALAP)
+             ┌────────────────┐┌───┐┌─┐
+        q_0: ┤ Delay(200[dt]) ├┤ X ├┤M├
+             └─────┬───┬──────┘└┬─┬┘└╥┘
+        q_1: ──────┤ X ├────────┤M├──╫─
+                   └───┘        └╥┘  ║
+        c: 2/════════════════════╩═══╩═
+                                 1   0
+
+        (expected, ASAP)
+             ┌───┐┌─┐┌────────────────┐
+        q_0: ┤ X ├┤M├┤ Delay(200[dt]) ├
+             ├───┤└╥┘└──────┬─┬───────┘
+        q_1: ┤ X ├─╫────────┤M├────────
+             └───┘ ║        └╥┘
+        c: 2/══════╩═════════╩═════════
+                   0         1
+
+        """
+        qc = QuantumCircuit(2, 2)
+        qc.x(0)
+        qc.x(1)
+        qc.measure(0, 0)
+        qc.measure(1, 1)
+
+        durations = InstructionDurations(
+            [("x", [0], 200), ("x", [1], 400), ("measure", None, 1000)]
+        )
+        pm = PassManager(ALAPSchedule(durations))
+        qc_alap = pm.run(qc)
+
+        alap_expected = QuantumCircuit(2, 2)
+        alap_expected.delay(200, 0)
+        alap_expected.x(0)
+        alap_expected.x(1)
+        alap_expected.measure(0, 0)
+        alap_expected.measure(1, 1)
+
+        self.assertEqual(qc_alap, alap_expected)
+
+        pm = PassManager(ASAPSchedule(durations))
+        qc_asap = pm.run(qc)
+
+        asap_expected = QuantumCircuit(2, 2)
+        asap_expected.x(0)
+        asap_expected.x(1)
+        asap_expected.measure(0, 0)  # immediately start after X gate
+        asap_expected.measure(1, 1)
+        asap_expected.delay(200, 0)
+
+        self.assertEqual(qc_asap, asap_expected)
+
+    def test_parallel_gate_different_length_with_barrier(self):
+        """Test circuit having two parallel instruction with different length with barrier.
+
+        (input)
+             ┌───┐┌─┐
+        q_0: ┤ X ├┤M├───
+             ├───┤└╥┘┌─┐
+        q_1: ┤ X ├─╫─┤M├
+             └───┘ ║ └╥┘
+        c: 2/══════╩══╩═
+                   0  1
+
+        (expected, ALAP)
+             ┌────────────────┐┌───┐ ░ ┌─┐
+        q_0: ┤ Delay(200[dt]) ├┤ X ├─░─┤M├───
+             └─────┬───┬──────┘└───┘ ░ └╥┘┌─┐
+        q_1: ──────┤ X ├─────────────░──╫─┤M├
+                   └───┘             ░  ║ └╥┘
+        c: 2/═══════════════════════════╩══╩═
+                                        0  1
+
+        (expected, ASAP)
+             ┌───┐┌────────────────┐ ░ ┌─┐
+        q_0: ┤ X ├┤ Delay(200[dt]) ├─░─┤M├───
+             ├───┤└────────────────┘ ░ └╥┘┌─┐
+        q_1: ┤ X ├───────────────────░──╫─┤M├
+             └───┘                   ░  ║ └╥┘
+        c: 2/═══════════════════════════╩══╩═
+                                        0  1
+        """
+        qc = QuantumCircuit(2, 2)
+        qc.x(0)
+        qc.x(1)
+        qc.barrier()
+        qc.measure(0, 0)
+        qc.measure(1, 1)
+
+        durations = InstructionDurations(
+            [("x", [0], 200), ("x", [1], 400), ("measure", None, 1000)]
+        )
+        pm = PassManager(ALAPSchedule(durations))
+        qc_alap = pm.run(qc)
+
+        alap_expected = QuantumCircuit(2, 2)
+        alap_expected.delay(200, 0)
+        alap_expected.x(0)
+        alap_expected.x(1)
+        alap_expected.barrier()
+        alap_expected.measure(0, 0)
+        alap_expected.measure(1, 1)
+
+        self.assertEqual(qc_alap, alap_expected)
+
+        pm = PassManager(ASAPSchedule(durations))
+        qc_asap = pm.run(qc)
+
+        asap_expected = QuantumCircuit(2, 2)
+        asap_expected.x(0)
+        asap_expected.delay(200, 0)
+        asap_expected.x(1)
+        asap_expected.barrier()
+        asap_expected.measure(0, 0)
+        asap_expected.measure(1, 1)
+
+        self.assertEqual(qc_asap, asap_expected)
+
+    def test_measure_after_c_if_on_edge_locking(self):
+        """Test if ALAP/ASAP schedules circuits with c_if after measure with a common clbit.
+
+        The scheduler is configured to reproduce behavior of the 0.20.0,
+        in which clbit lock is applied to the end-edge of measure instruction.
+        See https://github.com/Qiskit/qiskit-terra/pull/7655
+
+        (input)
+             ┌─┐
+        q_0: ┤M├──────────────
+             └╥┘   ┌───┐
+        q_1: ─╫────┤ X ├──────
+              ║    └─╥─┘   ┌─┐
+        q_2: ─╫──────╫─────┤M├
+              ║ ┌────╨────┐└╥┘
+        c: 1/═╩═╡ c_0 = T ╞═╩═
+              0 └─────────┘ 0
+
+        (ASAP scheduled)
                                 ┌─┐┌────────────────┐
         q_0: ───────────────────┤M├┤ Delay(200[dt]) ├─────────────────────
              ┌─────────────────┐└╥┘└─────┬───┬──────┘
@@ -235,10 +418,10 @@ def test_measure_after_c_if(self):
              └─────────────────┘ ║       └─╥─┘       ┌─┐┌────────────────┐
         q_2: ────────────────────╫─────────╫─────────┤M├┤ Delay(200[dt]) ├
                                  ║    ┌────╨────┐    └╥┘└────────────────┘
-        c: 1/════════════════════╩════╡ c_0 = T ╞═════╩═══════════════════
+        c: 1/════════════════════╩════╡ c_0=0x1 ╞═════╩═══════════════════
                                  0    └─────────┘     0
 
-        (scheduled - ALAP)
+        (ALAP scheduled)
                                 ┌─┐┌────────────────┐
         q_0: ───────────────────┤M├┤ Delay(200[dt]) ├───
              ┌─────────────────┐└╥┘└─────┬───┬──────┘
@@ -246,8 +429,9 @@ def test_measure_after_c_if(self):
              └┬────────────────┤ ║       └─╥─┘       ┌─┐
         q_2: ─┤ Delay(200[dt]) ├─╫─────────╫─────────┤M├
               └────────────────┘ ║    ┌────╨────┐    └╥┘
-        c: 1/════════════════════╩════╡ c_0 = T ╞═════╩═
+        c: 1/════════════════════╩════╡ c_0=0x1 ╞═════╩═
                                  0    └─────────┘     0
+
         """
         qc = QuantumCircuit(3, 1)
         qc.measure(0, 0)
@@ -255,27 +439,284 @@ def test_measure_after_c_if(self):
         qc.measure(2, 0)
 
         durations = InstructionDurations([("x", None, 200), ("measure", None, 1000)])
-        actual_asap = PassManager(ASAPSchedule(durations)).run(qc)
-        actual_alap = PassManager(ALAPSchedule(durations)).run(qc)
+
+        # lock at the end edge
+        actual_asap = PassManager(ASAPSchedule(durations, clbit_write_latency=1000)).run(qc)
+        actual_alap = PassManager(ALAPSchedule(durations, clbit_write_latency=1000)).run(qc)
 
         # start times of 2nd measure depends on ASAP/ALAP
         expected_asap = QuantumCircuit(3, 1)
         expected_asap.measure(0, 0)
-        expected_asap.delay(200, 0)
         expected_asap.delay(1000, 1)
         expected_asap.x(1).c_if(0, 1)
         expected_asap.measure(2, 0)
-        expected_asap.delay(200, 2)  # delay after measure on q_2
+        expected_asap.delay(200, 0)
+        expected_asap.delay(200, 2)
         self.assertEqual(expected_asap, actual_asap)
 
-        expected_aslp = QuantumCircuit(3, 1)
-        expected_aslp.measure(0, 0)
-        expected_aslp.delay(200, 0)
-        expected_aslp.delay(1000, 1)
-        expected_aslp.x(1).c_if(0, 1)
-        expected_aslp.delay(200, 2)
-        expected_aslp.measure(2, 0)  # delay before measure on q_2
-        self.assertEqual(expected_aslp, actual_alap)
+        expected_alap = QuantumCircuit(3, 1)
+        expected_alap.measure(0, 0)
+        expected_alap.delay(1000, 1)
+        expected_alap.x(1).c_if(0, 1)
+        expected_alap.delay(200, 2)
+        expected_alap.measure(2, 0)
+        expected_alap.delay(200, 0)
+        self.assertEqual(expected_alap, actual_alap)
+
+    @data([100, 200], [500, 0], [1000, 200])
+    @unpack
+    def test_active_reset_circuit(self, write_lat, cond_lat):
+        """Test practical example of reset circuit.
+
+        Because of the stimulus pulse overlap with the previous XGate on the q register,
+        measure instruction is always triggered after XGate regardless of write latency.
+        Thus only conditional latency matters in the scheduling.
+
+        (input)
+             ┌─┐   ┌───┐   ┌─┐   ┌───┐   ┌─┐   ┌───┐
+          q: ┤M├───┤ X ├───┤M├───┤ X ├───┤M├───┤ X ├───
+             └╥┘   └─╥─┘   └╥┘   └─╥─┘   └╥┘   └─╥─┘
+              ║ ┌────╨────┐ ║ ┌────╨────┐ ║ ┌────╨────┐
+        c: 1/═╩═╡ c_0=0x1 ╞═╩═╡ c_0=0x1 ╞═╩═╡ c_0=0x1 ╞
+              0 └─────────┘ 0 └─────────┘ 0 └─────────┘
+
+        """
+        qc = QuantumCircuit(1, 1)
+        qc.measure(0, 0)
+        qc.x(0).c_if(0, 1)
+        qc.measure(0, 0)
+        qc.x(0).c_if(0, 1)
+        qc.measure(0, 0)
+        qc.x(0).c_if(0, 1)
+
+        durations = InstructionDurations([("x", None, 100), ("measure", None, 1000)])
+        actual_asap = PassManager(
+            ASAPSchedule(durations, clbit_write_latency=write_lat, conditional_latency=cond_lat)
+        ).run(qc)
+        actual_alap = PassManager(
+            ALAPSchedule(durations, clbit_write_latency=write_lat, conditional_latency=cond_lat)
+        ).run(qc)
+
+        expected = QuantumCircuit(1, 1)
+        expected.measure(0, 0)
+        if cond_lat > 0:
+            expected.delay(cond_lat, 0)
+        expected.x(0).c_if(0, 1)
+        expected.measure(0, 0)
+        if cond_lat > 0:
+            expected.delay(cond_lat, 0)
+        expected.x(0).c_if(0, 1)
+        expected.measure(0, 0)
+        if cond_lat > 0:
+            expected.delay(cond_lat, 0)
+        expected.x(0).c_if(0, 1)
+
+        self.assertEqual(expected, actual_asap)
+        self.assertEqual(expected, actual_alap)
+
+    def test_random_complicated_circuit(self):
+        """Test scheduling complicated circuit with control flow.
+
+        (input)
+             ┌────────────────┐   ┌───┐    ░                  ┌───┐   »
+        q_0: ┤ Delay(100[dt]) ├───┤ X ├────░──────────────────┤ X ├───»
+             └────────────────┘   └─╥─┘    ░       ┌───┐      └─╥─┘   »
+        q_1: ───────────────────────╫──────░───────┤ X ├────────╫─────»
+                                    ║      ░ ┌─┐   └─╥─┘        ║     »
+        q_2: ───────────────────────╫──────░─┤M├─────╫──────────╫─────»
+                               ┌────╨────┐ ░ └╥┘┌────╨────┐┌────╨────┐»
+        c: 1/══════════════════╡ c_0=0x1 ╞════╩═╡ c_0=0x0 ╞╡ c_0=0x0 ╞»
+                               └─────────┘    0 └─────────┘└─────────┘»
+        «     ┌────────────────┐┌───┐
+        «q_0: ┤ Delay(300[dt]) ├┤ X ├─────■─────
+        «     └────────────────┘└───┘   ┌─┴─┐
+        «q_1: ────────■─────────────────┤ X ├───
+        «           ┌─┴─┐        ┌─┐    └─╥─┘
+        «q_2: ──────┤ X ├────────┤M├──────╫─────
+        «           └───┘        └╥┘ ┌────╨────┐
+        «c: 1/════════════════════╩══╡ c_0=0x0 ╞
+        «                         0  └─────────┘
+
+        (ASAP scheduled) duration = 2800 dt
+             ┌────────────────┐┌────────────────┐   ┌───┐    ░ ┌─────────────────┐»
+        q_0: ┤ Delay(100[dt]) ├┤ Delay(100[dt]) ├───┤ X ├────░─┤ Delay(1400[dt]) ├»
+             ├────────────────┤└────────────────┘   └─╥─┘    ░ ├─────────────────┤»
+        q_1: ┤ Delay(300[dt]) ├───────────────────────╫──────░─┤ Delay(1200[dt]) ├»
+             ├────────────────┤                       ║      ░ └───────┬─┬───────┘»
+        q_2: ┤ Delay(300[dt]) ├───────────────────────╫──────░─────────┤M├────────»
+             └────────────────┘                  ┌────╨────┐ ░         └╥┘        »
+        c: 1/════════════════════════════════════╡ c_0=0x1 ╞════════════╩═════════»
+                                                 └─────────┘            0         »
+        «                                     ┌───┐   ┌────────────────┐»
+        «q_0: ────────────────────────────────┤ X ├───┤ Delay(300[dt]) ├»
+        «        ┌───┐                        └─╥─┘   └────────────────┘»
+        «q_1: ───┤ X ├──────────────────────────╫─────────────■─────────»
+        «        └─╥─┘   ┌────────────────┐     ║           ┌─┴─┐       »
+        «q_2: ─────╫─────┤ Delay(300[dt]) ├─────╫───────────┤ X ├───────»
+        «     ┌────╨────┐└────────────────┘┌────╨────┐      └───┘       »
+        «c: 1/╡ c_0=0x0 ╞══════════════════╡ c_0=0x0 ╞══════════════════»
+        «     └─────────┘                  └─────────┘                  »
+        «           ┌───┐                  ┌────────────────┐
+        «q_0: ──────┤ X ├────────────■─────┤ Delay(700[dt]) ├
+        «     ┌─────┴───┴──────┐   ┌─┴─┐   ├────────────────┤
+        «q_1: ┤ Delay(400[dt]) ├───┤ X ├───┤ Delay(700[dt]) ├
+        «     ├────────────────┤   └─╥─┘   └──────┬─┬───────┘
+        «q_2: ┤ Delay(300[dt]) ├─────╫────────────┤M├────────
+        «     └────────────────┘┌────╨────┐       └╥┘
+        «c: 1/══════════════════╡ c_0=0x0 ╞════════╩═════════
+        «                       └─────────┘        0
+
+        (ALAP scheduled) duration = 3100
+             ┌────────────────┐┌────────────────┐   ┌───┐    ░ ┌─────────────────┐»
+        q_0: ┤ Delay(100[dt]) ├┤ Delay(100[dt]) ├───┤ X ├────░─┤ Delay(1400[dt]) ├»
+             ├────────────────┤└────────────────┘   └─╥─┘    ░ ├─────────────────┤»
+        q_1: ┤ Delay(300[dt]) ├───────────────────────╫──────░─┤ Delay(1200[dt]) ├»
+             ├────────────────┤                       ║      ░ └───────┬─┬───────┘»
+        q_2: ┤ Delay(300[dt]) ├───────────────────────╫──────░─────────┤M├────────»
+             └────────────────┘                  ┌────╨────┐ ░         └╥┘        »
+        c: 1/════════════════════════════════════╡ c_0=0x1 ╞════════════╩═════════»
+                                                 └─────────┘            0         »
+        «                                     ┌───┐   ┌────────────────┐»
+        «q_0: ────────────────────────────────┤ X ├───┤ Delay(300[dt]) ├»
+        «        ┌───┐   ┌────────────────┐   └─╥─┘   └────────────────┘»
+        «q_1: ───┤ X ├───┤ Delay(300[dt]) ├─────╫─────────────■─────────»
+        «        └─╥─┘   ├────────────────┤     ║           ┌─┴─┐       »
+        «q_2: ─────╫─────┤ Delay(600[dt]) ├─────╫───────────┤ X ├───────»
+        «     ┌────╨────┐└────────────────┘┌────╨────┐      └───┘       »
+        «c: 1/╡ c_0=0x0 ╞══════════════════╡ c_0=0x0 ╞══════════════════»
+        «     └─────────┘                  └─────────┘                  »
+        «           ┌───┐                  ┌────────────────┐
+        «q_0: ──────┤ X ├────────────■─────┤ Delay(700[dt]) ├
+        «     ┌─────┴───┴──────┐   ┌─┴─┐   ├────────────────┤
+        «q_1: ┤ Delay(100[dt]) ├───┤ X ├───┤ Delay(700[dt]) ├
+        «     └──────┬─┬───────┘   └─╥─┘   └────────────────┘
+        «q_2: ───────┤M├─────────────╫───────────────────────
+        «            └╥┘        ┌────╨────┐
+        «c: 1/════════╩═════════╡ c_0=0x0 ╞══════════════════
+        «             0         └─────────┘
+
+        """
+        qc = QuantumCircuit(3, 1)
+        qc.delay(100, 0)
+        qc.x(0).c_if(0, 1)
+        qc.barrier()
+        qc.measure(2, 0)
+        qc.x(1).c_if(0, 0)
+        qc.x(0).c_if(0, 0)
+        qc.delay(300, 0)
+        qc.cx(1, 2)
+        qc.x(0)
+        qc.cx(0, 1).c_if(0, 0)
+        qc.measure(2, 0)
+
+        durations = InstructionDurations(
+            [("x", None, 100), ("measure", None, 1000), ("cx", None, 200)]
+        )
+
+        actual_asap = PassManager(
+            ASAPSchedule(durations, clbit_write_latency=100, conditional_latency=200)
+        ).run(qc)
+        actual_alap = PassManager(
+            ALAPSchedule(durations, clbit_write_latency=100, conditional_latency=200)
+        ).run(qc)
+
+        expected_asap = QuantumCircuit(3, 1)
+        expected_asap.delay(100, 0)
+        expected_asap.delay(100, 0)  # due to conditional latency of 200dt
+        expected_asap.delay(300, 1)
+        expected_asap.delay(300, 2)
+        expected_asap.x(0).c_if(0, 1)
+        expected_asap.barrier()
+        expected_asap.delay(1400, 0)
+        expected_asap.delay(1200, 1)
+        expected_asap.measure(2, 0)
+        expected_asap.x(1).c_if(0, 0)
+        expected_asap.x(0).c_if(0, 0)
+        expected_asap.delay(300, 0)
+        expected_asap.x(0)
+        expected_asap.delay(300, 2)
+        expected_asap.cx(1, 2)
+        expected_asap.delay(400, 1)
+        expected_asap.cx(0, 1).c_if(0, 0)
+        expected_asap.delay(700, 0)  # creg is released at t0 of cx(0,1).c_if(0,0)
+        expected_asap.delay(
+            700, 1
+        )  # no creg write until 100dt. thus measure can move left by 300dt.
+        expected_asap.delay(300, 2)
+        expected_asap.measure(2, 0)
+        self.assertEqual(expected_asap, actual_asap)
+        self.assertEqual(actual_asap.duration, 3100)
+
+        expected_alap = QuantumCircuit(3, 1)
+        expected_alap.delay(100, 0)
+        expected_alap.delay(100, 0)  # due to conditional latency of 200dt
+        expected_alap.delay(300, 1)
+        expected_alap.delay(300, 2)
+        expected_alap.x(0).c_if(0, 1)
+        expected_alap.barrier()
+        expected_alap.delay(1400, 0)
+        expected_alap.delay(1200, 1)
+        expected_alap.measure(2, 0)
+        expected_alap.x(1).c_if(0, 0)
+        expected_alap.x(0).c_if(0, 0)
+        expected_alap.delay(300, 0)
+        expected_alap.x(0)
+        expected_alap.delay(300, 1)
+        expected_alap.delay(600, 2)
+        expected_alap.cx(1, 2)
+        expected_alap.delay(100, 1)
+        expected_alap.cx(0, 1).c_if(0, 0)
+        expected_alap.measure(2, 0)
+        expected_alap.delay(700, 0)
+        expected_alap.delay(700, 1)
+        self.assertEqual(expected_alap, actual_alap)
+        self.assertEqual(actual_alap.duration, 3100)
+
+    def test_dag_introduces_extra_dependency_between_conditionals(self):
+        """Test dependency between conditional operations in the scheduling.
+
+        In the below example circuit, the conditional x on q1 could start at time 0,
+        however it must be scheduled after the conditional x on q0 in ASAP scheduling.
+        That is because circuit model used in the transpiler passes (DAGCircuit)
+        interprets instructions acting on common clbits must be run in the order
+        given by the original circuit (QuantumCircuit).
+
+        (input)
+             ┌────────────────┐   ┌───┐
+        q_0: ┤ Delay(100[dt]) ├───┤ X ├───
+             └─────┬───┬──────┘   └─╥─┘
+        q_1: ──────┤ X ├────────────╫─────
+                   └─╥─┘            ║
+                ┌────╨────┐    ┌────╨────┐
+        c: 1/═══╡ c_0=0x1 ╞════╡ c_0=0x1 ╞
+                └─────────┘    └─────────┘
+
+        (ASAP scheduled)
+             ┌────────────────┐   ┌───┐
+        q_0: ┤ Delay(100[dt]) ├───┤ X ├──────────────
+             ├────────────────┤   └─╥─┘      ┌───┐
+        q_1: ┤ Delay(100[dt]) ├─────╫────────┤ X ├───
+             └────────────────┘     ║        └─╥─┘
+                               ┌────╨────┐┌────╨────┐
+        c: 1/══════════════════╡ c_0=0x1 ╞╡ c_0=0x1 ╞
+                               └─────────┘└─────────┘
+        """
+        qc = QuantumCircuit(2, 1)
+        qc.delay(100, 0)
+        qc.x(0).c_if(0, True)
+        qc.x(1).c_if(0, True)
+
+        durations = InstructionDurations([("x", None, 160)])
+        pm = PassManager(ASAPSchedule(durations))
+        scheduled = pm.run(qc)
+
+        expected = QuantumCircuit(2, 1)
+        expected.delay(100, 0)
+        expected.delay(100, 1)  # due to extra dependency on clbits
+        expected.x(0).c_if(0, True)
+        expected.x(1).c_if(0, True)
+
+        self.assertEqual(expected, scheduled)
 
 
 if __name__ == "__main__":