The RTL coding mistakes that cost the most power and area are the ones that make logic toggle when it does no useful work, or that build more hardware than the function needs. Clocks that run when registers hold their value, glitchy deep logic, oversized buses, resets on every flop and accidental latches are the usual culprits. Synthesis tools recover some of this, but they cannot undo an architecture or coding style that asks for waste. Below are the ten most common mistakes, why each one hurts, and the fix a reviewer would expect.
Key takeaways
- Dynamic power follows P = α × C × V² × f. RTL style mainly controls α (how often nodes toggle) and C (how much logic and wiring exists).
- The clock network toggles every cycle, so clock gating idle registers is usually the biggest single RTL-level saving.
- Area mistakes become power mistakes: every extra cell adds switched capacitance and leakage.
- Write load enables, not hand-built gated clocks. Let the synthesis tool insert library integrated clock gating (ICG) cells.
- Reset only what needs a known value, size every bus to its real range, and never leave a combinational block able to infer a latch.
Why RTL style decides power and area
Switching power in CMOS is proportional to the activity factor α, the switched capacitance C, the square of the supply voltage and the clock frequency. Voltage and frequency are usually fixed by the product specification and the implementation team. The RTL designer has most control over the other two terms: how many nodes exist and how often they change. Leakage power, the other main component, grows with the number and size of cells, so area that is not needed costs power even when the block is idle.
Synthesis tools are good at local optimisation. They remove constant logic, share some operators and pick cell sizes. They are not good at knowing that a register only needs updating once every hundred cycles, that a counter never exceeds 200, or that a datapath result is ignored half the time. That knowledge lives in the designer's head and has to be expressed in the RTL.
The 10 mistakes
1. Unoptimised clock gating
Mistake: using enable signals directly on flip-flops through a feedback multiplexer, so the register clock still toggles every cycle, or worse, gating a clock with a plain AND gate in RTL.
Impact: the clock pins of every flop, and the clock tree driving them, burn dynamic power on every edge even when the stored value does not change. A hand-made AND gate on a clock can also glitch if the enable changes while the clock is high, creating false clock edges.
Fix: use integrated clock gating (ICG) cells provided by the library. In practice, write a clean load enable (as in the example below) and let the synthesis tool infer the ICG, which contains a latch so the enable cannot glitch the clock. Group registers that share an enable so one ICG covers a useful number of flops; gating a single flop rarely pays for the ICG itself.
2. Redundant or glitchy logic
Mistake: poorly structured combinational logic, such as long ripple chains or unbalanced paths that reconverge, which produces glitches before the output settles.
Impact: each glitch is a real transition that charges and discharges capacitance, so glitches raise dynamic power without doing useful work. In deep arithmetic logic such as array multipliers, glitch power can be a large part of the total.
Fix: balance logic paths, use pipelining where depth is excessive, and minimise cascaded combinational logic. Registering the inputs of a large block and holding them stable when the result is not needed (operand isolation) also stops glitches propagating.
3. Non-pipelined long combinational paths
Mistake: long chains of combinational logic without pipelining.
Impact: long paths increase critical path delay. To close timing, the tool upsizes cells and adds buffers, which increases both area and power. The extra depth also gives glitches more room to multiply.
Fix: insert pipeline registers to break long paths. Pipeline registers cost flops and clock power, so this is a trade: it pays when it lets the tool use smaller cells, or lets the design run at a lower voltage for the same throughput.
4. Inefficient state encoding
Mistake: picking a state encoding without thinking about the transition pattern, for example plain binary for an FSM that mostly steps through states in sequence.
Impact: binary encoding can flip several state bits on one transition (3 to 4 flips three bits), which raises switching power in the state register and its decode logic.
Fix: use Gray code for reduced switching when the common transitions are between neighbouring states, or one-hot for simpler decoding. Note the trade-offs: Gray only guarantees a single bit change between adjacent codes, not for every jump in an arbitrary FSM, and one-hot needs one flop per state (more area) while changing exactly two bits per transition. For small FSMs the difference is minor; it matters for wide or very active controllers. Most synthesis tools can re-encode FSMs automatically if you let them.
5. Unnecessary use of reset
Mistake: putting a reset, synchronous or asynchronous, on every flip-flop when it is not needed.
Impact: resettable flops are larger than plain flops, a synchronous reset adds a gate in front of every D input, and the reset net becomes a high fan-out signal that needs its own buffer tree and timing closure. All of that adds area and power.
Fix: use reset only where necessary, for example on control registers, FSM state, valid flags and counters, not on pure data paths whose contents are qualified by a valid bit. Confirm the policy with the verification team, since X values on unreset data registers must not leak into control logic.
6. Overuse of high fan-out signals
Mistake: driving one register or signal to a very large number of destinations, such as a single enable or select feeding thousands of flops.
Impact: the implementation tools must build large buffer trees to drive the load, which adds area and power and often creates timing problems.
Fix: plan for buffer trees or use register duplication so each copy drives a local group. Better still, ask whether every destination needs the signal on every cycle; a hierarchical enable often removes most of the load.
7. Inefficient memory access
Mistake: reading or writing memories more frequently than needed, for example reading the same word on every cycle or leaving chip select asserted while idle.
Impact: each SRAM access costs far more energy than a flop update, so memory power is often a large share of total power in SoCs (the exact share depends heavily on the design).
Fix: use memory banking, access gating (drive chip enable low when no access is needed), and burst-mode operations. Hold a recently read word in a register if it is reused, and use the memory's light-sleep or retention modes when the macro supports them.
8. Wider datapaths than required
Mistake: using 32-bit buses when 8-bit is sufficient, often because integer or a default width was used out of habit.
Impact: wastes area and increases switching power. Extra upper bits may be trimmed by the tool only if it can prove they are constant, which it often cannot across module boundaries or registers.
Fix: right-size all variables and buses from the actual value range. A counter that never exceeds 200 needs 8 bits, not 32.
9. Lack of power-aware RTL constructs (unintended latches)
Mistake: not using always @(*) properly, leading to unintended latches when a signal is not assigned on every path through the block.
Impact: latches add cells and enable logic the designer never planned, complicate static timing analysis and test, and can cause simulation and silicon to disagree. The area cost is usually small; the bigger risk is functional and timing trouble.
Fix: use always_comb (SystemVerilog) and ensure complete assignments. The simplest habit is to assign a default value to every output at the top of the block, as in the decoder example below. Lint tools flag inferred latches; treat that warning as an error.
10. Redundant sequential logic
Mistake: using flip-flops where shift registers (SRLs) or RAMs could be used, such as a 64-deep delay line or a large register file built from discrete flops.
Impact: more flip-flops means higher area and power, plus more clock load.
Fix: use FPGA SRLs (if applicable) or optimised memory blocks. On FPGAs, write delay lines without a reset so the tool can map them to lookup-table shift registers. On ASICs, a register file compiler or SRAM macro is usually far denser than flops once the array passes a few hundred bits.
Summary table
| Mistake | Mainly costs | Fix in one line |
|---|---|---|
| No clock gating | Clock power | Write load enables, let synthesis insert ICGs |
| Glitchy logic | Dynamic power | Balance paths, isolate operands |
| Long combinational paths | Area and power from upsizing | Pipeline where it lets cells shrink |
| Poor state encoding | Switching in FSMs | Match encoding to transition pattern |
| Reset everywhere | Area, reset tree | Reset control, not datapath |
| High fan-out | Buffer area and power | Duplicate registers, local enables |
| Wasteful memory access | Memory power | Gate chip enable, bank, burst |
| Oversized buses | Area and switching | Size from the real value range |
| Inferred latches | Area, timing, risk | Defaults at top of always_comb |
| Flops instead of SRL or RAM | Area, clock load | Use SRLs, register files or SRAM |
A worked example: clock gating a register bank
The numbers here are illustrative, not from a real design. Take a block with 2,000 data flops that is updated, on average, in 1 cycle out of 10. Without gating, all 2,000 clock pins toggle every cycle. With the flops grouped behind, say, 32 ICG cells, the clock pins toggle only in the 10% of cycles where data is loaded. The clock power at the flops drops by roughly 90%, minus the small cost of the ICG cells themselves and the clock tree segment that still runs up to each ICG. The data inputs no longer need recirculation multiplexers either, which saves area. This is why clock gating is usually the first thing a power review checks.
Code examples
The three modules below show the patterns: a datapath register with a load enable and no reset, a control flag that keeps its reset, and a decoder that cannot infer a latch.
// Register with a load enable: synthesis can map this to an ICG cell
module data_reg #(parameter W = 32) (
input logic clk,
input logic load,
input logic [W-1:0] d,
output logic [W-1:0] q
);
always_ff @(posedge clk)
if (load) q <= d; // no reset on a pure datapath register
endmodule
// Control flag: keeps its reset
module busy_flag (
input logic clk,
input logic rst_n,
input logic start,
input logic done,
output logic busy
);
always_ff @(posedge clk or negedge rst_n)
if (!rst_n) busy <= 1'b0;
else if (start) busy <= 1'b1;
else if (done) busy <= 1'b0;
endmodule
// Decoder with a default value at the top: no latch can be inferred
module sel_decode (
input logic [1:0] sel,
input logic [7:0] a, b, c,
output logic [7:0] y
);
always_comb begin
y = 8'h00; // default first
case (sel)
2'd0: y = a;
2'd1: y = b;
2'd2: y = c;
endcase
end
endmodule
Without the line y = 8'h00;, the case for sel = 3 would leave y unassigned in plain Verilog always @(*), and the tool would infer a latch to hold its old value. With always_comb, tools also warn when the block does not behave as pure combinational logic.
General fixes
- Use synthesis directives (
full_case,parallel_case) carefully, or better, not at all. They change what synthesis builds without changing simulation, which can create mismatches. Clifford Cummings' SNUG paper on these directives, often called the "evil twins" paper, explains the risk. In SystemVerilog,uniqueandprioritycase give the same intent with simulation checks. - On
defaultassignments in case statements: when the case items already cover every value, adefaultitem adds no logic and is not needed for synthesis. What matters for latch avoidance is that every output gets a value on every path, which a default assignment at the top of the block guarantees. For FSMs, adefaultbranch that returns to a known state is still good practice for recovery. - Leverage power-aware synthesis and power intent formats (e.g. UPF, standardised as IEEE 1801, or CPF from Si2) for multi-voltage and power-gated designs. These describe power domains, isolation, retention and level shifting outside the RTL.
- Run an RTL power estimation tool with realistic activity from simulation early, while fixes are still cheap.
Common review questions
When reviewers or interviewers probe this topic, they tend to ask: what fraction of registers are clock gated, and how many flops sit behind each ICG; which registers have resets and why; what the widest bus is and whether its range is justified; whether any lint latch warnings were waived; and how memory chip enables behave when the block is idle. Having those answers ready is a good sign the RTL was written with power in mind.
FAQ
Does the synthesis tool not fix these mistakes automatically?
Partly. Tools infer clock gating from enables, remove constant logic and can re-encode FSMs. They cannot know the real value range of a bus, which registers do not need a reset, or that a memory read is redundant. Those decisions belong in the RTL.
Is one-hot always the lowest power FSM encoding?
No. One-hot changes exactly two bits per transition and decodes cheaply, but it uses more flops, which adds clock load and leakage. For small FSMs binary or Gray can be equal or better. Measure with realistic activity.
Should I use asynchronous or synchronous reset to save power?
The bigger saving is resetting fewer flops. Between the two styles, the choice is usually set by the library, the methodology and the reset strategy of the SoC, not by power.
Can clock gating ever increase power?
Yes, if an ICG gates only one or two flops or the enable is almost always on. The ICG and its enable logic then cost more than they save. Tools usually apply a minimum bit-width rule for this reason.
By avoiding these mistakes, RTL designers can significantly reduce dynamic power, leakage power, and area in ASIC/FPGA designs, and they make timing closure easier at the same time.


Your comments will be moderated before it appears here.