2026-07-06
You've written RTL where one pipeline stage does a 32-bit multiply and the next stage does a trivial XOR. The multiply stage sets your clock frequency; the XOR stage sits idle for 90% of the period. Retiming is the synthesis transformation that fixes this by sliding flip-flops across combinational logic without changing the circuit's input/output behavior — you keep the same number of pipeline stages, but rebalance where the clock edges land.
The math comes from Leiserson and Saxe (1983). Model the circuit as a directed graph where nodes are combinational blocks and edges have a weight equal to the number of flip-flops on that path and a delay equal to the combinational delay of the source node. A legal retiming assigns each node an integer r(v), and the new weight on edge (u,v) becomes w(u,v) + r(v) − r(u). Two constraints: all edge weights must stay ≥ 0 (you can't have negative flip-flops), and around every cycle the total flip-flop count is invariant (feedback loops preserve their state count).
Concrete example: a MAC unit with stage 1 = 4ns multiply, stage 2 = 1ns add, stage 3 = 0.5ns register-file write. Clock period is limited by the 4ns multiply → 250 MHz max. Retiming can push part of the multiply's combinational logic past the stage-1 flip-flop, absorbing it into stage 2. If you split the multiply into a 2.5ns partial-product generation and a 1.5ns final add, then merge the 1.5ns with the existing 1ns adder, you get stages of 2.5ns / 2.5ns / 0.5ns → 400 MHz. Same latency (3 cycles), same silicon, 60% more throughput.
Rule of thumb: the minimum achievable clock period after retiming equals the longest combinational path between any two flip-flops in the original circuit's transitive fanout — you can never beat that. If your slowest single gate is 2ns, no amount of retiming gets you below 500 MHz.
Practical caveats: retiming preserves functional equivalence but breaks cycle-level equivalence. Signals that were observable at flip-flop X now appear one cycle earlier or later, which wrecks scan chains, breaks assertions written against internal nodes, and confuses debug. Most tools (Synopsys Design Compiler, Vivado) require you to explicitly enable retiming with a set_optimize_registers or RETIMING_FORWARD attribute, and they refuse to cross reset, clock-gating, or initial-value boundaries.
