Verilog · Chapter 10.1 · Operators & Operands
Operator Precedence in Verilog — Which Operator Binds First
The moment an expression contains more than one operator, a question arises: which one binds first? In an add-then-multiply expression the multiply binds tighter, which is familiar from arithmetic. But Verilog has a long precedence ladder with surprises that catch every engineer at least once, such as equality binding tighter than bitwise AND, so a masked-equality check does not group the way most people expect. Precedence is the set of rules that decides this, and getting it wrong is uniquely punishing in hardware: the expression compiles, simulates, and silently builds the wrong gates. This lesson teaches the precedence ladder, associativity which decides how same-level operators group, the classic traps, and the one discipline that makes them all moot: parenthesize when you mix operator families. Precedence governs every multi-operator expression in the pages that follow.
Foundation18 min readVerilogOperator PrecedenceOperatorsExpressionsRTL Design
Chapter 10 · Section 10.1 · Operators & Operands
1. The Engineering Problem
An engineer writes what looks like an obvious status check — "assert match when the masked value equals the target":
assign match = value & mask == target; // intent: (value & mask) == targetIt compiles. It simulates. And it is wrong — silently. Verilog evaluates it as:
assign match = value & (mask == target); // what Verilog actually buildsbecause the equality operator == binds tighter than the bitwise AND &. So instead of "AND value with mask, then compare to target," the hardware compares mask to target first (producing a single bit), then ANDs that 1-bit result into value — a completely different circuit, with a different width and a different meaning. Nothing flags it: the syntax is legal, the simulation runs, and match is just quietly incorrect.
In hardware, a precedence mistake is not a crash — it is the wrong gates, built silently. The expression you wrote and the expression Verilog evaluated are different circuits, and only the precedence rules tell them apart.
Precedence is the bookkeeping that decides which operator binds first when several share an expression. It is invisible when you guess right and devastating when you guess wrong. This page makes the rules explicit — and then gives you the discipline that means you never have to rely on remembering them.
2. Mental Model — Precedence Is Binding Order
Visual A — precedence as binding order
a + b * c — precedence inserts invisible parentheses
data flow3. The Hardware View — Grouping Is Gate Structure
Precedence is not an abstract parsing detail; it picks the circuit. Take the §1 expression both ways:
(value & mask) == target value & (mask == target)
┌──────────────┐ ┌──────────────────────┐
value ─┐ mask ──┐
AND─→ N-bit ─┐ ==─→ 1-bit ─┐
mask ──┘ ==─→ match target ─┘ AND─→ match
target ────────────┘ value ─────────────┘
(AND feeds a comparator) (comparator feeds an AND)These are different schematics with different bit widths: the left compares two N-bit values; the right compares two values to produce one bit, then ANDs it into an N-bit operand. Same source tokens, opposite hardware — and the synthesizer builds exactly what precedence says, not what you meant. This is why a precedence bug is so dangerous: there is no error, no warning, just a circuit that computes the wrong function. The grouping is the gate structure.
4. The Precedence Ladder
Here is the Verilog operator precedence, highest (binds first) at the top to lowest (binds last) at the bottom, per IEEE 1364-2005. (Each operator's behaviour is its own sub-page; here only the order matters.)
| Precedence | Operators | Family | Associativity |
|---|---|---|---|
| 1 (tightest) | ! ~ + - (unary), & ~& | ~| ^ ~^ (reduction) | unary | right |
| 2 | ** | power | left |
| 3 | * / % | multiplicative | left |
| 4 | + - (binary) | additive | left |
| 5 | << >> <<< >>> | shift | left |
| 6 | < <= > >= | relational | left |
| 7 | == != === !== | equality | left |
| 8 | & (binary) | bitwise AND | left |
| 9 | ^ ^~ ~^ (binary) | bitwise XOR/XNOR | left |
| 10 | | (binary) | bitwise OR | left |
| 11 | && | logical AND | left |
| 12 | || | logical OR | left |
| 13 (loosest) | ?: | conditional | right |
Three facts on this ladder cause almost every precedence bug, and they are worth memorizing as surprises:
- Equality and relational bind tighter than bitwise
&/|/^(rows 6–7 above rows 8–10). This is the §1 trap:a & b == cisa & (b == c). - Bitwise
&binds tighter than^, which binds tighter than|(rows 8 → 9 → 10). Soa | b & cisa | (b & c). - Arithmetic binds tighter than shift (row 4 above row 5). So
a + b << 1is(a + b) << 1.
Unary operators are tightest and the conditional ?: is loosest — those two ends are intuitive. The trouble is all in the middle, where families that feel unordered actually have a strict rank.
Visual B — the surprise: == binds tighter than &
5. The Classic Traps
Four mixings account for the overwhelming majority of precedence bugs. Each is "compiles fine, builds wrong."
// TRAP 1 — bitwise vs equality (== is TIGHTER than &)
y = a & b == c; // → a & (b == c) ⚠ rarely what you want
y = (a & b) == c; // ← parenthesize for the usual intent
// TRAP 2 — bitwise OR vs AND (& is TIGHTER than |)
y = a | b & c; // → a | (b & c)
y = (a | b) & c; // ← parenthesize if you meant OR first
// TRAP 3 — arithmetic vs shift (+ is TIGHTER than <<)
y = a + b << 2; // → (a + b) << 2 ⚠ not a + (b << 2)
y = a + (b << 2); // ← parenthesize for word-offset addressing
// TRAP 4 — logical AND vs OR (&& is TIGHTER than ||)
y = p || q && r; // → p || (q && r)
y = (p || q) && r; // ← parenthesize if you meant OR firstThere is no need to memorize which way each one breaks. The pattern is simpler: any time two different operator families share an expression, the grouping is non-obvious — so make it explicit with parentheses. The traps exist only for code that relied on the bare ladder.
6. Associativity — Same-Level Ties
When two operators have the same precedence, associativity decides grouping. Almost all Verilog binary operators are left-associative:
y = a - b - c; // left-assoc → (a - b) - c (correct subtraction order)
y = a + b - c; // left-assoc → (a + b) - cThe one that bites is the conditional operator ?:, which is right-associative. A chain of ternaries nests to the right:
y = s1 ? a : s2 ? b : c; // right-assoc → s1 ? a : (s2 ? b : c)
// reads as: if s1 → a; else if s2 → b; else c (a priority mux — usually intended)The right-associative grouping is what makes a ternary chain behave like an if/else-if priority ladder — usually exactly what you want, which is why nested ternaries are a common mux idiom. Knowing it is right-associative is what lets you read the chain correctly.
7. The Parenthesization Discipline
The precedence table is worth understanding, but you should not write code that depends on remembering it. The professional discipline is simple and absolute:
- Parenthesize whenever an expression mixes operator families.
(a & b) == c,a + (b << 2),(p || q) && r. The parentheses cost nothing, document intent, and make the grouping immune to the reader's (and your) memory of the ladder. - Rely on bare precedence only within one obvious family — pure arithmetic (
a + b - c), where the rules match universal intuition. Even there, parenthesize a multiply mixed with add if it aids reading. - Parentheses do not change hardware when they match the precedence —
a + (b * c)anda + b * cbuild the identical adder-after-multiplier. They are free clarity. When they don't match, they fix a bug.
The payoff: an engineer who parenthesizes mixed-family expressions never hits a precedence trap, and every reviewer can verify the grouping without consulting a table. This is why production RTL is dense with parentheses — not from doubt, but from discipline.
Visual C — parenthesize when families mix
The decision: bare precedence or parentheses?
data flow8. Worked Examples
Three expressions, read correctly via the ladder, then parenthesized for safety.
8.1 Example 1 — the mask-and-compare
// As written — equality binds tighter than &:
assign hit = addr & MASK == BASE; // → addr & (MASK == BASE) ⚠ wrong
// Parenthesized for the intended "mask then compare":
assign hit = (addr & MASK) == BASE; // ✓ AND first, then compareThe first line ANDs addr with a 1-bit comparison result — almost never the intent. The second builds the address-decode the engineer meant. This is the §1 bug, fixed by one pair of parentheses.
8.2 Example 2 — flag combination
// As written — & binds tighter than |:
assign go = start | busy & ready; // → start | (busy & ready)
// Parenthesized if "either start or busy, AND ready" was intended:
assign go = (start | busy) & ready; // ✓ different logicBoth are legal; they are different functions. The bare form gates ready only with busy; the parenthesized form gates it with the OR of start and busy. Only parentheses make clear which circuit you want.
8.3 Example 3 — word-offset address
// As written — + binds tighter than <<:
assign waddr = base + index << 2; // → (base + index) << 2 ⚠ shifts the sum
// Parenthesized for "scale the index to a word offset, then add the base":
assign waddr = base + (index << 2); // ✓ index*4 added to baseThe classic addressing bug: the engineer wanted index << 2 (multiply by 4 for word addressing) added to base, but bare precedence shifts the sum. The parentheses build the right address-generation hardware.
9. Industry Perspective
- Parenthesization is a coding-standard rule. Essentially every RTL style guide mandates parentheses for mixed-family expressions and forbids relying on bare precedence beyond simple arithmetic — exactly because precedence bugs are silent and the ladder is non-intuitive in the middle.
- Lint flags suspicious precedence. Standard lint decks warn on
&/|mixed with==/!=without parentheses, and on bitwise mixed with logical, catching the §5 traps automatically. A "confusing precedence" lint warning is treated as an error. - Precedence bugs are silent and expensive. Because the design compiles and simulates, a precedence error can survive to gate-level or even silicon if the affected case isn't exercised — among the more insidious RTL defects, and a frequent interview topic.
- Readability is correctness here. Parentheses that match precedence don't change the hardware but make review reliable — a reviewer should never have to mentally run the precedence table to verify a line.
10. Common Mistakes
- Assuming
&/|bind tighter than==/<— they bind looser;a & b == cisa & (b == c)(§1, DebugLab 1). - Assuming
|and&are equal —&binds tighter than|;a | b & cisa | (b & c)(DebugLab 2). - Assuming shift binds tighter than add — it binds looser;
a + b << 2is(a + b) << 2(DebugLab 3). - Reading a ternary chain left-associative —
?:is right-associative;s1 ? a : s2 ? b : cnests to the right. - Not parenthesizing mixed families — the root habit behind all of the above; the fix for every one.
11. Debugging Lab
Three operator-precedence debug post-mortems
12. Interview Q&A
13. Exercises
Exercise 1 — Insert the invisible parentheses
For each expression, rewrite it with the parentheses Verilog inserts by precedence: (a) a + b * c; (b) a & b | c; (c) a == b & c; (d) a + b << c; (e) s1 ? x : s2 ? y : z.
Exercise 2 — Spot the trap
Each line's intent is given in the comment. State whether the bare expression matches the intent, and if not, give the parenthesized fix.
y = en & mode == 3'b101; // intent: en AND (mode == 5)
y = a | b | c & d; // intent: a OR b OR (c AND d)
y = base + offset << 4; // intent: base + (offset << 4)
y = ready && valid || flush; // intent: (ready AND valid) OR flushExercise 3 — Read the ladder
Using the §4 table, state which operator binds first in each: (a) a << 2 + 1; (b) ~a == b; (c) a < b == c; (d) &data | flag (note: &data is unary reduction).
Exercise 4 — Fix the address bug
The block below computes a byte address as base + row*stride + col, where stride is a power of two applied via a shift. Identify the precedence bug and give the corrected, fully-parenthesized expression.
assign byte_addr = base + row << STRIDE_LOG2 + col;14. Summary
Operator precedence decides which operator binds first when several share an expression — and because a Verilog expression is hardware, the grouping precedence chooses is the gate structure that gets built. A precedence mistake is a silent wrong circuit, not a crash.
The core ideas:
- Higher precedence binds tighter, forming sub-expressions first — Verilog inserts invisible parentheses (
a + b * c→a + (b * c)). - The ladder is non-intuitive in the middle. The traps: equality/relational bind tighter than bitwise
&/|/^(a & b == c→a & (b == c));&binds tighter than|; arithmetic binds tighter than shift;&&binds tighter than||. - Associativity breaks same-level ties — almost everything is left-associative; the conditional
?:is right-associative (which makes ternary chains read as priority ladders). - Grouping is gate structure — the same tokens grouped two ways build two different circuits with different widths.
The discipline this page instils:
- Parenthesize whenever operator families mix —
(a & b) == c,a + (b << 2),(p || q) && r. This neutralizes every trap and never has to consult the table. - Rely on bare precedence only within pure arithmetic, where the rules match intuition.
- Parentheses that match precedence are free clarity; where they don't, they fix a silent bug.
You can now read and safely write multi-operator expressions. The chapter continues into the operators themselves — first the computation workhorses: Chapter 10.2 Arithmetic Operator drills + - * / % ** and the datapath hardware (adders, multipliers, dividers) they build, including the width and signedness rules the Chapter 10 overview previewed.
Related Tutorials
- Verilog Operators & Operands — Chapter 10 overview; the operator families this precedence ladder orders.
- reg — Chapter 5.2.1; the operands these expressions combine.
- parameter — Chapter 6.1; constant operands often appearing in precedence-sensitive expressions.
- RTL Designing — Chapter 3; the combinational hardware these grouped expressions become.