Exclusive branches can keep one adder each with an output mux. They can also share one adder with muxes on the differing inputs.
The shared form saves whole operators and pays muxes. The split form pays whole operators and one output mux.
Sharing wins on area exactly when the saved operator exceeds the added muxes. All numbers below are library units of the fictional course.lib, with the adder cost as a stated EXAMPLE parameter.
course.lib MUX2 area = 5 units per bit; EXAMPLE adder cost = 12 units per bit
8-bit adder A = 8 x 12 = 96 units; 8-bit 2:1 mux M = 8 x 5 = 40 units
case 1, branches a + b and a + c (operand a shared, one input muxed)
split: 2 x 96 + 40 = 232 units
shared: 96 + 40 = 136 units
saving = 232 - 136 = 96 units -> sharing wins on area
case 2, branches a + b and c + d (no shared operand, two inputs muxed)
split: 2 x 96 + 40 = 232 units
shared: 96 + 2 x 40 = 176 units
saving = 232 - 176 = 56 units -> sharing wins on area
case 3, two 8-bit ANDs with no shared operand (course.lib AND2 area = 3 units per bit)
operator = 8 x 3 = 24 units
split: 2 x 24 + 40 = 88 units
shared: 24 + 2 x 40 = 104 units
saving = 88 - 104 = -16 units -> sharing loses; keep the two gates
break-even for two-operand branches with both inputs muxed
saving per bit = operator cost per bit - 5 units, so sharing needs more than 5 units per bitThe share pass looks for mutually exclusive operators it can merge. The block below holds two exclusive adds around a shared operand.
Its assertions pin two adders before the pass, and two after. The tool left both adders in place instead of paying the input muxes, so the design keeps the split-form cost of 232 units from case 1 above.
read_verilog <<EOV
module share2(input [7:0] a, b, c, input sel, output [7:0] y);
reg [7:0] r;
always @* begin
if (sel) r = a + b;
else r = a + c;
end
assign y = r;
endmodule
EOV
hierarchy -top share2
proc
opt_clean
select -assert-count 2 t:$add
share
select -assert-count 2 t:$add
opt_clean
select -assert-count 2 t:$add
stat -liberty course.lib
log PASS: share pass keeps two exclusive addersThe shared path pays an input mux then the adder. The split path pays the adder then the output mux.
EXAMPLE delays: 8-bit adder 9.0 ns, 8-bit 2:1 mux 1.5 ns, EXAMPLE 100 MHz clock (10.0 ns period)
shared path = input mux + adder = 1.5 + 9.0 = 10.5 ns
split path = adder + output mux = 9.0 + 1.5 = 10.5 ns
both miss the 10.0 ns period by 0.5 ns; the mux sits on the path either wayDelay rarely decides sharing, so area decides. Real loads and fanout still vary by tool and library.
The program below replays every number in this lesson. Its nine self-checks repeat the hand arithmetic and the delay tie.
import sys
MUX_BIT = 5.0
ADD_BIT = 12.0
AND_BIT = 3.0
D_ADD = 9.0
D_MUX = 1.5
def split_area(op_bit, width):
return 2.0 * op_bit * width + MUX_BIT * width
def shared_area(op_bit, width, muxed_inputs):
return op_bit * width + muxed_inputs * MUX_BIT * width
def close(got, want):
return abs(got - want) < 1e-9
got = [
split_area(ADD_BIT, 8),
shared_area(ADD_BIT, 8, 1),
split_area(ADD_BIT, 8) - shared_area(ADD_BIT, 8, 1),
shared_area(ADD_BIT, 8, 2),
split_area(ADD_BIT, 8) - shared_area(ADD_BIT, 8, 2),
split_area(AND_BIT, 8) - shared_area(AND_BIT, 8, 2),
split_area(5.0, 8) - shared_area(5.0, 8, 2),
D_MUX + D_ADD,
D_ADD + D_MUX,
]
want = [232.0, 136.0, 96.0, 176.0, 56.0, -16.0, 0.0, 10.5, 10.5]
for i in range(len(got)):
if not close(got[i], want[i]):
print("FAIL: check " + str(i + 1) + " got " + str(got[i]) + " want " + str(want[i]))
sys.exit(1)
print("PASS: break-even model matches hand arithmetic on 9 checks")96 units. Split costs 232 units and shared costs 136 units, so sharing wins.
Sharing loses by 16 units: 88 split against 104 shared. Keep the two gates.
The split-form 232 units: two 96-unit adders plus one 40-unit output mux.