Operation | Safe full-precision width |
|---|---|
Unsigned add W + W | W+1 |
Signed add W + W | W+1 |
Unsigned multiply A x B | A+B |
Signed multiply A x B | A+B |
Sum N same-width terms | W + ceil(log2 N) |
MAC of N products | A+B+ceil(log2 N), adjusted for numeric bounds |
Applying the rule to the running design gives a 32-bit product and a 42-bit worst-case accumulator for 1024 terms. That width becomes the input contract for the later quantizer rather than being rediscovered inside the MAC.
Read each bar as one signal; bits left of the dashed line are integer bits (sign included), bits right of it are fraction bits.
Multiply: widths add, 16 + 16 = 32. The fraction grows from 15 to 30 bits, so the product's binary point moves right.
Accumulate: the fraction stays at 30 bits; only the integer part grows, by ceil(log2 1024) = 10 bits.
Contract: the quantizer later receives all 42 bits and drops 15 fraction bits at one place.
SystemVerilog expression sizing can truncate before assignment. Extend operands into the intended result width before adding, and store multiplication in a full-width signed destination.
// snippet: explicit extension avoids expression-width surprises
logic signed [15:0] a, b;
logic signed [16:0] sum_full;
assign sum_full = {a[15], a} + {b[15], b};
localparam int PW = AW + BW;
logic signed [PW-1:0] product_full;
assign product_full = $signed(x) * $signed(y);Bounds can justify fewer bits than the worst-case formula, but that is an architectural assumption and belongs in an assertion and reference model.
The full product needs 32 bits and 1024 terms add 10 growth bits, so start with 42 bits before considering tighter value bounds.