Enables and gated clocks

Write a register that holds its value with an enable, see why an AND gate on the clock adds clock edges, and know the clock-gating cell a real flow uses instead.

Lesson
R2.3
Time
25 min
Runs on
Your browser
Check
5 questions

To save power, an engineer stopped a log buffer's clock with assign gclk = clk & en;. The enable came from a flop, so it changed just after each rising edge, while the clock was still high. Every time en rose, the AND gate passed the rest of that high phase as an extra clock edge. The buffer wrote one entry twice per burst, and every later entry landed one place off.

The same writes, two waysen and din change 1 ns after each edge. One value per edge, printed 1 ns after it. The last two rows are the write pointer of the gated design and of the enabled design.
0123456789clkendin0011223344wptr gated02346wptr enable01234
  1. Cycle 3

    en rose 1 ns after edge 2, while clk was high, so gclk rose too. That extra edge wrote 11 into entry 0. Edge 3 wrote 11 again, into entry 1: the gated pointer is 2.

  2. Cycle 5

    Inside the burst en stays 1, so gclk copies clk and each edge writes once. The gated pointer stays one ahead.

  3. Cycle 6

    en falls while clk is high. gclk falls early: a pulse cut short, but no extra rising edge.

  4. Cycle 8

    One more write, and en rises while clk is high again. The gated pointer jumps by 2, to 6. The enabled one moves by 1, to 4.

Measured on the lab bench. Read back, the gated log is 11 11 22 33 44 44; the enabled log is 11 22 33 44.

An enable belongs on the data path

A register that must hold its value in some cycles needs an enable: a condition inside the clocked block, if (en) q <= d;. The clock reaches the flop on every edge. At each edge the flop takes either the new value or its own old one, decided by en.

An AND gate on the clock

assign gclk = clk & en;
always_ff @(posedge gclk or negedge rst_n)
  if (!rst_n) wptr <= 3'd0;
  else        wptr <= wptr + 3'd1;

Every flop is clocked by a logic gate. When en rises while clk is high, gclk gets an extra rising edge.

An enable inside the block

always_ff @(posedge clk or negedge rst_n)
  if (!rst_n)  wptr <= 3'd0;
  else if (en) wptr <= wptr + 3'd1;

The clock is untouched. en is read only at the edge, after it has had a whole period to settle.

Synthesis builds the enable as a flop with an enable pin, or as a flop with a multiplexer that feeds the old value back. Either way the decision sits on the data path, where the flop's setup time protects it. The cell names in the Gates tab say which kind of flop you got.

RTL inside always_ffCell on this flow
q <= d;$_DFF_P_
if (en) q <= d;$_DFFE_PP_
asynchronous rst_n, then q <= d;$_DFF_PN0_
asynchronous rst_n, then if (en) q <= d;$_DFFE_PN0P_
synchronous rst_n, then if (en) q <= d;$_SDFFE_PN0P_

Measured one bit at a time with Yosys 0.70. P: rising clock edge. N0: active-low reset to 0. The last P: active-high enable.

Why the AND gate adds edges

An enable made by a flop changes a moment after the rising edge, while the clock is still high. That is the worst moment for an AND gate. A rising en raises gclk at once, and a falling en cuts the clock pulse short.

An enable that changes while clk is highArrows show signals. The enable comes from a flop clocked by clk, so it changes just after each rising edge.
An enable that changes while clk is highArrows show signals. The enable comes from a flop clocked by clk, so it changes just after each rising edge. manager en flop changes after eachedge manager clk high for half a period bridge AND gate gclk = clk & en completer wptr and 8entries clocked by gclk enclkgclk
  1. Edge k

    clk rises while en is 0, so gclk stays low. The en flop takes its new value at this edge.

  2. 1 ns later

    en rises while clk is still high. The AND output rises with it: an edge the clock never had. The bank writes.

    Follow the calls, values and waits
    1. data en rises: en flop → AND gate
    2. data extra edge: AND gate → wptr and 8 entries
  3. Edge k+1

    clk rises with en at 1: the write that was meant. The data has not changed yet, so it is written twice.

Read the diagram as text
  • en flop: changes after each edge
  • clk: high for half a period
  • AND gate: gclk = clk & en
  • wptr and 8 entries: clocked by gclk
  • en flop → AND gate: en
  • clk → AND gate: clk
  • AND gate → wptr and 8 entries: gclk

The timing of the lab bench. Verilator's lint reports nothing about the gated clock: the code is legal.

Two more rules follow. A clock is never made by logic inside the design: a divider is a counter that produces an enable, and the block stays on one clock. A clock is never used as data either.

Clock gating done right: the ICG cell

Stopping the clock does save power, and chips do it everywhere. They use an integrated clock-gating cell (ICG), a library cell with a latch in front of the AND gate. The latch is open while clk is low and closed while it is high, so the enable cannot change during a high phase.

Inside a clock-gating cellArrows show signals. The latch passes en only while clk is low.
Inside a clock-gating cellArrows show signals. The latch passes en only while clk is low. manager en from a flop manager clk free-running bridge Latch open while clk is low bridge AND gate clk & held en completer gclk whole pulses only enheld enclk
  1. clk low

    The latch is open: a new en passes to the AND gate. The AND output is 0, because clk is 0.

    Follow the calls, values and waits
    1. data en: en → Latch
    2. data en: Latch → AND gate
  2. clk high

    The latch is closed. A change on en stops at the latch, so gclk is either a whole clock pulse or nothing.

Read the diagram as text
  • en: from a flop
  • clk: free-running
  • Latch: open while clk is low
  • AND gate: clk & held en
  • gclk: whole pulses only
  • en → Latch: en
  • Latch → AND gate: held en
  • clk → AND gate: clk
  • AND gate → gclk

A latch-and-AND model of this cell, run on the lab bench, writes 11 22 33 44: no extra entries.

A real flow inserts ICGs from enables you wrote, or you instantiate the library's cell by name. Yosys has a clockgate command for this, but it needs a cell library. This site's flow maps to built-in gates, which include no ICG, so here clock gating stays a description.

The latch-and-AND model, for reading only
Put in place of the AND line, it fixes the lab bench in simulation. On this flow it synthesizes to one $_DLATCH_N_ latch and an AND gate: 175 cells.
// A model of a clock-gating cell, for reading. In a chip this is one
// library cell, inserted by the flow or instantiated; never hand-written.
logic en_l;
always_latch if (!clk) en_l = en;   // open while clk is low, closed while it is high
assign gclk = clk & en_l;
Design brief
PortDirectionWidthMeaning
clkinput1clock
rst_ninput1reset, asynchronous, active-low
eninput1when 1 at a rising edge, write din at wptr and add one to wptr
dininput[7:0]data to write
raddrinput[2:0]entry to read
rdataoutput[7:0]shows the entry at raddr at once
Clock
one clock, clk, reaching every flop untouched
Reset
wptr to 0; the 8 entries have no reset, because none is read before it is written
Parameters
Memory: 64 entry flops and 3 pointer flops
Corner cases
en changing while clk is high; wptr wrapping from 7 to 0

The lab ships the AND-gated version. Run it and read the doubled entries in the log line. Then move the enable into the two blocks and run again.

Asked in interviews
  1. How do you write a register with an enable?

    Put the condition inside the clocked block: else if (en) q <= d;, with nothing about en in the event list. Synthesis builds an enabled flop or a feedback mux, and the clock stays untouched.

  2. Why not gate a clock with an AND gate, and what is an ICG?

    An enable from a flop changes while the clock is high, so the AND output gains or loses part of a pulse. An ICG is a library cell that latches the enable while the clock is low, so it only ever passes whole pulses.

  3. Can you make a slower clock with a divider in RTL?

    Write the divider as a counter that produces an enable, and keep one clock. A divided clock made by logic is a new clock domain that the tools must be told about. Real clock generation belongs to the clocking logic.

Try it

Four values were written and the log holds six: 11 and 44 twice. Each time en rose while clk was high, gclk had an extra edge. Synthesize: 174 cells and 67 flops, every one clocked by the AND output.

Delete gclk and its assign, clock both blocks with clk, and add the enable: else if (en) wptr <= wptr + 3'd1; and if (en) mem[wptr] <= din;. Run again: the pointer ends at 4 and the log reads 11 22 33 44 xx xx xx xx. Synthesize: 176 cells, 67 flops, all on clk; the pointer flops are now $_DFFE_PN0P_.

RTLIcarus Verilog · in your browser · Lint: Verilator service · Synthesize: Yosys service
Edit the design, then Run, Lint or Synthesize
Expected: e0 en=0 din=00 wptr=0 e1 en=0 din=00 wptr=0 e2 en=0 din=00 wptr=0 e3 en=1 din=11 wptr=2 e4 en=1 din=22 wptr=3 e5 en=1 din=33 wptr=4 e6 en=0 din=33 wptr=4 e7 en=0 din=33 wptr=4 e8 en=1 din=44 wptr=6 e9 en=0 din=44 wptr=6 log: 11 11 22 33 44 44 xx xx

Original design · 174 cells · 67 flip-flops · 0 latches · 5 logic levels. Synthesis uses Yosys; counts depend on the toolchain.

Read included file: tb.sv
`timescale 1ns/1ps
module tb;
  logic clk = 0;
  always #5 clk = ~clk;                   // rising edges at 5, 15, 25, ...
  int edge_n = -1;
  always @(posedge clk) edge_n = edge_n + 1;
  logic       rst_n = 1;
  logic       en = 0;
  logic [7:0] din = 0;
  logic [2:0] raddr = 0;
  logic [7:0] rdata;
  logbuf dut (.clk(clk), .rst_n(rst_n), .en(en), .din(din), .raddr(raddr), .rdata(rdata));
  // Advance to the next rising edge, wait for the design to settle, then print one line.
  task automatic tick();
    @(posedge clk); #1;
    $display("e%0d en=%b din=%h wptr=%0d", edge_n, en, din, dut.wptr);
  endtask
  initial begin
    #1 rst_n = 0;                           // reset asserted before edge 0
    tick(); tick(); rst_n = 1;              // released 1 ns after edge 1
    tick();                                 // e2: idle
    // From here en and din change 1 ns after an edge, while clk is still high,
    // the way the output of a flop clocked by clk would.
    din = 8'h11; en = 1; tick();            // e3: three writes, one per edge ...
    din = 8'h22;         tick();            // e4
    din = 8'h33;         tick();            // e5
    en = 0;              tick();            // e6: ... then nothing to write
                         tick();            // e7
    din = 8'h44; en = 1; tick();            // e8: one more write
    en = 0;              tick();            // e9
    $write("log:");                         // read the log back, entry 0 first
    for (int i = 0; i < 8; i++) begin
      raddr = i; #1 $write(" %h", rdata);
    end
    $display("");
    $finish(0);                             // quiet: no "$finish called" line
  end
endmodule

Check yourself

Predict the outputA register with an enable takes 7, then 9, then sees two edges with en low while d changes to 4 and 5. What is the output line?
module tb;
  logic clk = 0, rst_n = 0, en = 0;
  logic [7:0] d = 0, q;
  always_ff @(posedge clk or negedge rst_n)
    if (!rst_n)  q <= 8'd0;
    else if (en) q <= d;
  task automatic step(); #5 clk = 1; #5 clk = 0; endtask
  initial begin
    step();                       // one edge in reset: q = 0
    rst_n = 1;
    en = 1; d = 8'd7; step();
    d = 8'd9;         step();
    en = 0; d = 8'd4; step();
    d = 8'd5;         step();
    $display("q=%0d", q);
    $finish(0);
  end
endmodule
Spot the bugEach option is the body of a counter with an enable. The bench raises en 1 ns after an edge, holds it for two edges, and expects q = 2. Which body fails?
ConceptWhy can assign gclk = clk & en; give the flops behind it an extra rising edge?
ConceptA clock-gating cell puts a latch in front of its AND gate. What is the latch for?
Read the waveformThis is the gated design near the end of the bench. The bench raises en 1 ns after edge 7 for one write. By how much does wptr move at edge 8, and why?
The gated log buffer, edges 6 to 9
0123clkendin3344wptr46

Columns are edges 6, 7, 8 and 9. One value per edge, printed 1 ns after it.

Remember

  • A register that holds is written with an enable inside the clocked block; the clock reaches the flop untouched.
  • An enable made by a flop changes while the clock is high, so clk & en gains an edge when en rises and loses part of a pulse when en falls.
  • Synthesis builds an enable as an enabled flop such as $_DFFE_PP_, or as a mux that feeds the old value back.
  • Real clock gating uses an ICG library cell, a latch plus an AND gate; this site's flow has none, so the lab uses the enable.
  • A divider is a counter that makes an enable; logic never makes a clock, and a clock is never data.