VHDL · Chapter 15.4 · Debugging and Simulation
Delta-Cycle and Race Bugs
The nastiest simulation bugs hide inside one moment of time. At a single instant, signals settle over successive delta cycles, which are zero-time steps within that instant. Two failure modes live there. A delta-read-too-early bug reads a signal before its combinational chain has finished settling across deltas, getting a stale value. A race happens when a clock and the data it should capture change at the same time, so which the simulator processes first becomes order-dependent. The classic trigger is a clock derived through combinational logic, such as a gated or divided clock, that arrives one delta late and races or misses the data. The tell-tale sign is a result that depends on process order when it should not. The fixes are familiar design rules: clock enables instead of gated clocks, registering across clock domains, and preferring a single clock. This lesson covers diagnosing these bugs and removing them.
Foundation14 min readVHDLDebuggingDelta CycleRaceGated ClockSimulation
1. Engineering intuition — when "the same time" hides an order
Simulated time can stand still while the design settles — those are delta cycles. Most of the time you can
ignore them, but they bite when two things happen at the same T and your logic secretly depends on which
goes first. The archetype: you build a clock out of combinational logic (gate it, divide it, route it through a
mux) and use it to capture data that changes on the original clock edge. The derived clock comes out one delta
later than the data's edge, so in simulation it arrives just behind — racing the data and capturing the wrong
value, or none. The symptom is unnerving: the answer changes if the simulator happens to evaluate processes in a
different order. Real synchronous design avoids this entirely by never making clocks in logic.
2. Formal explanation — delta reads and the derived-clock race
-- DERIVED CLOCK via logic → arrives ONE DELTA after the real edge → races the data it samples.
clk_div <= not clk_div when rising_edge(clk); -- a divided "clock" (a SIGNAL, updated a delta late)
-- ...used as a clock elsewhere:
process (clk_div) begin
if rising_edge(clk_div) then q <= d; end if; -- clk_div edge lags clk by a delta → races 'd'
end process;
-- 'd' may change on the SAME T (driven off clk); whether q captures old or new d is ORDER-DEPENDENT.
-- DELTA-READ-TOO-EARLY: reading a signal before its combinational chain settles.
a <= b; b <= c; -- when c changes: b updates (delta 1), THEN a (delta 2)
-- a process reading 'a' in the same first delta sees its OLD value — settled only after deltas.
-- DIAGNOSIS: many delta cycles at one time T, and a result that DEPENDS ON PROCESS ORDER
-- (a correct design must NOT depend on which process the simulator runs first within a delta).A derived clock (made in logic) updates a delta after the source edge, so it races same-time data. A
delta-read bug reads a signal before its chain settles. The signature of both is order dependence within a
single T — which a correct design must never have.
3. Production usage — the structural fixes
-- FIX 1: never gate/divide a clock in logic — use a CLOCK ENABLE on the real clock (7.5).
process (clk) begin
if rising_edge(clk) then
if tick_en = '1' then q <= d; end if; -- "half-rate" behavior, ONE clock, no derived edge
end if;
end process;
-- tick_en is a normal signal pulsed by a counter — no second clock, no delta race.
-- FIX 2: cross clock domains by REGISTERING (synchronizing), never by sampling a logic-made clock.
-- (proper CDC synchronizer — Module 17 — not a derived clock.)
-- FIX 3: keep the design on a SINGLE clock wherever possible; real clocks come from clock resources,
-- not combinational logic (gated/divided clocks belong to clock-management hardware, not RTL fabric).What hardware does this become? The clock-enable version is one clock feeding flip-flops with an en —
clean, race-free, and exactly what synthesis wants. The derived-clock version, even where it synthesizes,
creates a second clock domain through the logic fabric with skew and timing-closure problems — the simulation
delta race is the early warning of a real hardware hazard. So fixing the delta/race bug (enables, single clock,
proper CDC) removes both the simulation order-dependence and the physical clocking problem it predicts.
4. Structural interpretation — a derived clock racing the data
5. Simulation interpretation — the derived clock one delta late
Derived clock lags the real edge by a delta and races the data
8 cycles6. Debugging example — the order-dependent result
Expected: a deterministic design that behaves the same on any simulator. Observed: results change between simulators (or simulator versions), or after an unrelated edit — and the waveform shows many delta cycles at one time with a value that flips depending on process order. Root cause: the design depends on process evaluation order within a delta — almost always because a clock was made in combinational logic (gated or divided) and used to sample data that moves on the same edge, creating a delta race; or a signal was read before its chain settled. Fix: remove derived clocks — use a clock enable on the single real clock, register/synchronize across domains, and keep clocks coming from clock resources, not fabric logic; for delta reads, restructure so the value is stable when read (or use a variable). Engineering takeaway: a correct design never depends on intra-delta process order — if a result does, you have a race, usually a logic-made clock; replace it with an enable on one real clock.
-- BUG: clock made in logic → delta-races the data it samples; order-dependent.
-- clk_div <= not clk_div when rising_edge(clk);
-- process (clk_div) begin if rising_edge(clk_div) then q <= d; end if; end process;
-- FIX: one clock + a clock enable — no derived edge, no race.
process (clk) begin if rising_edge(clk) then if tick_en='1' then q <= d; end if; end if; end process;7. Common mistakes & what to watch for
- Clocks made in logic. Gated/divided/muxed clocks arrive a delta late and race data; use clock enables on one real clock instead.
- Order-dependent results. If the answer depends on intra-delta process order, it is a race — a correct design never does.
- Sampling across domains with a logic clock. Cross domains by registering/synchronizing (CDC), never by a derived clock.
- Reading a signal before it settles. Combinational chains settle over deltas; read stable values or use a variable for immediate results.
- Blaming the simulator. Different delta-order outcomes mean your design has a race, not that the simulator is wrong.
8. Engineering insight & continuity
Delta-cycle and race bugs live inside a single instant: a clock derived in logic arrives a delta late and races the data it should capture, and the tell-tale is a result that depends on process order — which a correct design must never have. The fixes are structural and familiar: clock enables instead of gated/divided clocks, registering across domains, and a single clock from real clock resources. These bugs often only appear in simulation, foreshadowing the broader and more dangerous category where simulation and hardware disagree — the subject of the next lesson, Simulation vs Synthesis Mismatches.