Register retiming is a sequential optimisation that moves registers backwards or forwards through combinational logic so that the delay between registers is more evenly balanced, without changing the behaviour seen at the design's inputs and outputs. It lets synthesis shorten the critical path, and so raise the clock frequency, or reduce the number of flip-flops when timing already has margin. The price is that register names, counts and contents inside the design change, which affects verification, debug and DFT.
Register retiming is a sequential optimisation technique that moves registers through the combinational logic gates of a design to optimise timing and area.
Key takeaways
- Retiming moves flip-flops across gates; it does not add or remove latency on any input-to-output path unless pipelining is requested.
- Used for timing, it equalises stage delays so the slowest stage gets faster. Used for area, it merges registers where many fan in to one gate.
- Purely combinational logic can be pipelined by adding register stages at the output and letting retiming push them inwards.
- Testbenches that check only primary inputs and outputs still work. Anything that probes internal registers by name will break.
- Reset values, equivalence checking, scan insertion and timing exceptions all need attention after retiming.
Why retiming is needed
When you describe circuits at the RT level prior to logic synthesis, it is usually very difficult and time-consuming, if not impossible, to find the optimal register locations and code them into the HDL description. Gate delays are not known accurately until the design is mapped to a library, and they change again as the tool restructures logic. With register retiming, the locations of the flip-flops in a sequential design can be automatically adjusted to equalise as nearly as possible the delays of the stages.
This capability is particularly useful when some stages of a design exceed the timing goal while other stages fall short. If no path exceeds the timing goal, register retiming can be used to reduce the number of flip-flops, where possible.
How it works
The formal basis is the retiming algorithm of Leiserson and Saxe ("Retiming Synchronous Circuitry", Algorithmica, 1991). The circuit is modelled as a graph: each gate is a node with a delay, and each edge carries a count of registers. A retiming assigns an integer to every node, meaning "move this many registers from the outputs of this node to its inputs" (or the reverse for a negative number). Two rules keep the behaviour the same:
- Moving one register from every output of a gate to every input of that gate (backward retiming), or from every input to every output (forward retiming), does not change the sequence of values seen outside.
- The number of registers on any edge must stay zero or positive, and the total number of registers around any feedback loop is fixed. Retiming can redistribute registers inside a loop but cannot add or remove them.
Within these rules the tool searches for the placement that gives the shortest clock period, or the fewest registers that still meet the target period. Commercial tools add library delays, setup times and constraints on which cells may move.
Forward and backward moves
| Move | What happens | Typical effect |
|---|---|---|
| Forward (inputs to output of a gate) | Registers on all inputs of a gate are replaced by one register on its output | Fewer flops when a gate has several inputs; reset value is easy to compute |
| Backward (output to inputs of a gate) | One register on the output becomes one register on each input | More flops when a gate has several inputs; an equivalent reset state may not exist |
Worked example with numbers
Take a small multiply-accumulate style path, y = (a + b) × c, with 8-bit inputs. In the first version the RTL registers the three inputs and puts the adder and the multiplier in the same cycle. The delays below are illustrative only, not from a real library.
- Input registers to adder to multiplier to output register: 0.4 ns adder + 1.2 ns multiplier = 1.6 ns of logic.
- Stage before the input registers: almost no logic.
Retiming moves the a and b registers forward through the adder. The second stage now holds only the multiplier (1.2 ns) and the first stage holds the adder (0.4 ns). With, say, 0.2 ns of clock-to-Q plus setup overhead, the minimum period drops from 1.8 ns to 1.4 ns, about 555 MHz to about 714 MHz. Because two 8-bit registers (a and b) were replaced by one 9-bit register for the sum, the flop count for that part also falls from 16 to 9 bits.
// Before retiming: registers at the inputs, adder and multiplier in one stage
module mac_before (
input wire clk,
input wire [7:0] a, b, c,
output reg [16:0] y
);
reg [7:0] ra, rb, rc;
always @(posedge clk) begin
ra <= a;
rb <= b;
rc <= c;
y <= (ra + rb) * rc; // adder + multiplier in one cycle
end
endmodule
// After retiming: the a and b registers moved forward through the adder
module mac_after (
input wire clk,
input wire [7:0] a, b, c,
output reg [16:0] y
);
reg [8:0] s1; // one 9-bit register replaces two 8-bit ones
reg [7:0] rc;
always @(posedge clk) begin
s1 <= a + b; // adder now in the first stage
rc <= c;
y <= s1 * rc; // only the multiplier in the second stage
end
endmodule
module tb_retime;
reg clk = 0;
reg [7:0] a = 0, b = 0, c = 0;
wire [16:0] y0, y1;
integer i, errors = 0;
mac_before u0 (.clk(clk), .a(a), .b(b), .c(c), .y(y0));
mac_after u1 (.clk(clk), .a(a), .b(b), .c(c), .y(y1));
always #5 clk = ~clk;
initial begin
for (i = 0; i < 1000; i = i + 1) begin
@(negedge clk);
if (i > 2 && y0 !== y1) errors = errors + 1;
a = $random; b = $random; c = $random;
end
if (errors == 0) $display("PASS: outputs match for 1000 cycles");
else $display("FAIL: %0d mismatches", errors);
$finish;
end
endmodule
Both modules give the same y on every cycle, with the same two-cycle latency, which is exactly the promise of retiming: the outside view is unchanged while the inside is rebalanced. In practice you write the first version and let the synthesis tool find the second.
Pipelining combinational designs
Purely combinational designs can also be retimed by introducing pipelining into the design. In this case, you first specify the desired number of pipeline stages and the preferred flip-flop from the target library. The appropriate number of registers is added at the outputs of the design. Then the registers are moved through the combinational logic to retime the design for optimal clock period and area.
A common RTL habit follows from this: write the function as plain combinational logic, add N register stages at the output, and enable retiming on that block. The tool then places the stages where the delays balance, which is usually better than a hand-picked split.
Effect on verification
Register retiming leaves the behaviour of the circuit at the primary inputs and primary outputs unchanged (unless you choose special options that do not preserve the reset state of the design or add pipeline stages). Therefore you do not need to change any simulation testbenches developed for the original RTL design.
Retiming does, however, change the location, contents and names of registers in the design. A verification strategy that uses internal register inputs and outputs as reference points will no longer work. Retiming can also change the function of hierarchical cells inside a design and add clock, clear, set and enable pins to the interfaces of the hierarchical cells.
In practice this means:
- Equivalence checking: ordinary combinational equivalence checking matches registers one to one, which retiming breaks. Logic equivalence tools need sequential or retiming-aware modes, and usually guidance files written by the synthesis tool.
- Gate-level simulation and debug: probes, force statements and assertions bound to internal register names must be updated or limited to block boundaries.
- Reset: backward moves can create register states with no equivalent reset value. Tools either refuse such moves or need an option that relaxes reset equivalence, which must then be justified.
- DFT: scan insertion runs on the retimed netlist. Retimed registers are normal flops for scan, but test points or registers that DFT relies on should be excluded from moves.
Where retiming is limited
- Registers on clock-domain crossings, synchroniser chains and registers with timing exceptions (false paths, multicycle paths) should not be moved; most tools exclude them or let you mark them as fixed.
- Registers driving primary outputs or used by software (status and control registers) are often kept in place for predictability.
- Feedback loops limit what can be gained: a loop's register count is fixed, so its delay per register sets a floor on the clock period.
- Registers that differ in reset, enable or clock cannot always be merged or moved across the same gate.
Common mistakes
- Expecting retiming to fix a timing problem that sits inside a feedback loop, such as an accumulator or a state machine's next-state logic.
- Binding assertions or debug probes to register names that will not survive synthesis.
- Enabling retiming on a block containing synchronisers without marking them as fixed.
- Forgetting to switch equivalence checking to a sequential mode and then chasing false mismatches.
FAQ
Does retiming change latency?
Not on its own. It only moves existing registers. Latency changes only when you ask the tool to add pipeline stages.
Is retiming the same as pipelining?
No. Pipelining adds registers and therefore latency. Retiming moves registers that already exist. The two are often combined: add stages, then retime them into the right places.
Do FPGA tools retime too?
Yes. FPGA synthesis and place-and-route tools from the major vendors offer register retiming, usually as an option, with the same limits on synchronisers, timing exceptions and initial values.


Your comments will be moderated before it appears here.