RTL & Logic Design · lesson 8

Asynchronous Logic, Hazards, and Races

By Tabish Iqbal · Last updated:

How hazards, critical races and oscillation arise in asynchronous logic, and why RTL design stays synchronous and crosses clock domains with synchronizers.

10 min read · Free, no login

Every design method in this course so far has assumed a single clock: a register changes only at a clock edge, and every piece of combinational logic between registers is given a full clock period to settle before the next edge samples it. Asynchronous design drops that assumption โ€” outputs can change the instant an input changes, with no clock deciding when it is safe to look. This lesson is about why that freedom causes real, specific problems (hazards, races, oscillation), and why the practical answer in RTL methodology is not to solve those problems inside asynchronous logic, but to avoid building asynchronous logic in the first place.

Reading time: about 10 minutes.

8.1 Pulse-mode and fundamental-mode machines

Asynchronous sequential circuits are usually divided into two operating assumptions. A pulse-mode machine takes its inputs as pulses, never two at once, and each pulse must be wide enough to trigger the state change but short enough to end before the new state feeds back through the circuit. A fundamental-mode machine is a level-mode assumption: inputs are levels, only one input changes at a time, and only when the circuit is stable, so the internal state settles before the next change is allowed. Both describe how a circuit is supposed to be used, not something the gates enforce. Even when the assumptions are honoured, unequal path delays still cause the hazards and races below; violating them adds further failures on top.

8.2 Hazards

A hazard is a momentary, unintended glitch in a combinational output during an input change. A static hazard occurs when an output is supposed to stay constant through a single input change but briefly glitches to the other value and back; it needs only two reconverging paths with unequal delay. A dynamic hazard occurs when an output is supposed to change once but changes three or more times (for example 0, 1, 0, 1) on the way, which requires three or more reconvergent paths with different delays, so it appears only in multi-level logic. In both cases the gate briefly sees an input combination that is an artifact of one path arriving before another. In two-level sum-of-products logic, a static-1 hazard is removed by adding the consensus term, as lesson 2 showed.

time input transition begins F should stay at 1 throughout (ideal) unwanted glitch F = 1 F = 1
Two reconverging paths with unequal delay momentarily make the gate see an input pattern the designer never intended as a stable combination โ€” the dip to 0 and back is the static-1 hazard, not a change the logic equation predicts.

A combinational hazard is purely a timing artifact โ€” the Boolean equation for F may be entirely correct, and a static timing snapshot taken before and after the transition would show F = 1 both times, with the glitch invisible to anything that does not look during the transition itself. That is exactly what makes a hazard dangerous specifically in asynchronous logic: if something downstream is already clocked, as in all of this course's RTL so far, a single clock edge samples F at one instant and the momentary dip is simply never seen, provided it has settled before the next setup/hold window. If something downstream is itself asynchronous and reacts to any change in F, including the glitch, the glitch becomes a real, unwanted event with consequences โ€” which is the core reason a hazard that is harmless in one context is a correctness bug in another.

Asynchronous sequential circuits add one more kind. An essential hazard is a fundamental-mode sequential hazard: an input change reaches some of the state-variable logic before the state change it causes has propagated everywhere, so part of the circuit acts on the new input with the old state. It is a property of the flow table, detectable from it, and cannot be removed by adding logic terms; the fix is to add delay in the feedback path so the state change always arrives after the input change has settled.

8.3 Critical races

A race occurs when a single input change requires two or more state variables to change, and the eventual stable state may depend on the order those changes complete in. A race is non-critical if every completion order reaches the same final state, and critical if different orders reach different final states, so correctness depends on which physical path happens to be faster on a given die, at a given temperature. A critical race is a design error in the state assignment. Choosing an assignment where every transition changes only one state variable removes it, but with the minimum number of state variables that is often impossible. The standard fixes are to add state variables, to route the transition through intermediate unstable states (a cycle) so each step changes one variable, or to arrange the assignment so the remaining races are non-critical.

8.4 Oscillation

A feedback loop in combinational logic โ€” an output that affects, through some path, its own input โ€” does not always settle. If the loop keeps flipping instead of converging, the result is sustained oscillation. An asynchronous latch built carelessly from feedback gates can oscillate this way. A master-slave flip-flop does contain feedback: it stores state in two latches, each with a continuously closed, even-inversion (bistable) cross-coupled loop, and those loops are exactly what can go metastable. What the clock guarantees is that master and slave are never transparent at the same time, so no loop through the flip-flop, from its output back to its input, is ever combinationally closed between edges. That turns a continuous-time feedback problem into one discrete decision per edge.

8.5 Why RTL methodology avoids asynchronous design

Every structure built in this course โ€” from the flip-flop template in the Verilog path's sequential-logic lesson through the FSMs, counters and shift registers in this one โ€” is synchronous specifically because hazards, critical races and oscillation are exactly the class of problem a single global clock makes irrelevant inside one clock domain. A clocked register only cares what its input looks like in a narrow setup/hold window around the active edge; on a data path, any glitch that has settled by then, hazard or not, has no effect on correctness. That holds for data only: clocks, asynchronous resets and clock-gating enables act on every change, so they must be glitch-free. Static timing analysis, the standard sign-off method for verifying a synchronous design meets its timing, checks exactly this โ€” that every combinational path between registers settles before the next edge โ€” and it has no comparable procedure for verifying an asynchronous circuit is race-free and hazard-free in general, which is a large part of why synchronous RTL remains the default: it is tractably verifiable at the scale real chips require.

This does not mean every signal in a real chip is safely synchronous. A signal from one clock domain sampled in an unrelated one can change inside the setup/hold window, which risks metastability rather than a combinational hazard. RTL methodology's answer is a synchronizer: a chain of two or more flip-flops on the receiving clock, giving a metastable first stage time to resolve before ordinary logic uses the value. The chain reduces the risk rather than removing it, making the probability of an unresolved value negligible: MTBF = e^(t_r/ฯ„) / (T_w ยท f_clk ยท f_data), growing exponentially with the resolution time t_r. A 2-flop synchronizer is valid only for a single-bit level signal. Multi-bit buses need Gray coding, a handshake or an asynchronous FIFO, and a fast-to-slow pulse needs stretching or a toggle synchronizer. The Verilog path's common RTL building blocks lesson covers building the synchronizer; the point here is that it keeps both sides of the crossing synchronous.

A 2-flop synchronizer register (illustrative, not a repeat of the linked lesson)
module sync2 (
    input  wire clk,
    input  wire rst_n,
    input  wire async_in,
    output wire sync_out
);
    reg stage1, stage2;

    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            stage1 <= 1'b0;
            stage2 <= 1'b0;
        end else begin
            stage1 <= async_in;
            stage2 <= stage1;
        end
    end

    assign sync_out = stage2;
endmodule

Driving async_in high at an arbitrary, clock-unrelated moment and observing the pipeline in Icarus Verilog confirms only the two-stage latency โ€” the new value takes exactly two clock edges to reach sync_out. A zero-delay RTL simulation cannot show metastability at all, so it says nothing about how well the chain resolves it:

after 1st posedge: stage1=1 sync_out=0
after 2nd posedge: stage1=1 sync_out=1
SELF-CHECK PASSED

Both stages are ordinary synchronous flip-flops; Yosys's synthesis and check pass reported zero problems and no inferred latches. The only asynchronous thing here is async_in.

8.6 Interview questions

What is the difference between a static hazard and a dynamic hazard? A static hazard is a momentary glitch in an output that should stay constant through a single input change; two reconverging paths with unequal delay are enough. A dynamic hazard is an output that should change once but changes three or more times, which needs three or more reconvergent paths with different delays, so it occurs only in multi-level logic.

What makes a race "critical" rather than just a race? A race is one input change requiring two or more state variables to change. It is non-critical if every completion order reaches the same final state, and critical if different orders reach different final states. A critical race is a state-assignment error, fixed by adding state variables, routing through intermediate unstable states, or accepting only non-critical races.

Why does a synchronous flip-flop not suffer from the oscillation that an asynchronous feedback latch can? Its master and slave latches each hold a closed, bistable cross-coupled loop (which is what can go metastable), but the clock ensures the two latches are never transparent at the same time. No loop through the flip-flop is ever combinationally closed between edges, so state changes once per edge instead of continuously.

Why does RTL methodology avoid asynchronous design rather than just designing around hazards and races carefully? Hazards and critical races are not checked by static timing analysis, the standard tool for verifying a synchronous design's timing โ€” there is no equivalently scalable, tractable verification method for general asynchronous logic. A single clock domain makes these failure modes irrelevant on data paths, provided clocks, asynchronous resets and clock-gating enables are kept glitch-free, which is why it is the default, not merely a style preference.

If a signal genuinely has to cross between two unrelated clock domains, does that mean the receiving logic has to be asynchronous? No โ€” the standard answer is a synchronizer, typically a chain of two or more flip-flops on the receiving clock, placed directly in the signal's path so that a metastable value almost certainly resolves before ordinary synchronous logic uses it. This is valid for a single-bit level signal; buses need Gray coding, a handshake or an asynchronous FIFO. The receiving domain stays fully synchronous; only the single wire entering the synchronizer is treated as asynchronous.

Key takeaways

  • A hazard is a momentary glitch caused by reconverging paths with unequal delay, even on a single input change; it is harmless on a settled data path, but a bug wherever something reacts to any change, including clocks and resets.
  • A critical race means the circuit's final state depends on which of two signal paths happens to be faster โ€” a design error in the state assignment, not something to be tolerated as manufacturing variance.
  • A flip-flop's latches do hold closed bistable loops, but because master and slave are never transparent together, no loop through the flip-flop is combinationally closed between edges.
  • RTL methodology avoids asynchronous design because hazards and races are not reliably verifiable at scale, while static timing analysis gives synchronous design a scalable, tractable way to confirm correctness.
  • A genuine clock-domain crossing is handled with a synchronizer, not by writing asynchronous logic โ€” this keeps both sides of the crossing fully synchronous and makes metastability failures improbable rather than impossible, for single-bit level signals.