Verilog · Chapter 10.2 · Operators & Operands
Arithmetic Operators in Verilog — Adders, Multipliers & the Width Rules
The arithmetic operators are the datapath workhorses, covering addition, subtraction, multiplication, division, modulo, and power. Each builds a recognizable and costed piece of hardware, so a plus becomes an adder, a star becomes a multiplier, and a divide becomes an expensive divider. Writing one is committing that hardware to silicon. Two rules govern every arithmetic expression and cause most arithmetic bugs. The first is width, since adding two N-bit values can produce an N-plus-one-bit result, and if the destination is too narrow the carry is silently dropped as overflow. The second is signedness, because signed and unsigned arithmetic build different hardware and give different answers on negative or large values. This page drills each operator, the hardware it infers, the width rule, the signedness rules, and the synthesis limits that make some operators expensive or restricted.
Foundation20 min readVerilogArithmetic OperatorsDatapathOverflowSignednessRTL Design
Chapter 10 · Section 10.2 · Operators & Operands
1. The Engineering Problem
An engineer sums two 8-bit sensor readings:
reg [7:0] a, b, sum;
// ...
sum = a + b; // a = 200, b = 100200 + 100 = 300. But sum reads back 44. Nothing crashed; no warning fired. The 8-bit sum can hold only 0–255, and 300 does not fit — the result overflowed, wrapping to 300 − 256 = 44. The carry-out bit, the 9th bit that would have made the answer correct, had nowhere to go because the destination was 8 bits wide.
An N-bit adder produces an (N+1)-bit result, but Verilog evaluates the addition in the width of its destination — so if the destination is too narrow, the carry is silently dropped and the arithmetic is wrong. This is overflow, and it is the most common arithmetic bug in RTL.
The fix is not to "be careful" but to size the result correctly: a 9-bit sum holds 300 exactly. Arithmetic operators are not free abstract math — they are sized datapath hardware, and the width of every result is a design decision. This page teaches the operators, the hardware they build, and above all the width and signedness rules that decide whether your arithmetic is correct.
2. Mental Model — Arithmetic Operators Are Sized Datapath Hardware
Visual A — arithmetic operators build the datapath
Each arithmetic operator → a datapath primitive
data flow3. The Hardware View — Cost and Width per Operator
Make the model concrete with the hardware and width each operator produces:
| Operator | Operation | Hardware | Result width needed | Cost |
|---|---|---|---|---|
+ | add | adder | max(N,M) +1 (carry) | moderate, grows with width |
- | subtract | subtractor | max(N,M) +1 (borrow) | moderate |
* | multiply | multiplier | N + M (full product) | large (≈ width²) |
/ | divide | divider | width of dividend | huge; restricted |
% | modulo | divider (remainder) | width of divisor | huge; restricted |
** | power | (constant only) | grows fast | impractical unless constant |
Two practical takeaways jump out and recur through this page:
*is expensive,/and%are worse. A multiplier dominates a block's area; a general divider is so large that RTL avoids/and%with variable divisors entirely. The cheap operators (+,-) are the everyday datapath; the expensive ones are used deliberately, often pipelined or replaced.- Results grow. Add needs one more bit; multiply needs the sum of widths. The destination must be sized to hold the grown result, or the high bits vanish (§5).
4. The Six Operators
A brief tour — each operator's behaviour and the hardware it builds. (Combine them with the precedence rules from 10.1 when several share an expression.)
sum = a + b; // ADD → adder; result can need one extra bit (carry)
diff = a - b; // SUBTRACT → subtractor; two's-complement; borrow/underflow
prod = a * b; // MULTIPLY → multiplier; product needs width(a)+width(b) bits
quot = a / b; // DIVIDE → integer division, truncates toward zero
rem = a % b; // MODULO → remainder; sign follows the dividend
pwr = a ** b; // POWER → a^b; practical only for constant operandsSpecifics worth knowing:
/truncates toward zero —7 / 2is3, not3.5(integer division). The fractional part is discarded.%(modulo) returns the remainder, and its sign follows the dividend —-7 % 3is-1,7 % -3is1. (Signed modulo is a common surprise.)**(power) is rarely synthesizable with variable operands; its main RTL use is2 ** Nfor a constantNto compute a power-of-two value at elaboration.x/zpoison arithmetic — if any operand bit isxorz, the entire result isx(4-state propagation, §7). A single unknown bit corrupts the whole sum.
5. The Width Rule — Sizing Results to Prevent Overflow
This is the rule that prevents the §1 bug. Verilog evaluates an arithmetic expression in a context width that is the maximum of the operand widths and the destination width. So the destination size matters:
reg [7:0] a, b;
reg [7:0] narrow;
narrow = a + b; // context = 8 bits → carry DROPPED → overflow (the §1 bug)
reg [8:0] wide;
wide = a + b; // context = 9 bits → operands zero-extended to 9 → carry KEPT ✓
reg [15:0] product;
product = a * b; // 8×8 → product needs 16 bits; [15:0] holds it fully ✓The rules to apply every time:
- Add/subtract: to keep the carry/borrow, the destination must be one bit wider than the operands.
8 + 8 → 9 bits. - Multiply: the destination must be the sum of the operand widths.
8 × 8 → 16 bits. - If you intend to discard the overflow (e.g. modular wrap-around is desired), keeping the narrow destination is fine — but make it deliberate, with a comment, not an accident.
The mechanism: the destination width joins the context, so a wider destination causes the operands to be zero/sign-extended before the operation, preserving the high bits. A narrow destination truncates after. Sizing the result is how you choose correctness over silent wrap.
Visual B — the overflow width rule
6. Signedness — Signed vs Unsigned Arithmetic
Whether operands are signed or unsigned changes the hardware and the answer. By default a plain reg/wire is unsigned; integer and reg signed/wire signed are signed.
reg signed [7:0] s;
reg [7:0] u;
s = -1; // signed: bit pattern 1111_1111, VALUE = -1
u = -1; // unsigned: same bits 1111_1111, but VALUE = 255
// Mixing types: if ANY operand is unsigned, the operation is UNSIGNED.
reg signed [8:0] r;
r = s + u; // u is unsigned → the whole add is unsigned → surprising resultThe rules that matter:
- A value's type decides its meaning. The bit pattern
1111_1111is−1if signed,255if unsigned. Same bits, different value, different arithmetic. - Mixed signedness goes unsigned. If any operand in an arithmetic expression is unsigned, Verilog performs the whole operation as unsigned — a frequent source of "why is my signed math wrong?" bugs. Keep operands consistently typed, or use
$signed(...)/$unsigned(...)to force an interpretation. - Signed operations sign-extend; unsigned zero-extend. When widened to the context, a signed operand replicates its sign bit; an unsigned one fills with zeros — which changes the high bits of the result.
Signedness is where the data-type choices of Chapter 5 (reg vs reg signed vs integer) start governing computation. Getting it wrong produces a design that works on small positive values and fails on negatives or large values — a corner-case bug that escapes light testing.
7. The 4-State Effect and Synthesizability
Two more practical realities of arithmetic in real RTL.
x/z poison the whole result. Arithmetic is not bit-local — a carry propagates — so a single x bit in an operand makes the entire result x:
reg [7:0] a = 8'b0000_00xx; // two unknown low bits
reg [7:0] b = 8'd1;
// a + b → 8'bxxxx_xxxx — the unknown propagates through the carry chainThis is why an unreset register feeding an adder yields all-x downstream (a common simulation symptom).
Synthesizability is uneven across the operators:
+,-,*— synthesizable; the tool builds optimized adders and multipliers./,%— synthesizable only for constant power-of-two divisors, where they reduce to a shift (/) or a bit-mask (%). With a variable or non-power-of-two divisor, general division is either rejected or builds an enormous, slow divider; production RTL avoids it (using iterative dividers, reciprocal multiplication, or restructuring the math).**— synthesizable essentially only with constant operands (e.g.2 ** Nfor constantN); variable power is not practical hardware.
The discipline: reach for +, -, * freely (mindful of *'s cost); treat /, %, and variable ** as red flags that need a power-of-two constant or a deliberate divider strategy.
Visual C — the arithmetic cost ladder
Arithmetic cost — cheap to prohibitive
data flow8. Worked Examples
Three datapath sizings — a safe adder, a multiplier width, and a power-of-two divide.
8.1 Example 1 — an adder that keeps its carry
module add8 (
input [7:0] a, b,
output [8:0] sum // 9 bits — holds the carry
);
assign sum = a + b; // context = 9 bits; sum[8] is the carry-out
endmoduleThe output is 9 bits, one wider than the operands, so the carry of an 8-bit add is preserved — 200 + 100 gives 9'd300, not 8'd44. sum[8] is the carry-out, often used as an overflow flag. Sizing the result is the whole fix.
8.2 Example 2 — a multiplier's full product
module mul8 (
input [7:0] a, b,
output [15:0] product // 16 bits = 8 + 8
);
assign product = a * b; // full product; 255 * 255 = 65025 fits in 16 bits
endmoduleAn 8×8 multiply can produce up to 255 × 255 = 65025, which needs 16 bits — the sum of the operand widths. A narrower output would truncate the upper product bits. (And note: this one * is a real, sizeable multiplier on the chip — far more area than the adder above.)
8.3 Example 3 — divide by a power of two
// Dividing by a constant power of two is FREE — it's a shift.
assign quotient = value / 4; // synthesizes to value >> 2 (cheap)
assign remainder = value % 4; // synthesizes to value & 2'b11 (cheap)
// Dividing by a VARIABLE is the expensive/restricted case:
// assign q = value / divisor; // ⚠ huge divider or non-synthesizable/ 4 and % 4 (constant power of two) reduce to a shift and a mask — essentially free wiring. The same operators with a variable divisor would build a full divider or fail synthesis. The lesson: the divisor, not just the operator, sets the cost.
9. Industry Perspective
- Datapath sizing is a core RTL skill. Engineers track the width of every intermediate result and size registers to hold (or deliberately discard) the grown value. Overflow analysis — "can this sum exceed the destination?" — is routine design work.
- Multipliers and dividers are managed carefully. A
*is often pipelined to meet timing; a/is almost never written directly with a variable divisor — it is replaced by shift-add, reciprocal multiplication, or a dedicated divider IP. Casually writing/is a flag in code review. - Signed/unsigned discipline prevents corner-case bugs. Mixed-signedness arithmetic is a classic silent defect; teams type operands consistently and use
$signed/$unsignedexplicitly where interpretation must be forced. - Lint and width checks catch overflow. Mature flows flag width-mismatched arithmetic assignments (a wide result into a narrow target) so unintended truncation surfaces at lint, not in silicon.
10. Common Mistakes
- Overflow / dropped carry — assigning an
N+1-bit sum to anN-bit destination; the carry vanishes silently (§1/§5, DebugLab 1). - Under-sizing a product — assigning an
N×Mmultiply to fewer thanN+Mbits; the upper product bits truncate (§5). - Variable division — writing
a / bora % bwith a non-constant or non-power-of-two divisor; huge or non-synthesizable (§7, DebugLab 2). - Mixed signedness — combining signed and unsigned operands; the operation goes unsigned and negatives misbehave (§6, DebugLab 3).
- Ignoring
*cost — treating multiply as free like in software; it is a large hardware block.
11. Debugging Lab
Three arithmetic-operator debug post-mortems
12. Interview Q&A
13. Exercises
Exercise 1 — Size the results
For each, give the minimum destination width to avoid overflow: (a) a + b with a, b 12-bit; (b) a * b with a 10-bit, b 6-bit; (c) the sum of four 8-bit values; (d) a - b with a, b 8-bit (when is the extra bit needed?).
Exercise 2 — Predict the overflow
Given reg [3:0] a = 4'd9, b = 4'd8; reg [3:0] s; s = a + b;, what value does s hold and why? What destination width makes it correct?
Exercise 3 — Replace the divide
Rewrite each to avoid a general divider: (a) q = x / 8; (b) r = x % 16; (c) y = x / 2 + x / 4 (powers of two). State the shift/mask each becomes.
Exercise 4 — Find the signedness bug
The block intends signed subtraction but misbehaves for negative a. Identify the bug and fix it.
module sub (input signed [7:0] a, input [7:0] b, output signed [8:0] d);
assign d = a - b;
endmodule14. Summary
The arithmetic operators — +, -, *, /, %, ** — build the datapath: adders, subtractors, multipliers, and dividers. Each is sized, costed hardware, and two rules govern every arithmetic expression.
The core ideas:
- Each operator infers costed hardware —
+/-moderate,*large (≈ width²),//%huge and restricted,**constant-only. - The width rule prevents overflow —
N-bit+N-bit needsN+1bits (carry);N×Mmultiply needsN+Mbits (full product). Verilog evaluates in the destination's context width, so a narrow destination silently drops the high bits. - Signedness changes the hardware and the answer — signed vs unsigned differ; if any operand is unsigned the whole operation is unsigned; signed extends the sign bit, unsigned zero-extends.
x/zpoison the result — one unknown bit propagates through the carry chain to corrupt every bit.- Synthesis is uneven —
+/-/*build real adders/multipliers;//%are cheap only for constant powers of two (shift/mask) and otherwise avoided; variable**is impractical.
The discipline this page instils:
- Size every result to hold its grown value (
+→ +1 bit,*→ sum of widths), or discard overflow deliberately. - Keep operands consistently signed or unsigned; force with
$signed/$unsignedwhen needed. - Treat
*as a real cost and bare//%(variable divisor) as red flags — use shifts for power-of-two divides.
You can now build correct, correctly-sized datapath arithmetic. The chapter continues with the logic operators — first the whole-operand boolean kind, which beginners constantly confuse with the bitwise kind: Chapter 10.3 Logical Operator drills &&, ||, ! — operators that reduce each operand to true/false — and how they differ in hardware and result from the bitwise operators of 10.4.
Related Tutorials
- Operator Precedence — Chapter 10.1; how arithmetic operators bind when mixed with others.
- Verilog Operators & Operands — Chapter 10 overview; the operator-as-hardware model this page applies to the datapath.
- integer — Chapter 5.2.2; the signed 32-bit type whose signedness governs arithmetic.
- Scalar vs Vector vs Arrays — Chapter 5.2.4; the vector widths that determine arithmetic result sizing.