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

  • RTL Foundations36
  • Combinational Design25
  • Sequential Design27
    • Flip-Flops and Registers
    • Counters and Timers
    • Shift Registers and LFSRs
    • Pipelining
      • Throughput and Latency
      • A Three-Stage Arithmetic Pipeline
      • Valid/Ready Flow Control
      • Pipelined MAC
      • Precision Verification
    • Stalls, Bubbles and Hazards
    • Retiming
  • FSM & Control27
  • Timing & Clocking39
  • Reset & CDC34
  • Memory & Buses57
  • Advanced RTL28
  • Senior & Interview76
  • Glossary and Notation
Loading lesson

←Pipelining
  1. Topics
  2. RTL Design
  3. Sequential Design
  4. Pipelining
  5. A Three-Stage Arithmetic Pipeline
On this page

    Part 3 of 9, lesson 16 of 27Part 3 of 9/Sequential/Lesson 16 of 27Intermediate

    A Three-Stage Arithmetic Pipeline

    A three-stage multiply-add pipeline with valid tracking and a randomized reference check.

    Register the operand c in stage 1 but use it directly in stage 3 and you add a different item's c to the product. Every operand needed later must travel with its data.

    After this lesson you can: split y = a*b + c into three stages, carry a valid bit and the late operand along, and predict when each result is consumed.

    Splitting y = a·b + c

    The multiply is the slow operation, so it gets its own stage. A register rank follows each of the three stages.

    Stage

    Work

    Registers written

    1

    Capture the operands

    a1, b1, c1, v1

    2

    Multiply

    p2 = a1 * b1, c2 = c1, v2

    3

    Add

    y = p2 + c2, out_v

    The three-stage multiply-add pipeline: rank 1 captures a, b and c, rank 2 holds the product p2 and the delayed c2, rank 3 holds y; the valid flops v1, v2 and out_v run in a lane of their own underneath

    Read each column as one stage: logic on the left of a register rank, the rank itself on the right. The bottom lane is the valid bit for whatever sits in the column above it.

    • c rides along: c1 and c2 do no work. They exist only so c reaches the adder in the same cycle as its own product.

    • One valid flop per rank: v1, v2 and out_v line up with ranks 1, 2 and 3, so out_v is high exactly when y holds a real result.

    • Latency 3: an item needs three edges to reach y, and a new item can enter at every edge behind it.

    The operand c is unused until stage 3, so registers carry it alongside its product. A valid bit travels with the data and marks the cycles holding real results.

    Use edge 0 as the first acceptance. Nonblocking assignments capture operands after edge 0, update product and delayed c after edge 1, and update y and out_v after edge 2. The first result is then available during the interval before edge 3; a downstream register consumes it at edge 3. The waveform and scoreboard use this pre-edge consumption convention, not post-update availability.

    • Cycle 0: inputs a = 2, b = 3, c = 1 enter.

    • Cycle 1: the second operation (4, 5, 2) enters.

    • Cycle 2: in_v is low, a gap cycle; held values are ignored.

    • Cycle 3 (pre-edge): the third operation (7, 2, 3) enters; the first result 7 is consumed with out_v already high.

    • Cycle 4 (pre-edge): the second result 0x16 (22) is consumed.

    • Cycle 5: a gap cycle; out_v is low.

    • Cycle 6 (pre-edge): the third result 0x11 (17) is consumed.

    • Cycles 7–9: idle; out_v stays low. Unconditionally written y may change and is ignored.

    Reset only the valid bits. The data registers are always overwritten before they are used, and consumers must ignore y while out_v is low, so leaving data unreset is safe here. A data reset can add area and slow timing on many targets, though the exact cost depends on the device and reset network. It also leaves them free to be retimed later.

    Balancing the stages

    • Give the slow multiply its own stage; the add then has slack.

    • Split a slow multiplier further: partial products first, then compression and the final add (see the Wallace tree idea).

    • Trust the timing report for the critical stage, not intuition.

    Common mistakes

    If c were registered in stage 1 but used directly in stage 3 you would add a different item's c to the product. Every operand needed later must be delayed by the same number of stages as the data it belongs with. Mis-aligned pipelines are the most common pipelining bug.

    Check yourself In the waveform, why does y show x in the first cycles even though nothing is wrong?

    The datapath registers are not reset, so before the first valid data passes through them they hold unknown values. out_v is 0 then, which is what tells the consumer to ignore y.

    Testbench

    The waveform's three offers and gap are driven first, followed by randomized traffic checked against a queue-based reference. Each accepted input records its rising-edge cycle; the corresponding result must be consumed exactly three cycles later. The clock is 10 ns; monitors sample before NBA updates, and falling-edge stimulus avoids a sampling race.

    mac_pipe.sv
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    module mac_pipe (
      input  logic        clk, rst_n,
      input  logic        in_v,
      input  logic [3:0]  a, b, c,
      output logic        out_v,
      output logic [8:0]  y
    );
      // valid bits are reset; datapath registers are not (they are always written before use)
      logic        v1, v2;
      logic [3:0]  a1, b1, c1, c2;
      logic [7:0]  p2;
    
      always_ff @(posedge clk or negedge rst_n)
        if (!rst_n) begin v1 <= 1'b0; v2 <= 1'b0; out_v <= 1'b0; end
        else        begin v1 <= in_v; v2 <= v1;   out_v <= v2;   end
    
      always_ff @(posedge clk) begin
        a1 <= a;  b1 <= b;  c1 <= c;            // stage 1: capture
        p2 <= a1 * b1;  c2 <= c1;                // stage 2: multiply, carry c along
        y  <= p2 + {4'b0, c2};                   // stage 3: add
      end
    endmodule
    
    • Give the slow multiply its own stage; the add then has slack.

    • Delay each late operand by the same number of stages as the data it belongs with.

    • Reset only the valid bits; consumers ignore y while out_v is low.

    • Practice: Design a 3-stage pipelined ALU with stall support

    • Code lab: Pipelined multiplier stage

    • Interview: run a mock interview on this topic

    Related topics

    • Valid/ready flow control

    Previous

    77 / 350

    Next: Valid/Ready Flow Control

    Up next

    Up nextValid/Ready Flow Control

    Sign in to save progress

    Practice this lessonPrevious lessonReport an error

    See also

    • Pipelined MAC
    • Precision Verification
    Pre-edge samples: y = a*b + c, consumption latency 3 cycles; hexclkin_va247b352c123out_vy (hex)x71611
    Each column is sampled immediately before its rising edge. Inputs are accepted at edges 0, 1 and 3; downstream consumes results (7, 0x16 = 22, 0x11 = 17) at edges 3, 4 and 6. Register updates occur after the edge, between these observations.