Multipliers, MACs and the long path
Measure what a multiplier costs, find where the longest path of a multiply-accumulate unit really runs, and place the register that shortens it.
- Lesson
- R4.5
- Time
- 30 min
- Runs on
- Your browser
- Check
- 5 questions
A multiply-accumulate unit measured 39 levels of logic on this flow. Its designer blamed the 8 x 8 multiplier, which is 30 levels alone, and put a register after it. The count fell to 35, not 30. The accumulator's adder now held the longest path, and no register can go inside it.
| Version | Cells | Flops | Levels of logic on this flow | Longest path runs |
|---|---|---|---|---|
| one stage: multiply and add | 445 | 21 | 39 | from a[2] to acc[19] |
| a register after the multiplier | 456 | 38 | 35 | from acc[1] to acc[19] |
| also a register inside the multiplier | 474 | 63 | 35 | still inside the accumulator |
| the 8 x 8 multiplier alone, registered | 346 | 16 | 30 |
Measured with Yosys 0.70 on this flow. The path ends were read from its ltp -noff report.
What a multiplier costs
A multiplier forms one partial product per bit of b: a copy of a, shifted, kept only where that bit of b is 1. Rows of adders then sum the partial products. An 8 x 8 multiplier has eight rows, and a 16 x 16 has sixteen rows of twice the width.
| Operator | Cells | Levels of logic on this flow |
|---|---|---|
| 4 x 4 multiplier | 64 | 12 |
| 8 x 8 multiplier | 330 | 30 |
| 12 x 12 multiplier | 802 | 45 |
| 16 x 16 multiplier | 1477 | 62 |
| 8 x 8 multiplier, both operands signed | 395 | 30 |
| 8-bit adder, 8-bit sum | 34 | 14 |
| 16-bit adder, 16-bit sum | 77 | 30 |
| 32-bit adder, 32-bit sum | 172 | 57 |
Measured with Yosys 0.70, each operator in a module of its own. Doubling the width multiplies the cells by about four and a half and the levels by about two.
Two things stand out. The cells grow with the square of the width, because rows and row width both double. And on this flow an adder is nearly as deep as a multiplier of the same output width: a 16-bit adder is 30 levels, the 8 x 8 multiplier 30 too. The carry has to reach the top bit either way.
Sign is the other trap. A multiply is signed only when both operands are signed; one unsigned operand makes the whole operation unsigned. Then -3 is read as 253, and the upper half of the product comes out wrong.
Where the long path of a MAC runs
A multiply-accumulate unit (MAC) adds each product into a running sum, the accumulator. Its output feeds its own adder, so the adder and the accumulator form a loop. The question is which part of the circuit sets the levels. Ask the tool: Yosys's ltp names the cells on the longest path, and the Gates tab shows its length.
- One stage: 39 levels
With no register in between, Yosys merges the multiply and the add into one multiply-accumulate cell. The longest path starts at
a[2]and ends atacc[19]: 39 levels, fewer than 30 plus 35.Follow the calls, values and waits
- data 3 x 4: a, b → multiplier
- data 12: adder → acc
- The cut: 35 levels
A register after the multiplier splits the path. The multiplier stage is 30 levels. The accumulator stage, from
accaround through the adder and back, is 35, so 35 is the new count. - The loop
The adder reads the old accumulator and writes the new one. A register inside that loop would make the adder add an older sum, so the loop's 35 levels stay whatever you do to the multiplier.
Follow the calls, values and waits
- data acc: acc → adder
Read the diagram as text
- a, b: 8 bits each
- multiplier: 30 levels alone
- prod_q: 16 flops, cut only
- adder: 20 bits
- acc: 20 flops
- a, b → multiplier: operands
- multiplier → prod_q: product
- prod_q → adder: prod_q
- adder → acc: sum
- acc → adder: old acc
Measured with Yosys 0.70. A further register inside the multiplier leaves the count at 35.
Why are 30 and 35 not 65? The low bits of the product are ready early, and the adder starts its carry on them while the high bits are still forming. Yosys builds the multiply and the add as one array, so the two depths overlap. Levels of a chain are measured, never added.
- Cycle 0
Edge 2 takes the pair (3, 4) and stores 12 in
prod_q. The accumulator has not seen it yet. - Cycle 1
Edge 3 adds 12 into
accand raisesacc_valid. Two edges from taking the pair to adding it: latency 2. - Cycle 2
The gap in the input reaches the accumulator one edge later. Here
accstill adds the (5, 6) pair taken at edge 3. - Cycle 3
The valid bit of the gap arrives now:
acc_validis 0 andaccholds 42. Without the valid pipe, the adder would use the wrong cycle's valid.
Measured on the lesson's bench, edges 2 to 8, with the product register added.
The lab: cut the MAC and measure
One stage
acc_valid <= in_valid;
if (in_valid)
acc <= acc + AW'(a * b);
445 cells, 21 flops, 39 levels. Latency 1.
A register after the multiplier
prod_q <= a * b;
prod_valid <= in_valid;
acc_valid <= prod_valid;
if (prod_valid)
acc <= acc + AW'(prod_q);
456 cells, 38 flops, 35 levels. Latency 2, same sums.
| Port | Direction | Width | Meaning |
|---|---|---|---|
| clk | input | 1 | clock |
| rst_n | input | 1 | reset, asynchronous, active-low |
| in_valid | input | 1 | a and b hold a pair this cycle |
| a, b | input | [W-1:0] | the operands |
| acc | output | [AW-1:0] | the running sum: every pair with in_valid = 1 adds a x b into acc |
| acc_valid | output | 1 | 1 after the edge that adds a pair |
- Latency
- 1 as shipped; 2 after the cut, with the valid bit moving through the same stages
- Parameters
W= 8, the operand width;AW= 2W + 4, the accumulator width- Corner cases
- a gap in the input; 255 x 255; four bits of headroom, so up to 16 full-scale products before acc wraps
Run the starter: five pairs, none wrong, final acc 65223, latency 1. Synthesize: 39 levels. Add prod_q and prod_valid as in the right panel and run again. The sums stay the same, the latency becomes 2, and the levels drop to 35. Measured: forget the valid pipe and four of the five sums are wrong.
Then set W to 16 and Synthesize both versions: 1696 cells and 69 levels in one stage, 1734 cells and 64 levels with the cut. The wider accumulator's loop again sets the count.
Asked in interviews
How do you speed up a MAC?
Put a register between the multiplier and the accumulator and move the valid bit with it. That takes the multiply out of the accumulator's cycle. After that the accumulator's adder, in its loop, is the floor; measured here, 39 levels became 35.
What does a multiplier cost?
Cells grow with the square of the width and depth with the width. Measured on this flow: 330 cells and 30 levels at 8 x 8, 1477 and 62 at 16 x 16. A constant operand or a smaller width is the cheap fix.
Where is the critical path in a datapath, and how do you find it?
Ask the tool. A static timing report names the slowest path in a real flow; here Yosys's
ltplists the cells on the longest path. It often ends somewhere unexpected, such as an accumulator rather than the multiplier.
Try it
Each pair is multiplied and added at the edge that takes it: 12 at edge 2, 12 + 30 = 42 at edge 3, and 65123 after 255 x 255. The gap at edge 4 leaves the sum alone.
With the product register the same lines appear one edge later, the last line reads latency 2, and Synthesize reports 35 levels instead of 39.
Synthesize the design to measure its cells and inspect the mapped gates.
e0 in_valid=0 a=0 b=0 acc=0 acc_valid=0 e1 in_valid=0 a=0 b=0 acc=0 acc_valid=0 e2 in_valid=1 a=3 b=4 acc=12 acc_valid=1 e3 in_valid=1 a=5 b=6 acc=42 acc_valid=1 e4 in_valid=0 a=0 b=0 acc=42 acc_valid=0 e5 in_valid=1 a=7 b=8 acc=98 acc_valid=1 e6 in_valid=1 a=255 b=255 acc=65123 acc_valid=1 e7 in_valid=1 a=10 b=10 acc=65223 acc_valid=1 e8 in_valid=0 a=0 b=0 acc=65223 acc_valid=0 e9 in_valid=0 a=0 b=0 acc=65223 acc_valid=0 e10 in_valid=0 a=0 b=0 acc=65223 acc_valid=0 5 pairs, 0 wrong, final acc=65223, latency 1Original design · 445 cells · 21 flip-flops · 0 latches · 39 logic levels. Synthesis uses Yosys; counts depend on the toolchain.
Read included file: tb.sv
`timescale 1ns/1ps
module tb;
logic clk = 0;
always #5 clk = ~clk; // rising edges at 5, 15, 25, ...
logic rst_n = 1; // falls at 1 ns: a real edge, so an asynchronous reset acts before any clock
int edge_n = -1;
always @(posedge clk) edge_n = edge_n + 1;
logic in_valid = 0;
logic [7:0] a = 0, b = 0;
logic [19:0] acc;
logic acc_valid;
logic [19:0] sum = 0; // the model: the running sum, 20 bits like acc
logic [19:0] want[$]; // the sum the design should show for each pair taken
int took[$]; // the edge that took each pair
int pairs = 0, wrong = 0, latency = 0;
task automatic send(input logic [7:0] x, input logic [7:0] y);
in_valid = 1; a = x; b = y; tick();
endtask
task automatic idle();
in_valid = 0; a = 0; b = 0; tick();
endtask
mac dut (.clk(clk), .rst_n(rst_n), .in_valid(in_valid), .a(a), .b(b), .acc(acc), .acc_valid(acc_valid));
// Advance to the next rising edge, wait for the design to settle, then print one line.
task automatic tick();
@(posedge clk); #1;
if (in_valid) begin sum = sum + a * b; want.push_back(sum); took.push_back(edge_n); end
if (acc_valid) begin
latency = edge_n - took.pop_front() + 1;
if (acc !== want.pop_front()) wrong++;
pairs++;
end
$display("e%0d in_valid=%b a=%0d b=%0d acc=%0d acc_valid=%b", edge_n, in_valid, a, b, acc, acc_valid);
endtask
initial begin
#1 rst_n = 0; tick(); tick(); rst_n = 1; // asserted at 1 ns, edges 0 and 1 in reset, released 1 ns after edge 1
send(3, 4); send(5, 6); idle(); send(7, 8); send(255, 255); send(10, 10);
idle(); idle(); idle();
$display("%0d pairs, %0d wrong, final acc=%0d, latency %0d", pairs, wrong, acc, latency);
$finish(0); // quiet: no "$finish called" line
end
endmodule
Check yourself
Remember
- A multiplier's cells grow with the square of its width and its levels with the width: 330 cells and 30 levels at 8 x 8, 1477 and 62 at 16 x 16, on this flow.
- A multiply is signed only when both operands are signed; one unsigned operand makes the whole operation unsigned.
- Find the long path by asking the tool, never by adding operator depths: the one-stage MAC is 39 levels, not 30 plus 35.
- A register between the multiplier and the accumulator takes the multiply out of the loop; the valid bit moves through the same stages, and the latency grows by one.
- The accumulator's adder sits in a loop, so it sets a floor that more multiplier stages cannot lower: 35 levels here.