Menu

Verilog Operators: Arithmetic, Comparison, Logical, and Conditional

The core operators in Verilog - arithmetic, comparison, logical, and the conditional ?: - with the rules and gotchas around mixed widths and signedness.

This page includes runnable editors - edit, run, and see output instantly.

What "Operator" Means in Verilog

Operators take signal-bearing expressions and produce new signal-bearing expressions. a + b is hardware - the synthesizer turns it into an adder. a == b is hardware - it builds a comparator. None of this is "code that runs"; it's all "circuits the operator describes".

The full operator menu is small enough to memorize. This doc covers the arithmetic, comparison, logical, and conditional operators. Bitwise and Reduction covers the bit-level operators that don't have direct software analogs.

Arithmetic: + - * / %

Three things to know:

  1. Division is integer division. 7/2 is 3, not 3.5. Verilog doesn't have a float type (well, real, but that's simulation-only).
  2. Division and modulo are expensive in hardware. A /4 is fine (the synthesizer turns it into a right shift), but a /n for runtime-variable n builds a multi-cycle divider you almost never want.
  3. Overflow wraps. Adding two N-bit unsigned values that exceed N bits truncates to N bits. If you want to capture the carry, declare the result one bit wider:
wire [7:0] a, b;
wire [8:0] sum = a + b;   // 9-bit sum captures carry out

Comparison: == != < > <= >=

All six comparison operators return a single-bit result: 1 if true, 0 if false, x if either operand has an x or z bit. To handle the x/z case, reach for === and !== (the case-equality operators) covered in X and Z Values.

<= here is the comparison operator (less-than-or-equal). The same characters also mean non-blocking assignment in a procedural block (q <= d). Context tells them apart: inside an expression it's a comparison; on the left of a value being assigned, it's the assignment. The overlap is confusing but unambiguous to the parser.

Logical: && || !

Logical operators treat their operands as booleans (anything non-zero is true, zero is false) and produce a single-bit result:

The crucial distinction to internalize: logical && is not bitwise &.

4'b1100 && 4'b0011   // 1 (both are non-zero, logical AND is true)
4'b1100 &  4'b0011   // 4'b0000 (no bit positions are 1 in both)

Same for || vs |. Use logical in if conditions and ?: predicates; use bitwise when you want each bit position to AND/OR independently.

&& and || are short-circuit in the C sense: the right-hand side isn't evaluated if the result is already determined by the left. This matters for testbenches that check pointers or function calls; it rarely matters for synthesizable RTL where everything happens in parallel anyway.

The Conditional Operator: ?:

The ternary is your best friend for building muxes and conditional expressions:

The pattern cond ? a : b is a 1-bit-select 2-to-1 multiplexer in hardware. Chaining them is fine for 3- and 4-way selects, but past that a case statement (covered in Case Statement) is dramatically more readable.

The ?: operator is also the standard way to give a register a default value that you only override sometimes:

next_value = update ? new_data : next_value;

Width Rules: The Beginner Pitfall

Verilog's width rules for arithmetic operators are notoriously surprising. The short version:

  • The width of the result is the maximum of the widths of the inputs.
  • If the result is assigned to something wider, it gets zero-extended (or sign-extended if signed).
  • The width of intermediate computations is also the result width, which means overflow happens in the intermediate.

The first multiplication overflows because the operand widths are both 8 bits, the result width is therefore 8 bits, and 40000 doesn't fit. The second works because we explicitly widened a before multiplying.

The fix-line techniques:

  • Use the concatenation operator {8'b0, a} to zero-extend.
  • Use $unsigned(a) or $signed(a) to influence interpretation.
  • Cast intermediate expressions to wider widths when in doubt.

Operator Precedence

You won't memorize the table, but you'll lean on a few rules:

  • *, /, % bind tighter than +, -.
  • Comparison binds tighter than logical (&&, ||).
  • The conditional operator ?: has very low precedence - usually need parens around its branches in larger expressions.
  • When in doubt, parenthesize. The simulator doesn't charge by the character.
out = (mode == 2'd0) ? a + b : a - b;   // clearer with parens

What Comes Next

You've covered every operator that looks like its software cousin. The next doc - Bitwise and Reduction - handles the operators that don't have direct software analogs: bitwise AND/OR/XOR per bit, plus the reduction forms that collapse a whole vector to a single bit.

Frequently Asked Questions

What operators does Verilog support?

Verilog has four broad operator families: arithmetic (+, -, *, /, %), comparison (==, !=, <, >, <=, >=), logical (&&, ||, !), and bitwise/reduction (&, |, ^, ~, ~&, ~|, ~^). Plus shift operators (<<, >>, <<<, >>>), concatenation {}, replication {N{...}}, and the conditional ?:.

What is the difference between && and & in Verilog?

&& is logical AND - it treats each operand as a boolean (zero or non-zero) and returns a 1-bit result. & is bitwise AND - it pairs up bits position by position. 4'b1100 && 4'b0011 is 1 (both are non-zero); 4'b1100 & 4'b0011 is 4'b0000. Use && in conditions, & for bit-level operations.

How does division work in Verilog?

Integer division - the fractional part is discarded. 7 / 2 is 3, not 3.5. The % operator gives the remainder: 7 % 2 is 1. Division by zero produces x (unknown) in simulation. Both operators are technically synthesizable but produce very large, slow hardware on FPGAs and ASICs - avoid them in synthesizable paths unless the divisor is a power of 2.

What is the conditional operator in Verilog?

?: is the ternary or conditional operator, copied from C. cond ? a : b evaluates to a if cond is true (non-zero) and b otherwise. It's the single most-used construct for multiplexer-like logic: out = sel ? in1 : in0 builds a 2-to-1 mux. Chains of ?: build wider muxes, though case is usually clearer past two inputs.

Coddy programming languages illustration

Learn to code with Coddy

GET STARTED