Synthesis · lesson 10

Power, retiming and hierarchy

Three levers beyond the basic flow: clock gating to cut power, retiming to move registers, and ungrouping and dont_touch to control hierarchy.

7 min read · Free, no login

The basic flow reads, constrains, synthesizes and reports. Real projects add a few more levers. This lesson covers the three used most often: clock gating, retiming and hierarchy control. Each one changes the netlist in a way that must be understood before it is switched on.

Reading time: about 7 minutes.

10.1 Clock gating

A register that loads a new value only when an enable is true still receives a clock edge every cycle, and the clock pin and the recirculating mux both consume power. Clock gating inserts a cell that stops the clock to a group of registers when the enable is false, so the registers do not toggle at all.

Let us consider 16 flops that load only when en is high. In RTL this looks like if (en) q <= d;. Without gating, the tool builds 16 muxes feeding 16 flops. With gating, it builds a single gate on the clock and removes the 16 muxes.

In Genus, set the attribute before synthesis:

set_db lp_insert_clock_gating true

Two further attributes control how large a gated group may be:

set_db design:* .lp_clock_gating_min_flops 6
set_db design:* .lp_clock_gating_max_flops 18

A group smaller than the minimum is not worth gating because the gate itself costs area and power. A group larger than the maximum would make a single gate drive too many clock pins. After synthesis, check what was done:

report_clock_gating

The report lists how many registers were gated, how many gates were used, and the registers that were left alone. One caution: the clock gate must have a glitch-free enable. Library clock-gating cells (integrated clock gates) are built to guarantee this, and the tool should be allowed to use them. Without a suitable clock-gating cell, the tool may skip or restrict gating, so check the log.

10.2 Retiming

Retiming moves registers across combinational logic to balance the delay between stages, without changing the overall latency or the function. Let us consider two pipeline stages with logic delays of 6 ns and 4 ns. The clock period must be at least 6 ns, plus overhead. If registers are moved so the stages become 5 ns and 5 ns, the period can drop to 5 ns. The total logic is the same, only the cut points moved.

Two things follow:

  • The number of registers after retiming might not equal the number before, because registers can be merged or duplicated.
  • The combinational logic itself is not optimized by retiming.

To retime selected modules automatically, set the retime attribute on them before synthesis:

set_db module:chip_top/stage_a .retime true
set_db module:chip_top/stage_b/inner .retime true
syn_generic
syn_map

Retiming is performed during synthesis for the marked modules, with the aim of minimizing delay. It can also be used to reduce register count when timing is already met. For a quick manual attempt on a design, the retime command with a -min_delay option exists, and its arguments are listed in the tool help.

When is retiming a good idea? It suits datapaths and pipelines, where the logic can be balanced. It suits control logic poorly, because registers that hold state cannot move freely. Two cautions: retimed register names change, with a suffix set by an attribute, which can break scripts that refer to old names. And formal equivalence checking needs extra setup, because registers no longer match one-to-one with the RTL.

10.3 Hierarchy control

By default, Genus may remove module boundaries during optimization, mostly at high effort, which helps results because logic can be shared and simplified across the boundary. However, flat netlists have drawbacks: they are harder to debug, and hierarchical place and route or formal comparison may need the original structure.

The attribute that controls automatic ungrouping is:

set_db auto_ungroup none

With none, the hierarchy is kept as written. Other settings let the tool ungroup small modules as it sees fit, and the allowed values are listed in the tool help for your version.

For a single block, dont_touch is the finer tool:

set_db [get_db modules *fifo_ctrl] .dont_touch true

This tells the tool to leave the module exactly as it is. It is useful for hand-crafted blocks, for IP netlists, and for logic whose structure was verified separately. Thus, auto_ungroup is a global switch, dont_touch is a local one, and a project often uses both.

The same ideas apply to library cells: marking a cell avoid discourages it, and dont_touch protects an instantiated cell from being replaced.

10.4 Choosing among the levers

GoalLeverCost
Lower dynamic powerClock gatingSmall area for gates, enable timing must be checked
Faster clock on pipelinesRetimingRegister names change, formal checks need extra care
Keep structure for debug or LECauto_ungroup none, dont_touchFewer cross-boundary optimizations, a bit more area

Use the table as a starting point. No lever is free, so measure area, timing and power before and after, with the same constraints.

10.5 Exercise

On the accumulator, the sum register loads only when en is high. Turn on clock gating, run synthesis, and read report_clock_gating. Then compare the cell counts from report_gates with and without gating. The mux per bit should disappear and one gating cell should appear. If your small library has no suitable clock-gating cell, gating may be skipped or restricted, and the log will usually say why. That is a useful result too.

10.6 Why an interviewer asks about this

These topics separate people who ran a basic flow from people who owned a block's PPA. Likely questions: "how does clock gating save power", "what does retiming change and what does it keep the same", and "why would you keep hierarchy". Good answers mention the cost of each: gating needs a clean enable, retiming changes register identity, and hierarchy limits optimization.

Key takeaways

  • Clock gating removes the per-bit enable mux and stops clock toggling, with group size limits that keep the gate worthwhile.
  • Retiming moves registers to balance stages and keeps function and latency, but it changes register names and the number of registers.
  • auto_ungroup none keeps hierarchy globally and dont_touch protects one module or cell.
  • Measure each lever before and after, because none is free.