Skip to content
DenseChip
Learn
PracticeCode labInterview

DenseChip

Navigate

Menu

LearnPracticeCode labInterview

Tools

All toolsSV editorCode repoResume
DenseChip

Practice, courses, and chip-design tools in one focused workspace.

LearnPracticeCode labInterviewToolsLearning paths

© 2026 DenseChip

AboutContactPrivacyTerms
Skip to lesson

Course contents

  • RTL Foundations57
  • Combinational Design58
  • Sequential Design61
    • Flip-Flops and Registers
    • Counters and Timers
    • Shift Registers and LFSRs
    • Pipelining
    • Stalls, Bubbles and Hazards
      • retiming and hazard management
      • Structural Hazard and Stall Logic
      • Pipeline Flush on Branch Resolve
      • Retiming-Friendly Coding Notes
      • Data Hazards and Forwarding
      • A Round-Robin Arbiter from a Priority Encoder
      • Key takeaways
      • Common pitfalls
      • Worked Examples and Waveforms
      • Corner Cases and Stress Tests
      • Integration and Sign-off Notes
    • Retiming
  • FSM & Control66
  • Timing & Clocking64
  • Reset & CDC58
  • Memory & Buses94
  • Advanced RTL60
  • Senior & Interview175
  • Glossary and Notation
Loading lesson

‹Stalls, Bubbles and Hazards
  1. Topics
  2. RTL Design
  3. Sequential Design
  4. Stalls, Bubbles and Hazards
  5. Retiming-Friendly Coding Notes

Part 3 · Sequential · Intermediate

Retiming-Friendly Coding Notes

Coding habits that let synthesis move registers across logic safely.

On this page

    Why this matters

    Retiming can balance stages for you only if the code lets it: unnecessary enables and resets pin registers in place. Adding output ranks first changes the latency contract.

    After this lesson you can: write a pipeline that a synthesis tool can retime, and separate adding register ranks from the legal movement of registers.

    Same pipeline, two codings

    Retiming moves registers across combinational logic to balance stage delays; the outputs stay the same. A tool can only do it where the code leaves the registers free to move. Here is one three-rank multiply pipeline written two ways.

    retime_blocked.sv
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    // Before: every datapath register is reset and written only when its valid bit is high.
    module retime_blocked (
      input  logic        clk, rst_n,
      input  logic        in_v,
      input  logic [7:0]  a, b,
      output logic        out_v,
      output logic [15:0] y
    );
      logic        v1, v2;
      logic [7:0]  a_r, b_r;
      logic [15:0] mul_r;
    
      always_ff @(posedge clk or negedge rst_n)
        if (!rst_n) begin
          v1 <= 0; v2 <= 0; out_v <= 0;
          a_r <= '0; b_r <= '0; mul_r <= '0; y <= '0;    // datapath reset
        end else begin
          v1 <= in_v; v2 <= v1; out_v <= v2;
          if (in_v) begin a_r <= a; b_r <= b; end        // enable mux on every data bit
          if (v1)   mul_r <= a_r * b_r;
          if (v2)   y     <= mul_r;
        end
    endmodule
    design.sv
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    // snippet
    // After: only the valid bits are reset; the data registers are written every cycle.
    always_ff @(posedge clk or negedge rst_n)
      if (!rst_n) begin v1 <= 0; v2 <= 0; out_v <= 0; end
      else begin v1 <= in_v; v2 <= v1; out_v <= v2; end
    
    always_ff @(posedge clk) begin                      // no reset, no enable
      a_r   <= a;
      b_r   <= b;
      mul_r <= a_r * b_r;
      y     <= mul_r;
    end

    Both versions give the same y whenever out_v is high; consumers ignore y otherwise. The difference is what the tool may do with the 48 data flops. In the second version they are plain D flip-flops with one clock and nothing attached, so the tool may slide the mul_r and y ranks into the multiplier to balance the two stages. In the first, every data flop carries a hold mux and a reset value, and both pin it in place. The full second version is under Testbench below.

    What pins a register

    Two things that stop retiming: an enable mux whose feedback loop ties the flop to its own output, and two flops with different reset behaviour that cannot become one flop after the gate they feed

    Read each panel as a register the tool would like to move, and the part of the circuit that forbids it.

    • Enable mux: the flop's output loops back into its own input. Moving the flop means moving the loop with it, which many flows will not do.

    • Mixed reset: moving r1 and r2 forward across f merges them into one flop. Its reset value would be f of 0 and an unknown, which is not defined.

    • The fix for both is in the second coding above: no enable on free-running data, and no reset on data registers.

    Coding habits

    Habit

    Why it helps

    Reset only control registers; leave datapath registers unreset

    Registers with different reset values cannot be moved across each other freely.

    Use one clock and one reset style per pipeline

    Registers in different domains are never retimed together.

    Write datapath registers unconditionally and qualify their use with valid; keep enables for stalls and held state

    An if (valid) write still infers an enable. Unconditional writes avoid that mux where valid already marks the data. Stall and state retention still need enables. Extra enable logic can restrict retiming depending on the tool flow.

    Keep control and data in the same stage count

    A valid bit with fewer stages than its data breaks alignment after retiming.

    Do not put keep or dont_touch on pipeline registers

    The tool is forbidden from moving them.

    Avoid initial values that matter

    Power-up values must match after moving a register.

    Adding ranks is a separate step

    1. Add ranks. N extra output ranks add N cycles of consumption latency. Extend the valid delay by the same N and update consumers and the reference model.

    2. Let the tool move them. Legal retiming then redistributes those registers while keeping the new latency and a consistent initial state.

    The listing below already contains the extra output rank, so its contract is three-cycle latency. A supported synthesis flow may redistribute it when retiming is enabled and the register, reset and boundary constraints permit.

    Warning

    Limits. Coding style alone does not prove that registers moved or that timing improved: enable a supported transformation and read its log. Options and legal cell forms differ by flow; for one concrete reference, Yosys ABC documentation distinguishes mapping engines and sequential-cell restrictions. Compare boundary behaviour before and after, then compare constrained input, internal and output setup/hold reports at the same corners and loads. Retiming also renames or removes internal registers, so debug probes and gate-level internal signals change; check equivalence at the outputs.

    Check yourself Why should the valid-bit pipeline have exactly the same number of stages as its data?

    Because valid marks which data slots are real. If the two paths differ, valid will point at the wrong data (or a bubble) after the data moves.

    Testbench

    The RTL below has three register ranks: capture, multiply, and one extra output rank. Accept at edge 0, update product after edge 1, update y/out_v after edge 2, consume at edge 3. The pre-edge checker independently predicts valid on every cycle (including bubbles), checks data and exact three-cycle latency, and drains. Directed isolated/back-to-back offers precede random traffic.

    retime_friendly.sv
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    // The extra output rank changes the contract to three-cycle consumption latency.
    // A supported tool may redistribute y's register while preserving this contract.
    module retime_friendly (
      input  logic        clk, rst_n,
      input  logic        in_v,
      input  logic [7:0]  a, b,
      output logic        out_v,
      output logic [15:0] y
    );
      logic        v1, v2;
      logic [7:0]  a_r, b_r;
      logic [15:0] mul_r;
    
      always_ff @(posedge clk or negedge rst_n)
        if (!rst_n) begin v1 <= 0; v2 <= 0; out_v <= 0; end
        else begin v1 <= in_v; v2 <= v1; out_v <= v2; end   // control path has the SAME 3 stages as data
    
      always_ff @(posedge clk) begin                       // datapath: no reset, no enable
        a_r   <= a;
        b_r   <= b;
        mul_r <= a_r * b_r;
        y     <= mul_r;                                      // extra stage left for the tool to redistribute
      end
    endmodule
    

    Remember

    • Write data every cycle and qualify its use with valid, rather than gating the data write.

    • Adding N output ranks adds N cycles of latency; extend the valid delay and the consumers.

    • Retiming moves register names, so check equivalence at the outputs.

    How this was checked

    The testbench checks the authored RTL contract. It does not perform or prove a tool retiming transformation.

    No mapped netlist, static timing report or measured frequency gain is supplied.

    Keep going

    • Interview: run a mock interview on this topic

    Related topics

    • Retiming

    Previous

    160 / 694

    Next: Data Hazards and Forwarding

    Up next

    Up nextData Hazards and Forwarding

    Sign in to save progress

    Practice this lessonReport an error

    See also

    • retiming and hazard management
    • Structural Hazard and Stall Logic
    • A Round-Robin Arbiter from a Priority Encoder