Clock Gating in Verilog: Glitch-Free Integrated Clock Gate (ICG)

Clock gating is the single most common dynamic-power-saving technique in digital design: stop the clock to idle logic and you kill its switching power. But you cannot simply AND the clock with an enable — that creates glitches and runt pulses that corrupt flip-flops. This page shows production-style clock gating verilog code for a latch-based integrated clock gate (ICG) that is glitch-free by construction.

We build gclk = clk & en_lat, where en_lat is captured by a level-sensitive latch while clk is low, then contrast it with the broken gclk = clk & en form. A second example drives a counter from the gated clock so you can see the enable actually start and stop counting.

Latch-based integrated clock gate vs naive AND

The latch samples en only during the low phase of the clock. Because the enable can only change en_lat while clk is low, the product clk & en_lat can never be cut mid-pulse — no glitch. The testbench counts rising edges of the gated clock with en=0 (expect 0) and en=1 (expect 3), then flips the enable in the middle of a high phase to prove the ICG output stays clean.

// icg.v : latch-based integrated clock gate (glitch-free) vs naive AND gate
module icg(input clk, input en, output gclk);
  reg en_lat;
  // level-sensitive latch: sample en only while clk is LOW
  always @(*) if(!clk) en_lat = en;
  assign gclk = clk & en_lat;      // enable change during HIGH phase cannot create a glitch
endmodule

module tb;
  reg clk, en;
  wire gclk_icg;
  wire gclk_naive = clk & en;      // WRONG: en change during HIGH phase glitches

  integer icg_edges, naive_edges;
  reg prev_icg, prev_naive;

  icg dut(.clk(clk), .en(en), .gclk(gclk_icg));

  // count rising edges of each gated clock
  always @(posedge gclk_icg) icg_edges = icg_edges + 1;
  always @(posedge gclk_naive) naive_edges = naive_edges + 1;

  initial begin
    clk = 0; en = 0;
    icg_edges = 0; naive_edges = 0;

    // ---- Phase A: en=0 across 3 full clock periods -> expect 0 gated edges ----
    repeat (3) begin
      #5 clk = 1; #5 clk = 0;
    end
    $display("en=0 : icg rising edges = %0d (expect 0)", icg_edges);
    if (icg_edges !== 0) begin $display("FAIL: gated clock ticked while disabled"); $finish; end

    // ---- Phase B: enable while clk LOW, run 3 periods -> expect 3 gated edges ----
    en = 1;                 // changed during LOW phase (the safe way)
    repeat (3) begin
      #5 clk = 1; #5 clk = 0;
    end
    $display("en=1 : icg rising edges = %0d (expect 3)", icg_edges);
    if (icg_edges !== 3) begin $display("FAIL: wrong enabled edge count"); $finish; end

    // ---- Phase C: flip en HIGH->LOW in the MIDDLE of a HIGH phase ----
    // Naive AND would chop the clock and create a spurious falling/rising glitch.
    // The ICG latch holds en_lat until clk returns LOW, so gclk stays clean.
    en = 1; #5 clk = 1;     // rising edge #4 of icg
    #2 en = 0;              // mid-high toggle: naive gclk drops now (glitch), icg holds
    #3 clk = 0;             // period ends; latch now samples en=0
    $display("after mid-high toggle: icg edges = %0d (expect 4)", icg_edges);
    if (icg_edges !== 4) begin $display("FAIL: mid-high toggle disturbed icg clock"); $finish; end

    // One more full period with en held 0 -> icg must NOT tick again
    #5 clk = 1; #5 clk = 0;
    $display("next period (en latched 0): icg edges = %0d (expect 4)", icg_edges);
    if (icg_edges !== 4) begin $display("FAIL: icg ticked after being gated off"); $finish; end

    $display("ALL PASS");
    $finish;
  end
endmodule
en=0 : icg rising edges = 0 (expect 0)
en=1 : icg rising edges = 3 (expect 3)
after mid-high toggle: icg edges = 4 (expect 4)
next period (en latched 0): icg edges = 4 (expect 4)
ALL PASS

Simulation prints the edge count at each phase and ends with ALL PASS. The naive gated clock is left in the testbench so you can probe gclk_naive in a waveform viewer and watch it glitch on the mid-high toggle.

Driving a counter with the gated clock

Here the ICG output feeds a 4-bit counter. When en=0 the gated clock never rises, so the counter freezes; when en=1 it advances once per cycle. The testbench checks the counter holds at 0 while disabled, counts up to 5 when enabled, holds through three more gated cycles, then resumes.

// gated_counter.v : use a glitch-free gated clock to drive a counter
module icg(input clk, input en, output gclk);
  reg en_lat;
  always @(*) if(!clk) en_lat = en;
  assign gclk = clk & en_lat;
endmodule

module counter(input gclk, input rst, output reg [3:0] q);
  always @(posedge gclk or posedge rst)
    if (rst) q <= 4'd0;
    else     q <= q + 4'd1;
endmodule

module tb;
  reg clk, en, rst;
  wire gclk;
  wire [3:0] q;

  icg     cg (.clk(clk), .en(en), .gclk(gclk));
  counter c  (.gclk(gclk), .rst(rst), .q(q));

  task one_cycle;   // one full clock period
    begin #5 clk = 1; #5 clk = 0; end
  endtask

  initial begin
    clk = 0; en = 0; rst = 1;
    #3 rst = 0;                 // release reset while clk low
    $display("after reset: q = %0d (expect 0)", q);
    if (q !== 4'd0) begin $display("FAIL: reset"); $finish; end

    // 4 cycles while gated OFF -> counter frozen
    en = 0;
    repeat (4) one_cycle;
    $display("en=0 x4 cycles: q = %0d (expect 0)", q);
    if (q !== 4'd0) begin $display("FAIL: counted while disabled"); $finish; end

    // 5 cycles enabled -> counter advances to 5
    en = 1;
    repeat (5) one_cycle;
    $display("en=1 x5 cycles: q = %0d (expect 5)", q);
    if (q !== 4'd5) begin $display("FAIL: wrong enabled count"); $finish; end

    // gate off again for 3 cycles -> holds at 5
    en = 0;
    repeat (3) one_cycle;
    $display("en=0 x3 cycles: q = %0d (expect 5)", q);
    if (q !== 4'd5) begin $display("FAIL: did not hold"); $finish; end

    // enable 2 more -> 7
    en = 1;
    repeat (2) one_cycle;
    $display("en=1 x2 cycles: q = %0d (expect 7)", q);
    if (q !== 4'd7) begin $display("FAIL: resume count"); $finish; end

    $display("ALL PASS");
    $finish;
  end
endmodule
after reset: q = 0 (expect 0)
en=0 x4 cycles: q = 0 (expect 0)
en=1 x5 cycles: q = 5 (expect 5)
en=0 x3 cycles: q = 5 (expect 5)
en=1 x2 cycles: q = 7 (expect 7)
ALL PASS

The counter value is checked after each phase and the run ends with ALL PASS, confirming the gated clock cleanly starts and stops the register updates.

How the latch-based ICG works

A clock gate must obey one rule: the gating signal presented to the AND gate may only change while the clock is in its inactive (low) phase. If the enable changes while the clock is high, the product clk & en can drop in the middle of a pulse (a runt or glitch) or create an extra edge — either of which can illegally clock a flip-flop. The integrated clock gate enforces the rule with a negative level-sensitive latch. In the code, always @(*) if(!clk) en_lat = en; makes en_lat transparent only while clk is low and opaque (held) while clk is high. So any late-arriving enable change is parked until the next low phase, and gclk = clk & en_lat is glitch-free by construction.

Why the naive AND gate is wrong

The tempting one-liner assign gclk = clk & en; fails because en is a free-running signal from upstream logic and has no relationship to the clock phase. If en falls while clk is high, gclk immediately falls too, chopping the current high pulse short and producing a falling edge the downstream logic never expected. If en rises mid-high, you get a spurious rising edge — an illegal extra clock. In the first example, gclk_naive is wired up precisely so you can observe this on a waveform: at the mid-high toggle the ICG output is undisturbed while the naive output glitches.

Synthesis and timing notes

Do not actually write RTL latches in production code. Real flows instantiate the library's clock-gating cell (often called CKLNQD, ICG, or similar) through the synthesis tool's automatic clock-gate insertion (e.g. set_clock_gating_style in industry-standard synthesis tools/industry-standard synthesis tools). The behavioral latch shown here is the model of that cell — it is ideal for teaching and simulation but an inferred latch in a datapath is a classic lint error. Timing-wise the enable must satisfy setup to the rising edge of the clock at the latch, which is why tools enforce a tight timing arc on the gating enable. Clock gating also helps place-and-route: fewer toggling endpoints means less clock-tree dynamic power.

Common pitfalls

Three traps catch beginners. First, sampling the enable on the wrong phase — if you make the latch transparent while clk is high you have simply moved the glitch window, not removed it. Second, forgetting that the gated clock is now a derived clock: it must be declared as a generated clock in your SDC or static timing analysis will mis-analyze it. Third, gating a reset or a clock that feeds asynchronous logic — keep clock gating on well-behaved synchronous domains only. Also note the counter here uses an asynchronous reset outside the gated path so it can be cleared even when the clock is gated off.

Interview angle

A near-universal interview question is: “Why can't you gate a clock with a simple AND gate, and how do you fix it?” The answer is exactly this page: an asynchronous enable ANDed with the clock produces glitches and runt pulses, so you insert a negative-level-sensitive latch to hold the enable stable through the entire active phase, giving a glitch-free integrated clock gate. Follow-ups probe whether you'd ever write this as RTL (no — use the standard cell via automatic gating), what SDC constraint the gated clock needs (a generated clock), and how clock gating saves dynamic power (it stops unnecessary clock-tree and register toggling). Being able to sketch the latch-plus-AND and explain the phase argument cleanly is what separates a confident answer from a vague one.

Have questions about this topic?
Share your question in comments or talk to our mentor team for batch guidance.
▶ Try it in the ChipXpert Lab

Open this design in the free in-browser simulator — edit, compile and see the waveform/log instantly.

Run it in the Lab

Learn Verilog with mentors, labs and placement support

This free series is maintained by the ChipXpert training team in Hyderabad & Bengaluru. Go deeper with instructor-led courses on real EDA tools.

Explore VLSI Courses
Next batch starts 18th November 2026
Start your VLSI career with ChipXpert

Live online and classroom batches in Hyderabad & Bengaluru. Fill in your details and a counsellor will call you back.

  • Real EDA tools in your browser: industry-standard EDAReal EDA tools in your browser
  • Recorded sessions on the elearn portalRecorded sessions
  • Placement assistance: resume, mock interviewsPlacement assistance
  • Merit scholarship up to 60% · EMI optionsScholarship up to 60%
Hi! Ask me about courses, fees, batches or discounts. ×
BENGALURU
HYDERABAD