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

AboutRoadmapCareersInternshipsFAQContactPrivacyTerms
Skip to lesson

Course contents

  • Logic Synthesis Glossary
  • Logic Synthesis Cheatsheet
  • Synthesis Foundations14
  • Logic Optimisation and Mapping20
  • Constraints and Timing15
  • Sequential, FSM and Memory19
    • Flops, Resets and Enables
    • FSM Synthesis
    • Memory and Macro Inference
    • Retiming and Pipelining
      • What Retiming Moves
      • Latency, Throughput and Register Count
      • Reset, Initial State and Equivalence
      • Pipelining a Long Path by Hand
      • A Retiming Model and the Tool Flow
  • Datapath Synthesis14
  • Area, Power and Test14
  • Verification, ECO and Handoff15
  • Flows and Debugging15
  • Interview and Judgment11
Loading lesson

←Retiming and Pipelining
  1. Topics
  2. Logic Synthesis
  3. Sequential, FSM and Memory
  4. Retiming and Pipelining
  5. Pipelining a Long Path by Hand
On this page

    Part 4 of 9, lesson 18 of 19Part 4 of 9/Sequential, FSM and Memory/Lesson 18 of 19Intermediate

    Pipelining a Long Path by Hand

    Splitting a long combinational path into balanced stages with valid bits.

    A long path only gets faster when the cut removes logic from the critical path, and the valid bit is what makes the faster pipeline usable. This page measures both halves: stage delays and proof at the right latency.

    After this lesson you can: cut a datapath into stages with matching valid bits, and prove the pipeline against a reference at its latency.

    Cutting the long path

    Pipelining inserts registers into combinational logic. Unlike retiming, it adds latency: each new register stage adds one cycle, and inputs that join late must be delayed to meet their stage. The cut points should balance the stages, because the slowest stage sets the period and an uneven split wastes the faster stages. The three-stage datapath below multiplies two pairs, adds the products, then adds a fifth input; a three-bit valid shift register walks alongside and marks which stages hold real results.

    Predict first The unpipelined datapath reports one delay and the three stages report three smaller ones. Does the pipelined period beat the unpipelined delay, and by how much?

    It beats it by the ratio of the two recordings: the unpipelined arrival against the slowest stage arrival, a small win here because the middle stage keeps both a multiply and an add. The proof below then checks something stronger than speed: every valid output equals the reference at the right latency.

    yosys
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    read_verilog <<EOV
    module s1(input [5:0] a, b, output [11:0] p);
      assign p = a * b;
    endmodule
    module s2(input [11:0] p, input [5:0] c, d, output [11:0] s);
      assign s = p + c * d;
    endmodule
    module s3(input [11:0] s, input [5:0] e, output [11:0] y);
      assign y = s + e;
    endmodule
    module pipe3(input clk, input rst, input [5:0] a, b, c, d, e, input v_in, output [11:0] y, output v_out);
      reg [11:0] p, s, yq;
      reg [2:0] v;
      always @(posedge clk) begin
        if (rst) begin p <= 0; s <= 0; yq <= 0; v <= 0; end
        else begin
          p <= a * b;
          s <= p + c * d;
          yq <= s + e;
          v <= {v[1:0], v_in};
        end
      end
      assign y = yq;
      assign v_out = v[2];
    endmodule
    module ref3(input clk, input rst, input [5:0] a, b, c, d, e, input v_in, output [11:0] y, output v_out);
      reg [5:0] a2, b2, a3, b3, c1, d1;
      reg [11:0] od;
      reg [2:0] vv;
      wire [11:0] yc;
      assign yc = a3 * b3 + c1 * d1 + e;
      always @(posedge clk) begin
        if (rst) begin a2 <= 0; b2 <= 0; a3 <= 0; b3 <= 0; c1 <= 0; d1 <= 0; od <= 0; vv <= 0; end
        else begin
          a2 <= a; b2 <= b; a3 <= a2; b3 <= b2; c1 <= c; d1 <= d;
          od <= yc;
          vv <= {vv[1:0], v_in};
        end
      end
      assign y = od;
      assign v_out = vv[2];
    endmodule
    module pipe3w(input clk, input rst, input [5:0] a, b, c, d, e, input v_in, output [11:0] y, output v_out);
      wire [11:0] yi;
      wire vi;
      pipe3 p (.clk(clk), .rst(rst), .a(a), .b(b), .c(c), .d(d), .e(e), .v_in(v_in), .y(yi), .v_out(vi));
      assign y = vi ? yi : 12'd0;
      assign v_out = vi;
    endmodule
    module ref3w(input clk, input rst, input [5:0] a, b, c, d, e, input v_in, output [11:0] y, output v_out);
      wire [11:0] yi;
      wire vi;
      ref3 r (.clk(clk), .rst(rst), .a(a), .b(b), .c(c), .d(d), .e(e), .v_in(v_in), .y(yi), .v_out(vi));
      assign y = vi ? yi : 12'd0;
      assign v_out = vi;
    endmodule
    module pcomb(input [5:0] a, b, c, d, e, output [11:0] y);
      assign y = a * b + c * d + e;
    endmodule
    EOV
    hierarchy -check
    proc
    opt
    techmap
    opt
    design -save all
    hierarchy -check -top s1
    tee -o ltp_s1.txt ltp
    tee -o abc_s1.txt abc -liberty course.lib -constr typical.constr -D 100000
    tee -o stat_s1.txt stat -liberty course.lib
    design -load all
    hierarchy -check -top s2
    tee -o ltp_s2.txt ltp
    tee -o abc_s2.txt abc -liberty course.lib -constr typical.constr -D 100000
    tee -o stat_s2.txt stat -liberty course.lib
    design -load all
    hierarchy -check -top s3
    tee -o ltp_s3.txt ltp
    tee -o abc_s3.txt abc -liberty course.lib -constr typical.constr -D 100000
    tee -o stat_s3.txt stat -liberty course.lib
    design -load all
    hierarchy -check -top pipe3
    tee -o stat_p3.txt stat
    select -assert-count 39 t:$_SDFF_PP0_
    design -load all
    miter -equiv pipe3w ref3w miter
    hierarchy -check -top miter
    flatten
    tee -o equiv_p3.txt sat -seq 6 -prove trigger 0 -prove-skip 1 -verify -set-init-zero -set in_rst 0 -set-at 1 in_rst 1
    design -load all
    hierarchy -check -top pcomb
    tee -o ltp_comb.txt ltp
    tee -o abc_comb.txt abc -liberty course.lib -constr typical.constr -D 100000
    tee -o stat_comb.txt stat -liberty course.lib
    select -assert-none t:$mul t:$add
    log PASS: three stages timed and pipeline proved over steps 2 to 6

    What the three stages recorded

    Each stage was timed on its own top, with the same library and load. The three ABC arrivals are 2084.11, 2637.22 and 1170.26 ps over 19, 24 and 10 topological levels; the unpipelined function needs 2835.42 ps over 26 levels. The pipelined period is the slowest stage, so the measured speedup is 1.08x: a small win, and the chart names the culprit. Stage 2 keeps both a multiply and an add, so the cut barely shortens the critical path. A better cut would isolate the second multiply into its own stage; the method below stays correct either way, and the placement decides what it earns.

    Two independent metrics agree on the ranking, which is worth a pause. The topological level counts come from the generic netlists before any library mapping, while the picosecond arrivals come from ABC after mapping against the teaching library; neither knows about the other, yet both name stage 2 the slowest by a clear margin. When two such different lenses agree, the bottleneck is in the structure, not the measurement. That agreement is also what makes the modest speedup trustworthy: it is not a modelling artifact but the recorded consequence of leaving a multiply and an add in the same stage.

    Recorded stage delays: the highlighted stage is the slowest and sets the period. Read exact arrivals, including the unpipelined datapath, off the table below.

    Stage

    Logic

    Topological levels

    ABC delay (ps)

    ABC area

    1

    a * b

    19

    2084.11

    532

    2

    p + c * d

    24

    2637.22

    715

    3

    s + e

    10

    1170.26

    137

    Unpipelined

    a * b + c * d + e

    26

    2835.42

    1358

    Valid bits and flush

    The pipeline holds 39 flops across its data and valid registers, for 3 cycles of added latency. The reference model delays the early inputs to match: a and b wait two cycles, c and d wait one, e joins at once, and a three-deep valid shift marks the complete result. The wrappers mask both outputs with their valid bits, so fill cycles compare as 0 against 0; the check proves the valid-gated streams equal on steps 2 through 6 with reset asserted at step 1. With that short window only steps 5 and 6 carry valid results, so the proof checks two valid samples plus a masked fill, not a sustained rate. The rate evidence is the testbench below: 32 directed patterns plus a flush, with its own PASS line printed only when every valid output matches the skewed sum.

    pipe3.sv
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    module pipe3(input clk, input rst, input [5:0] a, b, c, d, e, input v_in, output [11:0] y, output v_out);
      reg [11:0] p, s, yq;
      reg [2:0] v;
      always @(posedge clk) begin
        if (rst) begin p <= 0; s <= 0; yq <= 0; v <= 0; end
        else begin
          p <= a * b;
          s <= p + c * d;
          yq <= s + e;
          v <= {v[1:0], v_in};
        end
      end
      assign y = yq;
      assign v_out = v[2];
    endmodule

    Break it: compare at the wrong latency

    The second run below is the changed variant: both sides keep their valid-mask wrappers, and the only change is the reference latency, 2 instead of 3, with the same reset at step 1. The proof fails with a found model, and the recorded trigger stays high across steps 4, 5, 6: under changing inputs the valid outputs never line up: the masked fill still agrees, then the streams diverge once valid data emerges and stay diverged into steady state. Latency is not a comment in the header; it is the number the comparison is calibrated to, and 2 against 3 never passes.

    yosys
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    read_verilog <<EOV
    module pipe3(input clk, input rst, input [5:0] a, b, c, d, e, input v_in, output [11:0] y, output v_out);
      reg [11:0] p, s, yq;
      reg [2:0] v;
      always @(posedge clk) begin
        if (rst) begin p <= 0; s <= 0; yq <= 0; v <= 0; end
        else begin
          p <= a * b;
          s <= p + c * d;
          yq <= s + e;
          v <= {v[1:0], v_in};
        end
      end
      assign y = yq;
      assign v_out = v[2];
    endmodule
    module ref2(input clk, input rst, input [5:0] a, b, c, d, e, input v_in, output [11:0] y, output v_out);
      reg [11:0] p, s;
      reg [1:0] vv;
      always @(posedge clk) begin
        if (rst) begin p <= 0; s <= 0; vv <= 0; end
        else begin
          p <= a * b;
          s <= p + c * d;
          vv <= {vv[0], v_in};
        end
      end
      assign y = s + e;
      assign v_out = vv[1];
    endmodule
    module ref2w(input clk, input rst, input [5:0] a, b, c, d, e, input v_in, output [11:0] y, output v_out);
      wire [11:0] yi;
      wire vi;
      ref2 r (.clk(clk), .rst(rst), .a(a), .b(b), .c(c), .d(d), .e(e), .v_in(v_in), .y(yi), .v_out(vi));
      assign y = vi ? yi : 12'd0;
      assign v_out = vi;
    endmodule
    module pipe3w2(input clk, input rst, input [5:0] a, b, c, d, e, input v_in, output [11:0] y, output v_out);
      wire [11:0] yi;
      wire vi;
      pipe3 p (.clk(clk), .rst(rst), .a(a), .b(b), .c(c), .d(d), .e(e), .v_in(v_in), .y(yi), .v_out(vi));
      assign y = vi ? yi : 12'd0;
      assign v_out = vi;
    endmodule
    EOV
    hierarchy -check
    proc
    opt
    techmap
    opt
    miter -equiv pipe3w2 ref2w miter_b
    hierarchy -check -top miter_b
    flatten
    tee -o equiv_lat2.txt sat -seq 6 -prove trigger 0 -prove-skip 1 -set-init-zero -set in_rst 0 -set-at 1 in_rst 1
    log PASS: wrong-latency reference comparison recorded

    Common mistakes

    A valid bit that skips a stage marks stale data as fresh. Shift one valid bit per data stage, clear all of them on reset, and mask the outputs before any comparison: the proof above only passes because the wrappers do.

    Latency cost and balance of the split.

    • Weak answer: pipelining is free speedup; the valid bits are just documentation.

    • Strong answer: three new stages add exactly 3 cycles of latency here, and the recorded slowest stage sets the period against the unpipelined arrival, a measured 1.08x. The valid-gated proof passes on steps 2 through 6 while the 2-cycle reference fires the trigger across steps 4, 5, 6: speed claims mean nothing until the latency they assume is proved.

    • Follow-up: the testbench passes but the proof fails. Where do you look? At the valid alignment: simulation checks only what it drives, while the proof quantifies over every input trace the skew can distinguish.

    Check yourself The slowest stage reports 2637.22 ps and the unpipelined datapath reports 2835.42 ps. What sets the pipelined period, and what is the measured speedup?

    The slowest stage sets it, so the period is 2637.22 ps against 2835.42 ps unpipelined: 1.08x. The gain is small because stage 2 keeps both a multiply and an add.

    Check yourself The wrong-latency reference fails while the 3-cycle one passes. What does that prove about the added latency?

    That the pipeline really adds 3 cycles: only the comparison calibrated to 3 agrees once valid data emerges, and any other number diverges there and stays diverged.

    • The slowest stage sets the period; an unbalanced cut wastes the faster stages.

    • One valid bit per data stage, cleared on reset, and outputs masked before comparison.

    • Latency is the number the comparison is calibrated to; any other number fails.

    Rule of thumb: balance the stages first on the recorded arrivals, then prove at exactly that latency before claiming speed.

    How this was checked

    Stage levels, ABC delays and areas, flop counts, both sat outcomes, and the simulation PASS line on this page come from recorded runs of the blocks shown: one Yosys 0.70 run times all three stages plus the unpipelined function against course.lib with typical.constr and proves the valid-gated pipeline over steps 2 to 6, a second Yosys 0.70 run records the wrong-latency failure, and the testbench runs under the course simulators. The library is fictional, so absolute delays and areas are teaching units; the balance is the point.

    • Interview: run a mock interview on this topic

    Related topics

    • Reset, initial state and equivalence

    • Pipelining

    Previous

    69 / 139

    Next: A Retiming Model and the Tool Flow

    Up next

    Up nextA Retiming Model and the Tool Flow

    Sign in to save progress

    Practice this lessonPrevious lessonReport an error

    See also

    • What Retiming Moves
    • Latency, Throughput and Register Count