Guarding a clocked load with if (en) keeps the old value on the cycles the enable is low. The question is what hardware that hold needs, and whether the enable saves any clock power by itself. This lesson infers a one-bit enabled flop, maps a 4-bit enabled register beside a 4-bit plain one, and breaks the hold with an AND-the-data variant. A short activity model then prices the one structure that does save clock power, labelled as a model throughout.
Guarding the load with if (en) infers an enabled flop: when the enable is low the flop keeps its value instead of loading new data. The first script pins the family on the if spelling: exactly one $dffe cell, with no plain, sync-reset, or async-reset variant.
read_verilog <<EOV
module en1(input clk, input en, input d, output reg q);
always @(posedge clk) if (en) q <= d;
endmodule
EOV
hierarchy -check -top en1
proc
opt
select -assert-count 1 t:$dffe
select -assert-none t:$dff
select -assert-none t:$adff
select -assert-none t:$sdff
tee -o stat_en1_generic.txt stat
write_json net_en1_generic.json
log PASS: enabled flop infers one $dffe cellThe histogram reads DFF x4, MUX2 x4: 4 DFFs for storage and 4 MUX2s recirculating the output when the enable is low. The clock still toggles every cycle, so the enable alone saves no clock power; it only stops new data from propagating.
Spelling | Generic cells (recorded) | Reading |
|---|---|---|
| 1 $dffe | One |
| 1 $and, 1 $dff | A |
Two structures implement the hold. A mux recirculates the output when the enable is low: a plain flop plus one select mux per bit, working in every library, with the clock pin toggling on every cycle either way. A clock-enable cell instead blocks capture inside the flop while leaving the clock tree untouched. The teaching library offers no such cell, so the mapping below is the place to watch what the tool actually builds: the second script maps the 4-bit enabled register and a 4-bit plain twin through synth, dfflegalize, dfflibmap, abc, and stat, asserting 4 DFFs in each. The recorded histogram for the enabled form reads DFF x4, MUX2 x4.
read_verilog <<EOV
module enreg4(input clk, input en, input [3:0] d, output reg [3:0] q);
always @(posedge clk) if (en) q <= d;
endmodule
EOV
read_liberty -lib course.lib
hierarchy -check -top enreg4
synth -top enreg4 -flatten
dfflegalize -cell $_DFF_P_ 01 -cell $_DFF_PN0_ 01
dfflibmap -liberty course.lib
tee -o abc_enreg4.txt abc -liberty course.lib -constr typical.constr -D 1000
tee -o stat_enreg4.txt stat -liberty course.lib
write_json net_enreg4.json
select -assert-count 4 t:DFF
design -reset
read_verilog <<EOV
module platter4(input clk, input [3:0] d, output reg [3:0] q);
always @(posedge clk) q <= d;
endmodule
EOV
read_liberty -lib course.lib
hierarchy -check -top platter4
synth -top platter4 -flatten
dfflegalize -cell $_DFF_P_ 01 -cell $_DFF_PN0_ 01
dfflibmap -liberty course.lib
tee -o abc_plain4.txt abc -liberty course.lib -constr typical.constr -D 1000
tee -o stat_plain4.txt stat -liberty course.lib
write_json net_plain4.json
select -assert-count 4 t:DFF
log PASS: enabled register maps to DFF plus feedback logic beside a plain one4-bit register | Mapped library cells (recorded) | Chip area (stat -liberty) |
|---|---|---|
Enabled | 4 DFF + 4 MUX2 | 52 |
Plain | 4 DFF | 32 |
Read the enabled row as the price of the hold: chip area 52 against 32 for the plain twin, so the four feedback muxes cost 20 units on top of storage. The clock pins toggle in both rows; the enable only selects which data reaches the flop. A library with a clock-enable cell would map the same $dffe to that cell instead, so the structure varies by library while the generic family stays put.
Neither row above saves clock power: in both, every clock pin toggles every cycle and the enable only steers data. The structure that does remove clock toggling is a clock gate, usually an integrated clock gater (ICG): a latch plus an AND placed in the clock path so the gated clock net stays still on idle cycles. That is the standard low-power technique, described here as background rather than as a run on this page: the teaching library has no gater cell and this flow inserts none, so there is no Yosys recording to quote. What stands in for the recording is a small activity model below, labelled as a model, counting clock-pin toggles for one 64-bit register with a shared enable at 10% activity over 1000 cycles.
import sys
# Clock-pin toggle model for one 64-bit register with a SHARED enable (model,
# not a tool measurement). Each clock pin costs one toggle unit per cycle it
# switches. Mux feedback keeps every clock pin toggling; a clock gate (ICG)
# switches the shared gated net only on cycles the enable is high.
N = 64
P_ENABLE = 0.1
CYCLES = 1000
GATER_OVERHEAD = 2.0
ungated = N * CYCLES
mux_feedback = N * CYCLES
gated = N * P_ENABLE * CYCLES + GATER_OVERHEAD * CYCLES
saving = (mux_feedback - gated) / mux_feedback
# Second operating point, worked by hand: at 50 percent enable activity the
# gated net toggles 64 x 0.5 x 1000 + 2 x 1000 = 34000 units, saving
# (64000 - 34000) / 64000 = 0.46875 of the pin toggles.
gated_half = N * 0.5 * CYCLES + GATER_OVERHEAD * CYCLES
saving_half = (mux_feedback - gated_half) / mux_feedback
checks = [
(mux_feedback == ungated, "enable alone toggles like the plain register"),
(gated == 64000 * P_ENABLE + 2000, "gated total follows N p CYCLES plus overhead"),
(gated < ungated, "the gate removes toggles at p 0.1"),
(round(saving, 3) == 0.869, "saving at p 0.1 rounds to 87 percent"),
(gated_half == 34000.0, "gated total at p 0.5"),
(saving_half == 0.46875, "saving at p 0.5"),
]
for ok, what in checks:
if not ok:
print("FAIL: " + what)
sys.exit(1)
print("PASS: a shared gate removes about 87 percent of clock-pin toggles at p 0.1")
print("ungated " + str(ungated) + " gated " + str(gated) + " saving " + str(round(saving, 3)))activity model, shared enable at p = 0.1 over 1000 cycles (labelled model, not measured)
ungated or mux-feedback clock toggles = 64 x 1000 = 64000
gated clock toggles = 64 x 0.1 x 1000 + 2 x 1000 = 8400
saving = (64000 - 8400) / 64000 = 0.869, about 87% fewer pin togglesRead the model against the recorded rows: the $dffe-as-mux this library builds sits at 64000 toggle units, exactly where the plain register sits, while the gated variant sits near 8400. The saving belongs to the gate, not to the enable idiom. In a commercial flow the gate is inserted by the low-power step, with its own enable-timing checks; here it stays a hand-counted model, which is why the page never quotes it beside the recorded areas without the label.
The third script writes the one-bit flop as q <= d & en and records a different family: one $dff cell plus an AND gate, reading 1 $and, 1 $dff, with no $dffe anywhere. ANDing the data clears the flop whenever the enable is low instead of holding it, so the behaviour is not the same hold and the tool does not infer the enabled cell. The testbench below pins the difference: with the enable low across an edge the output clears to 0, while the guarded flop in the next section holds. Reach for data gating only when clearing is what the spec wants; keep the if (en) guard whenever the old value must survive.
read_verilog <<EOV
module enand1(input clk, input en, input d, output reg q);
always @(posedge clk) q <= d & en;
endmodule
EOV
hierarchy -check -top enand1
proc
opt
select -assert-count 1 t:$dff
select -assert-none t:$dffe
select -assert-count 1 t:$and
tee -o stat_enand1_generic.txt stat
write_json net_enand1_generic.json
log PASS: AND-the-data variant infers a plain flop plus gate// engine: icarus
module enand1_tb(input logic clk, input logic en, input logic d, output logic q);
always @(posedge clk) q <= d & en;
endmodule
module tb_anden;
logic clk = 0, en = 0, d = 1, q;
enand1_tb dut (.*);
always #5 clk = ~clk;
int errors = 0;
initial begin
@(posedge clk); #1;
if (q !== 1'b0) begin $display("FAIL: AND form held instead of clearing"); errors++; end
en = 1;
@(posedge clk); #1;
if (q !== 1'b1) begin $display("FAIL: AND form did not load with en"); errors++; end
if (errors == 0) $display("PASS: AND-the-data clears instead of holding");
$finish;
end
endmoduleThe testbench below exercises the behaviour the $dffe promises: without enable the flop holds across edges, with enable it loads. It runs on Icarus with four-state checks so a held value and a loaded value are distinguished exactly.
// engine: icarus
module en_hold (
input logic clk, input logic en, input logic d, output logic q
);
always @(posedge clk) if (en) q <= d;
endmodule
module tb_en_hold;
logic clk = 0, en = 0, d = 0, q;
en_hold dut (.*);
always #5 clk = ~clk;
int errors = 0;
initial begin
d = 1;
@(posedge clk); #1;
if (q !== 1'bx) begin $display("FAIL: flop changed without enable"); errors++; end
en = 1;
@(posedge clk); #1;
if (q !== 1'b1) begin $display("FAIL: flop did not load with enable"); errors++; end
en = 0; d = 0;
@(posedge clk); #1;
if (q !== 1'b1) begin $display("FAIL: flop did not hold without enable"); errors++; end
if (errors == 0) $display("PASS: enabled flop holds without enable and loads with it");
$finish;
end
endmoduleCalling the mux-feedback flop lower power. It saves data switching downstream, never clock toggling; only a gate in the clock path removes pin toggles, as the model quantifies.
A DFF plus per-bit feedback mux logic: the recorded 4-bit register reads DFF x4, MUX2 x4 at chip area 52. The $dffe behaviour stays identical; only the structure differs by library.
if (en) q <= d preferred over ANDing the clock with en?The first is data timed like any synchronous input, with the clock tree untouched. The second can glitch when en moves near a high clock, builds a new clock needing its own tree, and synthesis cannot time it as data.
Generic families, mapped histograms, and chip areas on this page come from three Yosys 0.70 runs recorded by the course checker (scripts/tools/run-rtl-testbenches.mjs with COURSE=synth): a one-bit enabled flop through proc and opt, a 4-bit enabled register and a plain twin through synth, dfflegalize, dfflibmap -liberty course.lib, abc -liberty course.lib -constr typical.constr -D 1000 and stat -liberty course.lib, and an AND-the-data variant that infers a plain flop plus gate. The 87% toggle saving is a stdlib-only Python activity model with checks at two operating points, labelled so on the page. The ICG description is standard background, not a run. The hold and clear testbenches run on Icarus. The libraries are fictional, so absolute areas are teaching units.